All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-13  6:28 ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-13  6:28 UTC (permalink / raw)
  Cc: Archit Taneja, Vinay Simha BN, Sumit Semwal, John Stultz,
	Thierry Reding, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, David Airlie, Jonathan Cameron,
	Ralf Baechle, Shawn Guo, open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Add support for the JDI lt070me05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
    https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
    git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27

Other fixes folded in were provided
by Archit Taneja <archit.taneja@gmail.com>

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
[sumit.semwal: Ported to the drm/panel framework]
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
[jstultz: Cherry-picked to mainline, folded down other fixes
 from Vinay and Archit]
Signed-off-by: John Stultz <john.stultz@linaro.org>
---
 .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/gpu/drm/panel/Kconfig                      |  11 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
 5 files changed, 725 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
 create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644
index 0000000..35c5ac7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
@@ -0,0 +1,27 @@
+JDI model LT070ME05000 1920x1200 7" DSI Panel
+
+Basic data sheet is at:
+	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+
+This panel has video mode implemented currently in the driver.
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+
+Optional properties:
+- power-supply: phandle of the regulator that provides the supply voltage
+- reset-gpio: phandle of gpio for reset line
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+	dsi@54300000 {
+		panel: panel@0 {
+			compatible = "jdi,lt070me05000";
+			reg = <0>;
+
+			power-supply = <...>;
+			reset-gpio = <...>;
+			backlight = <...>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a580f3e..ec42bb4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -130,6 +130,7 @@ invensense	InvenSense Inc.
 isee	ISEE 2007 S.L.
 isil	Intersil
 issi	Integrated Silicon Solutions Inc.
+jdi	Japan Display Inc.
 jedec	JEDEC Solid State Technology Association
 karo	Ka-Ro electronics GmbH
 keymile	Keymile GmbH
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 1500ab9..f41690e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
 	  To compile this driver as a module, choose M here: the module
 	  will be called panel-sharp-lq101r1sx01.
 
+config DRM_PANEL_JDI_LT070ME05000
+	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
+	  command mode panel as found in Google Nexus 7 (2013) devices.
+	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+	  24 bit RGB per pixel.
+
 config DRM_PANEL_SHARP_LS043T1LE01
 	tristate "Sharp LS043T1LE01 qHD video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f277eed..e6c6fc8 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644
index 0000000..051aa1b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2015 InforceComputing
+ * Author: Vinay Simha BN <simhavcs@gmail.com>
+ *
+ * Copyright (C) 2015 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+/*
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ */
+struct jdi_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	/* TODO: Add backilght support */
+	struct backlight_device *backlight;
+	struct regulator *supply;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *vcc_gpio;
+
+	struct regulator *backlit;
+	struct regulator *lvs7;
+	struct regulator *avdd;
+	struct regulator *iovdd;
+	struct gpio_desc *pwm_gpio;
+
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct jdi_panel, base);
+}
+
+static char MCAP[2] = {0xB0, 0x00};
+static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
+static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
+
+static char interface_ID_setting[2] = {0xB4, 0x0C};
+static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
+
+static char tear_scan_line[3] = {0x44, 0x03, 0x00};
+
+/* for fps control, set fps to 60.32Hz */
+static char LTPS_timing_setting[2] = {0xC6, 0x78};
+static char sequencer_timing_control[2] = {0xD6, 0x01};
+
+/* set brightness */
+static char write_display_brightness[] = {0x51, 0xFF};
+/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
+static char write_control_display[] = {0x53, 0x24};
+/*
+ * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
+ * disable SRE(sunlight readability enhancement)
+ */
+static char write_cabc[] = {0x55, 0x00};
+/* for cabc mode 0x1(-15%) */
+static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
+/* for cabc mode 0x2(-40%) */
+static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
+/* for cabc mode 0x3(-54%) */
+static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
+/* for pwm frequency and dimming control */
+static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
+		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
+		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(10);
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_tear_on(dsi,
+					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+		if (ret < 0)
+			return ret;
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
+					     sizeof(tear_scan_line));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+		mdelay(10);
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting,
+					     sizeof(interface_setting));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		backlight_control4[18] = 0x04;
+		backlight_control4[19] = 0x00;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		MCAP[1] = 0x03;
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+	} else {
+		/*
+		 * TODO : need to verify panel settings when
+		 * dsi cmd mode supported for apq8064 - simhavcs
+		 */
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
+					     sizeof(interface_setting_cmd));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
+					     sizeof(interface_ID_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, DSI_control,
+					     sizeof(DSI_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
+					     sizeof(LTPS_timing_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
+					     sizeof(sequencer_timing_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control1,
+					     sizeof(backlight_control1));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control2,
+					     sizeof(backlight_control2));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control3,
+					     sizeof(backlight_control3));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_off(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(100);
+
+	return 0;
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (!jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("disable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = false;
+
+	return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (!jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("unprepare\n");
+
+	ret = jdi_panel_off(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+		return ret;
+	}
+
+	regulator_disable(jdi->supply);
+
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+
+	jdi->prepared = false;
+
+	return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("prepare\n");
+
+	ret = regulator_enable(jdi->iovdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->avdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->backlit);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->lvs7);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->supply);
+	if (ret < 0)
+		return ret;
+
+	msleep(20);
+
+	if (jdi->vcc_gpio) {
+		gpiod_set_value(jdi->vcc_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->reset_gpio) {
+		gpiod_set_value(jdi->reset_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->pwm_gpio) {
+		gpiod_set_value(jdi->pwm_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->enable_gpio) {
+		gpiod_set_value(jdi->enable_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	ret = jdi_panel_init(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to init panel: %d\n", ret);
+		goto poweroff;
+	}
+
+	ret = jdi_panel_on(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+		goto poweroff;
+	}
+
+	jdi->prepared = true;
+
+	return 0;
+
+poweroff:
+	regulator_disable(jdi->supply);
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("enable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_UNBLANK;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+		.clock = 155000,
+		.hdisplay = 1200,
+		.hsync_start = 1200 + 48,
+		.hsync_end = 1200 + 48 + 32,
+		.htotal = 1200 + 48 + 32 + 60,
+		.vdisplay = 1920,
+		.vsync_start = 1920 + 3,
+		.vsync_end = 1920 + 3 + 5,
+		.vtotal = 1920 + 3 + 5 + 6,
+		.vrefresh = 60,
+		.flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+		.disable = jdi_panel_disable,
+		.unprepare = jdi_panel_unprepare,
+		.prepare = jdi_panel_prepare,
+		.enable = jdi_panel_enable,
+		.get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+		{ .compatible = "jdi,lt070me05000", },
+		{ }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	jdi->mode = &default_mode;
+
+	/* lvs5 */
+	jdi->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	/* l17 */
+	jdi->backlit = devm_regulator_get(dev, "backlit");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
+	if (IS_ERR(jdi->lvs7))
+		return PTR_ERR(jdi->lvs7);
+
+	jdi->avdd = devm_regulator_get(dev, "avdd");
+	if (IS_ERR(jdi->avdd))
+		return PTR_ERR(jdi->avdd);
+
+	jdi->iovdd = devm_regulator_get(dev, "iovdd");
+	if (IS_ERR(jdi->iovdd))
+		return PTR_ERR(jdi->iovdd);
+
+	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->vcc_gpio)) {
+		dev_err(dev, "cannot get vcc-gpio %ld\n",
+			PTR_ERR(jdi->vcc_gpio));
+		jdi->vcc_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->vcc_gpio, 0);
+	}
+
+	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(jdi->reset_gpio));
+		jdi->reset_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->reset_gpio, 0);
+	}
+
+	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpio %ld\n",
+			PTR_ERR(jdi->enable_gpio));
+		jdi->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->enable_gpio, 0);
+	}
+
+	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->pwm_gpio)) {
+		dev_err(dev, "cannot get pwm-gpio %ld\n",
+			PTR_ERR(jdi->pwm_gpio));
+		jdi->pwm_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->pwm_gpio, 0);
+	}
+
+	/* we don't have backlight right now, proceed further */
+#ifdef BACKLIGHT
+	np = of_parse_phandle(dev->of_node, "backlight", 0);
+	if (np) {
+		jdi->backlight = of_find_backlight_by_node(np);
+		of_node_put(np);
+
+		if (!jdi->backlight)
+			return -EPROBE_DEFER;
+	}
+#endif
+
+	drm_panel_init(&jdi->base);
+	jdi->base.funcs = &jdi_panel_funcs;
+	jdi->base.dev = &jdi->dsi->dev;
+
+	ret = drm_panel_add(&jdi->base);
+	if (ret < 0)
+		goto put_backlight;
+
+	return 0;
+
+put_backlight:
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+
+	return ret;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+	if (jdi->base.dev)
+		drm_panel_remove(&jdi->base);
+
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi;
+	int ret;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
+			MIPI_DSI_MODE_VIDEO_HSA |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+	if (!jdi)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, jdi);
+
+	jdi->dsi = dsi;
+
+	ret = jdi_panel_add(jdi);
+	if (ret < 0)
+		return ret;
+
+	return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = jdi_panel_disable(&jdi->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+			ret);
+
+	drm_panel_detach(&jdi->base);
+	jdi_panel_del(jdi);
+
+	return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+	jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+	.driver = {
+		.name = "panel-jdi-lt070me05000",
+		.of_match_table = jdi_of_match,
+	},
+	.probe = jdi_panel_probe,
+	.remove = jdi_panel_remove,
+	.shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
+MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

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

* [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-13  6:28 ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-13  6:28 UTC (permalink / raw)
  Cc: Archit Taneja, Vinay Simha BN, Sumit Semwal, John Stultz,
	Thierry Reding, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, David Airlie, Jonathan Cameron,
	Ralf Baechle, Shawn Guo, open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Add support for the JDI lt070me05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
    https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
    git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27

Other fixes folded in were provided
by Archit Taneja <archit.taneja-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
[sumit.semwal: Ported to the drm/panel framework]
Signed-off-by: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
[jstultz: Cherry-picked to mainline, folded down other fixes
 from Vinay and Archit]
Signed-off-by: John Stultz <john.stultz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/gpu/drm/panel/Kconfig                      |  11 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
 5 files changed, 725 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
 create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644
index 0000000..35c5ac7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
@@ -0,0 +1,27 @@
+JDI model LT070ME05000 1920x1200 7" DSI Panel
+
+Basic data sheet is at:
+	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+
+This panel has video mode implemented currently in the driver.
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+
+Optional properties:
+- power-supply: phandle of the regulator that provides the supply voltage
+- reset-gpio: phandle of gpio for reset line
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+	dsi@54300000 {
+		panel: panel@0 {
+			compatible = "jdi,lt070me05000";
+			reg = <0>;
+
+			power-supply = <...>;
+			reset-gpio = <...>;
+			backlight = <...>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a580f3e..ec42bb4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -130,6 +130,7 @@ invensense	InvenSense Inc.
 isee	ISEE 2007 S.L.
 isil	Intersil
 issi	Integrated Silicon Solutions Inc.
+jdi	Japan Display Inc.
 jedec	JEDEC Solid State Technology Association
 karo	Ka-Ro electronics GmbH
 keymile	Keymile GmbH
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 1500ab9..f41690e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
 	  To compile this driver as a module, choose M here: the module
 	  will be called panel-sharp-lq101r1sx01.
 
+config DRM_PANEL_JDI_LT070ME05000
+	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
+	  command mode panel as found in Google Nexus 7 (2013) devices.
+	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+	  24 bit RGB per pixel.
+
 config DRM_PANEL_SHARP_LS043T1LE01
 	tristate "Sharp LS043T1LE01 qHD video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f277eed..e6c6fc8 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644
index 0000000..051aa1b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2015 InforceComputing
+ * Author: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * Copyright (C) 2015 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+/*
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ */
+struct jdi_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	/* TODO: Add backilght support */
+	struct backlight_device *backlight;
+	struct regulator *supply;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *vcc_gpio;
+
+	struct regulator *backlit;
+	struct regulator *lvs7;
+	struct regulator *avdd;
+	struct regulator *iovdd;
+	struct gpio_desc *pwm_gpio;
+
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct jdi_panel, base);
+}
+
+static char MCAP[2] = {0xB0, 0x00};
+static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
+static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
+
+static char interface_ID_setting[2] = {0xB4, 0x0C};
+static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
+
+static char tear_scan_line[3] = {0x44, 0x03, 0x00};
+
+/* for fps control, set fps to 60.32Hz */
+static char LTPS_timing_setting[2] = {0xC6, 0x78};
+static char sequencer_timing_control[2] = {0xD6, 0x01};
+
+/* set brightness */
+static char write_display_brightness[] = {0x51, 0xFF};
+/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
+static char write_control_display[] = {0x53, 0x24};
+/*
+ * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
+ * disable SRE(sunlight readability enhancement)
+ */
+static char write_cabc[] = {0x55, 0x00};
+/* for cabc mode 0x1(-15%) */
+static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
+/* for cabc mode 0x2(-40%) */
+static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
+/* for cabc mode 0x3(-54%) */
+static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
+/* for pwm frequency and dimming control */
+static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
+		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
+		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(10);
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_tear_on(dsi,
+					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+		if (ret < 0)
+			return ret;
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
+					     sizeof(tear_scan_line));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+		mdelay(10);
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting,
+					     sizeof(interface_setting));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		backlight_control4[18] = 0x04;
+		backlight_control4[19] = 0x00;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		MCAP[1] = 0x03;
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+	} else {
+		/*
+		 * TODO : need to verify panel settings when
+		 * dsi cmd mode supported for apq8064 - simhavcs
+		 */
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
+					     sizeof(interface_setting_cmd));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
+					     sizeof(interface_ID_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, DSI_control,
+					     sizeof(DSI_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
+					     sizeof(LTPS_timing_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
+					     sizeof(sequencer_timing_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control1,
+					     sizeof(backlight_control1));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control2,
+					     sizeof(backlight_control2));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control3,
+					     sizeof(backlight_control3));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_off(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(100);
+
+	return 0;
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (!jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("disable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = false;
+
+	return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (!jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("unprepare\n");
+
+	ret = jdi_panel_off(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+		return ret;
+	}
+
+	regulator_disable(jdi->supply);
+
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+
+	jdi->prepared = false;
+
+	return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("prepare\n");
+
+	ret = regulator_enable(jdi->iovdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->avdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->backlit);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->lvs7);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->supply);
+	if (ret < 0)
+		return ret;
+
+	msleep(20);
+
+	if (jdi->vcc_gpio) {
+		gpiod_set_value(jdi->vcc_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->reset_gpio) {
+		gpiod_set_value(jdi->reset_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->pwm_gpio) {
+		gpiod_set_value(jdi->pwm_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->enable_gpio) {
+		gpiod_set_value(jdi->enable_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	ret = jdi_panel_init(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to init panel: %d\n", ret);
+		goto poweroff;
+	}
+
+	ret = jdi_panel_on(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+		goto poweroff;
+	}
+
+	jdi->prepared = true;
+
+	return 0;
+
+poweroff:
+	regulator_disable(jdi->supply);
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("enable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_UNBLANK;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+		.clock = 155000,
+		.hdisplay = 1200,
+		.hsync_start = 1200 + 48,
+		.hsync_end = 1200 + 48 + 32,
+		.htotal = 1200 + 48 + 32 + 60,
+		.vdisplay = 1920,
+		.vsync_start = 1920 + 3,
+		.vsync_end = 1920 + 3 + 5,
+		.vtotal = 1920 + 3 + 5 + 6,
+		.vrefresh = 60,
+		.flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+		.disable = jdi_panel_disable,
+		.unprepare = jdi_panel_unprepare,
+		.prepare = jdi_panel_prepare,
+		.enable = jdi_panel_enable,
+		.get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+		{ .compatible = "jdi,lt070me05000", },
+		{ }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	jdi->mode = &default_mode;
+
+	/* lvs5 */
+	jdi->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	/* l17 */
+	jdi->backlit = devm_regulator_get(dev, "backlit");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
+	if (IS_ERR(jdi->lvs7))
+		return PTR_ERR(jdi->lvs7);
+
+	jdi->avdd = devm_regulator_get(dev, "avdd");
+	if (IS_ERR(jdi->avdd))
+		return PTR_ERR(jdi->avdd);
+
+	jdi->iovdd = devm_regulator_get(dev, "iovdd");
+	if (IS_ERR(jdi->iovdd))
+		return PTR_ERR(jdi->iovdd);
+
+	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->vcc_gpio)) {
+		dev_err(dev, "cannot get vcc-gpio %ld\n",
+			PTR_ERR(jdi->vcc_gpio));
+		jdi->vcc_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->vcc_gpio, 0);
+	}
+
+	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(jdi->reset_gpio));
+		jdi->reset_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->reset_gpio, 0);
+	}
+
+	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpio %ld\n",
+			PTR_ERR(jdi->enable_gpio));
+		jdi->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->enable_gpio, 0);
+	}
+
+	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->pwm_gpio)) {
+		dev_err(dev, "cannot get pwm-gpio %ld\n",
+			PTR_ERR(jdi->pwm_gpio));
+		jdi->pwm_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->pwm_gpio, 0);
+	}
+
+	/* we don't have backlight right now, proceed further */
+#ifdef BACKLIGHT
+	np = of_parse_phandle(dev->of_node, "backlight", 0);
+	if (np) {
+		jdi->backlight = of_find_backlight_by_node(np);
+		of_node_put(np);
+
+		if (!jdi->backlight)
+			return -EPROBE_DEFER;
+	}
+#endif
+
+	drm_panel_init(&jdi->base);
+	jdi->base.funcs = &jdi_panel_funcs;
+	jdi->base.dev = &jdi->dsi->dev;
+
+	ret = drm_panel_add(&jdi->base);
+	if (ret < 0)
+		goto put_backlight;
+
+	return 0;
+
+put_backlight:
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+
+	return ret;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+	if (jdi->base.dev)
+		drm_panel_remove(&jdi->base);
+
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi;
+	int ret;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
+			MIPI_DSI_MODE_VIDEO_HSA |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+	if (!jdi)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, jdi);
+
+	jdi->dsi = dsi;
+
+	ret = jdi_panel_add(jdi);
+	if (ret < 0)
+		return ret;
+
+	return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = jdi_panel_disable(&jdi->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+			ret);
+
+	drm_panel_detach(&jdi->base);
+	jdi_panel_del(jdi);
+
+	return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+	jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+	.driver = {
+		.name = "panel-jdi-lt070me05000",
+		.of_match_table = jdi_of_match,
+	},
+	.probe = jdi_panel_probe,
+	.remove = jdi_panel_remove,
+	.shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
  2016-04-13  6:28 ` Vinay Simha BN
@ 2016-04-13 13:49   ` Thierry Reding
  -1 siblings, 0 replies; 39+ messages in thread
From: Thierry Reding @ 2016-04-13 13:49 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Archit Taneja, Sumit Semwal, John Stultz, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, David Airlie,
	Jonathan Cameron, Ralf Baechle, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

[-- Attachment #1: Type: text/plain, Size: 16723 bytes --]

On Wed, Apr 13, 2016 at 11:58:04AM +0530, Vinay Simha BN wrote:
> Add support for the JDI lt070me05000 WUXGA DSI panel used in
> Nexus 7 2013 devices.
> 
> Programming sequence for the panel is was originally found in the
> android-msm-flo-3.4-lollipop-release branch from:
>     https://android.googlesource.com/kernel/msm.git
> 
> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
> file in:
>     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
> 
> Other fixes folded in were provided
> by Archit Taneja <archit.taneja@gmail.com>
> 
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> [sumit.semwal: Ported to the drm/panel framework]
> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
> [jstultz: Cherry-picked to mainline, folded down other fixes
>  from Vinay and Archit]
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> ---
>  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  drivers/gpu/drm/panel/Kconfig                      |  11 +
>  drivers/gpu/drm/panel/Makefile                     |   1 +
>  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>  5 files changed, 725 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

What's the difference between this and the patch you sent earlier? I'm
going to assume that the newer one is the correct patch, so I'll ignore
the previous patch.

> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> new file mode 100644
> index 0000000..35c5ac7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt

The binding documentation should be a separate patch.

> @@ -0,0 +1,27 @@
> +JDI model LT070ME05000 1920x1200 7" DSI Panel
> +
> +Basic data sheet is at:
> +	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> +
> +This panel has video mode implemented currently in the driver.

That's information irrelevant to the DT binding, since you're presumably
talking about the Linux drm/panel driver, whereas the DT binding is
supposed to specify the description of the panel hardware in OS-agnostic
terms.

> +Required properties:
> +- compatible: should be "jdi,lt070me05000"
> +
> +Optional properties:
> +- power-supply: phandle of the regulator that provides the supply voltage
> +- reset-gpio: phandle of gpio for reset line
> +- backlight: phandle of the backlight device attached to the panel
> +
> +Example:
> +
> +	dsi@54300000 {
> +		panel: panel@0 {
> +			compatible = "jdi,lt070me05000";
> +			reg = <0>;
> +
> +			power-supply = <...>;
> +			reset-gpio = <...>;
> +			backlight = <...>;
> +		};
> +	};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index a580f3e..ec42bb4 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -130,6 +130,7 @@ invensense	InvenSense Inc.
>  isee	ISEE 2007 S.L.
>  isil	Intersil
>  issi	Integrated Silicon Solutions Inc.
> +jdi	Japan Display Inc.
>  jedec	JEDEC Solid State Technology Association
>  karo	Ka-Ro electronics GmbH
>  keymile	Keymile GmbH

This should be a separate patch as well.

> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 1500ab9..f41690e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called panel-sharp-lq101r1sx01.
>  
> +config DRM_PANEL_JDI_LT070ME05000
> +	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
> +	  command mode panel as found in Google Nexus 7 (2013) devices.
> +	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
> +	  24 bit RGB per pixel.
> +
>  config DRM_PANEL_SHARP_LS043T1LE01
>  	tristate "Sharp LS043T1LE01 qHD video mode panel"
>  	depends on OF

Please keep these sorted alphabetically. I do realize that the list
isn't sorted quite correctly at the moment, so you may as well leave
this as-is and I'll fix up the order when applying and after fixing
the current ordering.

> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index f277eed..e6c6fc8 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o

Same here.

> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> new file mode 100644
> index 0000000..051aa1b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> @@ -0,0 +1,685 @@
> +/*
> + * Copyright (C) 2015 InforceComputing
> + * Author: Vinay Simha BN <simhavcs@gmail.com>
> + *
> + * Copyright (C) 2015 Linaro Ltd
> + * Author: Sumit Semwal <sumit.semwal@linaro.org>

Should either of those copyright lines include 2016? We're a pretty good
way into 2016, and I'd be surprised if this wasn't touched this year at
all.

> +/*
> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
> + * JDI model LT070ME05000, and its data sheet is at:
> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> + */

This comment is oddly placed here above the struct jdi_panel definition.
Perhaps move that information into the header comment?

> +struct jdi_panel {
> +	struct drm_panel base;
> +	struct mipi_dsi_device *dsi;
> +
> +	/* TODO: Add backilght support */

"backlight". Also if this is still TODO, why even have parts of the code
here? Just drop everything that's not used and move that to a follow-up
patch that implements full support.

> +	struct backlight_device *backlight;
> +	struct regulator *supply;
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *enable_gpio;
> +	struct gpio_desc *vcc_gpio;
> +
> +	struct regulator *backlit;
> +	struct regulator *lvs7;
> +	struct regulator *avdd;
> +	struct regulator *iovdd;
> +	struct gpio_desc *pwm_gpio;

Most of these resources aren't specified in the device tree binding, so
your driver doesn't properly implement the binding. You'll have to fix
the driver or the binding.

> +
> +	bool prepared;
> +	bool enabled;
> +
> +	const struct drm_display_mode *mode;
> +};
> +
> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct jdi_panel, base);
> +}
> +
> +static char MCAP[2] = {0xB0, 0x00};
> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};

Please make all of these (and the ones below) arrays of u8, since that's
the type used in the prototype of the function that receives these as
parameters. Also, please use consistent capitalization and a space after
{ and before }.

> +static char interface_ID_setting[2] = {0xB4, 0x0C};
> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
> +
> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};

This is a standard DCS command, please turn this into a generic helper
such as mipi_dsi_dcs_set_tear_scanline().

> +/* for fps control, set fps to 60.32Hz */
> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
> +static char sequencer_timing_control[2] = {0xD6, 0x01};
> +
> +/* set brightness */
> +static char write_display_brightness[] = {0x51, 0xFF};

Same here.

> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
> +static char write_control_display[] = {0x53, 0x24};

And here.

> +/*
> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
> + * disable SRE(sunlight readability enhancement)
> + */
> +static char write_cabc[] = {0x55, 0x00};

And here.

> +static int jdi_panel_init(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(10);

Please don't use mdelay() because it will busy-loop for a very long time
and waste precious CPU cycles. Instead, use usleep_range() for these
cases (or msleep() for durations longer than ~10 ms). Same goes for the
other occurrences below.

> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
> +		if (ret < 0)
> +			return ret;

There are symbolic constants for the pixel formats, use them to convey
meaning.

> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;

These should be parameterized on the panel width and height, to make it
clear where the values come from.

> +
> +		ret = mipi_dsi_dcs_set_tear_on(dsi,
> +					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
> +					     sizeof(tear_scan_line));
> +		if (ret < 0)
> +			return ret;

Like I mentioned earlier, this should be a generic helper to help make
its intention clearer.

> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;

Same for these. I don't currently have access to the DCS 1.3
specification, but I suspect that some of the parameters for these
commands could be defined via symbolic names.

> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(10);
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting,
> +					     sizeof(interface_setting));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		backlight_control4[18] = 0x04;
> +		backlight_control4[19] = 0x00;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		MCAP[1] = 0x03;
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;

That's odd. Doesn't this break idempotence of this function? You modify
the global MCAP array by writing 0x03 to MCAP[1], so when this function
is called a second time, MCAP[1] will be 0x03 already in the first call
a few lines above, instead of the 0x00 that it was initially.

> +	} else {
> +		/*
> +		 * TODO : need to verify panel settings when
> +		 * dsi cmd mode supported for apq8064 - simhavcs
> +		 */
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;

This whole else clause is dead code because the condition for the if
clause is always true. Just drop it and add it back when you properly
support both modes.

> +static int jdi_panel_on(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_off(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;

Should this not be cleared at the end of the function so that the below
commands still get run in LP mode?

> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_tear_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(100);
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_disable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (!jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("disable\n");

This doesn't look very useful debug information. I think you should
remove it. Same for other similar occurrences below.

> +static const struct drm_panel_funcs jdi_panel_funcs = {
> +		.disable = jdi_panel_disable,
> +		.unprepare = jdi_panel_unprepare,
> +		.prepare = jdi_panel_prepare,
> +		.enable = jdi_panel_enable,
> +		.get_modes = jdi_panel_get_modes,
> +};
> +
> +static const struct of_device_id jdi_of_match[] = {
> +		{ .compatible = "jdi,lt070me05000", },
> +		{ }
> +};
> +MODULE_DEVICE_TABLE(of, jdi_of_match);

A single tab is enough to indent the above.

> +static int jdi_panel_add(struct jdi_panel *jdi)
> +{
> +	struct device *dev = &jdi->dsi->dev;
> +	int ret;
> +
> +	jdi->mode = &default_mode;

What do you keep this around for?

> +
> +	/* lvs5 */
> +	jdi->supply = devm_regulator_get(dev, "power");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	/* l17 */
> +	jdi->backlit = devm_regulator_get(dev, "backlit");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
> +	if (IS_ERR(jdi->lvs7))
> +		return PTR_ERR(jdi->lvs7);
> +
> +	jdi->avdd = devm_regulator_get(dev, "avdd");
> +	if (IS_ERR(jdi->avdd))
> +		return PTR_ERR(jdi->avdd);
> +
> +	jdi->iovdd = devm_regulator_get(dev, "iovdd");
> +	if (IS_ERR(jdi->iovdd))
> +		return PTR_ERR(jdi->iovdd);
> +
> +	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->vcc_gpio)) {
> +		dev_err(dev, "cannot get vcc-gpio %ld\n",
> +			PTR_ERR(jdi->vcc_gpio));
> +		jdi->vcc_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->vcc_gpio, 0);
> +	}
> +
> +	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpios %ld\n",
> +			PTR_ERR(jdi->reset_gpio));
> +		jdi->reset_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->reset_gpio, 0);
> +	}
> +
> +	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->enable_gpio)) {
> +		dev_err(dev, "cannot get enable-gpio %ld\n",
> +			PTR_ERR(jdi->enable_gpio));
> +		jdi->enable_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->enable_gpio, 0);
> +	}
> +
> +	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->pwm_gpio)) {
> +		dev_err(dev, "cannot get pwm-gpio %ld\n",
> +			PTR_ERR(jdi->pwm_gpio));
> +		jdi->pwm_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->pwm_gpio, 0);
> +	}

As I said earlier, most of these aren't specified in the binding, fix
either the binding or the code.

> +	/* we don't have backlight right now, proceed further */
> +#ifdef BACKLIGHT
> +	np = of_parse_phandle(dev->of_node, "backlight", 0);
> +	if (np) {
> +		jdi->backlight = of_find_backlight_by_node(np);
> +		of_node_put(np);
> +
> +		if (!jdi->backlight)
> +			return -EPROBE_DEFER;
> +	}
> +#endif

Again, if you don't support it, don't submit it. We don't want dead code
in the kernel.

> +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
> +MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
> +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");

Technically this doesn't support command mode yet, so you might want to
remove that from the description to avoid confusion.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-13 13:49   ` Thierry Reding
  0 siblings, 0 replies; 39+ messages in thread
From: Thierry Reding @ 2016-04-13 13:49 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Archit Taneja, Sumit Semwal, John Stultz, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, David Airlie,
	Jonathan Cameron, Ralf Baechle, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

[-- Attachment #1: Type: text/plain, Size: 16723 bytes --]

On Wed, Apr 13, 2016 at 11:58:04AM +0530, Vinay Simha BN wrote:
> Add support for the JDI lt070me05000 WUXGA DSI panel used in
> Nexus 7 2013 devices.
> 
> Programming sequence for the panel is was originally found in the
> android-msm-flo-3.4-lollipop-release branch from:
>     https://android.googlesource.com/kernel/msm.git
> 
> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
> file in:
>     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
> 
> Other fixes folded in were provided
> by Archit Taneja <archit.taneja@gmail.com>
> 
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> [sumit.semwal: Ported to the drm/panel framework]
> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
> [jstultz: Cherry-picked to mainline, folded down other fixes
>  from Vinay and Archit]
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> ---
>  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  drivers/gpu/drm/panel/Kconfig                      |  11 +
>  drivers/gpu/drm/panel/Makefile                     |   1 +
>  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>  5 files changed, 725 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

What's the difference between this and the patch you sent earlier? I'm
going to assume that the newer one is the correct patch, so I'll ignore
the previous patch.

> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> new file mode 100644
> index 0000000..35c5ac7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt

The binding documentation should be a separate patch.

> @@ -0,0 +1,27 @@
> +JDI model LT070ME05000 1920x1200 7" DSI Panel
> +
> +Basic data sheet is at:
> +	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> +
> +This panel has video mode implemented currently in the driver.

That's information irrelevant to the DT binding, since you're presumably
talking about the Linux drm/panel driver, whereas the DT binding is
supposed to specify the description of the panel hardware in OS-agnostic
terms.

> +Required properties:
> +- compatible: should be "jdi,lt070me05000"
> +
> +Optional properties:
> +- power-supply: phandle of the regulator that provides the supply voltage
> +- reset-gpio: phandle of gpio for reset line
> +- backlight: phandle of the backlight device attached to the panel
> +
> +Example:
> +
> +	dsi@54300000 {
> +		panel: panel@0 {
> +			compatible = "jdi,lt070me05000";
> +			reg = <0>;
> +
> +			power-supply = <...>;
> +			reset-gpio = <...>;
> +			backlight = <...>;
> +		};
> +	};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index a580f3e..ec42bb4 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -130,6 +130,7 @@ invensense	InvenSense Inc.
>  isee	ISEE 2007 S.L.
>  isil	Intersil
>  issi	Integrated Silicon Solutions Inc.
> +jdi	Japan Display Inc.
>  jedec	JEDEC Solid State Technology Association
>  karo	Ka-Ro electronics GmbH
>  keymile	Keymile GmbH

This should be a separate patch as well.

> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 1500ab9..f41690e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called panel-sharp-lq101r1sx01.
>  
> +config DRM_PANEL_JDI_LT070ME05000
> +	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
> +	  command mode panel as found in Google Nexus 7 (2013) devices.
> +	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
> +	  24 bit RGB per pixel.
> +
>  config DRM_PANEL_SHARP_LS043T1LE01
>  	tristate "Sharp LS043T1LE01 qHD video mode panel"
>  	depends on OF

Please keep these sorted alphabetically. I do realize that the list
isn't sorted quite correctly at the moment, so you may as well leave
this as-is and I'll fix up the order when applying and after fixing
the current ordering.

> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index f277eed..e6c6fc8 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o

Same here.

> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> new file mode 100644
> index 0000000..051aa1b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> @@ -0,0 +1,685 @@
> +/*
> + * Copyright (C) 2015 InforceComputing
> + * Author: Vinay Simha BN <simhavcs@gmail.com>
> + *
> + * Copyright (C) 2015 Linaro Ltd
> + * Author: Sumit Semwal <sumit.semwal@linaro.org>

Should either of those copyright lines include 2016? We're a pretty good
way into 2016, and I'd be surprised if this wasn't touched this year at
all.

> +/*
> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
> + * JDI model LT070ME05000, and its data sheet is at:
> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> + */

This comment is oddly placed here above the struct jdi_panel definition.
Perhaps move that information into the header comment?

> +struct jdi_panel {
> +	struct drm_panel base;
> +	struct mipi_dsi_device *dsi;
> +
> +	/* TODO: Add backilght support */

"backlight". Also if this is still TODO, why even have parts of the code
here? Just drop everything that's not used and move that to a follow-up
patch that implements full support.

> +	struct backlight_device *backlight;
> +	struct regulator *supply;
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *enable_gpio;
> +	struct gpio_desc *vcc_gpio;
> +
> +	struct regulator *backlit;
> +	struct regulator *lvs7;
> +	struct regulator *avdd;
> +	struct regulator *iovdd;
> +	struct gpio_desc *pwm_gpio;

Most of these resources aren't specified in the device tree binding, so
your driver doesn't properly implement the binding. You'll have to fix
the driver or the binding.

> +
> +	bool prepared;
> +	bool enabled;
> +
> +	const struct drm_display_mode *mode;
> +};
> +
> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct jdi_panel, base);
> +}
> +
> +static char MCAP[2] = {0xB0, 0x00};
> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};

Please make all of these (and the ones below) arrays of u8, since that's
the type used in the prototype of the function that receives these as
parameters. Also, please use consistent capitalization and a space after
{ and before }.

> +static char interface_ID_setting[2] = {0xB4, 0x0C};
> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
> +
> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};

This is a standard DCS command, please turn this into a generic helper
such as mipi_dsi_dcs_set_tear_scanline().

> +/* for fps control, set fps to 60.32Hz */
> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
> +static char sequencer_timing_control[2] = {0xD6, 0x01};
> +
> +/* set brightness */
> +static char write_display_brightness[] = {0x51, 0xFF};

Same here.

> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
> +static char write_control_display[] = {0x53, 0x24};

And here.

> +/*
> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
> + * disable SRE(sunlight readability enhancement)
> + */
> +static char write_cabc[] = {0x55, 0x00};

And here.

> +static int jdi_panel_init(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(10);

Please don't use mdelay() because it will busy-loop for a very long time
and waste precious CPU cycles. Instead, use usleep_range() for these
cases (or msleep() for durations longer than ~10 ms). Same goes for the
other occurrences below.

> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
> +		if (ret < 0)
> +			return ret;

There are symbolic constants for the pixel formats, use them to convey
meaning.

> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;

These should be parameterized on the panel width and height, to make it
clear where the values come from.

> +
> +		ret = mipi_dsi_dcs_set_tear_on(dsi,
> +					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
> +					     sizeof(tear_scan_line));
> +		if (ret < 0)
> +			return ret;

Like I mentioned earlier, this should be a generic helper to help make
its intention clearer.

> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;

Same for these. I don't currently have access to the DCS 1.3
specification, but I suspect that some of the parameters for these
commands could be defined via symbolic names.

> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(10);
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting,
> +					     sizeof(interface_setting));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		backlight_control4[18] = 0x04;
> +		backlight_control4[19] = 0x00;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		MCAP[1] = 0x03;
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;

That's odd. Doesn't this break idempotence of this function? You modify
the global MCAP array by writing 0x03 to MCAP[1], so when this function
is called a second time, MCAP[1] will be 0x03 already in the first call
a few lines above, instead of the 0x00 that it was initially.

> +	} else {
> +		/*
> +		 * TODO : need to verify panel settings when
> +		 * dsi cmd mode supported for apq8064 - simhavcs
> +		 */
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;

This whole else clause is dead code because the condition for the if
clause is always true. Just drop it and add it back when you properly
support both modes.

> +static int jdi_panel_on(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_off(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;

Should this not be cleared at the end of the function so that the below
commands still get run in LP mode?

> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_tear_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(100);
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_disable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (!jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("disable\n");

This doesn't look very useful debug information. I think you should
remove it. Same for other similar occurrences below.

> +static const struct drm_panel_funcs jdi_panel_funcs = {
> +		.disable = jdi_panel_disable,
> +		.unprepare = jdi_panel_unprepare,
> +		.prepare = jdi_panel_prepare,
> +		.enable = jdi_panel_enable,
> +		.get_modes = jdi_panel_get_modes,
> +};
> +
> +static const struct of_device_id jdi_of_match[] = {
> +		{ .compatible = "jdi,lt070me05000", },
> +		{ }
> +};
> +MODULE_DEVICE_TABLE(of, jdi_of_match);

A single tab is enough to indent the above.

> +static int jdi_panel_add(struct jdi_panel *jdi)
> +{
> +	struct device *dev = &jdi->dsi->dev;
> +	int ret;
> +
> +	jdi->mode = &default_mode;

What do you keep this around for?

> +
> +	/* lvs5 */
> +	jdi->supply = devm_regulator_get(dev, "power");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	/* l17 */
> +	jdi->backlit = devm_regulator_get(dev, "backlit");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
> +	if (IS_ERR(jdi->lvs7))
> +		return PTR_ERR(jdi->lvs7);
> +
> +	jdi->avdd = devm_regulator_get(dev, "avdd");
> +	if (IS_ERR(jdi->avdd))
> +		return PTR_ERR(jdi->avdd);
> +
> +	jdi->iovdd = devm_regulator_get(dev, "iovdd");
> +	if (IS_ERR(jdi->iovdd))
> +		return PTR_ERR(jdi->iovdd);
> +
> +	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->vcc_gpio)) {
> +		dev_err(dev, "cannot get vcc-gpio %ld\n",
> +			PTR_ERR(jdi->vcc_gpio));
> +		jdi->vcc_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->vcc_gpio, 0);
> +	}
> +
> +	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpios %ld\n",
> +			PTR_ERR(jdi->reset_gpio));
> +		jdi->reset_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->reset_gpio, 0);
> +	}
> +
> +	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->enable_gpio)) {
> +		dev_err(dev, "cannot get enable-gpio %ld\n",
> +			PTR_ERR(jdi->enable_gpio));
> +		jdi->enable_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->enable_gpio, 0);
> +	}
> +
> +	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->pwm_gpio)) {
> +		dev_err(dev, "cannot get pwm-gpio %ld\n",
> +			PTR_ERR(jdi->pwm_gpio));
> +		jdi->pwm_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->pwm_gpio, 0);
> +	}

As I said earlier, most of these aren't specified in the binding, fix
either the binding or the code.

> +	/* we don't have backlight right now, proceed further */
> +#ifdef BACKLIGHT
> +	np = of_parse_phandle(dev->of_node, "backlight", 0);
> +	if (np) {
> +		jdi->backlight = of_find_backlight_by_node(np);
> +		of_node_put(np);
> +
> +		if (!jdi->backlight)
> +			return -EPROBE_DEFER;
> +	}
> +#endif

Again, if you don't support it, don't submit it. We don't want dead code
in the kernel.

> +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
> +MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
> +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");

Technically this doesn't support command mode yet, so you might want to
remove that from the description to avoid confusion.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
  2016-04-13 13:49   ` Thierry Reding
  (?)
@ 2016-04-13 15:22   ` Vinay Simha
  -1 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-13 15:22 UTC (permalink / raw)
  To: Thierry Reding
  Cc: David Airlie, John Stultz, Rob Herring,
	open list:DRM PANEL DRIVERS, Ian Campbell, Jonathan Cameron,
	Pawel Moll,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Kumar Gala, Shawn Guo, Mark Rutland, Sumit Semwal, Ralf Baechle,
	Archit Taneja, open list

[-- Attachment #1: Type: text/plain, Size: 19757 bytes --]

In Previous email , forgot to add cc: archit.

regards,
vinay simha bn
On 13-Apr-2016 19:19, "Thierry Reding" <thierry.reding@gmail.com> wrote:

> On Wed, Apr 13, 2016 at 11:58:04AM +0530, Vinay Simha BN wrote:
> > Add support for the JDI lt070me05000 WUXGA DSI panel used in
> > Nexus 7 2013 devices.
> >
> > Programming sequence for the panel is was originally found in the
> > android-msm-flo-3.4-lollipop-release branch from:
> >     https://android.googlesource.com/kernel/msm.git
> >
> > And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
> > file in:
> >     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
> >
> > Other fixes folded in were provided
> > by Archit Taneja <archit.taneja@gmail.com>
> >
> > Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> > [sumit.semwal: Ported to the drm/panel framework]
> > Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
> > [jstultz: Cherry-picked to mainline, folded down other fixes
> >  from Vinay and Archit]
> > Signed-off-by: John Stultz <john.stultz@linaro.org>
> > ---
> >  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
> >  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
> >  drivers/gpu/drm/panel/Kconfig                      |  11 +
> >  drivers/gpu/drm/panel/Makefile                     |   1 +
> >  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685
> +++++++++++++++++++++
> >  5 files changed, 725 insertions(+)
> >  create mode 100644
> Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> >  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>
> What's the difference between this and the patch you sent earlier? I'm
> going to assume that the newer one is the correct patch, so I'll ignore
> the previous patch.
>
> > diff --git
> a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> > new file mode 100644
> > index 0000000..35c5ac7
> > --- /dev/null
> > +++
> b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>
> The binding documentation should be a separate patch.
>
> > @@ -0,0 +1,27 @@
> > +JDI model LT070ME05000 1920x1200 7" DSI Panel
> > +
> > +Basic data sheet is at:
> > +
> http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> > +
> > +This panel has video mode implemented currently in the driver.
>
> That's information irrelevant to the DT binding, since you're presumably
> talking about the Linux drm/panel driver, whereas the DT binding is
> supposed to specify the description of the panel hardware in OS-agnostic
> terms.
>
> > +Required properties:
> > +- compatible: should be "jdi,lt070me05000"
> > +
> > +Optional properties:
> > +- power-supply: phandle of the regulator that provides the supply
> voltage
> > +- reset-gpio: phandle of gpio for reset line
> > +- backlight: phandle of the backlight device attached to the panel
> > +
> > +Example:
> > +
> > +     dsi@54300000 {
> > +             panel: panel@0 {
> > +                     compatible = "jdi,lt070me05000";
> > +                     reg = <0>;
> > +
> > +                     power-supply = <...>;
> > +                     reset-gpio = <...>;
> > +                     backlight = <...>;
> > +             };
> > +     };
> > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt
> b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > index a580f3e..ec42bb4 100644
> > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > @@ -130,6 +130,7 @@ invensense        InvenSense Inc.
> >  isee ISEE 2007 S.L.
> >  isil Intersil
> >  issi Integrated Silicon Solutions Inc.
> > +jdi  Japan Display Inc.
> >  jedec        JEDEC Solid State Technology Association
> >  karo Ka-Ro electronics GmbH
> >  keymile      Keymile GmbH
>
> This should be a separate patch as well.
>
> > diff --git a/drivers/gpu/drm/panel/Kconfig
> b/drivers/gpu/drm/panel/Kconfig
> > index 1500ab9..f41690e 100644
> > --- a/drivers/gpu/drm/panel/Kconfig
> > +++ b/drivers/gpu/drm/panel/Kconfig
> > @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
> >         To compile this driver as a module, choose M here: the module
> >         will be called panel-sharp-lq101r1sx01.
> >
> > +config DRM_PANEL_JDI_LT070ME05000
> > +     tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
> > +       command mode panel as found in Google Nexus 7 (2013) devices.
> > +       The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
> > +       24 bit RGB per pixel.
> > +
> >  config DRM_PANEL_SHARP_LS043T1LE01
> >       tristate "Sharp LS043T1LE01 qHD video mode panel"
> >       depends on OF
>
> Please keep these sorted alphabetically. I do realize that the list
> isn't sorted quite correctly at the moment, so you may as well leave
> this as-is and I'll fix up the order when applying and after fixing
> the current ordering.
>
> > diff --git a/drivers/gpu/drm/panel/Makefile
> b/drivers/gpu/drm/panel/Makefile
> > index f277eed..e6c6fc8 100644
> > --- a/drivers/gpu/drm/panel/Makefile
> > +++ b/drivers/gpu/drm/panel/Makefile
> > @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) +=
> panel-samsung-ld9040.o
> >  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
> >  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
> >  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> > +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>
> Same here.
>
> > diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> > new file mode 100644
> > index 0000000..051aa1b
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> > @@ -0,0 +1,685 @@
> > +/*
> > + * Copyright (C) 2015 InforceComputing
> > + * Author: Vinay Simha BN <simhavcs@gmail.com>
> > + *
> > + * Copyright (C) 2015 Linaro Ltd
> > + * Author: Sumit Semwal <sumit.semwal@linaro.org>
>
> Should either of those copyright lines include 2016? We're a pretty good
> way into 2016, and I'd be surprised if this wasn't touched this year at
> all.
>
> > +/*
> > + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is
> a
> > + * JDI model LT070ME05000, and its data sheet is at:
> > + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> > + */
>
> This comment is oddly placed here above the struct jdi_panel definition.
> Perhaps move that information into the header comment?
>
> > +struct jdi_panel {
> > +     struct drm_panel base;
> > +     struct mipi_dsi_device *dsi;
> > +
> > +     /* TODO: Add backilght support */
>
> "backlight". Also if this is still TODO, why even have parts of the code
> here? Just drop everything that's not used and move that to a follow-up
> patch that implements full support.
>
> > +     struct backlight_device *backlight;
> > +     struct regulator *supply;
> > +     struct gpio_desc *reset_gpio;
> > +     struct gpio_desc *enable_gpio;
> > +     struct gpio_desc *vcc_gpio;
> > +
> > +     struct regulator *backlit;
> > +     struct regulator *lvs7;
> > +     struct regulator *avdd;
> > +     struct regulator *iovdd;
> > +     struct gpio_desc *pwm_gpio;
>
> Most of these resources aren't specified in the device tree binding, so
> your driver doesn't properly implement the binding. You'll have to fix
> the driver or the binding.
>
> > +
> > +     bool prepared;
> > +     bool enabled;
> > +
> > +     const struct drm_display_mode *mode;
> > +};
> > +
> > +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
> > +{
> > +     return container_of(panel, struct jdi_panel, base);
> > +}
> > +
> > +static char MCAP[2] = {0xB0, 0x00};
> > +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22,
> 0x00};
> > +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
>
> Please make all of these (and the ones below) arrays of u8, since that's
> the type used in the prototype of the function that receives these as
> parameters. Also, please use consistent capitalization and a space after
> { and before }.
>
> > +static char interface_ID_setting[2] = {0xB4, 0x0C};
> > +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
> > +
> > +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
>
> This is a standard DCS command, please turn this into a generic helper
> such as mipi_dsi_dcs_set_tear_scanline().
>
> > +/* for fps control, set fps to 60.32Hz */
> > +static char LTPS_timing_setting[2] = {0xC6, 0x78};
> > +static char sequencer_timing_control[2] = {0xD6, 0x01};
> > +
> > +/* set brightness */
> > +static char write_display_brightness[] = {0x51, 0xFF};
>
> Same here.
>
> > +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm
> dimming */
> > +static char write_control_display[] = {0x53, 0x24};
>
> And here.
>
> > +/*
> > + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
> > + * disable SRE(sunlight readability enhancement)
> > + */
> > +static char write_cabc[] = {0x55, 0x00};
>
> And here.
>
> > +static int jdi_panel_init(struct jdi_panel *jdi)
> > +{
> > +     struct mipi_dsi_device *dsi = jdi->dsi;
> > +     int ret;
> > +
> > +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > +     if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > +             ret = mipi_dsi_dcs_soft_reset(dsi);
> > +             if (ret < 0)
> > +                     return ret;
> > +
> > +             mdelay(10);
>
> Please don't use mdelay() because it will busy-loop for a very long time
> and waste precious CPU cycles. Instead, use usleep_range() for these
> cases (or msleep() for durations longer than ~10 ms). Same goes for the
> other occurrences below.
>
> > +
> > +             ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
> > +             if (ret < 0)
> > +                     return ret;
>
> There are symbolic constants for the pixel formats, use them to convey
> meaning.
>
> > +
> > +             ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> > +             if (ret < 0)
> > +                     return ret;
> > +
> > +             ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> > +             if (ret < 0)
> > +                     return ret;
>
> These should be parameterized on the panel width and height, to make it
> clear where the values come from.
>
> > +
> > +             ret = mipi_dsi_dcs_set_tear_on(dsi,
> > +
> MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> > +             if (ret < 0)
> > +                     return ret;
> > +             mdelay(5);
> > +
> > +             ret = mipi_dsi_generic_write(dsi, tear_scan_line,
> > +                                          sizeof(tear_scan_line));
> > +             if (ret < 0)
> > +                     return ret;
>
> Like I mentioned earlier, this should be a generic helper to help make
> its intention clearer.
>
> > +
> > +             ret = mipi_dsi_dcs_write_buffer(dsi,
> write_display_brightness,
> > +
>  sizeof(write_display_brightness)
> > +                                             );
> > +             if (ret < 0)
> > +                     return ret;
> > +
> > +             ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> > +
>  sizeof(write_control_display));
> > +             if (ret < 0)
> > +                     return ret;
> > +
> > +             ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> > +                                             sizeof(write_cabc));
> > +             if (ret < 0)
> > +                     return ret;
>
> Same for these. I don't currently have access to the DCS 1.3
> specification, but I suspect that some of the parameters for these
> commands could be defined via symbolic names.
>
> > +
> > +             ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> > +             if (ret < 0)
> > +                     return ret;
> > +             mdelay(120);
> > +
> > +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> > +             if (ret < 0)
> > +                     return ret;
> > +             mdelay(10);
> > +
> > +             ret = mipi_dsi_generic_write(dsi, interface_setting,
> > +                                          sizeof(interface_setting));
> > +             if (ret < 0)
> > +                     return ret;
> > +             mdelay(20);
> > +
> > +             backlight_control4[18] = 0x04;
> > +             backlight_control4[19] = 0x00;
> > +
> > +             ret = mipi_dsi_generic_write(dsi, backlight_control4,
> > +                                          sizeof(backlight_control4));
> > +             if (ret < 0)
> > +                     return ret;
> > +             mdelay(20);
> > +
> > +             MCAP[1] = 0x03;
> > +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> > +             if (ret < 0)
> > +                     return ret;
>
> That's odd. Doesn't this break idempotence of this function? You modify
> the global MCAP array by writing 0x03 to MCAP[1], so when this function
> is called a second time, MCAP[1] will be 0x03 already in the first call
> a few lines above, instead of the 0x00 that it was initially.
>
> > +     } else {
> > +             /*
> > +              * TODO : need to verify panel settings when
> > +              * dsi cmd mode supported for apq8064 - simhavcs
> > +              */
> > +             ret = mipi_dsi_dcs_soft_reset(dsi);
> > +             if (ret < 0)
> > +                     return ret;
>
> This whole else clause is dead code because the condition for the if
> clause is always true. Just drop it and add it back when you properly
> support both modes.
>
> > +static int jdi_panel_on(struct jdi_panel *jdi)
> > +{
> > +     struct mipi_dsi_device *dsi = jdi->dsi;
> > +     int ret;
> > +
> > +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > +     ret = mipi_dsi_dcs_set_display_on(dsi);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +
> > +static int jdi_panel_off(struct jdi_panel *jdi)
> > +{
> > +     struct mipi_dsi_device *dsi = jdi->dsi;
> > +     int ret;
> > +
> > +     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>
> Should this not be cleared at the end of the function so that the below
> commands still get run in LP mode?
>
> > +
> > +     ret = mipi_dsi_dcs_set_display_off(dsi);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     ret = mipi_dsi_dcs_set_tear_off(dsi);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     msleep(100);
> > +
> > +     return 0;
> > +}
> > +
> > +static int jdi_panel_disable(struct drm_panel *panel)
> > +{
> > +     struct jdi_panel *jdi = to_jdi_panel(panel);
> > +
> > +     if (!jdi->enabled)
> > +             return 0;
> > +
> > +     DRM_DEBUG("disable\n");
>
> This doesn't look very useful debug information. I think you should
> remove it. Same for other similar occurrences below.
>
> > +static const struct drm_panel_funcs jdi_panel_funcs = {
> > +             .disable = jdi_panel_disable,
> > +             .unprepare = jdi_panel_unprepare,
> > +             .prepare = jdi_panel_prepare,
> > +             .enable = jdi_panel_enable,
> > +             .get_modes = jdi_panel_get_modes,
> > +};
> > +
> > +static const struct of_device_id jdi_of_match[] = {
> > +             { .compatible = "jdi,lt070me05000", },
> > +             { }
> > +};
> > +MODULE_DEVICE_TABLE(of, jdi_of_match);
>
> A single tab is enough to indent the above.
>
> > +static int jdi_panel_add(struct jdi_panel *jdi)
> > +{
> > +     struct device *dev = &jdi->dsi->dev;
> > +     int ret;
> > +
> > +     jdi->mode = &default_mode;
>
> What do you keep this around for?
>
> > +
> > +     /* lvs5 */
> > +     jdi->supply = devm_regulator_get(dev, "power");
> > +     if (IS_ERR(jdi->supply))
> > +             return PTR_ERR(jdi->supply);
> > +
> > +     /* l17 */
> > +     jdi->backlit = devm_regulator_get(dev, "backlit");
> > +     if (IS_ERR(jdi->supply))
> > +             return PTR_ERR(jdi->supply);
> > +
> > +     jdi->lvs7 = devm_regulator_get(dev, "lvs7");
> > +     if (IS_ERR(jdi->lvs7))
> > +             return PTR_ERR(jdi->lvs7);
> > +
> > +     jdi->avdd = devm_regulator_get(dev, "avdd");
> > +     if (IS_ERR(jdi->avdd))
> > +             return PTR_ERR(jdi->avdd);
> > +
> > +     jdi->iovdd = devm_regulator_get(dev, "iovdd");
> > +     if (IS_ERR(jdi->iovdd))
> > +             return PTR_ERR(jdi->iovdd);
> > +
> > +     jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
> > +     if (IS_ERR(jdi->vcc_gpio)) {
> > +             dev_err(dev, "cannot get vcc-gpio %ld\n",
> > +                     PTR_ERR(jdi->vcc_gpio));
> > +             jdi->vcc_gpio = NULL;
> > +     } else {
> > +             gpiod_direction_output(jdi->vcc_gpio, 0);
> > +     }
> > +
> > +     jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> > +     if (IS_ERR(jdi->reset_gpio)) {
> > +             dev_err(dev, "cannot get reset-gpios %ld\n",
> > +                     PTR_ERR(jdi->reset_gpio));
> > +             jdi->reset_gpio = NULL;
> > +     } else {
> > +             gpiod_direction_output(jdi->reset_gpio, 0);
> > +     }
> > +
> > +     jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> > +     if (IS_ERR(jdi->enable_gpio)) {
> > +             dev_err(dev, "cannot get enable-gpio %ld\n",
> > +                     PTR_ERR(jdi->enable_gpio));
> > +             jdi->enable_gpio = NULL;
> > +     } else {
> > +             gpiod_direction_output(jdi->enable_gpio, 0);
> > +     }
> > +
> > +     jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
> > +     if (IS_ERR(jdi->pwm_gpio)) {
> > +             dev_err(dev, "cannot get pwm-gpio %ld\n",
> > +                     PTR_ERR(jdi->pwm_gpio));
> > +             jdi->pwm_gpio = NULL;
> > +     } else {
> > +             gpiod_direction_output(jdi->pwm_gpio, 0);
> > +     }
>
> As I said earlier, most of these aren't specified in the binding, fix
> either the binding or the code.
>
> > +     /* we don't have backlight right now, proceed further */
> > +#ifdef BACKLIGHT
> > +     np = of_parse_phandle(dev->of_node, "backlight", 0);
> > +     if (np) {
> > +             jdi->backlight = of_find_backlight_by_node(np);
> > +             of_node_put(np);
> > +
> > +             if (!jdi->backlight)
> > +                     return -EPROBE_DEFER;
> > +     }
> > +#endif
>
> Again, if you don't support it, don't submit it. We don't want dead code
> in the kernel.
>
> > +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
> > +MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
> > +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel
> driver");
>
> Technically this doesn't support command mode yet, so you might want to
> remove that from the description to avoid confusion.
>
> Thierry
>

[-- Attachment #2: Type: text/html, Size: 25137 bytes --]

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

* [RESEND][PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
  2016-04-13  6:28 ` Vinay Simha BN
@ 2016-04-14 10:47   ` Vinay Simha BN
  -1 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-14 10:47 UTC (permalink / raw)
  Cc: Vinay Simha BN, Archit Taneja, Sumit Semwal, John Stultz,
	Thierry Reding, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, David Airlie, Ralf Baechle,
	Jonathan Cameron, Shawn Guo, open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Add support for the JDI lt070me05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
    https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
    git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27

Other fixes folded in were provided
by Archit Taneja <archit.taneja@gmail.com>

Cc: Archit Taneja <archit.taneja@gmail.com>
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
[sumit.semwal: Ported to the drm/panel framework]
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
[jstultz: Cherry-picked to mainline, folded down other fixes
 from Vinay and Archit]
Signed-off-by: John Stultz <john.stultz@linaro.org>
Acked-by: John Stultz <john.stultz@linaro.org>
[vinay simha bn: added interface setting cmd mode, cmd or video
mode panel setting selection in panel_init based on mode_flags]
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/gpu/drm/panel/Kconfig                      |  11 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
 5 files changed, 725 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
 create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644
index 0000000..35c5ac7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
@@ -0,0 +1,27 @@
+JDI model LT070ME05000 1920x1200 7" DSI Panel
+
+Basic data sheet is at:
+	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+
+This panel has video mode implemented currently in the driver.
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+
+Optional properties:
+- power-supply: phandle of the regulator that provides the supply voltage
+- reset-gpio: phandle of gpio for reset line
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+	dsi@54300000 {
+		panel: panel@0 {
+			compatible = "jdi,lt070me05000";
+			reg = <0>;
+
+			power-supply = <...>;
+			reset-gpio = <...>;
+			backlight = <...>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a580f3e..ec42bb4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -130,6 +130,7 @@ invensense	InvenSense Inc.
 isee	ISEE 2007 S.L.
 isil	Intersil
 issi	Integrated Silicon Solutions Inc.
+jdi	Japan Display Inc.
 jedec	JEDEC Solid State Technology Association
 karo	Ka-Ro electronics GmbH
 keymile	Keymile GmbH
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 1500ab9..f41690e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
 	  To compile this driver as a module, choose M here: the module
 	  will be called panel-sharp-lq101r1sx01.
 
+config DRM_PANEL_JDI_LT070ME05000
+	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
+	  command mode panel as found in Google Nexus 7 (2013) devices.
+	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+	  24 bit RGB per pixel.
+
 config DRM_PANEL_SHARP_LS043T1LE01
 	tristate "Sharp LS043T1LE01 qHD video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f277eed..e6c6fc8 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644
index 0000000..051aa1b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2015 InforceComputing
+ * Author: Vinay Simha BN <simhavcs@gmail.com>
+ *
+ * Copyright (C) 2015 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+/*
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ */
+struct jdi_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	/* TODO: Add backilght support */
+	struct backlight_device *backlight;
+	struct regulator *supply;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *vcc_gpio;
+
+	struct regulator *backlit;
+	struct regulator *lvs7;
+	struct regulator *avdd;
+	struct regulator *iovdd;
+	struct gpio_desc *pwm_gpio;
+
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct jdi_panel, base);
+}
+
+static char MCAP[2] = {0xB0, 0x00};
+static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
+static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
+
+static char interface_ID_setting[2] = {0xB4, 0x0C};
+static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
+
+static char tear_scan_line[3] = {0x44, 0x03, 0x00};
+
+/* for fps control, set fps to 60.32Hz */
+static char LTPS_timing_setting[2] = {0xC6, 0x78};
+static char sequencer_timing_control[2] = {0xD6, 0x01};
+
+/* set brightness */
+static char write_display_brightness[] = {0x51, 0xFF};
+/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
+static char write_control_display[] = {0x53, 0x24};
+/*
+ * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
+ * disable SRE(sunlight readability enhancement)
+ */
+static char write_cabc[] = {0x55, 0x00};
+/* for cabc mode 0x1(-15%) */
+static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
+/* for cabc mode 0x2(-40%) */
+static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
+/* for cabc mode 0x3(-54%) */
+static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
+/* for pwm frequency and dimming control */
+static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
+		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
+		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(10);
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_tear_on(dsi,
+					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+		if (ret < 0)
+			return ret;
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
+					     sizeof(tear_scan_line));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+		mdelay(10);
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting,
+					     sizeof(interface_setting));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		backlight_control4[18] = 0x04;
+		backlight_control4[19] = 0x00;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		MCAP[1] = 0x03;
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+	} else {
+		/*
+		 * TODO : need to verify panel settings when
+		 * dsi cmd mode supported for apq8064 - simhavcs
+		 */
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
+					     sizeof(interface_setting_cmd));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
+					     sizeof(interface_ID_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, DSI_control,
+					     sizeof(DSI_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
+					     sizeof(LTPS_timing_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
+					     sizeof(sequencer_timing_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control1,
+					     sizeof(backlight_control1));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control2,
+					     sizeof(backlight_control2));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control3,
+					     sizeof(backlight_control3));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_off(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(100);
+
+	return 0;
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (!jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("disable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = false;
+
+	return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (!jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("unprepare\n");
+
+	ret = jdi_panel_off(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+		return ret;
+	}
+
+	regulator_disable(jdi->supply);
+
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+
+	jdi->prepared = false;
+
+	return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("prepare\n");
+
+	ret = regulator_enable(jdi->iovdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->avdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->backlit);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->lvs7);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->supply);
+	if (ret < 0)
+		return ret;
+
+	msleep(20);
+
+	if (jdi->vcc_gpio) {
+		gpiod_set_value(jdi->vcc_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->reset_gpio) {
+		gpiod_set_value(jdi->reset_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->pwm_gpio) {
+		gpiod_set_value(jdi->pwm_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->enable_gpio) {
+		gpiod_set_value(jdi->enable_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	ret = jdi_panel_init(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to init panel: %d\n", ret);
+		goto poweroff;
+	}
+
+	ret = jdi_panel_on(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+		goto poweroff;
+	}
+
+	jdi->prepared = true;
+
+	return 0;
+
+poweroff:
+	regulator_disable(jdi->supply);
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("enable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_UNBLANK;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+		.clock = 155000,
+		.hdisplay = 1200,
+		.hsync_start = 1200 + 48,
+		.hsync_end = 1200 + 48 + 32,
+		.htotal = 1200 + 48 + 32 + 60,
+		.vdisplay = 1920,
+		.vsync_start = 1920 + 3,
+		.vsync_end = 1920 + 3 + 5,
+		.vtotal = 1920 + 3 + 5 + 6,
+		.vrefresh = 60,
+		.flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+		.disable = jdi_panel_disable,
+		.unprepare = jdi_panel_unprepare,
+		.prepare = jdi_panel_prepare,
+		.enable = jdi_panel_enable,
+		.get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+		{ .compatible = "jdi,lt070me05000", },
+		{ }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	jdi->mode = &default_mode;
+
+	/* lvs5 */
+	jdi->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	/* l17 */
+	jdi->backlit = devm_regulator_get(dev, "backlit");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
+	if (IS_ERR(jdi->lvs7))
+		return PTR_ERR(jdi->lvs7);
+
+	jdi->avdd = devm_regulator_get(dev, "avdd");
+	if (IS_ERR(jdi->avdd))
+		return PTR_ERR(jdi->avdd);
+
+	jdi->iovdd = devm_regulator_get(dev, "iovdd");
+	if (IS_ERR(jdi->iovdd))
+		return PTR_ERR(jdi->iovdd);
+
+	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->vcc_gpio)) {
+		dev_err(dev, "cannot get vcc-gpio %ld\n",
+			PTR_ERR(jdi->vcc_gpio));
+		jdi->vcc_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->vcc_gpio, 0);
+	}
+
+	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(jdi->reset_gpio));
+		jdi->reset_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->reset_gpio, 0);
+	}
+
+	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpio %ld\n",
+			PTR_ERR(jdi->enable_gpio));
+		jdi->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->enable_gpio, 0);
+	}
+
+	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->pwm_gpio)) {
+		dev_err(dev, "cannot get pwm-gpio %ld\n",
+			PTR_ERR(jdi->pwm_gpio));
+		jdi->pwm_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->pwm_gpio, 0);
+	}
+
+	/* we don't have backlight right now, proceed further */
+#ifdef BACKLIGHT
+	np = of_parse_phandle(dev->of_node, "backlight", 0);
+	if (np) {
+		jdi->backlight = of_find_backlight_by_node(np);
+		of_node_put(np);
+
+		if (!jdi->backlight)
+			return -EPROBE_DEFER;
+	}
+#endif
+
+	drm_panel_init(&jdi->base);
+	jdi->base.funcs = &jdi_panel_funcs;
+	jdi->base.dev = &jdi->dsi->dev;
+
+	ret = drm_panel_add(&jdi->base);
+	if (ret < 0)
+		goto put_backlight;
+
+	return 0;
+
+put_backlight:
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+
+	return ret;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+	if (jdi->base.dev)
+		drm_panel_remove(&jdi->base);
+
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi;
+	int ret;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
+			MIPI_DSI_MODE_VIDEO_HSA |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+	if (!jdi)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, jdi);
+
+	jdi->dsi = dsi;
+
+	ret = jdi_panel_add(jdi);
+	if (ret < 0)
+		return ret;
+
+	return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = jdi_panel_disable(&jdi->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+			ret);
+
+	drm_panel_detach(&jdi->base);
+	jdi_panel_del(jdi);
+
+	return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+	jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+	.driver = {
+		.name = "panel-jdi-lt070me05000",
+		.of_match_table = jdi_of_match,
+	},
+	.probe = jdi_panel_probe,
+	.remove = jdi_panel_remove,
+	.shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
+MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

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

* [RESEND][PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-14 10:47   ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-14 10:47 UTC (permalink / raw)
  Cc: Vinay Simha BN, Archit Taneja, Sumit Semwal, John Stultz,
	Thierry Reding, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, David Airlie, Ralf Baechle,
	Jonathan Cameron, Shawn Guo, open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Add support for the JDI lt070me05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
    https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
    git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27

Other fixes folded in were provided
by Archit Taneja <archit.taneja@gmail.com>

Cc: Archit Taneja <archit.taneja@gmail.com>
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
[sumit.semwal: Ported to the drm/panel framework]
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
[jstultz: Cherry-picked to mainline, folded down other fixes
 from Vinay and Archit]
Signed-off-by: John Stultz <john.stultz@linaro.org>
Acked-by: John Stultz <john.stultz@linaro.org>
[vinay simha bn: added interface setting cmd mode, cmd or video
mode panel setting selection in panel_init based on mode_flags]
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/gpu/drm/panel/Kconfig                      |  11 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
 5 files changed, 725 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
 create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644
index 0000000..35c5ac7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
@@ -0,0 +1,27 @@
+JDI model LT070ME05000 1920x1200 7" DSI Panel
+
+Basic data sheet is at:
+	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+
+This panel has video mode implemented currently in the driver.
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+
+Optional properties:
+- power-supply: phandle of the regulator that provides the supply voltage
+- reset-gpio: phandle of gpio for reset line
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+	dsi@54300000 {
+		panel: panel@0 {
+			compatible = "jdi,lt070me05000";
+			reg = <0>;
+
+			power-supply = <...>;
+			reset-gpio = <...>;
+			backlight = <...>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a580f3e..ec42bb4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -130,6 +130,7 @@ invensense	InvenSense Inc.
 isee	ISEE 2007 S.L.
 isil	Intersil
 issi	Integrated Silicon Solutions Inc.
+jdi	Japan Display Inc.
 jedec	JEDEC Solid State Technology Association
 karo	Ka-Ro electronics GmbH
 keymile	Keymile GmbH
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 1500ab9..f41690e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
 	  To compile this driver as a module, choose M here: the module
 	  will be called panel-sharp-lq101r1sx01.
 
+config DRM_PANEL_JDI_LT070ME05000
+	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
+	  command mode panel as found in Google Nexus 7 (2013) devices.
+	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+	  24 bit RGB per pixel.
+
 config DRM_PANEL_SHARP_LS043T1LE01
 	tristate "Sharp LS043T1LE01 qHD video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f277eed..e6c6fc8 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644
index 0000000..051aa1b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2015 InforceComputing
+ * Author: Vinay Simha BN <simhavcs@gmail.com>
+ *
+ * Copyright (C) 2015 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+/*
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ */
+struct jdi_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	/* TODO: Add backilght support */
+	struct backlight_device *backlight;
+	struct regulator *supply;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *vcc_gpio;
+
+	struct regulator *backlit;
+	struct regulator *lvs7;
+	struct regulator *avdd;
+	struct regulator *iovdd;
+	struct gpio_desc *pwm_gpio;
+
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct jdi_panel, base);
+}
+
+static char MCAP[2] = {0xB0, 0x00};
+static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
+static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
+
+static char interface_ID_setting[2] = {0xB4, 0x0C};
+static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
+
+static char tear_scan_line[3] = {0x44, 0x03, 0x00};
+
+/* for fps control, set fps to 60.32Hz */
+static char LTPS_timing_setting[2] = {0xC6, 0x78};
+static char sequencer_timing_control[2] = {0xD6, 0x01};
+
+/* set brightness */
+static char write_display_brightness[] = {0x51, 0xFF};
+/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
+static char write_control_display[] = {0x53, 0x24};
+/*
+ * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
+ * disable SRE(sunlight readability enhancement)
+ */
+static char write_cabc[] = {0x55, 0x00};
+/* for cabc mode 0x1(-15%) */
+static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
+/* for cabc mode 0x2(-40%) */
+static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
+/* for cabc mode 0x3(-54%) */
+static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
+/* for pwm frequency and dimming control */
+static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
+		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
+		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(10);
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_tear_on(dsi,
+					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+		if (ret < 0)
+			return ret;
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
+					     sizeof(tear_scan_line));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+		mdelay(10);
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting,
+					     sizeof(interface_setting));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		backlight_control4[18] = 0x04;
+		backlight_control4[19] = 0x00;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		MCAP[1] = 0x03;
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+	} else {
+		/*
+		 * TODO : need to verify panel settings when
+		 * dsi cmd mode supported for apq8064 - simhavcs
+		 */
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
+					     sizeof(interface_setting_cmd));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
+					     sizeof(interface_ID_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, DSI_control,
+					     sizeof(DSI_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
+					     sizeof(LTPS_timing_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
+					     sizeof(sequencer_timing_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control1,
+					     sizeof(backlight_control1));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control2,
+					     sizeof(backlight_control2));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control3,
+					     sizeof(backlight_control3));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_off(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(100);
+
+	return 0;
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (!jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("disable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = false;
+
+	return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (!jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("unprepare\n");
+
+	ret = jdi_panel_off(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+		return ret;
+	}
+
+	regulator_disable(jdi->supply);
+
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+
+	jdi->prepared = false;
+
+	return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("prepare\n");
+
+	ret = regulator_enable(jdi->iovdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->avdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->backlit);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->lvs7);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->supply);
+	if (ret < 0)
+		return ret;
+
+	msleep(20);
+
+	if (jdi->vcc_gpio) {
+		gpiod_set_value(jdi->vcc_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->reset_gpio) {
+		gpiod_set_value(jdi->reset_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->pwm_gpio) {
+		gpiod_set_value(jdi->pwm_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->enable_gpio) {
+		gpiod_set_value(jdi->enable_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	ret = jdi_panel_init(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to init panel: %d\n", ret);
+		goto poweroff;
+	}
+
+	ret = jdi_panel_on(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+		goto poweroff;
+	}
+
+	jdi->prepared = true;
+
+	return 0;
+
+poweroff:
+	regulator_disable(jdi->supply);
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("enable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_UNBLANK;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+		.clock = 155000,
+		.hdisplay = 1200,
+		.hsync_start = 1200 + 48,
+		.hsync_end = 1200 + 48 + 32,
+		.htotal = 1200 + 48 + 32 + 60,
+		.vdisplay = 1920,
+		.vsync_start = 1920 + 3,
+		.vsync_end = 1920 + 3 + 5,
+		.vtotal = 1920 + 3 + 5 + 6,
+		.vrefresh = 60,
+		.flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+		.disable = jdi_panel_disable,
+		.unprepare = jdi_panel_unprepare,
+		.prepare = jdi_panel_prepare,
+		.enable = jdi_panel_enable,
+		.get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+		{ .compatible = "jdi,lt070me05000", },
+		{ }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	jdi->mode = &default_mode;
+
+	/* lvs5 */
+	jdi->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	/* l17 */
+	jdi->backlit = devm_regulator_get(dev, "backlit");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
+	if (IS_ERR(jdi->lvs7))
+		return PTR_ERR(jdi->lvs7);
+
+	jdi->avdd = devm_regulator_get(dev, "avdd");
+	if (IS_ERR(jdi->avdd))
+		return PTR_ERR(jdi->avdd);
+
+	jdi->iovdd = devm_regulator_get(dev, "iovdd");
+	if (IS_ERR(jdi->iovdd))
+		return PTR_ERR(jdi->iovdd);
+
+	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->vcc_gpio)) {
+		dev_err(dev, "cannot get vcc-gpio %ld\n",
+			PTR_ERR(jdi->vcc_gpio));
+		jdi->vcc_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->vcc_gpio, 0);
+	}
+
+	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(jdi->reset_gpio));
+		jdi->reset_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->reset_gpio, 0);
+	}
+
+	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpio %ld\n",
+			PTR_ERR(jdi->enable_gpio));
+		jdi->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->enable_gpio, 0);
+	}
+
+	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->pwm_gpio)) {
+		dev_err(dev, "cannot get pwm-gpio %ld\n",
+			PTR_ERR(jdi->pwm_gpio));
+		jdi->pwm_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->pwm_gpio, 0);
+	}
+
+	/* we don't have backlight right now, proceed further */
+#ifdef BACKLIGHT
+	np = of_parse_phandle(dev->of_node, "backlight", 0);
+	if (np) {
+		jdi->backlight = of_find_backlight_by_node(np);
+		of_node_put(np);
+
+		if (!jdi->backlight)
+			return -EPROBE_DEFER;
+	}
+#endif
+
+	drm_panel_init(&jdi->base);
+	jdi->base.funcs = &jdi_panel_funcs;
+	jdi->base.dev = &jdi->dsi->dev;
+
+	ret = drm_panel_add(&jdi->base);
+	if (ret < 0)
+		goto put_backlight;
+
+	return 0;
+
+put_backlight:
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+
+	return ret;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+	if (jdi->base.dev)
+		drm_panel_remove(&jdi->base);
+
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi;
+	int ret;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
+			MIPI_DSI_MODE_VIDEO_HSA |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+	if (!jdi)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, jdi);
+
+	jdi->dsi = dsi;
+
+	ret = jdi_panel_add(jdi);
+	if (ret < 0)
+		return ret;
+
+	return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = jdi_panel_disable(&jdi->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+			ret);
+
+	drm_panel_detach(&jdi->base);
+	jdi_panel_del(jdi);
+
+	return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+	jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+	.driver = {
+		.name = "panel-jdi-lt070me05000",
+		.of_match_table = jdi_of_match,
+	},
+	.probe = jdi_panel_probe,
+	.remove = jdi_panel_remove,
+	.shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
+MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-14 12:38     ` Vinay Simha
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-14 12:38 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Archit Taneja, Sumit Semwal, John Stultz, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, David Airlie,
	Jonathan Cameron, Ralf Baechle, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Thierry Reding,

Thanks for the review. Will address your reviews and resend the patches.

On Wed, Apr 13, 2016 at 7:19 PM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Wed, Apr 13, 2016 at 11:58:04AM +0530, Vinay Simha BN wrote:
>> Add support for the JDI lt070me05000 WUXGA DSI panel used in
>> Nexus 7 2013 devices.
>>
>> Programming sequence for the panel is was originally found in the
>> android-msm-flo-3.4-lollipop-release branch from:
>>     https://android.googlesource.com/kernel/msm.git
>>
>> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
>> file in:
>>     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
>>
>> Other fixes folded in were provided
>> by Archit Taneja <archit.taneja@gmail.com>
>>
>> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
>> [sumit.semwal: Ported to the drm/panel framework]
>> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
>> [jstultz: Cherry-picked to mainline, folded down other fixes
>>  from Vinay and Archit]
>> Signed-off-by: John Stultz <john.stultz@linaro.org>
>> ---
>>  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>>  drivers/gpu/drm/panel/Kconfig                      |  11 +
>>  drivers/gpu/drm/panel/Makefile                     |   1 +
>>  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>>  5 files changed, 725 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>>  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>
> What's the difference between this and the patch you sent earlier? I'm
> going to assume that the newer one is the correct patch, so I'll ignore
> the previous patch.
>
>> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> new file mode 100644
>> index 0000000..35c5ac7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>
> The binding documentation should be a separate patch.
>
>> @@ -0,0 +1,27 @@
>> +JDI model LT070ME05000 1920x1200 7" DSI Panel
>> +
>> +Basic data sheet is at:
>> +     http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> +
>> +This panel has video mode implemented currently in the driver.
>
> That's information irrelevant to the DT binding, since you're presumably
> talking about the Linux drm/panel driver, whereas the DT binding is
> supposed to specify the description of the panel hardware in OS-agnostic
> terms.
>
>> +Required properties:
>> +- compatible: should be "jdi,lt070me05000"
>> +
>> +Optional properties:
>> +- power-supply: phandle of the regulator that provides the supply voltage
>> +- reset-gpio: phandle of gpio for reset line
>> +- backlight: phandle of the backlight device attached to the panel
>> +
>> +Example:
>> +
>> +     dsi@54300000 {
>> +             panel: panel@0 {
>> +                     compatible = "jdi,lt070me05000";
>> +                     reg = <0>;
>> +
>> +                     power-supply = <...>;
>> +                     reset-gpio = <...>;
>> +                     backlight = <...>;
>> +             };
>> +     };
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index a580f3e..ec42bb4 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -130,6 +130,7 @@ invensense        InvenSense Inc.
>>  isee ISEE 2007 S.L.
>>  isil Intersil
>>  issi Integrated Silicon Solutions Inc.
>> +jdi  Japan Display Inc.
>>  jedec        JEDEC Solid State Technology Association
>>  karo Ka-Ro electronics GmbH
>>  keymile      Keymile GmbH
>
> This should be a separate patch as well.
>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 1500ab9..f41690e 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>>         To compile this driver as a module, choose M here: the module
>>         will be called panel-sharp-lq101r1sx01.
>>
>> +config DRM_PANEL_JDI_LT070ME05000
>> +     tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
>> +       command mode panel as found in Google Nexus 7 (2013) devices.
>> +       The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
>> +       24 bit RGB per pixel.
>> +
>>  config DRM_PANEL_SHARP_LS043T1LE01
>>       tristate "Sharp LS043T1LE01 qHD video mode panel"
>>       depends on OF
>
> Please keep these sorted alphabetically. I do realize that the list
> isn't sorted quite correctly at the moment, so you may as well leave
> this as-is and I'll fix up the order when applying and after fixing
> the current ordering.
>
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index f277eed..e6c6fc8 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>
> Same here.
>
>> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> new file mode 100644
>> index 0000000..051aa1b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> @@ -0,0 +1,685 @@
>> +/*
>> + * Copyright (C) 2015 InforceComputing
>> + * Author: Vinay Simha BN <simhavcs@gmail.com>
>> + *
>> + * Copyright (C) 2015 Linaro Ltd
>> + * Author: Sumit Semwal <sumit.semwal@linaro.org>
>
> Should either of those copyright lines include 2016? We're a pretty good
> way into 2016, and I'd be surprised if this wasn't touched this year at
> all.
>
>> +/*
>> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
>> + * JDI model LT070ME05000, and its data sheet is at:
>> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> + */
>
> This comment is oddly placed here above the struct jdi_panel definition.
> Perhaps move that information into the header comment?
>
>> +struct jdi_panel {
>> +     struct drm_panel base;
>> +     struct mipi_dsi_device *dsi;
>> +
>> +     /* TODO: Add backilght support */
>
> "backlight". Also if this is still TODO, why even have parts of the code
> here? Just drop everything that's not used and move that to a follow-up
> patch that implements full support.
>
>> +     struct backlight_device *backlight;
>> +     struct regulator *supply;
>> +     struct gpio_desc *reset_gpio;
>> +     struct gpio_desc *enable_gpio;
>> +     struct gpio_desc *vcc_gpio;
>> +
>> +     struct regulator *backlit;
>> +     struct regulator *lvs7;
>> +     struct regulator *avdd;
>> +     struct regulator *iovdd;
>> +     struct gpio_desc *pwm_gpio;
>
> Most of these resources aren't specified in the device tree binding, so
> your driver doesn't properly implement the binding. You'll have to fix
> the driver or the binding.
>
>> +
>> +     bool prepared;
>> +     bool enabled;
>> +
>> +     const struct drm_display_mode *mode;
>> +};
>> +
>> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
>> +{
>> +     return container_of(panel, struct jdi_panel, base);
>> +}
>> +
>> +static char MCAP[2] = {0xB0, 0x00};
>> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
>> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
>
> Please make all of these (and the ones below) arrays of u8, since that's
> the type used in the prototype of the function that receives these as
> parameters. Also, please use consistent capitalization and a space after
> { and before }.
>
>> +static char interface_ID_setting[2] = {0xB4, 0x0C};
>> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
>> +
>> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
>
> This is a standard DCS command, please turn this into a generic helper
> such as mipi_dsi_dcs_set_tear_scanline().
>
>> +/* for fps control, set fps to 60.32Hz */
>> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
>> +static char sequencer_timing_control[2] = {0xD6, 0x01};
>> +
>> +/* set brightness */
>> +static char write_display_brightness[] = {0x51, 0xFF};
>
> Same here.
>
>> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
>> +static char write_control_display[] = {0x53, 0x24};
>
> And here.
>
>> +/*
>> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
>> + * disable SRE(sunlight readability enhancement)
>> + */
>> +static char write_cabc[] = {0x55, 0x00};
>
> And here.
>
>> +static int jdi_panel_init(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +     if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
>> +             ret = mipi_dsi_dcs_soft_reset(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             mdelay(10);
>
> Please don't use mdelay() because it will busy-loop for a very long time
> and waste precious CPU cycles. Instead, use usleep_range() for these
> cases (or msleep() for durations longer than ~10 ms). Same goes for the
> other occurrences below.
>
>> +
>> +             ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
>> +             if (ret < 0)
>> +                     return ret;
>
> There are symbolic constants for the pixel formats, use them to convey
> meaning.
>
>> +
>> +             ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
>> +             if (ret < 0)
>> +                     return ret;
>
> These should be parameterized on the panel width and height, to make it
> clear where the values come from.
>
>> +
>> +             ret = mipi_dsi_dcs_set_tear_on(dsi,
>> +                                            MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(5);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, tear_scan_line,
>> +                                          sizeof(tear_scan_line));
>> +             if (ret < 0)
>> +                     return ret;
>
> Like I mentioned earlier, this should be a generic helper to help make
> its intention clearer.
>
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
>> +                                             sizeof(write_display_brightness)
>> +                                             );
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
>> +                                             sizeof(write_control_display));
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
>> +                                             sizeof(write_cabc));
>> +             if (ret < 0)
>> +                     return ret;
>
> Same for these. I don't currently have access to the DCS 1.3
> specification, but I suspect that some of the parameters for these
> commands could be defined via symbolic names.
>
>> +
>> +             ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(120);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(10);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, interface_setting,
>> +                                          sizeof(interface_setting));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(20);
>> +
>> +             backlight_control4[18] = 0x04;
>> +             backlight_control4[19] = 0x00;
>> +
>> +             ret = mipi_dsi_generic_write(dsi, backlight_control4,
>> +                                          sizeof(backlight_control4));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(20);
>> +
>> +             MCAP[1] = 0x03;
>> +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +             if (ret < 0)
>> +                     return ret;
>
> That's odd. Doesn't this break idempotence of this function? You modify
> the global MCAP array by writing 0x03 to MCAP[1], so when this function
> is called a second time, MCAP[1] will be 0x03 already in the first call
> a few lines above, instead of the 0x00 that it was initially.
>
>> +     } else {
>> +             /*
>> +              * TODO : need to verify panel settings when
>> +              * dsi cmd mode supported for apq8064 - simhavcs
>> +              */
>> +             ret = mipi_dsi_dcs_soft_reset(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>
> This whole else clause is dead code because the condition for the if
> clause is always true. Just drop it and add it back when you properly
> support both modes.
>
>> +static int jdi_panel_on(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +     ret = mipi_dsi_dcs_set_display_on(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int jdi_panel_off(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>
> Should this not be cleared at the end of the function so that the below
> commands still get run in LP mode?
>
>> +
>> +     ret = mipi_dsi_dcs_set_display_off(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = mipi_dsi_dcs_set_tear_off(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     msleep(100);
>> +
>> +     return 0;
>> +}
>> +
>> +static int jdi_panel_disable(struct drm_panel *panel)
>> +{
>> +     struct jdi_panel *jdi = to_jdi_panel(panel);
>> +
>> +     if (!jdi->enabled)
>> +             return 0;
>> +
>> +     DRM_DEBUG("disable\n");
>
> This doesn't look very useful debug information. I think you should
> remove it. Same for other similar occurrences below.
>
>> +static const struct drm_panel_funcs jdi_panel_funcs = {
>> +             .disable = jdi_panel_disable,
>> +             .unprepare = jdi_panel_unprepare,
>> +             .prepare = jdi_panel_prepare,
>> +             .enable = jdi_panel_enable,
>> +             .get_modes = jdi_panel_get_modes,
>> +};
>> +
>> +static const struct of_device_id jdi_of_match[] = {
>> +             { .compatible = "jdi,lt070me05000", },
>> +             { }
>> +};
>> +MODULE_DEVICE_TABLE(of, jdi_of_match);
>
> A single tab is enough to indent the above.
>
>> +static int jdi_panel_add(struct jdi_panel *jdi)
>> +{
>> +     struct device *dev = &jdi->dsi->dev;
>> +     int ret;
>> +
>> +     jdi->mode = &default_mode;
>
> What do you keep this around for?
>
>> +
>> +     /* lvs5 */
>> +     jdi->supply = devm_regulator_get(dev, "power");
>> +     if (IS_ERR(jdi->supply))
>> +             return PTR_ERR(jdi->supply);
>> +
>> +     /* l17 */
>> +     jdi->backlit = devm_regulator_get(dev, "backlit");
>> +     if (IS_ERR(jdi->supply))
>> +             return PTR_ERR(jdi->supply);
>> +
>> +     jdi->lvs7 = devm_regulator_get(dev, "lvs7");
>> +     if (IS_ERR(jdi->lvs7))
>> +             return PTR_ERR(jdi->lvs7);
>> +
>> +     jdi->avdd = devm_regulator_get(dev, "avdd");
>> +     if (IS_ERR(jdi->avdd))
>> +             return PTR_ERR(jdi->avdd);
>> +
>> +     jdi->iovdd = devm_regulator_get(dev, "iovdd");
>> +     if (IS_ERR(jdi->iovdd))
>> +             return PTR_ERR(jdi->iovdd);
>> +
>> +     jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->vcc_gpio)) {
>> +             dev_err(dev, "cannot get vcc-gpio %ld\n",
>> +                     PTR_ERR(jdi->vcc_gpio));
>> +             jdi->vcc_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->vcc_gpio, 0);
>> +     }
>> +
>> +     jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->reset_gpio)) {
>> +             dev_err(dev, "cannot get reset-gpios %ld\n",
>> +                     PTR_ERR(jdi->reset_gpio));
>> +             jdi->reset_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->reset_gpio, 0);
>> +     }
>> +
>> +     jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->enable_gpio)) {
>> +             dev_err(dev, "cannot get enable-gpio %ld\n",
>> +                     PTR_ERR(jdi->enable_gpio));
>> +             jdi->enable_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->enable_gpio, 0);
>> +     }
>> +
>> +     jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->pwm_gpio)) {
>> +             dev_err(dev, "cannot get pwm-gpio %ld\n",
>> +                     PTR_ERR(jdi->pwm_gpio));
>> +             jdi->pwm_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->pwm_gpio, 0);
>> +     }
>
> As I said earlier, most of these aren't specified in the binding, fix
> either the binding or the code.
>
>> +     /* we don't have backlight right now, proceed further */
>> +#ifdef BACKLIGHT
>> +     np = of_parse_phandle(dev->of_node, "backlight", 0);
>> +     if (np) {
>> +             jdi->backlight = of_find_backlight_by_node(np);
>> +             of_node_put(np);
>> +
>> +             if (!jdi->backlight)
>> +                     return -EPROBE_DEFER;
>> +     }
>> +#endif
>
> Again, if you don't support it, don't submit it. We don't want dead code
> in the kernel.
>
>> +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
>> +MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
>> +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
>
> Technically this doesn't support command mode yet, so you might want to
> remove that from the description to avoid confusion.
>
> Thierry



-- 
Regards,

Vinay Simha.B.N.

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-14 12:38     ` Vinay Simha
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-14 12:38 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Archit Taneja, Sumit Semwal, John Stultz, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, David Airlie,
	Jonathan Cameron, Ralf Baechle, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Thierry Reding,

Thanks for the review. Will address your reviews and resend the patches.

On Wed, Apr 13, 2016 at 7:19 PM, Thierry Reding
<thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Wed, Apr 13, 2016 at 11:58:04AM +0530, Vinay Simha BN wrote:
>> Add support for the JDI lt070me05000 WUXGA DSI panel used in
>> Nexus 7 2013 devices.
>>
>> Programming sequence for the panel is was originally found in the
>> android-msm-flo-3.4-lollipop-release branch from:
>>     https://android.googlesource.com/kernel/msm.git
>>
>> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
>> file in:
>>     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
>>
>> Other fixes folded in were provided
>> by Archit Taneja <archit.taneja-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>
>> Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> [sumit.semwal: Ported to the drm/panel framework]
>> Signed-off-by: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> [jstultz: Cherry-picked to mainline, folded down other fixes
>>  from Vinay and Archit]
>> Signed-off-by: John Stultz <john.stultz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>>  drivers/gpu/drm/panel/Kconfig                      |  11 +
>>  drivers/gpu/drm/panel/Makefile                     |   1 +
>>  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>>  5 files changed, 725 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>>  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>
> What's the difference between this and the patch you sent earlier? I'm
> going to assume that the newer one is the correct patch, so I'll ignore
> the previous patch.
>
>> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> new file mode 100644
>> index 0000000..35c5ac7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>
> The binding documentation should be a separate patch.
>
>> @@ -0,0 +1,27 @@
>> +JDI model LT070ME05000 1920x1200 7" DSI Panel
>> +
>> +Basic data sheet is at:
>> +     http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> +
>> +This panel has video mode implemented currently in the driver.
>
> That's information irrelevant to the DT binding, since you're presumably
> talking about the Linux drm/panel driver, whereas the DT binding is
> supposed to specify the description of the panel hardware in OS-agnostic
> terms.
>
>> +Required properties:
>> +- compatible: should be "jdi,lt070me05000"
>> +
>> +Optional properties:
>> +- power-supply: phandle of the regulator that provides the supply voltage
>> +- reset-gpio: phandle of gpio for reset line
>> +- backlight: phandle of the backlight device attached to the panel
>> +
>> +Example:
>> +
>> +     dsi@54300000 {
>> +             panel: panel@0 {
>> +                     compatible = "jdi,lt070me05000";
>> +                     reg = <0>;
>> +
>> +                     power-supply = <...>;
>> +                     reset-gpio = <...>;
>> +                     backlight = <...>;
>> +             };
>> +     };
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index a580f3e..ec42bb4 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -130,6 +130,7 @@ invensense        InvenSense Inc.
>>  isee ISEE 2007 S.L.
>>  isil Intersil
>>  issi Integrated Silicon Solutions Inc.
>> +jdi  Japan Display Inc.
>>  jedec        JEDEC Solid State Technology Association
>>  karo Ka-Ro electronics GmbH
>>  keymile      Keymile GmbH
>
> This should be a separate patch as well.
>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 1500ab9..f41690e 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>>         To compile this driver as a module, choose M here: the module
>>         will be called panel-sharp-lq101r1sx01.
>>
>> +config DRM_PANEL_JDI_LT070ME05000
>> +     tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
>> +       command mode panel as found in Google Nexus 7 (2013) devices.
>> +       The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
>> +       24 bit RGB per pixel.
>> +
>>  config DRM_PANEL_SHARP_LS043T1LE01
>>       tristate "Sharp LS043T1LE01 qHD video mode panel"
>>       depends on OF
>
> Please keep these sorted alphabetically. I do realize that the list
> isn't sorted quite correctly at the moment, so you may as well leave
> this as-is and I'll fix up the order when applying and after fixing
> the current ordering.
>
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index f277eed..e6c6fc8 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>
> Same here.
>
>> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> new file mode 100644
>> index 0000000..051aa1b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> @@ -0,0 +1,685 @@
>> +/*
>> + * Copyright (C) 2015 InforceComputing
>> + * Author: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + *
>> + * Copyright (C) 2015 Linaro Ltd
>> + * Author: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>
> Should either of those copyright lines include 2016? We're a pretty good
> way into 2016, and I'd be surprised if this wasn't touched this year at
> all.
>
>> +/*
>> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
>> + * JDI model LT070ME05000, and its data sheet is at:
>> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> + */
>
> This comment is oddly placed here above the struct jdi_panel definition.
> Perhaps move that information into the header comment?
>
>> +struct jdi_panel {
>> +     struct drm_panel base;
>> +     struct mipi_dsi_device *dsi;
>> +
>> +     /* TODO: Add backilght support */
>
> "backlight". Also if this is still TODO, why even have parts of the code
> here? Just drop everything that's not used and move that to a follow-up
> patch that implements full support.
>
>> +     struct backlight_device *backlight;
>> +     struct regulator *supply;
>> +     struct gpio_desc *reset_gpio;
>> +     struct gpio_desc *enable_gpio;
>> +     struct gpio_desc *vcc_gpio;
>> +
>> +     struct regulator *backlit;
>> +     struct regulator *lvs7;
>> +     struct regulator *avdd;
>> +     struct regulator *iovdd;
>> +     struct gpio_desc *pwm_gpio;
>
> Most of these resources aren't specified in the device tree binding, so
> your driver doesn't properly implement the binding. You'll have to fix
> the driver or the binding.
>
>> +
>> +     bool prepared;
>> +     bool enabled;
>> +
>> +     const struct drm_display_mode *mode;
>> +};
>> +
>> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
>> +{
>> +     return container_of(panel, struct jdi_panel, base);
>> +}
>> +
>> +static char MCAP[2] = {0xB0, 0x00};
>> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
>> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
>
> Please make all of these (and the ones below) arrays of u8, since that's
> the type used in the prototype of the function that receives these as
> parameters. Also, please use consistent capitalization and a space after
> { and before }.
>
>> +static char interface_ID_setting[2] = {0xB4, 0x0C};
>> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
>> +
>> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
>
> This is a standard DCS command, please turn this into a generic helper
> such as mipi_dsi_dcs_set_tear_scanline().
>
>> +/* for fps control, set fps to 60.32Hz */
>> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
>> +static char sequencer_timing_control[2] = {0xD6, 0x01};
>> +
>> +/* set brightness */
>> +static char write_display_brightness[] = {0x51, 0xFF};
>
> Same here.
>
>> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
>> +static char write_control_display[] = {0x53, 0x24};
>
> And here.
>
>> +/*
>> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
>> + * disable SRE(sunlight readability enhancement)
>> + */
>> +static char write_cabc[] = {0x55, 0x00};
>
> And here.
>
>> +static int jdi_panel_init(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +     if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
>> +             ret = mipi_dsi_dcs_soft_reset(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             mdelay(10);
>
> Please don't use mdelay() because it will busy-loop for a very long time
> and waste precious CPU cycles. Instead, use usleep_range() for these
> cases (or msleep() for durations longer than ~10 ms). Same goes for the
> other occurrences below.
>
>> +
>> +             ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
>> +             if (ret < 0)
>> +                     return ret;
>
> There are symbolic constants for the pixel formats, use them to convey
> meaning.
>
>> +
>> +             ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
>> +             if (ret < 0)
>> +                     return ret;
>
> These should be parameterized on the panel width and height, to make it
> clear where the values come from.
>
>> +
>> +             ret = mipi_dsi_dcs_set_tear_on(dsi,
>> +                                            MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(5);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, tear_scan_line,
>> +                                          sizeof(tear_scan_line));
>> +             if (ret < 0)
>> +                     return ret;
>
> Like I mentioned earlier, this should be a generic helper to help make
> its intention clearer.
>
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
>> +                                             sizeof(write_display_brightness)
>> +                                             );
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
>> +                                             sizeof(write_control_display));
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
>> +                                             sizeof(write_cabc));
>> +             if (ret < 0)
>> +                     return ret;
>
> Same for these. I don't currently have access to the DCS 1.3
> specification, but I suspect that some of the parameters for these
> commands could be defined via symbolic names.
>
>> +
>> +             ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(120);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(10);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, interface_setting,
>> +                                          sizeof(interface_setting));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(20);
>> +
>> +             backlight_control4[18] = 0x04;
>> +             backlight_control4[19] = 0x00;
>> +
>> +             ret = mipi_dsi_generic_write(dsi, backlight_control4,
>> +                                          sizeof(backlight_control4));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(20);
>> +
>> +             MCAP[1] = 0x03;
>> +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +             if (ret < 0)
>> +                     return ret;
>
> That's odd. Doesn't this break idempotence of this function? You modify
> the global MCAP array by writing 0x03 to MCAP[1], so when this function
> is called a second time, MCAP[1] will be 0x03 already in the first call
> a few lines above, instead of the 0x00 that it was initially.
>
>> +     } else {
>> +             /*
>> +              * TODO : need to verify panel settings when
>> +              * dsi cmd mode supported for apq8064 - simhavcs
>> +              */
>> +             ret = mipi_dsi_dcs_soft_reset(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>
> This whole else clause is dead code because the condition for the if
> clause is always true. Just drop it and add it back when you properly
> support both modes.
>
>> +static int jdi_panel_on(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +     ret = mipi_dsi_dcs_set_display_on(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int jdi_panel_off(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>
> Should this not be cleared at the end of the function so that the below
> commands still get run in LP mode?
>
>> +
>> +     ret = mipi_dsi_dcs_set_display_off(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = mipi_dsi_dcs_set_tear_off(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     msleep(100);
>> +
>> +     return 0;
>> +}
>> +
>> +static int jdi_panel_disable(struct drm_panel *panel)
>> +{
>> +     struct jdi_panel *jdi = to_jdi_panel(panel);
>> +
>> +     if (!jdi->enabled)
>> +             return 0;
>> +
>> +     DRM_DEBUG("disable\n");
>
> This doesn't look very useful debug information. I think you should
> remove it. Same for other similar occurrences below.
>
>> +static const struct drm_panel_funcs jdi_panel_funcs = {
>> +             .disable = jdi_panel_disable,
>> +             .unprepare = jdi_panel_unprepare,
>> +             .prepare = jdi_panel_prepare,
>> +             .enable = jdi_panel_enable,
>> +             .get_modes = jdi_panel_get_modes,
>> +};
>> +
>> +static const struct of_device_id jdi_of_match[] = {
>> +             { .compatible = "jdi,lt070me05000", },
>> +             { }
>> +};
>> +MODULE_DEVICE_TABLE(of, jdi_of_match);
>
> A single tab is enough to indent the above.
>
>> +static int jdi_panel_add(struct jdi_panel *jdi)
>> +{
>> +     struct device *dev = &jdi->dsi->dev;
>> +     int ret;
>> +
>> +     jdi->mode = &default_mode;
>
> What do you keep this around for?
>
>> +
>> +     /* lvs5 */
>> +     jdi->supply = devm_regulator_get(dev, "power");
>> +     if (IS_ERR(jdi->supply))
>> +             return PTR_ERR(jdi->supply);
>> +
>> +     /* l17 */
>> +     jdi->backlit = devm_regulator_get(dev, "backlit");
>> +     if (IS_ERR(jdi->supply))
>> +             return PTR_ERR(jdi->supply);
>> +
>> +     jdi->lvs7 = devm_regulator_get(dev, "lvs7");
>> +     if (IS_ERR(jdi->lvs7))
>> +             return PTR_ERR(jdi->lvs7);
>> +
>> +     jdi->avdd = devm_regulator_get(dev, "avdd");
>> +     if (IS_ERR(jdi->avdd))
>> +             return PTR_ERR(jdi->avdd);
>> +
>> +     jdi->iovdd = devm_regulator_get(dev, "iovdd");
>> +     if (IS_ERR(jdi->iovdd))
>> +             return PTR_ERR(jdi->iovdd);
>> +
>> +     jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->vcc_gpio)) {
>> +             dev_err(dev, "cannot get vcc-gpio %ld\n",
>> +                     PTR_ERR(jdi->vcc_gpio));
>> +             jdi->vcc_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->vcc_gpio, 0);
>> +     }
>> +
>> +     jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->reset_gpio)) {
>> +             dev_err(dev, "cannot get reset-gpios %ld\n",
>> +                     PTR_ERR(jdi->reset_gpio));
>> +             jdi->reset_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->reset_gpio, 0);
>> +     }
>> +
>> +     jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->enable_gpio)) {
>> +             dev_err(dev, "cannot get enable-gpio %ld\n",
>> +                     PTR_ERR(jdi->enable_gpio));
>> +             jdi->enable_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->enable_gpio, 0);
>> +     }
>> +
>> +     jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->pwm_gpio)) {
>> +             dev_err(dev, "cannot get pwm-gpio %ld\n",
>> +                     PTR_ERR(jdi->pwm_gpio));
>> +             jdi->pwm_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->pwm_gpio, 0);
>> +     }
>
> As I said earlier, most of these aren't specified in the binding, fix
> either the binding or the code.
>
>> +     /* we don't have backlight right now, proceed further */
>> +#ifdef BACKLIGHT
>> +     np = of_parse_phandle(dev->of_node, "backlight", 0);
>> +     if (np) {
>> +             jdi->backlight = of_find_backlight_by_node(np);
>> +             of_node_put(np);
>> +
>> +             if (!jdi->backlight)
>> +                     return -EPROBE_DEFER;
>> +     }
>> +#endif
>
> Again, if you don't support it, don't submit it. We don't want dead code
> in the kernel.
>
>> +MODULE_AUTHOR("Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
>> +MODULE_AUTHOR("Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
>> +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
>
> Technically this doesn't support command mode yet, so you might want to
> remove that from the description to avoid confusion.
>
> Thierry



-- 
Regards,

Vinay Simha.B.N.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
  2016-04-13  6:28 ` Vinay Simha BN
@ 2016-04-14 14:40   ` Archit Taneja
  -1 siblings, 0 replies; 39+ messages in thread
From: Archit Taneja @ 2016-04-14 14:40 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Pawel Moll, Ian Campbell, open list:DRM PANEL DRIVERS, open list,
	Ralf Baechle, Rob Herring, Jonathan Cameron, Kumar Gala,
	Shawn Guo, Archit Taneja



On 4/13/2016 11:58 AM, Vinay Simha BN wrote:
> Add support for the JDI lt070me05000 WUXGA DSI panel used in
> Nexus 7 2013 devices.
>
> Programming sequence for the panel is was originally found in the
> android-msm-flo-3.4-lollipop-release branch from:
>      https://android.googlesource.com/kernel/msm.git
>
> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
> file in:
>      git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
>
> Other fixes folded in were provided
> by Archit Taneja <archit.taneja@gmail.com>
>
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> [sumit.semwal: Ported to the drm/panel framework]
> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
> [jstultz: Cherry-picked to mainline, folded down other fixes
>   from Vinay and Archit]
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> ---
>   .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>   .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>   drivers/gpu/drm/panel/Kconfig                      |  11 +
>   drivers/gpu/drm/panel/Makefile                     |   1 +
>   drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>   5 files changed, 725 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>   create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>
> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> new file mode 100644
> index 0000000..35c5ac7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> @@ -0,0 +1,27 @@
> +JDI model LT070ME05000 1920x1200 7" DSI Panel
> +
> +Basic data sheet is at:
> +	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> +
> +This panel has video mode implemented currently in the driver.
> +
> +Required properties:
> +- compatible: should be "jdi,lt070me05000"
> +
> +Optional properties:
> +- power-supply: phandle of the regulator that provides the supply voltage
> +- reset-gpio: phandle of gpio for reset line
> +- backlight: phandle of the backlight device attached to the panel
> +
> +Example:
> +
> +	dsi@54300000 {
> +		panel: panel@0 {
> +			compatible = "jdi,lt070me05000";
> +			reg = <0>;
> +
> +			power-supply = <...>;
> +			reset-gpio = <...>;
> +			backlight = <...>;
> +		};
> +	};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index a580f3e..ec42bb4 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -130,6 +130,7 @@ invensense	InvenSense Inc.
>   isee	ISEE 2007 S.L.
>   isil	Intersil
>   issi	Integrated Silicon Solutions Inc.
> +jdi	Japan Display Inc.
>   jedec	JEDEC Solid State Technology Association
>   karo	Ka-Ro electronics GmbH
>   keymile	Keymile GmbH
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 1500ab9..f41690e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>   	  To compile this driver as a module, choose M here: the module
>   	  will be called panel-sharp-lq101r1sx01.
>
> +config DRM_PANEL_JDI_LT070ME05000
> +	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
> +	  command mode panel as found in Google Nexus 7 (2013) devices.
> +	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
> +	  24 bit RGB per pixel.
> +
>   config DRM_PANEL_SHARP_LS043T1LE01
>   	tristate "Sharp LS043T1LE01 qHD video mode panel"
>   	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index f277eed..e6c6fc8 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> new file mode 100644
> index 0000000..051aa1b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> @@ -0,0 +1,685 @@
> +/*
> + * Copyright (C) 2015 InforceComputing
> + * Author: Vinay Simha BN <simhavcs@gmail.com>
> + *
> + * Copyright (C) 2015 Linaro Ltd
> + * Author: Sumit Semwal <sumit.semwal@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +
> +#include <video/mipi_display.h>
> +
> +/*
> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
> + * JDI model LT070ME05000, and its data sheet is at:
> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> + */
> +struct jdi_panel {
> +	struct drm_panel base;
> +	struct mipi_dsi_device *dsi;
> +
> +	/* TODO: Add backilght support */
> +	struct backlight_device *backlight;
> +	struct regulator *supply;
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *enable_gpio;
> +	struct gpio_desc *vcc_gpio;
> +
> +	struct regulator *backlit;
> +	struct regulator *lvs7;
> +	struct regulator *avdd;
> +	struct regulator *iovdd;
> +	struct gpio_desc *pwm_gpio;
> +
> +	bool prepared;
> +	bool enabled;
> +
> +	const struct drm_display_mode *mode;
> +};
> +
> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct jdi_panel, base);
> +}
> +
> +static char MCAP[2] = {0xB0, 0x00};
> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
> +
> +static char interface_ID_setting[2] = {0xB4, 0x0C};
> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
> +
> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
> +
> +/* for fps control, set fps to 60.32Hz */
> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
> +static char sequencer_timing_control[2] = {0xD6, 0x01};
> +
> +/* set brightness */
> +static char write_display_brightness[] = {0x51, 0xFF};
> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
> +static char write_control_display[] = {0x53, 0x24};
> +/*
> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
> + * disable SRE(sunlight readability enhancement)
> + */
> +static char write_cabc[] = {0x55, 0x00};
> +/* for cabc mode 0x1(-15%) */
> +static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
> +/* for cabc mode 0x2(-40%) */
> +static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
> +/* for cabc mode 0x3(-54%) */
> +static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
> +/* for pwm frequency and dimming control */
> +static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
> +		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
> +		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};

Apart from the u8 conversion as mentioned by Thierry, you should
convert these to const too.

> +
> +static int jdi_panel_init(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(10);
> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_tear_on(dsi,
> +					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
> +					     sizeof(tear_scan_line));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(10);
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting,
> +					     sizeof(interface_setting));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		backlight_control4[18] = 0x04;
> +		backlight_control4[19] = 0x00;

Any reason why these are overwritten? It would be better to have these
values in the array itself.

> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		MCAP[1] = 0x03;
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		/*
> +		 * TODO : need to verify panel settings when
> +		 * dsi cmd mode supported for apq8064 - simhavcs
> +		 */
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
> +					     sizeof(interface_setting_cmd));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
> +					     sizeof(interface_ID_setting));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, DSI_control,
> +					     sizeof(DSI_control));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
> +					     sizeof(LTPS_timing_setting));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
> +					     sizeof(sequencer_timing_control));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control1,
> +					     sizeof(backlight_control1));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control2,
> +					     sizeof(backlight_control2));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control3,
> +					     sizeof(backlight_control3));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_on(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_off(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_tear_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(100);
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_disable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (!jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("disable\n");
> +
> +	if (jdi->backlight) {
> +		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
> +		backlight_update_status(jdi->backlight);
> +	}
> +
> +	jdi->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_unprepare(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +	int ret;
> +
> +	if (!jdi->prepared)
> +		return 0;
> +
> +	DRM_DEBUG("unprepare\n");
> +
> +	ret = jdi_panel_off(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
> +		return ret;
> +	}
> +
> +	regulator_disable(jdi->supply);
> +
> +	if (jdi->vcc_gpio)
> +		gpiod_set_value(jdi->vcc_gpio, 0);
> +
> +	if (jdi->reset_gpio)
> +		gpiod_set_value(jdi->reset_gpio, 0);
> +
> +	if (jdi->enable_gpio)
> +		gpiod_set_value(jdi->enable_gpio, 0);
> +
> +	jdi->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_prepare(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +	int ret;
> +
> +	if (jdi->prepared)
> +		return 0;
> +
> +	DRM_DEBUG("prepare\n");
> +
> +	ret = regulator_enable(jdi->iovdd);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->avdd);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->backlit);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->lvs7);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->supply);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(20);
> +
> +	if (jdi->vcc_gpio) {
> +		gpiod_set_value(jdi->vcc_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->reset_gpio) {
> +		gpiod_set_value(jdi->reset_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->pwm_gpio) {
> +		gpiod_set_value(jdi->pwm_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->enable_gpio) {
> +		gpiod_set_value(jdi->enable_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	ret = jdi_panel_init(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to init panel: %d\n", ret);
> +		goto poweroff;
> +	}
> +
> +	ret = jdi_panel_on(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
> +		goto poweroff;
> +	}
> +
> +	jdi->prepared = true;
> +
> +	return 0;
> +
> +poweroff:
> +	regulator_disable(jdi->supply);
> +	if (jdi->reset_gpio)
> +		gpiod_set_value(jdi->reset_gpio, 0);
> +	if (jdi->enable_gpio)
> +		gpiod_set_value(jdi->enable_gpio, 0);
> +	if (jdi->vcc_gpio)
> +		gpiod_set_value(jdi->vcc_gpio, 0);
> +
> +	return ret;
> +}
> +
> +static int jdi_panel_enable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("enable\n");
> +
> +	if (jdi->backlight) {
> +		jdi->backlight->props.power = FB_BLANK_UNBLANK;
> +		backlight_update_status(jdi->backlight);
> +	}
> +
> +	jdi->enabled = true;
> +
> +	return 0;
> +}

The prepare/enable split seems a bit strange. Don't we want to put
mipi_dsi_dcs_set_display_on in the enable() op too?

> +
> +static const struct drm_display_mode default_mode = {
> +		.clock = 155000,

htotal * vtotal * vrefresh (1934 * 1340 * 60.32(as in comment)) comes
at around 156.3 Mhz. Could you plug in the correct frequency?

> +		.hdisplay = 1200,
> +		.hsync_start = 1200 + 48,
> +		.hsync_end = 1200 + 48 + 32,
> +		.htotal = 1200 + 48 + 32 + 60,
> +		.vdisplay = 1920,
> +		.vsync_start = 1920 + 3,
> +		.vsync_end = 1920 + 3 + 5,
> +		.vtotal = 1920 + 3 + 5 + 6,
> +		.vrefresh = 60,
> +		.flags = 0,
> +};
> +
> +static int jdi_panel_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
> +			default_mode.hdisplay, default_mode.vdisplay,
> +			default_mode.vrefresh);

We should use the dsi->dev device pointer here like used elsewhere, not
the drm device pointer.

> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs jdi_panel_funcs = {
> +		.disable = jdi_panel_disable,
> +		.unprepare = jdi_panel_unprepare,
> +		.prepare = jdi_panel_prepare,
> +		.enable = jdi_panel_enable,
> +		.get_modes = jdi_panel_get_modes,
> +};
> +
> +static const struct of_device_id jdi_of_match[] = {
> +		{ .compatible = "jdi,lt070me05000", },
> +		{ }
> +};
> +MODULE_DEVICE_TABLE(of, jdi_of_match);
> +
> +static int jdi_panel_add(struct jdi_panel *jdi)
> +{
> +	struct device *dev = &jdi->dsi->dev;
> +	int ret;
> +
> +	jdi->mode = &default_mode;
> +
> +	/* lvs5 */
> +	jdi->supply = devm_regulator_get(dev, "power");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);

You could consider using regulator_get_bulk() api here, it would clean
up the code a bit.

> +
> +	/* l17 */
> +	jdi->backlit = devm_regulator_get(dev, "backlit");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
> +	if (IS_ERR(jdi->lvs7))
> +		return PTR_ERR(jdi->lvs7);
> +
> +	jdi->avdd = devm_regulator_get(dev, "avdd");
> +	if (IS_ERR(jdi->avdd))
> +		return PTR_ERR(jdi->avdd);
> +
> +	jdi->iovdd = devm_regulator_get(dev, "iovdd");
> +	if (IS_ERR(jdi->iovdd))
> +		return PTR_ERR(jdi->iovdd);
> +
> +	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->vcc_gpio)) {
> +		dev_err(dev, "cannot get vcc-gpio %ld\n",
> +			PTR_ERR(jdi->vcc_gpio));
> +		jdi->vcc_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->vcc_gpio, 0);
> +	}
> +
> +	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpios %ld\n",
> +			PTR_ERR(jdi->reset_gpio));
> +		jdi->reset_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->reset_gpio, 0);
> +	}
> +
> +	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->enable_gpio)) {
> +		dev_err(dev, "cannot get enable-gpio %ld\n",
> +			PTR_ERR(jdi->enable_gpio));
> +		jdi->enable_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->enable_gpio, 0);
> +	}
> +
> +	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->pwm_gpio)) {
> +		dev_err(dev, "cannot get pwm-gpio %ld\n",
> +			PTR_ERR(jdi->pwm_gpio));
> +		jdi->pwm_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->pwm_gpio, 0);
> +	}
> +
> +	/* we don't have backlight right now, proceed further */
> +#ifdef BACKLIGHT
> +	np = of_parse_phandle(dev->of_node, "backlight", 0);
> +	if (np) {
> +		jdi->backlight = of_find_backlight_by_node(np);
> +		of_node_put(np);
> +
> +		if (!jdi->backlight)
> +			return -EPROBE_DEFER;
> +	}
> +#endif
> +
> +	drm_panel_init(&jdi->base);
> +	jdi->base.funcs = &jdi_panel_funcs;
> +	jdi->base.dev = &jdi->dsi->dev;
> +
> +	ret = drm_panel_add(&jdi->base);
> +	if (ret < 0)
> +		goto put_backlight;
> +
> +	return 0;
> +
> +put_backlight:
> +	if (jdi->backlight)
> +		put_device(&jdi->backlight->dev);
> +
> +	return ret;
> +}
> +
> +static void jdi_panel_del(struct jdi_panel *jdi)
> +{
> +	if (jdi->base.dev)
> +		drm_panel_remove(&jdi->base);
> +
> +	if (jdi->backlight)
> +		put_device(&jdi->backlight->dev);
> +}
> +
> +static int jdi_panel_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi;
> +	int ret;
> +
> +	dsi->lanes = 4;
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
> +			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
> +			MIPI_DSI_MODE_VIDEO_HSA |
> +			MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
> +	if (!jdi)
> +		return -ENOMEM;
> +
> +	mipi_dsi_set_drvdata(dsi, jdi);
> +
> +	jdi->dsi = dsi;
> +
> +	ret = jdi_panel_add(jdi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return mipi_dsi_attach(dsi);
> +}
> +
> +static int jdi_panel_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
> +	int ret;
> +
> +	ret = jdi_panel_disable(&jdi->base);
> +	if (ret < 0)
> +		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);

Ideally, the driver's remove op shouldn't try to disable the panel. It
should be done by the dsi host's connector itself. We can drop this
unless there is a specfic reason to do so.

Thanks for working on this.

Archit

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-14 14:40   ` Archit Taneja
  0 siblings, 0 replies; 39+ messages in thread
From: Archit Taneja @ 2016-04-14 14:40 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Pawel Moll, Ian Campbell, Archit Taneja, open list,
	open list:DRM PANEL DRIVERS, Rob Herring, Ralf Baechle,
	Kumar Gala, Shawn Guo, Jonathan Cameron



On 4/13/2016 11:58 AM, Vinay Simha BN wrote:
> Add support for the JDI lt070me05000 WUXGA DSI panel used in
> Nexus 7 2013 devices.
>
> Programming sequence for the panel is was originally found in the
> android-msm-flo-3.4-lollipop-release branch from:
>      https://android.googlesource.com/kernel/msm.git
>
> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
> file in:
>      git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
>
> Other fixes folded in were provided
> by Archit Taneja <archit.taneja@gmail.com>
>
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> [sumit.semwal: Ported to the drm/panel framework]
> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
> [jstultz: Cherry-picked to mainline, folded down other fixes
>   from Vinay and Archit]
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> ---
>   .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>   .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>   drivers/gpu/drm/panel/Kconfig                      |  11 +
>   drivers/gpu/drm/panel/Makefile                     |   1 +
>   drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>   5 files changed, 725 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>   create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>
> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> new file mode 100644
> index 0000000..35c5ac7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> @@ -0,0 +1,27 @@
> +JDI model LT070ME05000 1920x1200 7" DSI Panel
> +
> +Basic data sheet is at:
> +	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> +
> +This panel has video mode implemented currently in the driver.
> +
> +Required properties:
> +- compatible: should be "jdi,lt070me05000"
> +
> +Optional properties:
> +- power-supply: phandle of the regulator that provides the supply voltage
> +- reset-gpio: phandle of gpio for reset line
> +- backlight: phandle of the backlight device attached to the panel
> +
> +Example:
> +
> +	dsi@54300000 {
> +		panel: panel@0 {
> +			compatible = "jdi,lt070me05000";
> +			reg = <0>;
> +
> +			power-supply = <...>;
> +			reset-gpio = <...>;
> +			backlight = <...>;
> +		};
> +	};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index a580f3e..ec42bb4 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -130,6 +130,7 @@ invensense	InvenSense Inc.
>   isee	ISEE 2007 S.L.
>   isil	Intersil
>   issi	Integrated Silicon Solutions Inc.
> +jdi	Japan Display Inc.
>   jedec	JEDEC Solid State Technology Association
>   karo	Ka-Ro electronics GmbH
>   keymile	Keymile GmbH
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 1500ab9..f41690e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>   	  To compile this driver as a module, choose M here: the module
>   	  will be called panel-sharp-lq101r1sx01.
>
> +config DRM_PANEL_JDI_LT070ME05000
> +	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
> +	  command mode panel as found in Google Nexus 7 (2013) devices.
> +	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
> +	  24 bit RGB per pixel.
> +
>   config DRM_PANEL_SHARP_LS043T1LE01
>   	tristate "Sharp LS043T1LE01 qHD video mode panel"
>   	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index f277eed..e6c6fc8 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> new file mode 100644
> index 0000000..051aa1b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> @@ -0,0 +1,685 @@
> +/*
> + * Copyright (C) 2015 InforceComputing
> + * Author: Vinay Simha BN <simhavcs@gmail.com>
> + *
> + * Copyright (C) 2015 Linaro Ltd
> + * Author: Sumit Semwal <sumit.semwal@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +
> +#include <video/mipi_display.h>
> +
> +/*
> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
> + * JDI model LT070ME05000, and its data sheet is at:
> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> + */
> +struct jdi_panel {
> +	struct drm_panel base;
> +	struct mipi_dsi_device *dsi;
> +
> +	/* TODO: Add backilght support */
> +	struct backlight_device *backlight;
> +	struct regulator *supply;
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *enable_gpio;
> +	struct gpio_desc *vcc_gpio;
> +
> +	struct regulator *backlit;
> +	struct regulator *lvs7;
> +	struct regulator *avdd;
> +	struct regulator *iovdd;
> +	struct gpio_desc *pwm_gpio;
> +
> +	bool prepared;
> +	bool enabled;
> +
> +	const struct drm_display_mode *mode;
> +};
> +
> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct jdi_panel, base);
> +}
> +
> +static char MCAP[2] = {0xB0, 0x00};
> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
> +
> +static char interface_ID_setting[2] = {0xB4, 0x0C};
> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
> +
> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
> +
> +/* for fps control, set fps to 60.32Hz */
> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
> +static char sequencer_timing_control[2] = {0xD6, 0x01};
> +
> +/* set brightness */
> +static char write_display_brightness[] = {0x51, 0xFF};
> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
> +static char write_control_display[] = {0x53, 0x24};
> +/*
> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
> + * disable SRE(sunlight readability enhancement)
> + */
> +static char write_cabc[] = {0x55, 0x00};
> +/* for cabc mode 0x1(-15%) */
> +static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
> +/* for cabc mode 0x2(-40%) */
> +static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
> +/* for cabc mode 0x3(-54%) */
> +static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
> +/* for pwm frequency and dimming control */
> +static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
> +		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
> +		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};

Apart from the u8 conversion as mentioned by Thierry, you should
convert these to const too.

> +
> +static int jdi_panel_init(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(10);
> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_tear_on(dsi,
> +					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
> +					     sizeof(tear_scan_line));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(10);
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting,
> +					     sizeof(interface_setting));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		backlight_control4[18] = 0x04;
> +		backlight_control4[19] = 0x00;

Any reason why these are overwritten? It would be better to have these
values in the array itself.

> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		MCAP[1] = 0x03;
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		/*
> +		 * TODO : need to verify panel settings when
> +		 * dsi cmd mode supported for apq8064 - simhavcs
> +		 */
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
> +					     sizeof(interface_setting_cmd));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
> +					     sizeof(interface_ID_setting));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, DSI_control,
> +					     sizeof(DSI_control));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
> +					     sizeof(LTPS_timing_setting));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
> +					     sizeof(sequencer_timing_control));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control1,
> +					     sizeof(backlight_control1));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control2,
> +					     sizeof(backlight_control2));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control3,
> +					     sizeof(backlight_control3));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_on(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_off(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_tear_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(100);
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_disable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (!jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("disable\n");
> +
> +	if (jdi->backlight) {
> +		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
> +		backlight_update_status(jdi->backlight);
> +	}
> +
> +	jdi->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_unprepare(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +	int ret;
> +
> +	if (!jdi->prepared)
> +		return 0;
> +
> +	DRM_DEBUG("unprepare\n");
> +
> +	ret = jdi_panel_off(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
> +		return ret;
> +	}
> +
> +	regulator_disable(jdi->supply);
> +
> +	if (jdi->vcc_gpio)
> +		gpiod_set_value(jdi->vcc_gpio, 0);
> +
> +	if (jdi->reset_gpio)
> +		gpiod_set_value(jdi->reset_gpio, 0);
> +
> +	if (jdi->enable_gpio)
> +		gpiod_set_value(jdi->enable_gpio, 0);
> +
> +	jdi->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_prepare(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +	int ret;
> +
> +	if (jdi->prepared)
> +		return 0;
> +
> +	DRM_DEBUG("prepare\n");
> +
> +	ret = regulator_enable(jdi->iovdd);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->avdd);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->backlit);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->lvs7);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->supply);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(20);
> +
> +	if (jdi->vcc_gpio) {
> +		gpiod_set_value(jdi->vcc_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->reset_gpio) {
> +		gpiod_set_value(jdi->reset_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->pwm_gpio) {
> +		gpiod_set_value(jdi->pwm_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->enable_gpio) {
> +		gpiod_set_value(jdi->enable_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	ret = jdi_panel_init(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to init panel: %d\n", ret);
> +		goto poweroff;
> +	}
> +
> +	ret = jdi_panel_on(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
> +		goto poweroff;
> +	}
> +
> +	jdi->prepared = true;
> +
> +	return 0;
> +
> +poweroff:
> +	regulator_disable(jdi->supply);
> +	if (jdi->reset_gpio)
> +		gpiod_set_value(jdi->reset_gpio, 0);
> +	if (jdi->enable_gpio)
> +		gpiod_set_value(jdi->enable_gpio, 0);
> +	if (jdi->vcc_gpio)
> +		gpiod_set_value(jdi->vcc_gpio, 0);
> +
> +	return ret;
> +}
> +
> +static int jdi_panel_enable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("enable\n");
> +
> +	if (jdi->backlight) {
> +		jdi->backlight->props.power = FB_BLANK_UNBLANK;
> +		backlight_update_status(jdi->backlight);
> +	}
> +
> +	jdi->enabled = true;
> +
> +	return 0;
> +}

The prepare/enable split seems a bit strange. Don't we want to put
mipi_dsi_dcs_set_display_on in the enable() op too?

> +
> +static const struct drm_display_mode default_mode = {
> +		.clock = 155000,

htotal * vtotal * vrefresh (1934 * 1340 * 60.32(as in comment)) comes
at around 156.3 Mhz. Could you plug in the correct frequency?

> +		.hdisplay = 1200,
> +		.hsync_start = 1200 + 48,
> +		.hsync_end = 1200 + 48 + 32,
> +		.htotal = 1200 + 48 + 32 + 60,
> +		.vdisplay = 1920,
> +		.vsync_start = 1920 + 3,
> +		.vsync_end = 1920 + 3 + 5,
> +		.vtotal = 1920 + 3 + 5 + 6,
> +		.vrefresh = 60,
> +		.flags = 0,
> +};
> +
> +static int jdi_panel_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
> +			default_mode.hdisplay, default_mode.vdisplay,
> +			default_mode.vrefresh);

We should use the dsi->dev device pointer here like used elsewhere, not
the drm device pointer.

> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs jdi_panel_funcs = {
> +		.disable = jdi_panel_disable,
> +		.unprepare = jdi_panel_unprepare,
> +		.prepare = jdi_panel_prepare,
> +		.enable = jdi_panel_enable,
> +		.get_modes = jdi_panel_get_modes,
> +};
> +
> +static const struct of_device_id jdi_of_match[] = {
> +		{ .compatible = "jdi,lt070me05000", },
> +		{ }
> +};
> +MODULE_DEVICE_TABLE(of, jdi_of_match);
> +
> +static int jdi_panel_add(struct jdi_panel *jdi)
> +{
> +	struct device *dev = &jdi->dsi->dev;
> +	int ret;
> +
> +	jdi->mode = &default_mode;
> +
> +	/* lvs5 */
> +	jdi->supply = devm_regulator_get(dev, "power");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);

You could consider using regulator_get_bulk() api here, it would clean
up the code a bit.

> +
> +	/* l17 */
> +	jdi->backlit = devm_regulator_get(dev, "backlit");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
> +	if (IS_ERR(jdi->lvs7))
> +		return PTR_ERR(jdi->lvs7);
> +
> +	jdi->avdd = devm_regulator_get(dev, "avdd");
> +	if (IS_ERR(jdi->avdd))
> +		return PTR_ERR(jdi->avdd);
> +
> +	jdi->iovdd = devm_regulator_get(dev, "iovdd");
> +	if (IS_ERR(jdi->iovdd))
> +		return PTR_ERR(jdi->iovdd);
> +
> +	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->vcc_gpio)) {
> +		dev_err(dev, "cannot get vcc-gpio %ld\n",
> +			PTR_ERR(jdi->vcc_gpio));
> +		jdi->vcc_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->vcc_gpio, 0);
> +	}
> +
> +	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpios %ld\n",
> +			PTR_ERR(jdi->reset_gpio));
> +		jdi->reset_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->reset_gpio, 0);
> +	}
> +
> +	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->enable_gpio)) {
> +		dev_err(dev, "cannot get enable-gpio %ld\n",
> +			PTR_ERR(jdi->enable_gpio));
> +		jdi->enable_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->enable_gpio, 0);
> +	}
> +
> +	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->pwm_gpio)) {
> +		dev_err(dev, "cannot get pwm-gpio %ld\n",
> +			PTR_ERR(jdi->pwm_gpio));
> +		jdi->pwm_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->pwm_gpio, 0);
> +	}
> +
> +	/* we don't have backlight right now, proceed further */
> +#ifdef BACKLIGHT
> +	np = of_parse_phandle(dev->of_node, "backlight", 0);
> +	if (np) {
> +		jdi->backlight = of_find_backlight_by_node(np);
> +		of_node_put(np);
> +
> +		if (!jdi->backlight)
> +			return -EPROBE_DEFER;
> +	}
> +#endif
> +
> +	drm_panel_init(&jdi->base);
> +	jdi->base.funcs = &jdi_panel_funcs;
> +	jdi->base.dev = &jdi->dsi->dev;
> +
> +	ret = drm_panel_add(&jdi->base);
> +	if (ret < 0)
> +		goto put_backlight;
> +
> +	return 0;
> +
> +put_backlight:
> +	if (jdi->backlight)
> +		put_device(&jdi->backlight->dev);
> +
> +	return ret;
> +}
> +
> +static void jdi_panel_del(struct jdi_panel *jdi)
> +{
> +	if (jdi->base.dev)
> +		drm_panel_remove(&jdi->base);
> +
> +	if (jdi->backlight)
> +		put_device(&jdi->backlight->dev);
> +}
> +
> +static int jdi_panel_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi;
> +	int ret;
> +
> +	dsi->lanes = 4;
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
> +			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
> +			MIPI_DSI_MODE_VIDEO_HSA |
> +			MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
> +	if (!jdi)
> +		return -ENOMEM;
> +
> +	mipi_dsi_set_drvdata(dsi, jdi);
> +
> +	jdi->dsi = dsi;
> +
> +	ret = jdi_panel_add(jdi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return mipi_dsi_attach(dsi);
> +}
> +
> +static int jdi_panel_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
> +	int ret;
> +
> +	ret = jdi_panel_disable(&jdi->base);
> +	if (ret < 0)
> +		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);

Ideally, the driver's remove op shouldn't try to disable the panel. It
should be done by the dsi host's connector itself. We can drop this
unless there is a specfic reason to do so.

Thanks for working on this.

Archit

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [RESEND][PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
  2016-04-14 10:47   ` Vinay Simha BN
@ 2016-04-14 17:15     ` Rob Herring
  -1 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-04-14 17:15 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Archit Taneja, Sumit Semwal, John Stultz, Thierry Reding,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, David Airlie,
	Ralf Baechle, Jonathan Cameron, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

On Thu, Apr 14, 2016 at 04:17:45PM +0530, Vinay Simha BN wrote:
> Add support for the JDI lt070me05000 WUXGA DSI panel used in
> Nexus 7 2013 devices.
> 
> Programming sequence for the panel is was originally found in the
> android-msm-flo-3.4-lollipop-release branch from:
>     https://android.googlesource.com/kernel/msm.git
> 
> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
> file in:
>     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
> 
> Other fixes folded in were provided
> by Archit Taneja <archit.taneja@gmail.com>
> 
> Cc: Archit Taneja <archit.taneja@gmail.com>
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> [sumit.semwal: Ported to the drm/panel framework]
> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
> [jstultz: Cherry-picked to mainline, folded down other fixes
>  from Vinay and Archit]
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> Acked-by: John Stultz <john.stultz@linaro.org>
> [vinay simha bn: added interface setting cmd mode, cmd or video
> mode panel setting selection in panel_init based on mode_flags]
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> ---
>  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  drivers/gpu/drm/panel/Kconfig                      |  11 +
>  drivers/gpu/drm/panel/Makefile                     |   1 +
>  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>  5 files changed, 725 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> new file mode 100644
> index 0000000..35c5ac7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> @@ -0,0 +1,27 @@
> +JDI model LT070ME05000 1920x1200 7" DSI Panel
> +
> +Basic data sheet is at:
> +	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> +
> +This panel has video mode implemented currently in the driver.

As Thierry already mentioned, this has nothing to do with the binding.

> +
> +Required properties:
> +- compatible: should be "jdi,lt070me05000"
> +
> +Optional properties:
> +- power-supply: phandle of the regulator that provides the supply voltage
> +- reset-gpio: phandle of gpio for reset line

reset-gpios

> +- backlight: phandle of the backlight device attached to the panel
> +
> +Example:
> +
> +	dsi@54300000 {
> +		panel: panel@0 {
> +			compatible = "jdi,lt070me05000";
> +			reg = <0>;
> +
> +			power-supply = <...>;
> +			reset-gpio = <...>;
> +			backlight = <...>;
> +		};
> +	};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index a580f3e..ec42bb4 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -130,6 +130,7 @@ invensense	InvenSense Inc.
>  isee	ISEE 2007 S.L.
>  isil	Intersil
>  issi	Integrated Silicon Solutions Inc.
> +jdi	Japan Display Inc.
>  jedec	JEDEC Solid State Technology Association
>  karo	Ka-Ro electronics GmbH
>  keymile	Keymile GmbH
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 1500ab9..f41690e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called panel-sharp-lq101r1sx01.
>  
> +config DRM_PANEL_JDI_LT070ME05000
> +	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
> +	  command mode panel as found in Google Nexus 7 (2013) devices.
> +	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
> +	  24 bit RGB per pixel.
> +
>  config DRM_PANEL_SHARP_LS043T1LE01
>  	tristate "Sharp LS043T1LE01 qHD video mode panel"
>  	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index f277eed..e6c6fc8 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> new file mode 100644
> index 0000000..051aa1b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> @@ -0,0 +1,685 @@
> +/*
> + * Copyright (C) 2015 InforceComputing
> + * Author: Vinay Simha BN <simhavcs@gmail.com>
> + *
> + * Copyright (C) 2015 Linaro Ltd
> + * Author: Sumit Semwal <sumit.semwal@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +
> +#include <video/mipi_display.h>
> +
> +/*
> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
> + * JDI model LT070ME05000, and its data sheet is at:
> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> + */
> +struct jdi_panel {
> +	struct drm_panel base;
> +	struct mipi_dsi_device *dsi;
> +
> +	/* TODO: Add backilght support */
> +	struct backlight_device *backlight;
> +	struct regulator *supply;
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *enable_gpio;
> +	struct gpio_desc *vcc_gpio;
> +
> +	struct regulator *backlit;
> +	struct regulator *lvs7;
> +	struct regulator *avdd;
> +	struct regulator *iovdd;
> +	struct gpio_desc *pwm_gpio;
> +
> +	bool prepared;
> +	bool enabled;
> +
> +	const struct drm_display_mode *mode;
> +};
> +
> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct jdi_panel, base);
> +}
> +
> +static char MCAP[2] = {0xB0, 0x00};
> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
> +
> +static char interface_ID_setting[2] = {0xB4, 0x0C};
> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
> +
> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
> +
> +/* for fps control, set fps to 60.32Hz */
> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
> +static char sequencer_timing_control[2] = {0xD6, 0x01};
> +
> +/* set brightness */
> +static char write_display_brightness[] = {0x51, 0xFF};
> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
> +static char write_control_display[] = {0x53, 0x24};
> +/*
> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
> + * disable SRE(sunlight readability enhancement)
> + */
> +static char write_cabc[] = {0x55, 0x00};
> +/* for cabc mode 0x1(-15%) */
> +static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
> +/* for cabc mode 0x2(-40%) */
> +static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
> +/* for cabc mode 0x3(-54%) */
> +static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
> +/* for pwm frequency and dimming control */
> +static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
> +		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
> +		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
> +
> +static int jdi_panel_init(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(10);
> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_tear_on(dsi,
> +					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
> +					     sizeof(tear_scan_line));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(10);
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting,
> +					     sizeof(interface_setting));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		backlight_control4[18] = 0x04;
> +		backlight_control4[19] = 0x00;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		MCAP[1] = 0x03;
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		/*
> +		 * TODO : need to verify panel settings when
> +		 * dsi cmd mode supported for apq8064 - simhavcs
> +		 */
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
> +					     sizeof(interface_setting_cmd));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
> +					     sizeof(interface_ID_setting));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, DSI_control,
> +					     sizeof(DSI_control));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
> +					     sizeof(LTPS_timing_setting));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
> +					     sizeof(sequencer_timing_control));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control1,
> +					     sizeof(backlight_control1));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control2,
> +					     sizeof(backlight_control2));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control3,
> +					     sizeof(backlight_control3));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_on(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_off(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_tear_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(100);
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_disable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (!jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("disable\n");
> +
> +	if (jdi->backlight) {
> +		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
> +		backlight_update_status(jdi->backlight);
> +	}
> +
> +	jdi->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_unprepare(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +	int ret;
> +
> +	if (!jdi->prepared)
> +		return 0;
> +
> +	DRM_DEBUG("unprepare\n");
> +
> +	ret = jdi_panel_off(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
> +		return ret;
> +	}
> +
> +	regulator_disable(jdi->supply);
> +
> +	if (jdi->vcc_gpio)
> +		gpiod_set_value(jdi->vcc_gpio, 0);
> +
> +	if (jdi->reset_gpio)
> +		gpiod_set_value(jdi->reset_gpio, 0);
> +
> +	if (jdi->enable_gpio)
> +		gpiod_set_value(jdi->enable_gpio, 0);
> +
> +	jdi->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_prepare(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +	int ret;
> +
> +	if (jdi->prepared)
> +		return 0;
> +
> +	DRM_DEBUG("prepare\n");
> +
> +	ret = regulator_enable(jdi->iovdd);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->avdd);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->backlit);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->lvs7);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->supply);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(20);
> +
> +	if (jdi->vcc_gpio) {
> +		gpiod_set_value(jdi->vcc_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->reset_gpio) {
> +		gpiod_set_value(jdi->reset_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->pwm_gpio) {
> +		gpiod_set_value(jdi->pwm_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->enable_gpio) {
> +		gpiod_set_value(jdi->enable_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	ret = jdi_panel_init(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to init panel: %d\n", ret);
> +		goto poweroff;
> +	}
> +
> +	ret = jdi_panel_on(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
> +		goto poweroff;
> +	}
> +
> +	jdi->prepared = true;
> +
> +	return 0;
> +
> +poweroff:
> +	regulator_disable(jdi->supply);
> +	if (jdi->reset_gpio)
> +		gpiod_set_value(jdi->reset_gpio, 0);
> +	if (jdi->enable_gpio)
> +		gpiod_set_value(jdi->enable_gpio, 0);
> +	if (jdi->vcc_gpio)
> +		gpiod_set_value(jdi->vcc_gpio, 0);
> +
> +	return ret;
> +}
> +
> +static int jdi_panel_enable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("enable\n");
> +
> +	if (jdi->backlight) {
> +		jdi->backlight->props.power = FB_BLANK_UNBLANK;
> +		backlight_update_status(jdi->backlight);
> +	}
> +
> +	jdi->enabled = true;
> +
> +	return 0;
> +}
> +
> +static const struct drm_display_mode default_mode = {
> +		.clock = 155000,
> +		.hdisplay = 1200,
> +		.hsync_start = 1200 + 48,
> +		.hsync_end = 1200 + 48 + 32,
> +		.htotal = 1200 + 48 + 32 + 60,
> +		.vdisplay = 1920,
> +		.vsync_start = 1920 + 3,
> +		.vsync_end = 1920 + 3 + 5,
> +		.vtotal = 1920 + 3 + 5 + 6,
> +		.vrefresh = 60,
> +		.flags = 0,
> +};
> +
> +static int jdi_panel_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
> +			default_mode.hdisplay, default_mode.vdisplay,
> +			default_mode.vrefresh);
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs jdi_panel_funcs = {
> +		.disable = jdi_panel_disable,
> +		.unprepare = jdi_panel_unprepare,
> +		.prepare = jdi_panel_prepare,
> +		.enable = jdi_panel_enable,
> +		.get_modes = jdi_panel_get_modes,
> +};
> +
> +static const struct of_device_id jdi_of_match[] = {
> +		{ .compatible = "jdi,lt070me05000", },
> +		{ }
> +};
> +MODULE_DEVICE_TABLE(of, jdi_of_match);
> +
> +static int jdi_panel_add(struct jdi_panel *jdi)
> +{
> +	struct device *dev = &jdi->dsi->dev;
> +	int ret;
> +
> +	jdi->mode = &default_mode;
> +
> +	/* lvs5 */
> +	jdi->supply = devm_regulator_get(dev, "power");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	/* l17 */
> +	jdi->backlit = devm_regulator_get(dev, "backlit");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
> +	if (IS_ERR(jdi->lvs7))
> +		return PTR_ERR(jdi->lvs7);
> +
> +	jdi->avdd = devm_regulator_get(dev, "avdd");
> +	if (IS_ERR(jdi->avdd))
> +		return PTR_ERR(jdi->avdd);
> +
> +	jdi->iovdd = devm_regulator_get(dev, "iovdd");
> +	if (IS_ERR(jdi->iovdd))
> +		return PTR_ERR(jdi->iovdd);
> +
> +	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->vcc_gpio)) {
> +		dev_err(dev, "cannot get vcc-gpio %ld\n",
> +			PTR_ERR(jdi->vcc_gpio));
> +		jdi->vcc_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->vcc_gpio, 0);
> +	}
> +
> +	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpios %ld\n",
> +			PTR_ERR(jdi->reset_gpio));
> +		jdi->reset_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->reset_gpio, 0);
> +	}
> +
> +	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->enable_gpio)) {
> +		dev_err(dev, "cannot get enable-gpio %ld\n",
> +			PTR_ERR(jdi->enable_gpio));
> +		jdi->enable_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->enable_gpio, 0);
> +	}
> +
> +	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->pwm_gpio)) {
> +		dev_err(dev, "cannot get pwm-gpio %ld\n",
> +			PTR_ERR(jdi->pwm_gpio));
> +		jdi->pwm_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->pwm_gpio, 0);
> +	}
> +
> +	/* we don't have backlight right now, proceed further */
> +#ifdef BACKLIGHT
> +	np = of_parse_phandle(dev->of_node, "backlight", 0);
> +	if (np) {
> +		jdi->backlight = of_find_backlight_by_node(np);
> +		of_node_put(np);
> +
> +		if (!jdi->backlight)
> +			return -EPROBE_DEFER;
> +	}
> +#endif
> +
> +	drm_panel_init(&jdi->base);
> +	jdi->base.funcs = &jdi_panel_funcs;
> +	jdi->base.dev = &jdi->dsi->dev;
> +
> +	ret = drm_panel_add(&jdi->base);
> +	if (ret < 0)
> +		goto put_backlight;
> +
> +	return 0;
> +
> +put_backlight:
> +	if (jdi->backlight)
> +		put_device(&jdi->backlight->dev);
> +
> +	return ret;
> +}
> +
> +static void jdi_panel_del(struct jdi_panel *jdi)
> +{
> +	if (jdi->base.dev)
> +		drm_panel_remove(&jdi->base);
> +
> +	if (jdi->backlight)
> +		put_device(&jdi->backlight->dev);
> +}
> +
> +static int jdi_panel_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi;
> +	int ret;
> +
> +	dsi->lanes = 4;
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
> +			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
> +			MIPI_DSI_MODE_VIDEO_HSA |
> +			MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
> +	if (!jdi)
> +		return -ENOMEM;
> +
> +	mipi_dsi_set_drvdata(dsi, jdi);
> +
> +	jdi->dsi = dsi;
> +
> +	ret = jdi_panel_add(jdi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return mipi_dsi_attach(dsi);
> +}
> +
> +static int jdi_panel_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
> +	int ret;
> +
> +	ret = jdi_panel_disable(&jdi->base);
> +	if (ret < 0)
> +		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
> +
> +	ret = mipi_dsi_detach(dsi);
> +	if (ret < 0)
> +		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
> +			ret);
> +
> +	drm_panel_detach(&jdi->base);
> +	jdi_panel_del(jdi);
> +
> +	return 0;
> +}
> +
> +static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
> +
> +	jdi_panel_disable(&jdi->base);
> +}
> +
> +static struct mipi_dsi_driver jdi_panel_driver = {
> +	.driver = {
> +		.name = "panel-jdi-lt070me05000",
> +		.of_match_table = jdi_of_match,
> +	},
> +	.probe = jdi_panel_probe,
> +	.remove = jdi_panel_remove,
> +	.shutdown = jdi_panel_shutdown,
> +};
> +module_mipi_dsi_driver(jdi_panel_driver);
> +
> +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
> +MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
> +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.1.2
> 

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

* Re: [RESEND][PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-14 17:15     ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-04-14 17:15 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Pawel Moll, Ian Campbell, open list:DRM PANEL DRIVERS, open list,
	Ralf Baechle, Jonathan Cameron, Kumar Gala, Shawn Guo,
	Archit Taneja

On Thu, Apr 14, 2016 at 04:17:45PM +0530, Vinay Simha BN wrote:
> Add support for the JDI lt070me05000 WUXGA DSI panel used in
> Nexus 7 2013 devices.
> 
> Programming sequence for the panel is was originally found in the
> android-msm-flo-3.4-lollipop-release branch from:
>     https://android.googlesource.com/kernel/msm.git
> 
> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
> file in:
>     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
> 
> Other fixes folded in were provided
> by Archit Taneja <archit.taneja@gmail.com>
> 
> Cc: Archit Taneja <archit.taneja@gmail.com>
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> [sumit.semwal: Ported to the drm/panel framework]
> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
> [jstultz: Cherry-picked to mainline, folded down other fixes
>  from Vinay and Archit]
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> Acked-by: John Stultz <john.stultz@linaro.org>
> [vinay simha bn: added interface setting cmd mode, cmd or video
> mode panel setting selection in panel_init based on mode_flags]
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> ---
>  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  drivers/gpu/drm/panel/Kconfig                      |  11 +
>  drivers/gpu/drm/panel/Makefile                     |   1 +
>  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>  5 files changed, 725 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> new file mode 100644
> index 0000000..35c5ac7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> @@ -0,0 +1,27 @@
> +JDI model LT070ME05000 1920x1200 7" DSI Panel
> +
> +Basic data sheet is at:
> +	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> +
> +This panel has video mode implemented currently in the driver.

As Thierry already mentioned, this has nothing to do with the binding.

> +
> +Required properties:
> +- compatible: should be "jdi,lt070me05000"
> +
> +Optional properties:
> +- power-supply: phandle of the regulator that provides the supply voltage
> +- reset-gpio: phandle of gpio for reset line

reset-gpios

> +- backlight: phandle of the backlight device attached to the panel
> +
> +Example:
> +
> +	dsi@54300000 {
> +		panel: panel@0 {
> +			compatible = "jdi,lt070me05000";
> +			reg = <0>;
> +
> +			power-supply = <...>;
> +			reset-gpio = <...>;
> +			backlight = <...>;
> +		};
> +	};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index a580f3e..ec42bb4 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -130,6 +130,7 @@ invensense	InvenSense Inc.
>  isee	ISEE 2007 S.L.
>  isil	Intersil
>  issi	Integrated Silicon Solutions Inc.
> +jdi	Japan Display Inc.
>  jedec	JEDEC Solid State Technology Association
>  karo	Ka-Ro electronics GmbH
>  keymile	Keymile GmbH
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 1500ab9..f41690e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called panel-sharp-lq101r1sx01.
>  
> +config DRM_PANEL_JDI_LT070ME05000
> +	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
> +	  command mode panel as found in Google Nexus 7 (2013) devices.
> +	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
> +	  24 bit RGB per pixel.
> +
>  config DRM_PANEL_SHARP_LS043T1LE01
>  	tristate "Sharp LS043T1LE01 qHD video mode panel"
>  	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index f277eed..e6c6fc8 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> new file mode 100644
> index 0000000..051aa1b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
> @@ -0,0 +1,685 @@
> +/*
> + * Copyright (C) 2015 InforceComputing
> + * Author: Vinay Simha BN <simhavcs@gmail.com>
> + *
> + * Copyright (C) 2015 Linaro Ltd
> + * Author: Sumit Semwal <sumit.semwal@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +
> +#include <video/mipi_display.h>
> +
> +/*
> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
> + * JDI model LT070ME05000, and its data sheet is at:
> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
> + */
> +struct jdi_panel {
> +	struct drm_panel base;
> +	struct mipi_dsi_device *dsi;
> +
> +	/* TODO: Add backilght support */
> +	struct backlight_device *backlight;
> +	struct regulator *supply;
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *enable_gpio;
> +	struct gpio_desc *vcc_gpio;
> +
> +	struct regulator *backlit;
> +	struct regulator *lvs7;
> +	struct regulator *avdd;
> +	struct regulator *iovdd;
> +	struct gpio_desc *pwm_gpio;
> +
> +	bool prepared;
> +	bool enabled;
> +
> +	const struct drm_display_mode *mode;
> +};
> +
> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct jdi_panel, base);
> +}
> +
> +static char MCAP[2] = {0xB0, 0x00};
> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
> +
> +static char interface_ID_setting[2] = {0xB4, 0x0C};
> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
> +
> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
> +
> +/* for fps control, set fps to 60.32Hz */
> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
> +static char sequencer_timing_control[2] = {0xD6, 0x01};
> +
> +/* set brightness */
> +static char write_display_brightness[] = {0x51, 0xFF};
> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
> +static char write_control_display[] = {0x53, 0x24};
> +/*
> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
> + * disable SRE(sunlight readability enhancement)
> + */
> +static char write_cabc[] = {0x55, 0x00};
> +/* for cabc mode 0x1(-15%) */
> +static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
> +/* for cabc mode 0x2(-40%) */
> +static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
> +/* for cabc mode 0x3(-54%) */
> +static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
> +/* for pwm frequency and dimming control */
> +static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
> +		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
> +		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
> +
> +static int jdi_panel_init(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(10);
> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_tear_on(dsi,
> +					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
> +					     sizeof(tear_scan_line));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(10);
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting,
> +					     sizeof(interface_setting));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		backlight_control4[18] = 0x04;
> +		backlight_control4[19] = 0x00;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +		mdelay(20);
> +
> +		MCAP[1] = 0x03;
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		/*
> +		 * TODO : need to verify panel settings when
> +		 * dsi cmd mode supported for apq8064 - simhavcs
> +		 */
> +		ret = mipi_dsi_dcs_soft_reset(dsi);
> +		if (ret < 0)
> +			return ret;
> +
> +		mdelay(5);
> +
> +		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
> +					     sizeof(interface_setting_cmd));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
> +					     sizeof(interface_ID_setting));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, DSI_control,
> +					     sizeof(DSI_control));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
> +					     sizeof(LTPS_timing_setting));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
> +					     sizeof(sequencer_timing_control));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
> +						sizeof(write_display_brightness)
> +						);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
> +						sizeof(write_control_display));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
> +						sizeof(write_cabc));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control1,
> +					     sizeof(backlight_control1));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control2,
> +					     sizeof(backlight_control2));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control3,
> +					     sizeof(backlight_control3));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_generic_write(dsi, backlight_control4,
> +					     sizeof(backlight_control4));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
> +		if (ret < 0)
> +			return ret;
> +		mdelay(120);
> +
> +		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_on(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_off(struct jdi_panel *jdi)
> +{
> +	struct mipi_dsi_device *dsi = jdi->dsi;
> +	int ret;
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_dcs_set_tear_off(dsi);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(100);
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_disable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (!jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("disable\n");
> +
> +	if (jdi->backlight) {
> +		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
> +		backlight_update_status(jdi->backlight);
> +	}
> +
> +	jdi->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_unprepare(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +	int ret;
> +
> +	if (!jdi->prepared)
> +		return 0;
> +
> +	DRM_DEBUG("unprepare\n");
> +
> +	ret = jdi_panel_off(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
> +		return ret;
> +	}
> +
> +	regulator_disable(jdi->supply);
> +
> +	if (jdi->vcc_gpio)
> +		gpiod_set_value(jdi->vcc_gpio, 0);
> +
> +	if (jdi->reset_gpio)
> +		gpiod_set_value(jdi->reset_gpio, 0);
> +
> +	if (jdi->enable_gpio)
> +		gpiod_set_value(jdi->enable_gpio, 0);
> +
> +	jdi->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int jdi_panel_prepare(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +	int ret;
> +
> +	if (jdi->prepared)
> +		return 0;
> +
> +	DRM_DEBUG("prepare\n");
> +
> +	ret = regulator_enable(jdi->iovdd);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->avdd);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->backlit);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->lvs7);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regulator_enable(jdi->supply);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(20);
> +
> +	if (jdi->vcc_gpio) {
> +		gpiod_set_value(jdi->vcc_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->reset_gpio) {
> +		gpiod_set_value(jdi->reset_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->pwm_gpio) {
> +		gpiod_set_value(jdi->pwm_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	if (jdi->enable_gpio) {
> +		gpiod_set_value(jdi->enable_gpio, 1);
> +		usleep_range(10, 20);
> +	}
> +
> +	ret = jdi_panel_init(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to init panel: %d\n", ret);
> +		goto poweroff;
> +	}
> +
> +	ret = jdi_panel_on(jdi);
> +	if (ret) {
> +		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
> +		goto poweroff;
> +	}
> +
> +	jdi->prepared = true;
> +
> +	return 0;
> +
> +poweroff:
> +	regulator_disable(jdi->supply);
> +	if (jdi->reset_gpio)
> +		gpiod_set_value(jdi->reset_gpio, 0);
> +	if (jdi->enable_gpio)
> +		gpiod_set_value(jdi->enable_gpio, 0);
> +	if (jdi->vcc_gpio)
> +		gpiod_set_value(jdi->vcc_gpio, 0);
> +
> +	return ret;
> +}
> +
> +static int jdi_panel_enable(struct drm_panel *panel)
> +{
> +	struct jdi_panel *jdi = to_jdi_panel(panel);
> +
> +	if (jdi->enabled)
> +		return 0;
> +
> +	DRM_DEBUG("enable\n");
> +
> +	if (jdi->backlight) {
> +		jdi->backlight->props.power = FB_BLANK_UNBLANK;
> +		backlight_update_status(jdi->backlight);
> +	}
> +
> +	jdi->enabled = true;
> +
> +	return 0;
> +}
> +
> +static const struct drm_display_mode default_mode = {
> +		.clock = 155000,
> +		.hdisplay = 1200,
> +		.hsync_start = 1200 + 48,
> +		.hsync_end = 1200 + 48 + 32,
> +		.htotal = 1200 + 48 + 32 + 60,
> +		.vdisplay = 1920,
> +		.vsync_start = 1920 + 3,
> +		.vsync_end = 1920 + 3 + 5,
> +		.vtotal = 1920 + 3 + 5 + 6,
> +		.vrefresh = 60,
> +		.flags = 0,
> +};
> +
> +static int jdi_panel_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
> +			default_mode.hdisplay, default_mode.vdisplay,
> +			default_mode.vrefresh);
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs jdi_panel_funcs = {
> +		.disable = jdi_panel_disable,
> +		.unprepare = jdi_panel_unprepare,
> +		.prepare = jdi_panel_prepare,
> +		.enable = jdi_panel_enable,
> +		.get_modes = jdi_panel_get_modes,
> +};
> +
> +static const struct of_device_id jdi_of_match[] = {
> +		{ .compatible = "jdi,lt070me05000", },
> +		{ }
> +};
> +MODULE_DEVICE_TABLE(of, jdi_of_match);
> +
> +static int jdi_panel_add(struct jdi_panel *jdi)
> +{
> +	struct device *dev = &jdi->dsi->dev;
> +	int ret;
> +
> +	jdi->mode = &default_mode;
> +
> +	/* lvs5 */
> +	jdi->supply = devm_regulator_get(dev, "power");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	/* l17 */
> +	jdi->backlit = devm_regulator_get(dev, "backlit");
> +	if (IS_ERR(jdi->supply))
> +		return PTR_ERR(jdi->supply);
> +
> +	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
> +	if (IS_ERR(jdi->lvs7))
> +		return PTR_ERR(jdi->lvs7);
> +
> +	jdi->avdd = devm_regulator_get(dev, "avdd");
> +	if (IS_ERR(jdi->avdd))
> +		return PTR_ERR(jdi->avdd);
> +
> +	jdi->iovdd = devm_regulator_get(dev, "iovdd");
> +	if (IS_ERR(jdi->iovdd))
> +		return PTR_ERR(jdi->iovdd);
> +
> +	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->vcc_gpio)) {
> +		dev_err(dev, "cannot get vcc-gpio %ld\n",
> +			PTR_ERR(jdi->vcc_gpio));
> +		jdi->vcc_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->vcc_gpio, 0);
> +	}
> +
> +	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->reset_gpio)) {
> +		dev_err(dev, "cannot get reset-gpios %ld\n",
> +			PTR_ERR(jdi->reset_gpio));
> +		jdi->reset_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->reset_gpio, 0);
> +	}
> +
> +	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->enable_gpio)) {
> +		dev_err(dev, "cannot get enable-gpio %ld\n",
> +			PTR_ERR(jdi->enable_gpio));
> +		jdi->enable_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->enable_gpio, 0);
> +	}
> +
> +	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
> +	if (IS_ERR(jdi->pwm_gpio)) {
> +		dev_err(dev, "cannot get pwm-gpio %ld\n",
> +			PTR_ERR(jdi->pwm_gpio));
> +		jdi->pwm_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(jdi->pwm_gpio, 0);
> +	}
> +
> +	/* we don't have backlight right now, proceed further */
> +#ifdef BACKLIGHT
> +	np = of_parse_phandle(dev->of_node, "backlight", 0);
> +	if (np) {
> +		jdi->backlight = of_find_backlight_by_node(np);
> +		of_node_put(np);
> +
> +		if (!jdi->backlight)
> +			return -EPROBE_DEFER;
> +	}
> +#endif
> +
> +	drm_panel_init(&jdi->base);
> +	jdi->base.funcs = &jdi_panel_funcs;
> +	jdi->base.dev = &jdi->dsi->dev;
> +
> +	ret = drm_panel_add(&jdi->base);
> +	if (ret < 0)
> +		goto put_backlight;
> +
> +	return 0;
> +
> +put_backlight:
> +	if (jdi->backlight)
> +		put_device(&jdi->backlight->dev);
> +
> +	return ret;
> +}
> +
> +static void jdi_panel_del(struct jdi_panel *jdi)
> +{
> +	if (jdi->base.dev)
> +		drm_panel_remove(&jdi->base);
> +
> +	if (jdi->backlight)
> +		put_device(&jdi->backlight->dev);
> +}
> +
> +static int jdi_panel_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi;
> +	int ret;
> +
> +	dsi->lanes = 4;
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
> +			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
> +			MIPI_DSI_MODE_VIDEO_HSA |
> +			MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
> +	if (!jdi)
> +		return -ENOMEM;
> +
> +	mipi_dsi_set_drvdata(dsi, jdi);
> +
> +	jdi->dsi = dsi;
> +
> +	ret = jdi_panel_add(jdi);
> +	if (ret < 0)
> +		return ret;
> +
> +	return mipi_dsi_attach(dsi);
> +}
> +
> +static int jdi_panel_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
> +	int ret;
> +
> +	ret = jdi_panel_disable(&jdi->base);
> +	if (ret < 0)
> +		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
> +
> +	ret = mipi_dsi_detach(dsi);
> +	if (ret < 0)
> +		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
> +			ret);
> +
> +	drm_panel_detach(&jdi->base);
> +	jdi_panel_del(jdi);
> +
> +	return 0;
> +}
> +
> +static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
> +{
> +	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
> +
> +	jdi_panel_disable(&jdi->base);
> +}
> +
> +static struct mipi_dsi_driver jdi_panel_driver = {
> +	.driver = {
> +		.name = "panel-jdi-lt070me05000",
> +		.of_match_table = jdi_of_match,
> +	},
> +	.probe = jdi_panel_probe,
> +	.remove = jdi_panel_remove,
> +	.shutdown = jdi_panel_shutdown,
> +};
> +module_mipi_dsi_driver(jdi_panel_driver);
> +
> +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
> +MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
> +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.1.2
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH v2 1/4] dt-bindings: Add jdi panel vendor
  2016-04-14 17:15     ` Rob Herring
@ 2016-04-20  9:32       ` Vinay Simha BN
  -1 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20  9:32 UTC (permalink / raw)
  Cc: Vinay Simha BN, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Thierry Reding, Ralf Baechle,
	Jonathan Cameron, Shawn Guo,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Japan Display Inc.

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 0061d25..a9ae6c14 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -133,6 +133,7 @@ invensense	InvenSense Inc.
 isee	ISEE 2007 S.L.
 isil	Intersil
 issi	Integrated Silicon Solutions Inc.
+jdi	Japan Display Inc.
 jedec	JEDEC Solid State Technology Association
 karo	Ka-Ro electronics GmbH
 keymile	Keymile GmbH
-- 
2.1.2

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

* [PATCH v2 1/4] dt-bindings: Add jdi panel vendor
@ 2016-04-20  9:32       ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20  9:32 UTC (permalink / raw)
  Cc: Vinay Simha BN, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Thierry Reding, Ralf Baechle,
	Jonathan Cameron, Shawn Guo,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Japan Display Inc.

Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 0061d25..a9ae6c14 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -133,6 +133,7 @@ invensense	InvenSense Inc.
 isee	ISEE 2007 S.L.
 isil	Intersil
 issi	Integrated Silicon Solutions Inc.
+jdi	Japan Display Inc.
 jedec	JEDEC Solid State Technology Association
 karo	Ka-Ro electronics GmbH
 keymile	Keymile GmbH
-- 
2.1.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings
  2016-04-20  9:32       ` Vinay Simha BN
@ 2016-04-20  9:32         ` Vinay Simha BN
  -1 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20  9:32 UTC (permalink / raw)
  Cc: Vinay Simha BN, Thierry Reding, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Add documentation for lt070me05000 panel

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 .../bindings/display/panel/jdi,lt070me05000.txt    | 43 ++++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt

diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644
index 0000000..ffe0550
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
@@ -0,0 +1,43 @@
+JDI model LT070ME05000 1200x1920 7" DSI Panel
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+- power-supply: phandle of the regulator that provides the supply voltage
+  IOVCC , power supply for LCM (1.8V)
+- vddp-supply: phandle of the regulator that provides the supply voltage
+  Power IC supply (3-5V)
+- dcdc_en-supply: phandle of the regulator that provides the supply voltage
+  Power IC supply enable, High active
+- reset-gpio: phandle of gpio for reset line
+  This should be 8mA, gpio can be configured using mux and pinctrl.
+  XRES, Reset, Low active
+- enable-gpio: phandle of gpio for enable line
+  LED_EN, LED backlight enable, High active
+- vcc-gpio: phandle of regulator/gpio that provides the supply voltage
+  VDD, LED power supply (3-5V)
+
+Optional properties:
+- pwm-gpio: phandle of gpio/pwm
+  pwm backlight control, this should be mapped to the backlight level
+  display brightness (0x51)
+
+Example:
+
+	dsi0: qcom,mdss_dsi@4700000 {
+		panel@0 {
+			compatible = "jdi,lt070me05000";
+			reg = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&dsi_panel_pinctrl>;
+
+			power-supply = <&pm8921_lvs5>;
+			vddp-supply = <&pm8921_l17>;
+			dcdc_en-supply = <&pm8921_lvs7>;
+
+			reset-gpio = <&tlmm_pinmux 54 0>;
+			enable-gpio = <&pm8921_gpio 36 GPIO_ACTIVE_HIGH>;
+			vcc-gpio = <&pm8921_gpio 23 GPIO_ACTIVE_HIGH>;
+
+			pwm-gpio = <&pm8921_gpio 26 GPIO_ACTIVE_HIGH>;
+		};
+	};
-- 
2.1.2

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

* [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings
@ 2016-04-20  9:32         ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20  9:32 UTC (permalink / raw)
  Cc: Vinay Simha BN, Thierry Reding, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Add documentation for lt070me05000 panel

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 .../bindings/display/panel/jdi,lt070me05000.txt    | 43 ++++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt

diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644
index 0000000..ffe0550
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
@@ -0,0 +1,43 @@
+JDI model LT070ME05000 1200x1920 7" DSI Panel
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+- power-supply: phandle of the regulator that provides the supply voltage
+  IOVCC , power supply for LCM (1.8V)
+- vddp-supply: phandle of the regulator that provides the supply voltage
+  Power IC supply (3-5V)
+- dcdc_en-supply: phandle of the regulator that provides the supply voltage
+  Power IC supply enable, High active
+- reset-gpio: phandle of gpio for reset line
+  This should be 8mA, gpio can be configured using mux and pinctrl.
+  XRES, Reset, Low active
+- enable-gpio: phandle of gpio for enable line
+  LED_EN, LED backlight enable, High active
+- vcc-gpio: phandle of regulator/gpio that provides the supply voltage
+  VDD, LED power supply (3-5V)
+
+Optional properties:
+- pwm-gpio: phandle of gpio/pwm
+  pwm backlight control, this should be mapped to the backlight level
+  display brightness (0x51)
+
+Example:
+
+	dsi0: qcom,mdss_dsi@4700000 {
+		panel@0 {
+			compatible = "jdi,lt070me05000";
+			reg = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&dsi_panel_pinctrl>;
+
+			power-supply = <&pm8921_lvs5>;
+			vddp-supply = <&pm8921_l17>;
+			dcdc_en-supply = <&pm8921_lvs7>;
+
+			reset-gpio = <&tlmm_pinmux 54 0>;
+			enable-gpio = <&pm8921_gpio 36 GPIO_ACTIVE_HIGH>;
+			vcc-gpio = <&pm8921_gpio 23 GPIO_ACTIVE_HIGH>;
+
+			pwm-gpio = <&pm8921_gpio 26 GPIO_ACTIVE_HIGH>;
+		};
+	};
-- 
2.1.2

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

* [PATCH v2 3/4] drm/dsi: Implement set tear scanline
  2016-04-20  9:32       ` Vinay Simha BN
@ 2016-04-20  9:32         ` Vinay Simha BN
  -1 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20  9:32 UTC (permalink / raw)
  Cc: Vinay Simha BN, David Airlie, open list:DRM DRIVERS, open list

Provide a small convenience wrapper that transmits
a set_tear_scanline command.

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 22 ++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  2 ++
 2 files changed, 24 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index f5d8083..26aba75 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -983,6 +983,28 @@ int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
 EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
 
 /**
+ * mipi_dsi_set_tear_scanline() - turn on the display module's Tearing Effect
+ *    output signal on the TE signal line when display module reaches line N
+ *    defined by STS[n:0].
+ * @dsi: DSI peripheral device
+ * @param1: STS[10:8]
+ * @param2: STS[7:0]
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_set_tear_scanline(struct mipi_dsi_device *dsi,
+                             u8 param1, u8 param2)
+{
+        u8 payload[3] = { MIPI_DCS_SET_TEAR_SCANLINE, param1 , param2};
+        ssize_t err;
+
+        err = mipi_dsi_generic_write(dsi, &payload, sizeof(payload));
+        if (err < 0)
+                return err;
+
+        return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
+/**
  * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
  *    data used by the interface
  * @dsi: DSI peripheral device
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 7a9840f..37bd75d 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -263,6 +263,8 @@ int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
 				    u16 end);
 int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
 				  u16 end);
+int mipi_dsi_set_tear_scanline(struct mipi_dsi_device *dsi, u8 param1,
+				   u8 param2);
 int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
 			     enum mipi_dsi_dcs_tear_mode mode);
-- 
2.1.2

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

* [PATCH v2 3/4] drm/dsi: Implement set tear scanline
@ 2016-04-20  9:32         ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20  9:32 UTC (permalink / raw)
  Cc: Vinay Simha BN, David Airlie, open list:DRM DRIVERS, open list

Provide a small convenience wrapper that transmits
a set_tear_scanline command.

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 22 ++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  2 ++
 2 files changed, 24 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index f5d8083..26aba75 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -983,6 +983,28 @@ int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
 EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
 
 /**
+ * mipi_dsi_set_tear_scanline() - turn on the display module's Tearing Effect
+ *    output signal on the TE signal line when display module reaches line N
+ *    defined by STS[n:0].
+ * @dsi: DSI peripheral device
+ * @param1: STS[10:8]
+ * @param2: STS[7:0]
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_set_tear_scanline(struct mipi_dsi_device *dsi,
+                             u8 param1, u8 param2)
+{
+        u8 payload[3] = { MIPI_DCS_SET_TEAR_SCANLINE, param1 , param2};
+        ssize_t err;
+
+        err = mipi_dsi_generic_write(dsi, &payload, sizeof(payload));
+        if (err < 0)
+                return err;
+
+        return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
+/**
  * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
  *    data used by the interface
  * @dsi: DSI peripheral device
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 7a9840f..37bd75d 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -263,6 +263,8 @@ int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
 				    u16 end);
 int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
 				  u16 end);
+int mipi_dsi_set_tear_scanline(struct mipi_dsi_device *dsi, u8 param1,
+				   u8 param2);
 int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
 			     enum mipi_dsi_dcs_tear_mode mode);
-- 
2.1.2

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

* [PATCH v2 4/4] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
  2016-04-20  9:32       ` Vinay Simha BN
@ 2016-04-20  9:32         ` Vinay Simha BN
  -1 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20  9:32 UTC (permalink / raw)
  Cc: Vinay Simha BN, Archit Taneja, Sumit Semwal, John Stultz,
	Thierry Reding, David Airlie, open list,
	open list:DRM PANEL DRIVERS

Add support for the JDI lt070me05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
    https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
    git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27

Other fixes folded in were provided
by Archit Taneja <archit.taneja@gmail.com>

Cc: Archit Taneja <archit.taneja@gmail.com>
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
[sumit.semwal: Ported to the drm/panel framework]
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
[jstultz: Cherry-picked to mainline, folded down other fixes
 from Vinay and Archit]
Signed-off-by: John Stultz <john.stultz@linaro.org>
[vinay simha bn: removed interface setting cmd mode, video
mode panel setting selection]
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 drivers/gpu/drm/panel/Kconfig                  |  11 +
 drivers/gpu/drm/panel/Makefile                 |   1 +
 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c | 506 +++++++++++++++++++++++++
 3 files changed, 518 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 1500ab9..4a6fcc2 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -7,6 +7,17 @@ config DRM_PANEL
 menu "Display Panels"
 	depends on DRM && DRM_PANEL
 
+config DRM_PANEL_JDI_LT070ME05000
+	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video
+	  mode panel as found in Google Nexus 7 (2013) devices.
+	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+	  24 bit RGB per pixel.
+
 config DRM_PANEL_SIMPLE
 	tristate "support for simple panels"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f277eed..5d74ac2 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644
index 0000000..aeeba99
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2016 InforceComputing
+ * Author: Vinay Simha BN <simhavcs@gmail.com>
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ *
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ * http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+#define PANEL_DEV_REGULATOR_MAX   3
+
+const char *regs[] = {
+	"power",
+	"vddp",
+	"dcdc_en"
+};
+
+struct jdi_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	struct regulator_bulk_data supplies[PANEL_DEV_REGULATOR_MAX];
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *vcc_gpio;
+	struct gpio_desc *pwm_gpio;
+
+	/* TODO: Add backlight brightness support */
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct jdi_panel, base);
+}
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_soft_reset(dsi);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(10000, 20000);
+
+	ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_on(dsi,
+				       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+	if (ret < 0)
+		return ret;
+	usleep_range(5000, 10000);
+
+	ret = mipi_dsi_set_tear_scanline(dsi, 0x03, 0x00);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+				 (u8[]){ 0xFF }, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+				 (u8[]){ 0x24 }, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,
+				 (u8[]){ 0x00 }, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+	mdelay(120);
+
+	ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2);
+	if (ret < 0)
+		return ret;
+	mdelay(10);
+
+	ret = mipi_dsi_generic_write(dsi, (u8[])
+				     {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6);
+	if (ret < 0)
+		return ret;
+	mdelay(20);
+
+	ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_off(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(100);
+
+	return 0;
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (!jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("disable\n");
+
+	/* TODO to add the backlight brightness control */
+
+	jdi->enabled = false;
+
+	return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	struct regulator_bulk_data *s = jdi->supplies;
+	int num = PANEL_DEV_REGULATOR_MAX;
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	if (!jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("unprepare\n");
+
+	ret = jdi_panel_off(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_bulk_disable(num, s);
+	if (ret < 0) {
+		dev_err(dev, "regulator disable failed, %d\n", ret);
+		return ret;
+	}
+
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+
+	jdi->prepared = false;
+
+	return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	struct regulator_bulk_data *s = jdi->supplies;
+	int num = PANEL_DEV_REGULATOR_MAX;
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	if (jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("prepare\n");
+
+	ret = regulator_bulk_enable(num, s);
+	if (ret < 0) {
+		dev_err(dev, "regulator enable failed, %d\n", ret);
+		return ret;
+	}
+
+	msleep(20);
+
+	if (jdi->vcc_gpio) {
+		gpiod_set_value(jdi->vcc_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->reset_gpio) {
+		gpiod_set_value(jdi->reset_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->pwm_gpio) {
+		gpiod_set_value(jdi->pwm_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->enable_gpio) {
+		gpiod_set_value(jdi->enable_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	ret = jdi_panel_init(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to init panel: %d\n", ret);
+		goto poweroff;
+	}
+
+	ret = jdi_panel_on(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+		goto poweroff;
+	}
+
+	jdi->prepared = true;
+
+	return 0;
+
+poweroff:
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("enable\n");
+
+	/* TODO to add the backlight brightness control */
+	jdi->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+		.clock = 155493,
+		.hdisplay = 1200,
+		.hsync_start = 1200 + 48,
+		.hsync_end = 1200 + 48 + 32,
+		.htotal = 1200 + 48 + 32 + 60,
+		.vdisplay = 1920,
+		.vsync_start = 1920 + 3,
+		.vsync_end = 1920 + 3 + 5,
+		.vtotal = 1920 + 3 + 5 + 6,
+		.vrefresh = 60,
+		.flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	struct device *dev = &jdi->dsi->dev;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+	.disable = jdi_panel_disable,
+	.unprepare = jdi_panel_unprepare,
+	.prepare = jdi_panel_prepare,
+	.enable = jdi_panel_enable,
+	.get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+	{ .compatible = "jdi,lt070me05000", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+	struct device *dev = &jdi->dsi->dev;
+	struct regulator_bulk_data *s = jdi->supplies;
+	int num = PANEL_DEV_REGULATOR_MAX;
+	int i, ret;
+
+	jdi->mode = &default_mode;
+
+	for (i = 0; i < num; i++)
+		s[i].supply = regs[i];
+
+	ret = devm_regulator_bulk_get(dev, num, s);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to init regulator, ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->vcc_gpio)) {
+		dev_err(dev, "cannot get vcc-gpio %ld\n",
+			PTR_ERR(jdi->vcc_gpio));
+		jdi->vcc_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->vcc_gpio, 0);
+	}
+
+	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(jdi->reset_gpio));
+		jdi->reset_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->reset_gpio, 0);
+	}
+
+	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpio %ld\n",
+			PTR_ERR(jdi->enable_gpio));
+		jdi->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->enable_gpio, 0);
+	}
+
+	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->pwm_gpio)) {
+		dev_err(dev, "cannot get pwm-gpio %ld\n",
+			PTR_ERR(jdi->pwm_gpio));
+		jdi->pwm_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->pwm_gpio, 0);
+	}
+
+	drm_panel_init(&jdi->base);
+	jdi->base.funcs = &jdi_panel_funcs;
+	jdi->base.dev = &jdi->dsi->dev;
+
+	ret = drm_panel_add(&jdi->base);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+	if (jdi->base.dev)
+		drm_panel_remove(&jdi->base);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi;
+	int ret;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
+			MIPI_DSI_MODE_VIDEO_HSA |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+	if (!jdi)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, jdi);
+
+	jdi->dsi = dsi;
+
+	ret = jdi_panel_add(jdi);
+	if (ret < 0)
+		return ret;
+
+	return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = jdi_panel_disable(&jdi->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+			ret);
+
+	drm_panel_detach(&jdi->base);
+	jdi_panel_del(jdi);
+
+	return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+	jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+	.driver = {
+		.name = "panel-jdi-lt070me05000",
+		.of_match_table = jdi_of_match,
+	},
+	.probe = jdi_panel_probe,
+	.remove = jdi_panel_remove,
+	.shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
+MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video mode panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

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

* [PATCH v2 4/4] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-20  9:32         ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20  9:32 UTC (permalink / raw)
  Cc: Vinay Simha BN, Archit Taneja, Sumit Semwal, John Stultz,
	Thierry Reding, David Airlie, open list,
	open list:DRM PANEL DRIVERS

Add support for the JDI lt070me05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
    https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
    git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27

Other fixes folded in were provided
by Archit Taneja <archit.taneja@gmail.com>

Cc: Archit Taneja <archit.taneja@gmail.com>
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
[sumit.semwal: Ported to the drm/panel framework]
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
[jstultz: Cherry-picked to mainline, folded down other fixes
 from Vinay and Archit]
Signed-off-by: John Stultz <john.stultz@linaro.org>
[vinay simha bn: removed interface setting cmd mode, video
mode panel setting selection]
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 drivers/gpu/drm/panel/Kconfig                  |  11 +
 drivers/gpu/drm/panel/Makefile                 |   1 +
 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c | 506 +++++++++++++++++++++++++
 3 files changed, 518 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 1500ab9..4a6fcc2 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -7,6 +7,17 @@ config DRM_PANEL
 menu "Display Panels"
 	depends on DRM && DRM_PANEL
 
+config DRM_PANEL_JDI_LT070ME05000
+	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video
+	  mode panel as found in Google Nexus 7 (2013) devices.
+	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+	  24 bit RGB per pixel.
+
 config DRM_PANEL_SIMPLE
 	tristate "support for simple panels"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f277eed..5d74ac2 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644
index 0000000..aeeba99
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2016 InforceComputing
+ * Author: Vinay Simha BN <simhavcs@gmail.com>
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ *
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ * http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+#define PANEL_DEV_REGULATOR_MAX   3
+
+const char *regs[] = {
+	"power",
+	"vddp",
+	"dcdc_en"
+};
+
+struct jdi_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	struct regulator_bulk_data supplies[PANEL_DEV_REGULATOR_MAX];
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *vcc_gpio;
+	struct gpio_desc *pwm_gpio;
+
+	/* TODO: Add backlight brightness support */
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct jdi_panel, base);
+}
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_soft_reset(dsi);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(10000, 20000);
+
+	ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_on(dsi,
+				       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+	if (ret < 0)
+		return ret;
+	usleep_range(5000, 10000);
+
+	ret = mipi_dsi_set_tear_scanline(dsi, 0x03, 0x00);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+				 (u8[]){ 0xFF }, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+				 (u8[]){ 0x24 }, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,
+				 (u8[]){ 0x00 }, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+	mdelay(120);
+
+	ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2);
+	if (ret < 0)
+		return ret;
+	mdelay(10);
+
+	ret = mipi_dsi_generic_write(dsi, (u8[])
+				     {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6);
+	if (ret < 0)
+		return ret;
+	mdelay(20);
+
+	ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_off(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(100);
+
+	return 0;
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (!jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("disable\n");
+
+	/* TODO to add the backlight brightness control */
+
+	jdi->enabled = false;
+
+	return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	struct regulator_bulk_data *s = jdi->supplies;
+	int num = PANEL_DEV_REGULATOR_MAX;
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	if (!jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("unprepare\n");
+
+	ret = jdi_panel_off(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_bulk_disable(num, s);
+	if (ret < 0) {
+		dev_err(dev, "regulator disable failed, %d\n", ret);
+		return ret;
+	}
+
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+
+	jdi->prepared = false;
+
+	return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	struct regulator_bulk_data *s = jdi->supplies;
+	int num = PANEL_DEV_REGULATOR_MAX;
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	if (jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("prepare\n");
+
+	ret = regulator_bulk_enable(num, s);
+	if (ret < 0) {
+		dev_err(dev, "regulator enable failed, %d\n", ret);
+		return ret;
+	}
+
+	msleep(20);
+
+	if (jdi->vcc_gpio) {
+		gpiod_set_value(jdi->vcc_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->reset_gpio) {
+		gpiod_set_value(jdi->reset_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->pwm_gpio) {
+		gpiod_set_value(jdi->pwm_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->enable_gpio) {
+		gpiod_set_value(jdi->enable_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	ret = jdi_panel_init(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to init panel: %d\n", ret);
+		goto poweroff;
+	}
+
+	ret = jdi_panel_on(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+		goto poweroff;
+	}
+
+	jdi->prepared = true;
+
+	return 0;
+
+poweroff:
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("enable\n");
+
+	/* TODO to add the backlight brightness control */
+	jdi->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+		.clock = 155493,
+		.hdisplay = 1200,
+		.hsync_start = 1200 + 48,
+		.hsync_end = 1200 + 48 + 32,
+		.htotal = 1200 + 48 + 32 + 60,
+		.vdisplay = 1920,
+		.vsync_start = 1920 + 3,
+		.vsync_end = 1920 + 3 + 5,
+		.vtotal = 1920 + 3 + 5 + 6,
+		.vrefresh = 60,
+		.flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	struct device *dev = &jdi->dsi->dev;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+	.disable = jdi_panel_disable,
+	.unprepare = jdi_panel_unprepare,
+	.prepare = jdi_panel_prepare,
+	.enable = jdi_panel_enable,
+	.get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+	{ .compatible = "jdi,lt070me05000", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+	struct device *dev = &jdi->dsi->dev;
+	struct regulator_bulk_data *s = jdi->supplies;
+	int num = PANEL_DEV_REGULATOR_MAX;
+	int i, ret;
+
+	jdi->mode = &default_mode;
+
+	for (i = 0; i < num; i++)
+		s[i].supply = regs[i];
+
+	ret = devm_regulator_bulk_get(dev, num, s);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to init regulator, ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->vcc_gpio)) {
+		dev_err(dev, "cannot get vcc-gpio %ld\n",
+			PTR_ERR(jdi->vcc_gpio));
+		jdi->vcc_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->vcc_gpio, 0);
+	}
+
+	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(jdi->reset_gpio));
+		jdi->reset_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->reset_gpio, 0);
+	}
+
+	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpio %ld\n",
+			PTR_ERR(jdi->enable_gpio));
+		jdi->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->enable_gpio, 0);
+	}
+
+	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->pwm_gpio)) {
+		dev_err(dev, "cannot get pwm-gpio %ld\n",
+			PTR_ERR(jdi->pwm_gpio));
+		jdi->pwm_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->pwm_gpio, 0);
+	}
+
+	drm_panel_init(&jdi->base);
+	jdi->base.funcs = &jdi_panel_funcs;
+	jdi->base.dev = &jdi->dsi->dev;
+
+	ret = drm_panel_add(&jdi->base);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+	if (jdi->base.dev)
+		drm_panel_remove(&jdi->base);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi;
+	int ret;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
+			MIPI_DSI_MODE_VIDEO_HSA |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+	if (!jdi)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, jdi);
+
+	jdi->dsi = dsi;
+
+	ret = jdi_panel_add(jdi);
+	if (ret < 0)
+		return ret;
+
+	return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = jdi_panel_disable(&jdi->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+			ret);
+
+	drm_panel_detach(&jdi->base);
+	jdi_panel_del(jdi);
+
+	return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+	jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+	.driver = {
+		.name = "panel-jdi-lt070me05000",
+		.of_match_table = jdi_of_match,
+	},
+	.probe = jdi_panel_probe,
+	.remove = jdi_panel_remove,
+	.shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
+MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video mode panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-20  9:42     ` Vinay Simha
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-20  9:42 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Archit Taneja, Sumit Semwal, John Stultz, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, David Airlie,
	Jonathan Cameron, Ralf Baechle, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

On Wed, Apr 13, 2016 at 7:19 PM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Wed, Apr 13, 2016 at 11:58:04AM +0530, Vinay Simha BN wrote:
>> Add support for the JDI lt070me05000 WUXGA DSI panel used in
>> Nexus 7 2013 devices.
>>
>> Programming sequence for the panel is was originally found in the
>> android-msm-flo-3.4-lollipop-release branch from:
>>     https://android.googlesource.com/kernel/msm.git
>>
>> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
>> file in:
>>     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
>>
>> Other fixes folded in were provided
>> by Archit Taneja <archit.taneja@gmail.com>
>>
>> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
>> [sumit.semwal: Ported to the drm/panel framework]
>> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
>> [jstultz: Cherry-picked to mainline, folded down other fixes
>>  from Vinay and Archit]
>> Signed-off-by: John Stultz <john.stultz@linaro.org>
>> ---
>>  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>>  drivers/gpu/drm/panel/Kconfig                      |  11 +
>>  drivers/gpu/drm/panel/Makefile                     |   1 +
>>  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>>  5 files changed, 725 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>>  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>
> What's the difference between this and the patch you sent earlier? I'm
> going to assume that the newer one is the correct patch, so I'll ignore
> the previous patch.
>
>> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> new file mode 100644
>> index 0000000..35c5ac7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>
> The binding documentation should be a separate patch.
>
>> @@ -0,0 +1,27 @@
>> +JDI model LT070ME05000 1920x1200 7" DSI Panel
>> +
>> +Basic data sheet is at:
>> +     http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> +
>> +This panel has video mode implemented currently in the driver.
>
> That's information irrelevant to the DT binding, since you're presumably
> talking about the Linux drm/panel driver, whereas the DT binding is
> supposed to specify the description of the panel hardware in OS-agnostic
> terms.
>
>> +Required properties:
>> +- compatible: should be "jdi,lt070me05000"
>> +
>> +Optional properties:
>> +- power-supply: phandle of the regulator that provides the supply voltage
>> +- reset-gpio: phandle of gpio for reset line
>> +- backlight: phandle of the backlight device attached to the panel
>> +
>> +Example:
>> +
>> +     dsi@54300000 {
>> +             panel: panel@0 {
>> +                     compatible = "jdi,lt070me05000";
>> +                     reg = <0>;
>> +
>> +                     power-supply = <...>;
>> +                     reset-gpio = <...>;
>> +                     backlight = <...>;
>> +             };
>> +     };
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index a580f3e..ec42bb4 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -130,6 +130,7 @@ invensense        InvenSense Inc.
>>  isee ISEE 2007 S.L.
>>  isil Intersil
>>  issi Integrated Silicon Solutions Inc.
>> +jdi  Japan Display Inc.
>>  jedec        JEDEC Solid State Technology Association
>>  karo Ka-Ro electronics GmbH
>>  keymile      Keymile GmbH
>
> This should be a separate patch as well.
>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 1500ab9..f41690e 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>>         To compile this driver as a module, choose M here: the module
>>         will be called panel-sharp-lq101r1sx01.
>>
>> +config DRM_PANEL_JDI_LT070ME05000
>> +     tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
>> +       command mode panel as found in Google Nexus 7 (2013) devices.
>> +       The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
>> +       24 bit RGB per pixel.
>> +
>>  config DRM_PANEL_SHARP_LS043T1LE01
>>       tristate "Sharp LS043T1LE01 qHD video mode panel"
>>       depends on OF
>
> Please keep these sorted alphabetically. I do realize that the list
> isn't sorted quite correctly at the moment, so you may as well leave
> this as-is and I'll fix up the order when applying and after fixing
> the current ordering.
>
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index f277eed..e6c6fc8 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>
> Same here.
>
>> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> new file mode 100644
>> index 0000000..051aa1b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> @@ -0,0 +1,685 @@
>> +/*
>> + * Copyright (C) 2015 InforceComputing
>> + * Author: Vinay Simha BN <simhavcs@gmail.com>
>> + *
>> + * Copyright (C) 2015 Linaro Ltd
>> + * Author: Sumit Semwal <sumit.semwal@linaro.org>
>
> Should either of those copyright lines include 2016? We're a pretty good
> way into 2016, and I'd be surprised if this wasn't touched this year at
> all.
>
>> +/*
>> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
>> + * JDI model LT070ME05000, and its data sheet is at:
>> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> + */
>
> This comment is oddly placed here above the struct jdi_panel definition.
> Perhaps move that information into the header comment?
>
>> +struct jdi_panel {
>> +     struct drm_panel base;
>> +     struct mipi_dsi_device *dsi;
>> +
>> +     /* TODO: Add backilght support */
>
> "backlight". Also if this is still TODO, why even have parts of the code
> here? Just drop everything that's not used and move that to a follow-up
> patch that implements full support.
>
>> +     struct backlight_device *backlight;
>> +     struct regulator *supply;
>> +     struct gpio_desc *reset_gpio;
>> +     struct gpio_desc *enable_gpio;
>> +     struct gpio_desc *vcc_gpio;
>> +
>> +     struct regulator *backlit;
>> +     struct regulator *lvs7;
>> +     struct regulator *avdd;
>> +     struct regulator *iovdd;
>> +     struct gpio_desc *pwm_gpio;
>
> Most of these resources aren't specified in the device tree binding, so
> your driver doesn't properly implement the binding. You'll have to fix
> the driver or the binding.
>
>> +
>> +     bool prepared;
>> +     bool enabled;
>> +
>> +     const struct drm_display_mode *mode;
>> +};
>> +
>> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
>> +{
>> +     return container_of(panel, struct jdi_panel, base);
>> +}
>> +
>> +static char MCAP[2] = {0xB0, 0x00};
>> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
>> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
>
> Please make all of these (and the ones below) arrays of u8, since that's
> the type used in the prototype of the function that receives these as
> parameters. Also, please use consistent capitalization and a space after
> { and before }.
>
>> +static char interface_ID_setting[2] = {0xB4, 0x0C};
>> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
>> +
>> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
>
> This is a standard DCS command, please turn this into a generic helper
> such as mipi_dsi_dcs_set_tear_scanline().
>
>> +/* for fps control, set fps to 60.32Hz */
>> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
>> +static char sequencer_timing_control[2] = {0xD6, 0x01};
>> +
>> +/* set brightness */
>> +static char write_display_brightness[] = {0x51, 0xFF};
>
> Same here.
>
>> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
>> +static char write_control_display[] = {0x53, 0x24};
>
> And here.
>
>> +/*
>> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
>> + * disable SRE(sunlight readability enhancement)
>> + */
>> +static char write_cabc[] = {0x55, 0x00};
>
> And here.
>
>> +static int jdi_panel_init(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +     if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
>> +             ret = mipi_dsi_dcs_soft_reset(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             mdelay(10);
>
> Please don't use mdelay() because it will busy-loop for a very long time
> and waste precious CPU cycles. Instead, use usleep_range() for these
> cases (or msleep() for durations longer than ~10 ms). Same goes for the
> other occurrences below.
>
>> +
>> +             ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
>> +             if (ret < 0)
>> +                     return ret;
>
> There are symbolic constants for the pixel formats, use them to convey
> meaning.
>
>> +
>> +             ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
>> +             if (ret < 0)
>> +                     return ret;
>
> These should be parameterized on the panel width and height, to make it
> clear where the values come from.
>
>> +
>> +             ret = mipi_dsi_dcs_set_tear_on(dsi,
>> +                                            MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(5);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, tear_scan_line,
>> +                                          sizeof(tear_scan_line));
>> +             if (ret < 0)
>> +                     return ret;
>
> Like I mentioned earlier, this should be a generic helper to help make
> its intention clearer.
>
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
>> +                                             sizeof(write_display_brightness)
>> +                                             );
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
>> +                                             sizeof(write_control_display));
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
>> +                                             sizeof(write_cabc));
>> +             if (ret < 0)
>> +                     return ret;
>
> Same for these. I don't currently have access to the DCS 1.3
> specification, but I suspect that some of the parameters for these
> commands could be defined via symbolic names.
>
>> +
>> +             ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(120);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(10);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, interface_setting,
>> +                                          sizeof(interface_setting));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(20);
>> +
>> +             backlight_control4[18] = 0x04;
>> +             backlight_control4[19] = 0x00;
>> +
>> +             ret = mipi_dsi_generic_write(dsi, backlight_control4,
>> +                                          sizeof(backlight_control4));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(20);
>> +
>> +             MCAP[1] = 0x03;
>> +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +             if (ret < 0)
>> +                     return ret;
>
> That's odd. Doesn't this break idempotence of this function? You modify
> the global MCAP array by writing 0x03 to MCAP[1], so when this function
> is called a second time, MCAP[1] will be 0x03 already in the first call
> a few lines above, instead of the 0x00 that it was initially.
>
>> +     } else {
>> +             /*
>> +              * TODO : need to verify panel settings when
>> +              * dsi cmd mode supported for apq8064 - simhavcs
>> +              */
>> +             ret = mipi_dsi_dcs_soft_reset(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>
> This whole else clause is dead code because the condition for the if
> clause is always true. Just drop it and add it back when you properly
> support both modes.
>
>> +static int jdi_panel_on(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +     ret = mipi_dsi_dcs_set_display_on(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int jdi_panel_off(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>
> Should this not be cleared at the end of the function so that the below
> commands still get run in LP mode?
display off is done in HS mode as per the clock diagram of the generic
dsi panel datasheet.
>
>> +
>> +     ret = mipi_dsi_dcs_set_display_off(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = mipi_dsi_dcs_set_tear_off(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     msleep(100);
>> +
>> +     return 0;
>> +}
>> +
>> +static int jdi_panel_disable(struct drm_panel *panel)
>> +{
>> +     struct jdi_panel *jdi = to_jdi_panel(panel);
>> +
>> +     if (!jdi->enabled)
>> +             return 0;
>> +
>> +     DRM_DEBUG("disable\n");
>
> This doesn't look very useful debug information. I think you should
> remove it. Same for other similar occurrences below.
when we enable the drm.debug log in cmdline, we well get this information,
It is good to have debug logs of the panel got disabled or not.
If you suggest not required will remove in the v3 version
>
>> +static const struct drm_panel_funcs jdi_panel_funcs = {
>> +             .disable = jdi_panel_disable,
>> +             .unprepare = jdi_panel_unprepare,
>> +             .prepare = jdi_panel_prepare,
>> +             .enable = jdi_panel_enable,
>> +             .get_modes = jdi_panel_get_modes,
>> +};
>> +
>> +static const struct of_device_id jdi_of_match[] = {
>> +             { .compatible = "jdi,lt070me05000", },
>> +             { }
>> +};
>> +MODULE_DEVICE_TABLE(of, jdi_of_match);
>
> A single tab is enough to indent the above.
>
>> +static int jdi_panel_add(struct jdi_panel *jdi)
>> +{
>> +     struct device *dev = &jdi->dsi->dev;
>> +     int ret;
>> +
>> +     jdi->mode = &default_mode;
>
> What do you keep this around for?
This will have the default resolution of the dsi panel.
>
>> +
>> +     /* lvs5 */
>> +     jdi->supply = devm_regulator_get(dev, "power");
>> +     if (IS_ERR(jdi->supply))
>> +             return PTR_ERR(jdi->supply);
>> +
>> +     /* l17 */
>> +     jdi->backlit = devm_regulator_get(dev, "backlit");
>> +     if (IS_ERR(jdi->supply))
>> +             return PTR_ERR(jdi->supply);
>> +
>> +     jdi->lvs7 = devm_regulator_get(dev, "lvs7");
>> +     if (IS_ERR(jdi->lvs7))
>> +             return PTR_ERR(jdi->lvs7);
>> +
>> +     jdi->avdd = devm_regulator_get(dev, "avdd");
>> +     if (IS_ERR(jdi->avdd))
>> +             return PTR_ERR(jdi->avdd);
>> +
>> +     jdi->iovdd = devm_regulator_get(dev, "iovdd");
>> +     if (IS_ERR(jdi->iovdd))
>> +             return PTR_ERR(jdi->iovdd);
>> +
>> +     jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->vcc_gpio)) {
>> +             dev_err(dev, "cannot get vcc-gpio %ld\n",
>> +                     PTR_ERR(jdi->vcc_gpio));
>> +             jdi->vcc_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->vcc_gpio, 0);
>> +     }
>> +
>> +     jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->reset_gpio)) {
>> +             dev_err(dev, "cannot get reset-gpios %ld\n",
>> +                     PTR_ERR(jdi->reset_gpio));
>> +             jdi->reset_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->reset_gpio, 0);
>> +     }
>> +
>> +     jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->enable_gpio)) {
>> +             dev_err(dev, "cannot get enable-gpio %ld\n",
>> +                     PTR_ERR(jdi->enable_gpio));
>> +             jdi->enable_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->enable_gpio, 0);
>> +     }
>> +
>> +     jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->pwm_gpio)) {
>> +             dev_err(dev, "cannot get pwm-gpio %ld\n",
>> +                     PTR_ERR(jdi->pwm_gpio));
>> +             jdi->pwm_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->pwm_gpio, 0);
>> +     }
>
> As I said earlier, most of these aren't specified in the binding, fix
> either the binding or the code.
>
>> +     /* we don't have backlight right now, proceed further */
>> +#ifdef BACKLIGHT
>> +     np = of_parse_phandle(dev->of_node, "backlight", 0);
>> +     if (np) {
>> +             jdi->backlight = of_find_backlight_by_node(np);
>> +             of_node_put(np);
>> +
>> +             if (!jdi->backlight)
>> +                     return -EPROBE_DEFER;
>> +     }
>> +#endif
>
> Again, if you don't support it, don't submit it. We don't want dead code
> in the kernel.
>
>> +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
>> +MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
>> +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
>
> Technically this doesn't support command mode yet, so you might want to
> remove that from the description to avoid confusion.
>
> Thierry

all other inputs are fixed in the v2 version

-- 
Regards,

Vinay Simha.B.N.

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-20  9:42     ` Vinay Simha
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-20  9:42 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Archit Taneja, Sumit Semwal, John Stultz, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, David Airlie,
	Jonathan Cameron, Ralf Baechle, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

On Wed, Apr 13, 2016 at 7:19 PM, Thierry Reding
<thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Wed, Apr 13, 2016 at 11:58:04AM +0530, Vinay Simha BN wrote:
>> Add support for the JDI lt070me05000 WUXGA DSI panel used in
>> Nexus 7 2013 devices.
>>
>> Programming sequence for the panel is was originally found in the
>> android-msm-flo-3.4-lollipop-release branch from:
>>     https://android.googlesource.com/kernel/msm.git
>>
>> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
>> file in:
>>     git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
>>
>> Other fixes folded in were provided
>> by Archit Taneja <archit.taneja-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>
>> Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> [sumit.semwal: Ported to the drm/panel framework]
>> Signed-off-by: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> [jstultz: Cherry-picked to mainline, folded down other fixes
>>  from Vinay and Archit]
>> Signed-off-by: John Stultz <john.stultz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>  .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>>  drivers/gpu/drm/panel/Kconfig                      |  11 +
>>  drivers/gpu/drm/panel/Makefile                     |   1 +
>>  drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
>>  5 files changed, 725 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>>  create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>
> What's the difference between this and the patch you sent earlier? I'm
> going to assume that the newer one is the correct patch, so I'll ignore
> the previous patch.
>
>> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> new file mode 100644
>> index 0000000..35c5ac7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>
> The binding documentation should be a separate patch.
>
>> @@ -0,0 +1,27 @@
>> +JDI model LT070ME05000 1920x1200 7" DSI Panel
>> +
>> +Basic data sheet is at:
>> +     http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> +
>> +This panel has video mode implemented currently in the driver.
>
> That's information irrelevant to the DT binding, since you're presumably
> talking about the Linux drm/panel driver, whereas the DT binding is
> supposed to specify the description of the panel hardware in OS-agnostic
> terms.
>
>> +Required properties:
>> +- compatible: should be "jdi,lt070me05000"
>> +
>> +Optional properties:
>> +- power-supply: phandle of the regulator that provides the supply voltage
>> +- reset-gpio: phandle of gpio for reset line
>> +- backlight: phandle of the backlight device attached to the panel
>> +
>> +Example:
>> +
>> +     dsi@54300000 {
>> +             panel: panel@0 {
>> +                     compatible = "jdi,lt070me05000";
>> +                     reg = <0>;
>> +
>> +                     power-supply = <...>;
>> +                     reset-gpio = <...>;
>> +                     backlight = <...>;
>> +             };
>> +     };
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index a580f3e..ec42bb4 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -130,6 +130,7 @@ invensense        InvenSense Inc.
>>  isee ISEE 2007 S.L.
>>  isil Intersil
>>  issi Integrated Silicon Solutions Inc.
>> +jdi  Japan Display Inc.
>>  jedec        JEDEC Solid State Technology Association
>>  karo Ka-Ro electronics GmbH
>>  keymile      Keymile GmbH
>
> This should be a separate patch as well.
>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 1500ab9..f41690e 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>>         To compile this driver as a module, choose M here: the module
>>         will be called panel-sharp-lq101r1sx01.
>>
>> +config DRM_PANEL_JDI_LT070ME05000
>> +     tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
>> +       command mode panel as found in Google Nexus 7 (2013) devices.
>> +       The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
>> +       24 bit RGB per pixel.
>> +
>>  config DRM_PANEL_SHARP_LS043T1LE01
>>       tristate "Sharp LS043T1LE01 qHD video mode panel"
>>       depends on OF
>
> Please keep these sorted alphabetically. I do realize that the list
> isn't sorted quite correctly at the moment, so you may as well leave
> this as-is and I'll fix up the order when applying and after fixing
> the current ordering.
>
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index f277eed..e6c6fc8 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
>>  obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>  obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>  obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>
> Same here.
>
>> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> new file mode 100644
>> index 0000000..051aa1b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> @@ -0,0 +1,685 @@
>> +/*
>> + * Copyright (C) 2015 InforceComputing
>> + * Author: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + *
>> + * Copyright (C) 2015 Linaro Ltd
>> + * Author: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>
> Should either of those copyright lines include 2016? We're a pretty good
> way into 2016, and I'd be surprised if this wasn't touched this year at
> all.
>
>> +/*
>> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
>> + * JDI model LT070ME05000, and its data sheet is at:
>> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> + */
>
> This comment is oddly placed here above the struct jdi_panel definition.
> Perhaps move that information into the header comment?
>
>> +struct jdi_panel {
>> +     struct drm_panel base;
>> +     struct mipi_dsi_device *dsi;
>> +
>> +     /* TODO: Add backilght support */
>
> "backlight". Also if this is still TODO, why even have parts of the code
> here? Just drop everything that's not used and move that to a follow-up
> patch that implements full support.
>
>> +     struct backlight_device *backlight;
>> +     struct regulator *supply;
>> +     struct gpio_desc *reset_gpio;
>> +     struct gpio_desc *enable_gpio;
>> +     struct gpio_desc *vcc_gpio;
>> +
>> +     struct regulator *backlit;
>> +     struct regulator *lvs7;
>> +     struct regulator *avdd;
>> +     struct regulator *iovdd;
>> +     struct gpio_desc *pwm_gpio;
>
> Most of these resources aren't specified in the device tree binding, so
> your driver doesn't properly implement the binding. You'll have to fix
> the driver or the binding.
>
>> +
>> +     bool prepared;
>> +     bool enabled;
>> +
>> +     const struct drm_display_mode *mode;
>> +};
>> +
>> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
>> +{
>> +     return container_of(panel, struct jdi_panel, base);
>> +}
>> +
>> +static char MCAP[2] = {0xB0, 0x00};
>> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
>> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
>
> Please make all of these (and the ones below) arrays of u8, since that's
> the type used in the prototype of the function that receives these as
> parameters. Also, please use consistent capitalization and a space after
> { and before }.
>
>> +static char interface_ID_setting[2] = {0xB4, 0x0C};
>> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
>> +
>> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
>
> This is a standard DCS command, please turn this into a generic helper
> such as mipi_dsi_dcs_set_tear_scanline().
>
>> +/* for fps control, set fps to 60.32Hz */
>> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
>> +static char sequencer_timing_control[2] = {0xD6, 0x01};
>> +
>> +/* set brightness */
>> +static char write_display_brightness[] = {0x51, 0xFF};
>
> Same here.
>
>> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
>> +static char write_control_display[] = {0x53, 0x24};
>
> And here.
>
>> +/*
>> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
>> + * disable SRE(sunlight readability enhancement)
>> + */
>> +static char write_cabc[] = {0x55, 0x00};
>
> And here.
>
>> +static int jdi_panel_init(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +     if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
>> +             ret = mipi_dsi_dcs_soft_reset(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             mdelay(10);
>
> Please don't use mdelay() because it will busy-loop for a very long time
> and waste precious CPU cycles. Instead, use usleep_range() for these
> cases (or msleep() for durations longer than ~10 ms). Same goes for the
> other occurrences below.
>
>> +
>> +             ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
>> +             if (ret < 0)
>> +                     return ret;
>
> There are symbolic constants for the pixel formats, use them to convey
> meaning.
>
>> +
>> +             ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
>> +             if (ret < 0)
>> +                     return ret;
>
> These should be parameterized on the panel width and height, to make it
> clear where the values come from.
>
>> +
>> +             ret = mipi_dsi_dcs_set_tear_on(dsi,
>> +                                            MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(5);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, tear_scan_line,
>> +                                          sizeof(tear_scan_line));
>> +             if (ret < 0)
>> +                     return ret;
>
> Like I mentioned earlier, this should be a generic helper to help make
> its intention clearer.
>
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
>> +                                             sizeof(write_display_brightness)
>> +                                             );
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
>> +                                             sizeof(write_control_display));
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
>> +                                             sizeof(write_cabc));
>> +             if (ret < 0)
>> +                     return ret;
>
> Same for these. I don't currently have access to the DCS 1.3
> specification, but I suspect that some of the parameters for these
> commands could be defined via symbolic names.
>
>> +
>> +             ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(120);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(10);
>> +
>> +             ret = mipi_dsi_generic_write(dsi, interface_setting,
>> +                                          sizeof(interface_setting));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(20);
>> +
>> +             backlight_control4[18] = 0x04;
>> +             backlight_control4[19] = 0x00;
>> +
>> +             ret = mipi_dsi_generic_write(dsi, backlight_control4,
>> +                                          sizeof(backlight_control4));
>> +             if (ret < 0)
>> +                     return ret;
>> +             mdelay(20);
>> +
>> +             MCAP[1] = 0x03;
>> +             ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +             if (ret < 0)
>> +                     return ret;
>
> That's odd. Doesn't this break idempotence of this function? You modify
> the global MCAP array by writing 0x03 to MCAP[1], so when this function
> is called a second time, MCAP[1] will be 0x03 already in the first call
> a few lines above, instead of the 0x00 that it was initially.
>
>> +     } else {
>> +             /*
>> +              * TODO : need to verify panel settings when
>> +              * dsi cmd mode supported for apq8064 - simhavcs
>> +              */
>> +             ret = mipi_dsi_dcs_soft_reset(dsi);
>> +             if (ret < 0)
>> +                     return ret;
>
> This whole else clause is dead code because the condition for the if
> clause is always true. Just drop it and add it back when you properly
> support both modes.
>
>> +static int jdi_panel_on(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +     ret = mipi_dsi_dcs_set_display_on(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int jdi_panel_off(struct jdi_panel *jdi)
>> +{
>> +     struct mipi_dsi_device *dsi = jdi->dsi;
>> +     int ret;
>> +
>> +     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>
> Should this not be cleared at the end of the function so that the below
> commands still get run in LP mode?
display off is done in HS mode as per the clock diagram of the generic
dsi panel datasheet.
>
>> +
>> +     ret = mipi_dsi_dcs_set_display_off(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = mipi_dsi_dcs_set_tear_off(dsi);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     msleep(100);
>> +
>> +     return 0;
>> +}
>> +
>> +static int jdi_panel_disable(struct drm_panel *panel)
>> +{
>> +     struct jdi_panel *jdi = to_jdi_panel(panel);
>> +
>> +     if (!jdi->enabled)
>> +             return 0;
>> +
>> +     DRM_DEBUG("disable\n");
>
> This doesn't look very useful debug information. I think you should
> remove it. Same for other similar occurrences below.
when we enable the drm.debug log in cmdline, we well get this information,
It is good to have debug logs of the panel got disabled or not.
If you suggest not required will remove in the v3 version
>
>> +static const struct drm_panel_funcs jdi_panel_funcs = {
>> +             .disable = jdi_panel_disable,
>> +             .unprepare = jdi_panel_unprepare,
>> +             .prepare = jdi_panel_prepare,
>> +             .enable = jdi_panel_enable,
>> +             .get_modes = jdi_panel_get_modes,
>> +};
>> +
>> +static const struct of_device_id jdi_of_match[] = {
>> +             { .compatible = "jdi,lt070me05000", },
>> +             { }
>> +};
>> +MODULE_DEVICE_TABLE(of, jdi_of_match);
>
> A single tab is enough to indent the above.
>
>> +static int jdi_panel_add(struct jdi_panel *jdi)
>> +{
>> +     struct device *dev = &jdi->dsi->dev;
>> +     int ret;
>> +
>> +     jdi->mode = &default_mode;
>
> What do you keep this around for?
This will have the default resolution of the dsi panel.
>
>> +
>> +     /* lvs5 */
>> +     jdi->supply = devm_regulator_get(dev, "power");
>> +     if (IS_ERR(jdi->supply))
>> +             return PTR_ERR(jdi->supply);
>> +
>> +     /* l17 */
>> +     jdi->backlit = devm_regulator_get(dev, "backlit");
>> +     if (IS_ERR(jdi->supply))
>> +             return PTR_ERR(jdi->supply);
>> +
>> +     jdi->lvs7 = devm_regulator_get(dev, "lvs7");
>> +     if (IS_ERR(jdi->lvs7))
>> +             return PTR_ERR(jdi->lvs7);
>> +
>> +     jdi->avdd = devm_regulator_get(dev, "avdd");
>> +     if (IS_ERR(jdi->avdd))
>> +             return PTR_ERR(jdi->avdd);
>> +
>> +     jdi->iovdd = devm_regulator_get(dev, "iovdd");
>> +     if (IS_ERR(jdi->iovdd))
>> +             return PTR_ERR(jdi->iovdd);
>> +
>> +     jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->vcc_gpio)) {
>> +             dev_err(dev, "cannot get vcc-gpio %ld\n",
>> +                     PTR_ERR(jdi->vcc_gpio));
>> +             jdi->vcc_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->vcc_gpio, 0);
>> +     }
>> +
>> +     jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->reset_gpio)) {
>> +             dev_err(dev, "cannot get reset-gpios %ld\n",
>> +                     PTR_ERR(jdi->reset_gpio));
>> +             jdi->reset_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->reset_gpio, 0);
>> +     }
>> +
>> +     jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->enable_gpio)) {
>> +             dev_err(dev, "cannot get enable-gpio %ld\n",
>> +                     PTR_ERR(jdi->enable_gpio));
>> +             jdi->enable_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->enable_gpio, 0);
>> +     }
>> +
>> +     jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
>> +     if (IS_ERR(jdi->pwm_gpio)) {
>> +             dev_err(dev, "cannot get pwm-gpio %ld\n",
>> +                     PTR_ERR(jdi->pwm_gpio));
>> +             jdi->pwm_gpio = NULL;
>> +     } else {
>> +             gpiod_direction_output(jdi->pwm_gpio, 0);
>> +     }
>
> As I said earlier, most of these aren't specified in the binding, fix
> either the binding or the code.
>
>> +     /* we don't have backlight right now, proceed further */
>> +#ifdef BACKLIGHT
>> +     np = of_parse_phandle(dev->of_node, "backlight", 0);
>> +     if (np) {
>> +             jdi->backlight = of_find_backlight_by_node(np);
>> +             of_node_put(np);
>> +
>> +             if (!jdi->backlight)
>> +                     return -EPROBE_DEFER;
>> +     }
>> +#endif
>
> Again, if you don't support it, don't submit it. We don't want dead code
> in the kernel.
>
>> +MODULE_AUTHOR("Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
>> +MODULE_AUTHOR("Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
>> +MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
>
> Technically this doesn't support command mode yet, so you might want to
> remove that from the description to avoid confusion.
>
> Thierry

all other inputs are fixed in the v2 version

-- 
Regards,

Vinay Simha.B.N.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-20  9:46     ` Vinay Simha
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-20  9:46 UTC (permalink / raw)
  To: Archit Taneja
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Pawel Moll, Ian Campbell, open list:DRM PANEL DRIVERS, open list,
	Ralf Baechle, Rob Herring, Jonathan Cameron, Kumar Gala,
	Shawn Guo, Archit Taneja

On Thu, Apr 14, 2016 at 8:10 PM, Archit Taneja <architt@codeaurora.org> wrote:
>
>
> On 4/13/2016 11:58 AM, Vinay Simha BN wrote:
>>
>> Add support for the JDI lt070me05000 WUXGA DSI panel used in
>> Nexus 7 2013 devices.
>>
>> Programming sequence for the panel is was originally found in the
>> android-msm-flo-3.4-lollipop-release branch from:
>>      https://android.googlesource.com/kernel/msm.git
>>
>> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
>> file in:
>>      git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
>>
>> Other fixes folded in were provided
>> by Archit Taneja <archit.taneja@gmail.com>
>>
>> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
>> [sumit.semwal: Ported to the drm/panel framework]
>> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
>> [jstultz: Cherry-picked to mainline, folded down other fixes
>>   from Vinay and Archit]
>> Signed-off-by: John Stultz <john.stultz@linaro.org>
>> ---
>>   .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>>   .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>>   drivers/gpu/drm/panel/Kconfig                      |  11 +
>>   drivers/gpu/drm/panel/Makefile                     |   1 +
>>   drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685
>> +++++++++++++++++++++
>>   5 files changed, 725 insertions(+)
>>   create mode 100644
>> Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>>   create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>>
>> diff --git
>> a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> new file mode 100644
>> index 0000000..35c5ac7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> @@ -0,0 +1,27 @@
>> +JDI model LT070ME05000 1920x1200 7" DSI Panel
>> +
>> +Basic data sheet is at:
>> +
>> http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> +
>> +This panel has video mode implemented currently in the driver.
>> +
>> +Required properties:
>> +- compatible: should be "jdi,lt070me05000"
>> +
>> +Optional properties:
>> +- power-supply: phandle of the regulator that provides the supply voltage
>> +- reset-gpio: phandle of gpio for reset line
>> +- backlight: phandle of the backlight device attached to the panel
>> +
>> +Example:
>> +
>> +       dsi@54300000 {
>> +               panel: panel@0 {
>> +                       compatible = "jdi,lt070me05000";
>> +                       reg = <0>;
>> +
>> +                       power-supply = <...>;
>> +                       reset-gpio = <...>;
>> +                       backlight = <...>;
>> +               };
>> +       };
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index a580f3e..ec42bb4 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -130,6 +130,7 @@ invensense  InvenSense Inc.
>>   isee  ISEE 2007 S.L.
>>   isil  Intersil
>>   issi  Integrated Silicon Solutions Inc.
>> +jdi    Japan Display Inc.
>>   jedec JEDEC Solid State Technology Association
>>   karo  Ka-Ro electronics GmbH
>>   keymile       Keymile GmbH
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 1500ab9..f41690e 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>>           To compile this driver as a module, choose M here: the module
>>           will be called panel-sharp-lq101r1sx01.
>>
>> +config DRM_PANEL_JDI_LT070ME05000
>> +       tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI
>> video/
>> +         command mode panel as found in Google Nexus 7 (2013) devices.
>> +         The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
>> +         24 bit RGB per pixel.
>> +
>>   config DRM_PANEL_SHARP_LS043T1LE01
>>         tristate "Sharp LS043T1LE01 qHD video mode panel"
>>         depends on OF
>> diff --git a/drivers/gpu/drm/panel/Makefile
>> b/drivers/gpu/drm/panel/Makefile
>> index f277eed..e6c6fc8 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) +=
>> panel-samsung-ld9040.o
>>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> new file mode 100644
>> index 0000000..051aa1b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> @@ -0,0 +1,685 @@
>> +/*
>> + * Copyright (C) 2015 InforceComputing
>> + * Author: Vinay Simha BN <simhavcs@gmail.com>
>> + *
>> + * Copyright (C) 2015 Linaro Ltd
>> + * Author: Sumit Semwal <sumit.semwal@linaro.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> it
>> + * under the terms of the GNU General Public License version 2 as
>> published by
>> + * the Free Software Foundation.
>> + *
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/backlight.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_mipi_dsi.h>
>> +#include <drm/drm_panel.h>
>> +
>> +#include <video/mipi_display.h>
>> +
>> +/*
>> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
>> + * JDI model LT070ME05000, and its data sheet is at:
>> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> + */
>> +struct jdi_panel {
>> +       struct drm_panel base;
>> +       struct mipi_dsi_device *dsi;
>> +
>> +       /* TODO: Add backilght support */
>> +       struct backlight_device *backlight;
>> +       struct regulator *supply;
>> +       struct gpio_desc *reset_gpio;
>> +       struct gpio_desc *enable_gpio;
>> +       struct gpio_desc *vcc_gpio;
>> +
>> +       struct regulator *backlit;
>> +       struct regulator *lvs7;
>> +       struct regulator *avdd;
>> +       struct regulator *iovdd;
>> +       struct gpio_desc *pwm_gpio;
>> +
>> +       bool prepared;
>> +       bool enabled;
>> +
>> +       const struct drm_display_mode *mode;
>> +};
>> +
>> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
>> +{
>> +       return container_of(panel, struct jdi_panel, base);
>> +}
>> +
>> +static char MCAP[2] = {0xB0, 0x00};
>> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22,
>> 0x00};
>> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
>> +
>> +static char interface_ID_setting[2] = {0xB4, 0x0C};
>> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
>> +
>> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
>> +
>> +/* for fps control, set fps to 60.32Hz */
>> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
>> +static char sequencer_timing_control[2] = {0xD6, 0x01};
>> +
>> +/* set brightness */
>> +static char write_display_brightness[] = {0x51, 0xFF};
>> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming
>> */
>> +static char write_control_display[] = {0x53, 0x24};
>> +/*
>> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
>> + * disable SRE(sunlight readability enhancement)
>> + */
>> +static char write_cabc[] = {0x55, 0x00};
>> +/* for cabc mode 0x1(-15%) */
>> +static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00,
>> 0x32};
>> +/* for cabc mode 0x2(-40%) */
>> +static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16,
>> 0x87};
>> +/* for cabc mode 0x3(-54%) */
>> +static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45,
>> 0xB4};
>> +/* for pwm frequency and dimming control */
>> +static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67,
>> 0x78,
>> +               0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2,
>> 0xFA,
>> +               0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
>
>
> Apart from the u8 conversion as mentioned by Thierry, you should
> convert these to const too.
>
>
>> +
>> +static int jdi_panel_init(struct jdi_panel *jdi)
>> +{
>> +       struct mipi_dsi_device *dsi = jdi->dsi;
>> +       int ret;
>> +
>> +       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +       if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
>> +               ret = mipi_dsi_dcs_soft_reset(dsi);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               mdelay(10);
>> +
>> +               ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000,
>> 0x04AF);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_tear_on(dsi,
>> +
>> MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(5);
>> +
>> +               ret = mipi_dsi_generic_write(dsi, tear_scan_line,
>> +                                            sizeof(tear_scan_line));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi,
>> write_display_brightness,
>> +
>> sizeof(write_display_brightness)
>> +                                               );
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi,
>> write_control_display,
>> +
>> sizeof(write_control_display));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
>> +                                               sizeof(write_cabc));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(120);
>> +
>> +               ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(10);
>> +
>> +               ret = mipi_dsi_generic_write(dsi, interface_setting,
>> +                                            sizeof(interface_setting));
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(20);
>> +
>> +               backlight_control4[18] = 0x04;
>> +               backlight_control4[19] = 0x00;
>
>
> Any reason why these are overwritten? It would be better to have these
> values in the array itself.
>
>
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control4,
>> +                                            sizeof(backlight_control4));
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(20);
>> +
>> +               MCAP[1] = 0x03;
>> +               ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +               if (ret < 0)
>> +                       return ret;
>> +       } else {
>> +               /*
>> +                * TODO : need to verify panel settings when
>> +                * dsi cmd mode supported for apq8064 - simhavcs
>> +                */
>> +               ret = mipi_dsi_dcs_soft_reset(dsi);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               mdelay(5);
>> +
>> +               ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
>> +
>> sizeof(interface_setting_cmd));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
>> +
>> sizeof(interface_ID_setting));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, DSI_control,
>> +                                            sizeof(DSI_control));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
>> +                                            sizeof(LTPS_timing_setting));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi,
>> sequencer_timing_control,
>> +
>> sizeof(sequencer_timing_control));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi,
>> write_display_brightness,
>> +
>> sizeof(write_display_brightness)
>> +                                               );
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi,
>> write_control_display,
>> +
>> sizeof(write_control_display));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
>> +                                               sizeof(write_cabc));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control1,
>> +                                            sizeof(backlight_control1));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control2,
>> +                                            sizeof(backlight_control2));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control3,
>> +                                            sizeof(backlight_control3));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control4,
>> +                                            sizeof(backlight_control4));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000,
>> 0x04AF);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(120);
>> +
>> +               ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +               if (ret < 0)
>> +                       return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_on(struct jdi_panel *jdi)
>> +{
>> +       struct mipi_dsi_device *dsi = jdi->dsi;
>> +       int ret;
>> +
>> +       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +       ret = mipi_dsi_dcs_set_display_on(dsi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_off(struct jdi_panel *jdi)
>> +{
>> +       struct mipi_dsi_device *dsi = jdi->dsi;
>> +       int ret;
>> +
>> +       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> +       ret = mipi_dsi_dcs_set_display_off(dsi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = mipi_dsi_dcs_set_tear_off(dsi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       msleep(100);
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_disable(struct drm_panel *panel)
>> +{
>> +       struct jdi_panel *jdi = to_jdi_panel(panel);
>> +
>> +       if (!jdi->enabled)
>> +               return 0;
>> +
>> +       DRM_DEBUG("disable\n");
>> +
>> +       if (jdi->backlight) {
>> +               jdi->backlight->props.power = FB_BLANK_POWERDOWN;
>> +               backlight_update_status(jdi->backlight);
>> +       }
>> +
>> +       jdi->enabled = false;
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_unprepare(struct drm_panel *panel)
>> +{
>> +       struct jdi_panel *jdi = to_jdi_panel(panel);
>> +       int ret;
>> +
>> +       if (!jdi->prepared)
>> +               return 0;
>> +
>> +       DRM_DEBUG("unprepare\n");
>> +
>> +       ret = jdi_panel_off(jdi);
>> +       if (ret) {
>> +               dev_err(panel->dev, "failed to set panel off: %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       regulator_disable(jdi->supply);
>> +
>> +       if (jdi->vcc_gpio)
>> +               gpiod_set_value(jdi->vcc_gpio, 0);
>> +
>> +       if (jdi->reset_gpio)
>> +               gpiod_set_value(jdi->reset_gpio, 0);
>> +
>> +       if (jdi->enable_gpio)
>> +               gpiod_set_value(jdi->enable_gpio, 0);
>> +
>> +       jdi->prepared = false;
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_prepare(struct drm_panel *panel)
>> +{
>> +       struct jdi_panel *jdi = to_jdi_panel(panel);
>> +       int ret;
>> +
>> +       if (jdi->prepared)
>> +               return 0;
>> +
>> +       DRM_DEBUG("prepare\n");
>> +
>> +       ret = regulator_enable(jdi->iovdd);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = regulator_enable(jdi->avdd);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = regulator_enable(jdi->backlit);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = regulator_enable(jdi->lvs7);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = regulator_enable(jdi->supply);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       msleep(20);
>> +
>> +       if (jdi->vcc_gpio) {
>> +               gpiod_set_value(jdi->vcc_gpio, 1);
>> +               usleep_range(10, 20);
>> +       }
>> +
>> +       if (jdi->reset_gpio) {
>> +               gpiod_set_value(jdi->reset_gpio, 1);
>> +               usleep_range(10, 20);
>> +       }
>> +
>> +       if (jdi->pwm_gpio) {
>> +               gpiod_set_value(jdi->pwm_gpio, 1);
>> +               usleep_range(10, 20);
>> +       }
>> +
>> +       if (jdi->enable_gpio) {
>> +               gpiod_set_value(jdi->enable_gpio, 1);
>> +               usleep_range(10, 20);
>> +       }
>> +
>> +       ret = jdi_panel_init(jdi);
>> +       if (ret) {
>> +               dev_err(panel->dev, "failed to init panel: %d\n", ret);
>> +               goto poweroff;
>> +       }
>> +
>> +       ret = jdi_panel_on(jdi);
>> +       if (ret) {
>> +               dev_err(panel->dev, "failed to set panel on: %d\n", ret);
>> +               goto poweroff;
>> +       }
>> +
>> +       jdi->prepared = true;
>> +
>> +       return 0;
>> +
>> +poweroff:
>> +       regulator_disable(jdi->supply);
>> +       if (jdi->reset_gpio)
>> +               gpiod_set_value(jdi->reset_gpio, 0);
>> +       if (jdi->enable_gpio)
>> +               gpiod_set_value(jdi->enable_gpio, 0);
>> +       if (jdi->vcc_gpio)
>> +               gpiod_set_value(jdi->vcc_gpio, 0);
>> +
>> +       return ret;
>> +}
>> +
>> +static int jdi_panel_enable(struct drm_panel *panel)
>> +{
>> +       struct jdi_panel *jdi = to_jdi_panel(panel);
>> +
>> +       if (jdi->enabled)
>> +               return 0;
>> +
>> +       DRM_DEBUG("enable\n");
>> +
>> +       if (jdi->backlight) {
>> +               jdi->backlight->props.power = FB_BLANK_UNBLANK;
>> +               backlight_update_status(jdi->backlight);
>> +       }
>> +
>> +       jdi->enabled = true;
>> +
>> +       return 0;
>> +}
>
>
> The prepare/enable split seems a bit strange. Don't we want to put
> mipi_dsi_dcs_set_display_on in the enable() op too?
enable will be called after the prepare().
enable() - usually the backlight update status BLANK/UNBLANK is done
>
>> +
>> +static const struct drm_display_mode default_mode = {
>> +               .clock = 155000,
>
>
> htotal * vtotal * vrefresh (1934 * 1340 * 60.32(as in comment)) comes
> at around 156.3 Mhz. Could you plug in the correct frequency?
>
>> +               .hdisplay = 1200,
>> +               .hsync_start = 1200 + 48,
>> +               .hsync_end = 1200 + 48 + 32,
>> +               .htotal = 1200 + 48 + 32 + 60,
>> +               .vdisplay = 1920,
>> +               .vsync_start = 1920 + 3,
>> +               .vsync_end = 1920 + 3 + 5,
>> +               .vtotal = 1920 + 3 + 5 + 6,
>> +               .vrefresh = 60,
>> +               .flags = 0,
>> +};
>> +
>> +static int jdi_panel_get_modes(struct drm_panel *panel)
>> +{
>> +       struct drm_display_mode *mode;
>> +
>> +       mode = drm_mode_duplicate(panel->drm, &default_mode);
>> +       if (!mode) {
>> +               dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
>> +                       default_mode.hdisplay, default_mode.vdisplay,
>> +                       default_mode.vrefresh);
>
>
> We should use the dsi->dev device pointer here like used elsewhere, not
> the drm device pointer.
>
>
>> +               return -ENOMEM;
>> +       }
>> +
>> +       drm_mode_set_name(mode);
>> +
>> +       drm_mode_probed_add(panel->connector, mode);
>> +
>> +       return 1;
>> +}
>> +
>> +static const struct drm_panel_funcs jdi_panel_funcs = {
>> +               .disable = jdi_panel_disable,
>> +               .unprepare = jdi_panel_unprepare,
>> +               .prepare = jdi_panel_prepare,
>> +               .enable = jdi_panel_enable,
>> +               .get_modes = jdi_panel_get_modes,
>> +};
>> +
>> +static const struct of_device_id jdi_of_match[] = {
>> +               { .compatible = "jdi,lt070me05000", },
>> +               { }
>> +};
>> +MODULE_DEVICE_TABLE(of, jdi_of_match);
>> +
>> +static int jdi_panel_add(struct jdi_panel *jdi)
>> +{
>> +       struct device *dev = &jdi->dsi->dev;
>> +       int ret;
>> +
>> +       jdi->mode = &default_mode;
>> +
>> +       /* lvs5 */
>> +       jdi->supply = devm_regulator_get(dev, "power");
>> +       if (IS_ERR(jdi->supply))
>> +               return PTR_ERR(jdi->supply);
>
>
> You could consider using regulator_get_bulk() api here, it would clean
> up the code a bit.
>
>
>> +
>> +       /* l17 */
>> +       jdi->backlit = devm_regulator_get(dev, "backlit");
>> +       if (IS_ERR(jdi->supply))
>> +               return PTR_ERR(jdi->supply);
>> +
>> +       jdi->lvs7 = devm_regulator_get(dev, "lvs7");
>> +       if (IS_ERR(jdi->lvs7))
>> +               return PTR_ERR(jdi->lvs7);
>> +
>> +       jdi->avdd = devm_regulator_get(dev, "avdd");
>> +       if (IS_ERR(jdi->avdd))
>> +               return PTR_ERR(jdi->avdd);
>> +
>> +       jdi->iovdd = devm_regulator_get(dev, "iovdd");
>> +       if (IS_ERR(jdi->iovdd))
>> +               return PTR_ERR(jdi->iovdd);
>> +
>> +       jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
>> +       if (IS_ERR(jdi->vcc_gpio)) {
>> +               dev_err(dev, "cannot get vcc-gpio %ld\n",
>> +                       PTR_ERR(jdi->vcc_gpio));
>> +               jdi->vcc_gpio = NULL;
>> +       } else {
>> +               gpiod_direction_output(jdi->vcc_gpio, 0);
>> +       }
>> +
>> +       jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +       if (IS_ERR(jdi->reset_gpio)) {
>> +               dev_err(dev, "cannot get reset-gpios %ld\n",
>> +                       PTR_ERR(jdi->reset_gpio));
>> +               jdi->reset_gpio = NULL;
>> +       } else {
>> +               gpiod_direction_output(jdi->reset_gpio, 0);
>> +       }
>> +
>> +       jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
>> +       if (IS_ERR(jdi->enable_gpio)) {
>> +               dev_err(dev, "cannot get enable-gpio %ld\n",
>> +                       PTR_ERR(jdi->enable_gpio));
>> +               jdi->enable_gpio = NULL;
>> +       } else {
>> +               gpiod_direction_output(jdi->enable_gpio, 0);
>> +       }
>> +
>> +       jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
>> +       if (IS_ERR(jdi->pwm_gpio)) {
>> +               dev_err(dev, "cannot get pwm-gpio %ld\n",
>> +                       PTR_ERR(jdi->pwm_gpio));
>> +               jdi->pwm_gpio = NULL;
>> +       } else {
>> +               gpiod_direction_output(jdi->pwm_gpio, 0);
>> +       }
>> +
>> +       /* we don't have backlight right now, proceed further */
>> +#ifdef BACKLIGHT
>> +       np = of_parse_phandle(dev->of_node, "backlight", 0);
>> +       if (np) {
>> +               jdi->backlight = of_find_backlight_by_node(np);
>> +               of_node_put(np);
>> +
>> +               if (!jdi->backlight)
>> +                       return -EPROBE_DEFER;
>> +       }
>> +#endif
>> +
>> +       drm_panel_init(&jdi->base);
>> +       jdi->base.funcs = &jdi_panel_funcs;
>> +       jdi->base.dev = &jdi->dsi->dev;
>> +
>> +       ret = drm_panel_add(&jdi->base);
>> +       if (ret < 0)
>> +               goto put_backlight;
>> +
>> +       return 0;
>> +
>> +put_backlight:
>> +       if (jdi->backlight)
>> +               put_device(&jdi->backlight->dev);
>> +
>> +       return ret;
>> +}
>> +
>> +static void jdi_panel_del(struct jdi_panel *jdi)
>> +{
>> +       if (jdi->base.dev)
>> +               drm_panel_remove(&jdi->base);
>> +
>> +       if (jdi->backlight)
>> +               put_device(&jdi->backlight->dev);
>> +}
>> +
>> +static int jdi_panel_probe(struct mipi_dsi_device *dsi)
>> +{
>> +       struct jdi_panel *jdi;
>> +       int ret;
>> +
>> +       dsi->lanes = 4;
>> +       dsi->format = MIPI_DSI_FMT_RGB888;
>> +       dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
>> +                       MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP
>> |
>> +                       MIPI_DSI_MODE_VIDEO_HSA |
>> +                       MIPI_DSI_CLOCK_NON_CONTINUOUS;
>> +
>> +       jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
>> +       if (!jdi)
>> +               return -ENOMEM;
>> +
>> +       mipi_dsi_set_drvdata(dsi, jdi);
>> +
>> +       jdi->dsi = dsi;
>> +
>> +       ret = jdi_panel_add(jdi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       return mipi_dsi_attach(dsi);
>> +}
>> +
>> +static int jdi_panel_remove(struct mipi_dsi_device *dsi)
>> +{
>> +       struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
>> +       int ret;
>> +
>> +       ret = jdi_panel_disable(&jdi->base);
>> +       if (ret < 0)
>> +               dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
>
>
> Ideally, the driver's remove op shouldn't try to disable the panel. It
> should be done by the dsi host's connector itself. We can drop this
> unless there is a specfic reason to do so.
>
panel_disable() - sets the backlight brightness off
fyi,
include/drm/drm_panel.h
Before stopping video transmission from the display controller it can be
necessary to turn off the panel to avoid visual glitches. This is done in
the .disable() function.
> Thanks for working on this.
>
> Archit
>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project



-- 
Regards,

Vinay Simha.B.N.

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

* Re: [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-20  9:46     ` Vinay Simha
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-20  9:46 UTC (permalink / raw)
  To: Archit Taneja
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Pawel Moll, Ian Campbell, open list:DRM PANEL DRIVERS, open list,
	Ralf Baechle, Rob Herring, Jonathan Cameron, Kumar Gala,
	Shawn Guo, Archit Taneja

On Thu, Apr 14, 2016 at 8:10 PM, Archit Taneja <architt-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org> wrote:
>
>
> On 4/13/2016 11:58 AM, Vinay Simha BN wrote:
>>
>> Add support for the JDI lt070me05000 WUXGA DSI panel used in
>> Nexus 7 2013 devices.
>>
>> Programming sequence for the panel is was originally found in the
>> android-msm-flo-3.4-lollipop-release branch from:
>>      https://android.googlesource.com/kernel/msm.git
>>
>> And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
>> file in:
>>      git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27
>>
>> Other fixes folded in were provided
>> by Archit Taneja <archit.taneja-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>
>> Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> [sumit.semwal: Ported to the drm/panel framework]
>> Signed-off-by: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> [jstultz: Cherry-picked to mainline, folded down other fixes
>>   from Vinay and Archit]
>> Signed-off-by: John Stultz <john.stultz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>   .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
>>   .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>>   drivers/gpu/drm/panel/Kconfig                      |  11 +
>>   drivers/gpu/drm/panel/Makefile                     |   1 +
>>   drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685
>> +++++++++++++++++++++
>>   5 files changed, 725 insertions(+)
>>   create mode 100644
>> Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>>   create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>>
>> diff --git
>> a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> new file mode 100644
>> index 0000000..35c5ac7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> @@ -0,0 +1,27 @@
>> +JDI model LT070ME05000 1920x1200 7" DSI Panel
>> +
>> +Basic data sheet is at:
>> +
>> http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> +
>> +This panel has video mode implemented currently in the driver.
>> +
>> +Required properties:
>> +- compatible: should be "jdi,lt070me05000"
>> +
>> +Optional properties:
>> +- power-supply: phandle of the regulator that provides the supply voltage
>> +- reset-gpio: phandle of gpio for reset line
>> +- backlight: phandle of the backlight device attached to the panel
>> +
>> +Example:
>> +
>> +       dsi@54300000 {
>> +               panel: panel@0 {
>> +                       compatible = "jdi,lt070me05000";
>> +                       reg = <0>;
>> +
>> +                       power-supply = <...>;
>> +                       reset-gpio = <...>;
>> +                       backlight = <...>;
>> +               };
>> +       };
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index a580f3e..ec42bb4 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -130,6 +130,7 @@ invensense  InvenSense Inc.
>>   isee  ISEE 2007 S.L.
>>   isil  Intersil
>>   issi  Integrated Silicon Solutions Inc.
>> +jdi    Japan Display Inc.
>>   jedec JEDEC Solid State Technology Association
>>   karo  Ka-Ro electronics GmbH
>>   keymile       Keymile GmbH
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 1500ab9..f41690e 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
>>           To compile this driver as a module, choose M here: the module
>>           will be called panel-sharp-lq101r1sx01.
>>
>> +config DRM_PANEL_JDI_LT070ME05000
>> +       tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI
>> video/
>> +         command mode panel as found in Google Nexus 7 (2013) devices.
>> +         The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
>> +         24 bit RGB per pixel.
>> +
>>   config DRM_PANEL_SHARP_LS043T1LE01
>>         tristate "Sharp LS043T1LE01 qHD video mode panel"
>>         depends on OF
>> diff --git a/drivers/gpu/drm/panel/Makefile
>> b/drivers/gpu/drm/panel/Makefile
>> index f277eed..e6c6fc8 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) +=
>> panel-samsung-ld9040.o
>>   obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>   obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
>> +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
>> diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> new file mode 100644
>> index 0000000..051aa1b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
>> @@ -0,0 +1,685 @@
>> +/*
>> + * Copyright (C) 2015 InforceComputing
>> + * Author: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + *
>> + * Copyright (C) 2015 Linaro Ltd
>> + * Author: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> it
>> + * under the terms of the GNU General Public License version 2 as
>> published by
>> + * the Free Software Foundation.
>> + *
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/backlight.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_mipi_dsi.h>
>> +#include <drm/drm_panel.h>
>> +
>> +#include <video/mipi_display.h>
>> +
>> +/*
>> + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
>> + * JDI model LT070ME05000, and its data sheet is at:
>> + *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
>> + */
>> +struct jdi_panel {
>> +       struct drm_panel base;
>> +       struct mipi_dsi_device *dsi;
>> +
>> +       /* TODO: Add backilght support */
>> +       struct backlight_device *backlight;
>> +       struct regulator *supply;
>> +       struct gpio_desc *reset_gpio;
>> +       struct gpio_desc *enable_gpio;
>> +       struct gpio_desc *vcc_gpio;
>> +
>> +       struct regulator *backlit;
>> +       struct regulator *lvs7;
>> +       struct regulator *avdd;
>> +       struct regulator *iovdd;
>> +       struct gpio_desc *pwm_gpio;
>> +
>> +       bool prepared;
>> +       bool enabled;
>> +
>> +       const struct drm_display_mode *mode;
>> +};
>> +
>> +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
>> +{
>> +       return container_of(panel, struct jdi_panel, base);
>> +}
>> +
>> +static char MCAP[2] = {0xB0, 0x00};
>> +static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22,
>> 0x00};
>> +static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
>> +
>> +static char interface_ID_setting[2] = {0xB4, 0x0C};
>> +static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
>> +
>> +static char tear_scan_line[3] = {0x44, 0x03, 0x00};
>> +
>> +/* for fps control, set fps to 60.32Hz */
>> +static char LTPS_timing_setting[2] = {0xC6, 0x78};
>> +static char sequencer_timing_control[2] = {0xD6, 0x01};
>> +
>> +/* set brightness */
>> +static char write_display_brightness[] = {0x51, 0xFF};
>> +/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming
>> */
>> +static char write_control_display[] = {0x53, 0x24};
>> +/*
>> + * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
>> + * disable SRE(sunlight readability enhancement)
>> + */
>> +static char write_cabc[] = {0x55, 0x00};
>> +/* for cabc mode 0x1(-15%) */
>> +static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00,
>> 0x32};
>> +/* for cabc mode 0x2(-40%) */
>> +static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16,
>> 0x87};
>> +/* for cabc mode 0x3(-54%) */
>> +static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45,
>> 0xB4};
>> +/* for pwm frequency and dimming control */
>> +static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67,
>> 0x78,
>> +               0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2,
>> 0xFA,
>> +               0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
>
>
> Apart from the u8 conversion as mentioned by Thierry, you should
> convert these to const too.
>
>
>> +
>> +static int jdi_panel_init(struct jdi_panel *jdi)
>> +{
>> +       struct mipi_dsi_device *dsi = jdi->dsi;
>> +       int ret;
>> +
>> +       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +       if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
>> +               ret = mipi_dsi_dcs_soft_reset(dsi);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               mdelay(10);
>> +
>> +               ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000,
>> 0x04AF);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_tear_on(dsi,
>> +
>> MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(5);
>> +
>> +               ret = mipi_dsi_generic_write(dsi, tear_scan_line,
>> +                                            sizeof(tear_scan_line));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi,
>> write_display_brightness,
>> +
>> sizeof(write_display_brightness)
>> +                                               );
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi,
>> write_control_display,
>> +
>> sizeof(write_control_display));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
>> +                                               sizeof(write_cabc));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(120);
>> +
>> +               ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(10);
>> +
>> +               ret = mipi_dsi_generic_write(dsi, interface_setting,
>> +                                            sizeof(interface_setting));
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(20);
>> +
>> +               backlight_control4[18] = 0x04;
>> +               backlight_control4[19] = 0x00;
>
>
> Any reason why these are overwritten? It would be better to have these
> values in the array itself.
>
>
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control4,
>> +                                            sizeof(backlight_control4));
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(20);
>> +
>> +               MCAP[1] = 0x03;
>> +               ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +               if (ret < 0)
>> +                       return ret;
>> +       } else {
>> +               /*
>> +                * TODO : need to verify panel settings when
>> +                * dsi cmd mode supported for apq8064 - simhavcs
>> +                */
>> +               ret = mipi_dsi_dcs_soft_reset(dsi);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               mdelay(5);
>> +
>> +               ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
>> +
>> sizeof(interface_setting_cmd));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
>> +
>> sizeof(interface_ID_setting));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, DSI_control,
>> +                                            sizeof(DSI_control));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
>> +                                            sizeof(LTPS_timing_setting));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi,
>> sequencer_timing_control,
>> +
>> sizeof(sequencer_timing_control));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi,
>> write_display_brightness,
>> +
>> sizeof(write_display_brightness)
>> +                                               );
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi,
>> write_control_display,
>> +
>> sizeof(write_control_display));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
>> +                                               sizeof(write_cabc));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control1,
>> +                                            sizeof(backlight_control1));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control2,
>> +                                            sizeof(backlight_control2));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control3,
>> +                                            sizeof(backlight_control3));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_generic_write(dsi, backlight_control4,
>> +                                            sizeof(backlight_control4));
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000,
>> 0x04AF);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
>> +               if (ret < 0)
>> +                       return ret;
>> +               mdelay(120);
>> +
>> +               ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> +               if (ret < 0)
>> +                       return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_on(struct jdi_panel *jdi)
>> +{
>> +       struct mipi_dsi_device *dsi = jdi->dsi;
>> +       int ret;
>> +
>> +       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> +       ret = mipi_dsi_dcs_set_display_on(dsi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_off(struct jdi_panel *jdi)
>> +{
>> +       struct mipi_dsi_device *dsi = jdi->dsi;
>> +       int ret;
>> +
>> +       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> +       ret = mipi_dsi_dcs_set_display_off(dsi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = mipi_dsi_dcs_set_tear_off(dsi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       msleep(100);
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_disable(struct drm_panel *panel)
>> +{
>> +       struct jdi_panel *jdi = to_jdi_panel(panel);
>> +
>> +       if (!jdi->enabled)
>> +               return 0;
>> +
>> +       DRM_DEBUG("disable\n");
>> +
>> +       if (jdi->backlight) {
>> +               jdi->backlight->props.power = FB_BLANK_POWERDOWN;
>> +               backlight_update_status(jdi->backlight);
>> +       }
>> +
>> +       jdi->enabled = false;
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_unprepare(struct drm_panel *panel)
>> +{
>> +       struct jdi_panel *jdi = to_jdi_panel(panel);
>> +       int ret;
>> +
>> +       if (!jdi->prepared)
>> +               return 0;
>> +
>> +       DRM_DEBUG("unprepare\n");
>> +
>> +       ret = jdi_panel_off(jdi);
>> +       if (ret) {
>> +               dev_err(panel->dev, "failed to set panel off: %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       regulator_disable(jdi->supply);
>> +
>> +       if (jdi->vcc_gpio)
>> +               gpiod_set_value(jdi->vcc_gpio, 0);
>> +
>> +       if (jdi->reset_gpio)
>> +               gpiod_set_value(jdi->reset_gpio, 0);
>> +
>> +       if (jdi->enable_gpio)
>> +               gpiod_set_value(jdi->enable_gpio, 0);
>> +
>> +       jdi->prepared = false;
>> +
>> +       return 0;
>> +}
>> +
>> +static int jdi_panel_prepare(struct drm_panel *panel)
>> +{
>> +       struct jdi_panel *jdi = to_jdi_panel(panel);
>> +       int ret;
>> +
>> +       if (jdi->prepared)
>> +               return 0;
>> +
>> +       DRM_DEBUG("prepare\n");
>> +
>> +       ret = regulator_enable(jdi->iovdd);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = regulator_enable(jdi->avdd);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = regulator_enable(jdi->backlit);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = regulator_enable(jdi->lvs7);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       ret = regulator_enable(jdi->supply);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       msleep(20);
>> +
>> +       if (jdi->vcc_gpio) {
>> +               gpiod_set_value(jdi->vcc_gpio, 1);
>> +               usleep_range(10, 20);
>> +       }
>> +
>> +       if (jdi->reset_gpio) {
>> +               gpiod_set_value(jdi->reset_gpio, 1);
>> +               usleep_range(10, 20);
>> +       }
>> +
>> +       if (jdi->pwm_gpio) {
>> +               gpiod_set_value(jdi->pwm_gpio, 1);
>> +               usleep_range(10, 20);
>> +       }
>> +
>> +       if (jdi->enable_gpio) {
>> +               gpiod_set_value(jdi->enable_gpio, 1);
>> +               usleep_range(10, 20);
>> +       }
>> +
>> +       ret = jdi_panel_init(jdi);
>> +       if (ret) {
>> +               dev_err(panel->dev, "failed to init panel: %d\n", ret);
>> +               goto poweroff;
>> +       }
>> +
>> +       ret = jdi_panel_on(jdi);
>> +       if (ret) {
>> +               dev_err(panel->dev, "failed to set panel on: %d\n", ret);
>> +               goto poweroff;
>> +       }
>> +
>> +       jdi->prepared = true;
>> +
>> +       return 0;
>> +
>> +poweroff:
>> +       regulator_disable(jdi->supply);
>> +       if (jdi->reset_gpio)
>> +               gpiod_set_value(jdi->reset_gpio, 0);
>> +       if (jdi->enable_gpio)
>> +               gpiod_set_value(jdi->enable_gpio, 0);
>> +       if (jdi->vcc_gpio)
>> +               gpiod_set_value(jdi->vcc_gpio, 0);
>> +
>> +       return ret;
>> +}
>> +
>> +static int jdi_panel_enable(struct drm_panel *panel)
>> +{
>> +       struct jdi_panel *jdi = to_jdi_panel(panel);
>> +
>> +       if (jdi->enabled)
>> +               return 0;
>> +
>> +       DRM_DEBUG("enable\n");
>> +
>> +       if (jdi->backlight) {
>> +               jdi->backlight->props.power = FB_BLANK_UNBLANK;
>> +               backlight_update_status(jdi->backlight);
>> +       }
>> +
>> +       jdi->enabled = true;
>> +
>> +       return 0;
>> +}
>
>
> The prepare/enable split seems a bit strange. Don't we want to put
> mipi_dsi_dcs_set_display_on in the enable() op too?
enable will be called after the prepare().
enable() - usually the backlight update status BLANK/UNBLANK is done
>
>> +
>> +static const struct drm_display_mode default_mode = {
>> +               .clock = 155000,
>
>
> htotal * vtotal * vrefresh (1934 * 1340 * 60.32(as in comment)) comes
> at around 156.3 Mhz. Could you plug in the correct frequency?
>
>> +               .hdisplay = 1200,
>> +               .hsync_start = 1200 + 48,
>> +               .hsync_end = 1200 + 48 + 32,
>> +               .htotal = 1200 + 48 + 32 + 60,
>> +               .vdisplay = 1920,
>> +               .vsync_start = 1920 + 3,
>> +               .vsync_end = 1920 + 3 + 5,
>> +               .vtotal = 1920 + 3 + 5 + 6,
>> +               .vrefresh = 60,
>> +               .flags = 0,
>> +};
>> +
>> +static int jdi_panel_get_modes(struct drm_panel *panel)
>> +{
>> +       struct drm_display_mode *mode;
>> +
>> +       mode = drm_mode_duplicate(panel->drm, &default_mode);
>> +       if (!mode) {
>> +               dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
>> +                       default_mode.hdisplay, default_mode.vdisplay,
>> +                       default_mode.vrefresh);
>
>
> We should use the dsi->dev device pointer here like used elsewhere, not
> the drm device pointer.
>
>
>> +               return -ENOMEM;
>> +       }
>> +
>> +       drm_mode_set_name(mode);
>> +
>> +       drm_mode_probed_add(panel->connector, mode);
>> +
>> +       return 1;
>> +}
>> +
>> +static const struct drm_panel_funcs jdi_panel_funcs = {
>> +               .disable = jdi_panel_disable,
>> +               .unprepare = jdi_panel_unprepare,
>> +               .prepare = jdi_panel_prepare,
>> +               .enable = jdi_panel_enable,
>> +               .get_modes = jdi_panel_get_modes,
>> +};
>> +
>> +static const struct of_device_id jdi_of_match[] = {
>> +               { .compatible = "jdi,lt070me05000", },
>> +               { }
>> +};
>> +MODULE_DEVICE_TABLE(of, jdi_of_match);
>> +
>> +static int jdi_panel_add(struct jdi_panel *jdi)
>> +{
>> +       struct device *dev = &jdi->dsi->dev;
>> +       int ret;
>> +
>> +       jdi->mode = &default_mode;
>> +
>> +       /* lvs5 */
>> +       jdi->supply = devm_regulator_get(dev, "power");
>> +       if (IS_ERR(jdi->supply))
>> +               return PTR_ERR(jdi->supply);
>
>
> You could consider using regulator_get_bulk() api here, it would clean
> up the code a bit.
>
>
>> +
>> +       /* l17 */
>> +       jdi->backlit = devm_regulator_get(dev, "backlit");
>> +       if (IS_ERR(jdi->supply))
>> +               return PTR_ERR(jdi->supply);
>> +
>> +       jdi->lvs7 = devm_regulator_get(dev, "lvs7");
>> +       if (IS_ERR(jdi->lvs7))
>> +               return PTR_ERR(jdi->lvs7);
>> +
>> +       jdi->avdd = devm_regulator_get(dev, "avdd");
>> +       if (IS_ERR(jdi->avdd))
>> +               return PTR_ERR(jdi->avdd);
>> +
>> +       jdi->iovdd = devm_regulator_get(dev, "iovdd");
>> +       if (IS_ERR(jdi->iovdd))
>> +               return PTR_ERR(jdi->iovdd);
>> +
>> +       jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
>> +       if (IS_ERR(jdi->vcc_gpio)) {
>> +               dev_err(dev, "cannot get vcc-gpio %ld\n",
>> +                       PTR_ERR(jdi->vcc_gpio));
>> +               jdi->vcc_gpio = NULL;
>> +       } else {
>> +               gpiod_direction_output(jdi->vcc_gpio, 0);
>> +       }
>> +
>> +       jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +       if (IS_ERR(jdi->reset_gpio)) {
>> +               dev_err(dev, "cannot get reset-gpios %ld\n",
>> +                       PTR_ERR(jdi->reset_gpio));
>> +               jdi->reset_gpio = NULL;
>> +       } else {
>> +               gpiod_direction_output(jdi->reset_gpio, 0);
>> +       }
>> +
>> +       jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
>> +       if (IS_ERR(jdi->enable_gpio)) {
>> +               dev_err(dev, "cannot get enable-gpio %ld\n",
>> +                       PTR_ERR(jdi->enable_gpio));
>> +               jdi->enable_gpio = NULL;
>> +       } else {
>> +               gpiod_direction_output(jdi->enable_gpio, 0);
>> +       }
>> +
>> +       jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
>> +       if (IS_ERR(jdi->pwm_gpio)) {
>> +               dev_err(dev, "cannot get pwm-gpio %ld\n",
>> +                       PTR_ERR(jdi->pwm_gpio));
>> +               jdi->pwm_gpio = NULL;
>> +       } else {
>> +               gpiod_direction_output(jdi->pwm_gpio, 0);
>> +       }
>> +
>> +       /* we don't have backlight right now, proceed further */
>> +#ifdef BACKLIGHT
>> +       np = of_parse_phandle(dev->of_node, "backlight", 0);
>> +       if (np) {
>> +               jdi->backlight = of_find_backlight_by_node(np);
>> +               of_node_put(np);
>> +
>> +               if (!jdi->backlight)
>> +                       return -EPROBE_DEFER;
>> +       }
>> +#endif
>> +
>> +       drm_panel_init(&jdi->base);
>> +       jdi->base.funcs = &jdi_panel_funcs;
>> +       jdi->base.dev = &jdi->dsi->dev;
>> +
>> +       ret = drm_panel_add(&jdi->base);
>> +       if (ret < 0)
>> +               goto put_backlight;
>> +
>> +       return 0;
>> +
>> +put_backlight:
>> +       if (jdi->backlight)
>> +               put_device(&jdi->backlight->dev);
>> +
>> +       return ret;
>> +}
>> +
>> +static void jdi_panel_del(struct jdi_panel *jdi)
>> +{
>> +       if (jdi->base.dev)
>> +               drm_panel_remove(&jdi->base);
>> +
>> +       if (jdi->backlight)
>> +               put_device(&jdi->backlight->dev);
>> +}
>> +
>> +static int jdi_panel_probe(struct mipi_dsi_device *dsi)
>> +{
>> +       struct jdi_panel *jdi;
>> +       int ret;
>> +
>> +       dsi->lanes = 4;
>> +       dsi->format = MIPI_DSI_FMT_RGB888;
>> +       dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
>> +                       MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP
>> |
>> +                       MIPI_DSI_MODE_VIDEO_HSA |
>> +                       MIPI_DSI_CLOCK_NON_CONTINUOUS;
>> +
>> +       jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
>> +       if (!jdi)
>> +               return -ENOMEM;
>> +
>> +       mipi_dsi_set_drvdata(dsi, jdi);
>> +
>> +       jdi->dsi = dsi;
>> +
>> +       ret = jdi_panel_add(jdi);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       return mipi_dsi_attach(dsi);
>> +}
>> +
>> +static int jdi_panel_remove(struct mipi_dsi_device *dsi)
>> +{
>> +       struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
>> +       int ret;
>> +
>> +       ret = jdi_panel_disable(&jdi->base);
>> +       if (ret < 0)
>> +               dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
>
>
> Ideally, the driver's remove op shouldn't try to disable the panel. It
> should be done by the dsi host's connector itself. We can drop this
> unless there is a specfic reason to do so.
>
panel_disable() - sets the backlight brightness off
fyi,
include/drm/drm_panel.h
Before stopping video transmission from the display controller it can be
necessary to turn off the panel to avoid visual glitches. This is done in
the .disable() function.
> Thanks for working on this.
>
> Archit
>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project



-- 
Regards,

Vinay Simha.B.N.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 3/4] drm/dsi: Implement set tear scanline
  2016-04-20  9:32         ` Vinay Simha BN
@ 2016-04-20  9:53           ` kbuild test robot
  -1 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-04-20  9:53 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: kbuild-all, Vinay Simha BN, David Airlie, open list:DRM DRIVERS,
	open list

[-- Attachment #1: Type: text/plain, Size: 2052 bytes --]

Hi,

[auto build test ERROR on tegra-drm/drm/tegra/for-next]
[also build test ERROR on v4.6-rc4 next-20160420]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Vinay-Simha-BN/dt-bindings-Add-jdi-panel-vendor/20160420-173456
base:   git://anongit.freedesktop.org/tegra/linux.git drm/tegra/for-next
config: x86_64-randconfig-x012-201616 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:6:0,
                    from include/linux/kernel.h:6,
                    from include/linux/list.h:8,
                    from include/linux/kobject.h:20,
                    from include/linux/device.h:17,
                    from include/drm/drm_mipi_dsi.h:15,
                    from drivers/gpu/drm/drm_mipi_dsi.c:28:
>> drivers/gpu/drm/drm_mipi_dsi.c:1006:15: error: 'mipi_dsi_dcs_set_tear_scanline' undeclared here (not in a function)
    EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
                  ^
   include/linux/export.h:57:16: note: in definition of macro '__EXPORT_SYMBOL'
     extern typeof(sym) sym;     \
                   ^
>> drivers/gpu/drm/drm_mipi_dsi.c:1006:1: note: in expansion of macro 'EXPORT_SYMBOL'
    EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
    ^

vim +/mipi_dsi_dcs_set_tear_scanline +1006 drivers/gpu/drm/drm_mipi_dsi.c

  1000	        err = mipi_dsi_generic_write(dsi, &payload, sizeof(payload));
  1001	        if (err < 0)
  1002	                return err;
  1003	
  1004	        return 0;
  1005	}
> 1006	EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
  1007	/**
  1008	 * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
  1009	 *    data used by the interface

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 24624 bytes --]

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

* Re: [PATCH v2 3/4] drm/dsi: Implement set tear scanline
@ 2016-04-20  9:53           ` kbuild test robot
  0 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-04-20  9:53 UTC (permalink / raw)
  Cc: Vinay Simha BN, kbuild-all, open list:DRM DRIVERS, open list

[-- Attachment #1: Type: text/plain, Size: 2052 bytes --]

Hi,

[auto build test ERROR on tegra-drm/drm/tegra/for-next]
[also build test ERROR on v4.6-rc4 next-20160420]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Vinay-Simha-BN/dt-bindings-Add-jdi-panel-vendor/20160420-173456
base:   git://anongit.freedesktop.org/tegra/linux.git drm/tegra/for-next
config: x86_64-randconfig-x012-201616 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:6:0,
                    from include/linux/kernel.h:6,
                    from include/linux/list.h:8,
                    from include/linux/kobject.h:20,
                    from include/linux/device.h:17,
                    from include/drm/drm_mipi_dsi.h:15,
                    from drivers/gpu/drm/drm_mipi_dsi.c:28:
>> drivers/gpu/drm/drm_mipi_dsi.c:1006:15: error: 'mipi_dsi_dcs_set_tear_scanline' undeclared here (not in a function)
    EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
                  ^
   include/linux/export.h:57:16: note: in definition of macro '__EXPORT_SYMBOL'
     extern typeof(sym) sym;     \
                   ^
>> drivers/gpu/drm/drm_mipi_dsi.c:1006:1: note: in expansion of macro 'EXPORT_SYMBOL'
    EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
    ^

vim +/mipi_dsi_dcs_set_tear_scanline +1006 drivers/gpu/drm/drm_mipi_dsi.c

  1000	        err = mipi_dsi_generic_write(dsi, &payload, sizeof(payload));
  1001	        if (err < 0)
  1002	                return err;
  1003	
  1004	        return 0;
  1005	}
> 1006	EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
  1007	/**
  1008	 * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
  1009	 *    data used by the interface

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 24624 bytes --]

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

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

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

* [PATCH v3] drm/dsi: Implement set tear scanline compile fix
  2016-04-20  9:53           ` kbuild test robot
@ 2016-04-20 10:24             ` Vinay Simha BN
  -1 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20 10:24 UTC (permalink / raw)
  Cc: Vinay Simha BN, David Airlie, open list:DRM DRIVERS, open list

Provide a small convenience wrapper that transmits
a set_tear_scanline command.
compilation fix

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 26aba75..bba6db2 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -1003,7 +1003,7 @@ int mipi_dsi_set_tear_scanline(struct mipi_dsi_device *dsi,
 
         return 0;
 }
-EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
+EXPORT_SYMBOL(mipi_dsi_set_tear_scanline);
 /**
  * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
  *    data used by the interface
-- 
2.1.2

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

* [PATCH v3] drm/dsi: Implement set tear scanline compile fix
@ 2016-04-20 10:24             ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-20 10:24 UTC (permalink / raw)
  Cc: Vinay Simha BN, David Airlie, open list:DRM DRIVERS, open list

Provide a small convenience wrapper that transmits
a set_tear_scanline command.
compilation fix

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 26aba75..bba6db2 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -1003,7 +1003,7 @@ int mipi_dsi_set_tear_scanline(struct mipi_dsi_device *dsi,
 
         return 0;
 }
-EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
+EXPORT_SYMBOL(mipi_dsi_set_tear_scanline);
 /**
  * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
  *    data used by the interface
-- 
2.1.2

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

* Re: [PATCH v2 1/4] dt-bindings: Add jdi panel vendor
@ 2016-04-21 15:33         ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-04-21 15:33 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Thierry Reding, Ralf Baechle, Jonathan Cameron, Shawn Guo,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

On Wed, Apr 20, 2016 at 03:02:30PM +0530, Vinay Simha BN wrote:
> Japan Display Inc.
> 
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v2 1/4] dt-bindings: Add jdi panel vendor
@ 2016-04-21 15:33         ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-04-21 15:33 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Thierry Reding, Ralf Baechle, Jonathan Cameron, Shawn Guo,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

On Wed, Apr 20, 2016 at 03:02:30PM +0530, Vinay Simha BN wrote:
> Japan Display Inc.
> 
> Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings
@ 2016-04-21 15:45           ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-04-21 15:45 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Thierry Reding, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

On Wed, Apr 20, 2016 at 03:02:31PM +0530, Vinay Simha BN wrote:
> Add documentation for lt070me05000 panel
> 
> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> ---
>  .../bindings/display/panel/jdi,lt070me05000.txt    | 43 ++++++++++++++++++++++
>  1 file changed, 43 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> new file mode 100644
> index 0000000..ffe0550
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> @@ -0,0 +1,43 @@
> +JDI model LT070ME05000 1200x1920 7" DSI Panel
> +
> +Required properties:
> +- compatible: should be "jdi,lt070me05000"
> +- power-supply: phandle of the regulator that provides the supply voltage
> +  IOVCC , power supply for LCM (1.8V)
> +- vddp-supply: phandle of the regulator that provides the supply voltage
> +  Power IC supply (3-5V)
> +- dcdc_en-supply: phandle of the regulator that provides the supply voltage
> +  Power IC supply enable, High active
> +- reset-gpio: phandle of gpio for reset line
> +  This should be 8mA, gpio can be configured using mux and pinctrl.
> +  XRES, Reset, Low active
> +- enable-gpio: phandle of gpio for enable line
> +  LED_EN, LED backlight enable, High active

These should all be -gpios instead.

> +- vcc-gpio: phandle of regulator/gpio that provides the supply voltage
> +  VDD, LED power supply (3-5V)

Is it a regulator or gpio?

> +
> +Optional properties:
> +- pwm-gpio: phandle of gpio/pwm

This should use the PWM binding. It may not be a GPIO on some hosts.

> +  pwm backlight control, this should be mapped to the backlight level
> +  display brightness (0x51)
> +
> +Example:
> +
> +	dsi0: qcom,mdss_dsi@4700000 {
> +		panel@0 {
> +			compatible = "jdi,lt070me05000";
> +			reg = <0>;
> +			pinctrl-names = "default";
> +			pinctrl-0 = <&dsi_panel_pinctrl>;
> +
> +			power-supply = <&pm8921_lvs5>;
> +			vddp-supply = <&pm8921_l17>;
> +			dcdc_en-supply = <&pm8921_lvs7>;
> +
> +			reset-gpio = <&tlmm_pinmux 54 0>;
> +			enable-gpio = <&pm8921_gpio 36 GPIO_ACTIVE_HIGH>;
> +			vcc-gpio = <&pm8921_gpio 23 GPIO_ACTIVE_HIGH>;
> +
> +			pwm-gpio = <&pm8921_gpio 26 GPIO_ACTIVE_HIGH>;
> +		};
> +	};
> -- 
> 2.1.2
> 

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

* Re: [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings
@ 2016-04-21 15:45           ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-04-21 15:45 UTC (permalink / raw)
  To: Vinay Simha BN
  Cc: Thierry Reding, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

On Wed, Apr 20, 2016 at 03:02:31PM +0530, Vinay Simha BN wrote:
> Add documentation for lt070me05000 panel
> 
> Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
>  .../bindings/display/panel/jdi,lt070me05000.txt    | 43 ++++++++++++++++++++++
>  1 file changed, 43 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> new file mode 100644
> index 0000000..ffe0550
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> @@ -0,0 +1,43 @@
> +JDI model LT070ME05000 1200x1920 7" DSI Panel
> +
> +Required properties:
> +- compatible: should be "jdi,lt070me05000"
> +- power-supply: phandle of the regulator that provides the supply voltage
> +  IOVCC , power supply for LCM (1.8V)
> +- vddp-supply: phandle of the regulator that provides the supply voltage
> +  Power IC supply (3-5V)
> +- dcdc_en-supply: phandle of the regulator that provides the supply voltage
> +  Power IC supply enable, High active
> +- reset-gpio: phandle of gpio for reset line
> +  This should be 8mA, gpio can be configured using mux and pinctrl.
> +  XRES, Reset, Low active
> +- enable-gpio: phandle of gpio for enable line
> +  LED_EN, LED backlight enable, High active

These should all be -gpios instead.

> +- vcc-gpio: phandle of regulator/gpio that provides the supply voltage
> +  VDD, LED power supply (3-5V)

Is it a regulator or gpio?

> +
> +Optional properties:
> +- pwm-gpio: phandle of gpio/pwm

This should use the PWM binding. It may not be a GPIO on some hosts.

> +  pwm backlight control, this should be mapped to the backlight level
> +  display brightness (0x51)
> +
> +Example:
> +
> +	dsi0: qcom,mdss_dsi@4700000 {
> +		panel@0 {
> +			compatible = "jdi,lt070me05000";
> +			reg = <0>;
> +			pinctrl-names = "default";
> +			pinctrl-0 = <&dsi_panel_pinctrl>;
> +
> +			power-supply = <&pm8921_lvs5>;
> +			vddp-supply = <&pm8921_l17>;
> +			dcdc_en-supply = <&pm8921_lvs7>;
> +
> +			reset-gpio = <&tlmm_pinmux 54 0>;
> +			enable-gpio = <&pm8921_gpio 36 GPIO_ACTIVE_HIGH>;
> +			vcc-gpio = <&pm8921_gpio 23 GPIO_ACTIVE_HIGH>;
> +
> +			pwm-gpio = <&pm8921_gpio 26 GPIO_ACTIVE_HIGH>;
> +		};
> +	};
> -- 
> 2.1.2
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings
  2016-04-21 15:45           ` Rob Herring
@ 2016-04-22  6:55             ` Vinay Simha
  -1 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-22  6:55 UTC (permalink / raw)
  To: Rob Herring
  Cc: Thierry Reding, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Sumit.semwal, John Stultz, Archit Taneja

On Thu, Apr 21, 2016 at 9:15 PM, Rob Herring <robh@kernel.org> wrote:
> On Wed, Apr 20, 2016 at 03:02:31PM +0530, Vinay Simha BN wrote:
>> Add documentation for lt070me05000 panel
>>
>> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
>> ---
>>  .../bindings/display/panel/jdi,lt070me05000.txt    | 43 ++++++++++++++++++++++
>>  1 file changed, 43 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>>
>> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> new file mode 100644
>> index 0000000..ffe0550
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> @@ -0,0 +1,43 @@
>> +JDI model LT070ME05000 1200x1920 7" DSI Panel
>> +
>> +Required properties:
>> +- compatible: should be "jdi,lt070me05000"
>> +- power-supply: phandle of the regulator that provides the supply voltage
>> +  IOVCC , power supply for LCM (1.8V)
>> +- vddp-supply: phandle of the regulator that provides the supply voltage
>> +  Power IC supply (3-5V)
>> +- dcdc_en-supply: phandle of the regulator that provides the supply voltage
>> +  Power IC supply enable, High active
>> +- reset-gpio: phandle of gpio for reset line
>> +  This should be 8mA, gpio can be configured using mux and pinctrl.
>> +  XRES, Reset, Low active
>> +- enable-gpio: phandle of gpio for enable line
>> +  LED_EN, LED backlight enable, High active
>
> These should all be -gpios instead.
will implement in v3
>
>> +- vcc-gpio: phandle of regulator/gpio that provides the supply voltage
>> +  VDD, LED power supply (3-5V)
>
> Is it a regulator or gpio?
>
VDD is LED power supply, but in nexus 7 2nd gen they are using gpio 23
instead of regulator.
if we use vcc-supply, not sure we can give the gpio device tree entry
to it in nexus 7 dts. Any inputs ?
>> +
>> +Optional properties:
>> +- pwm-gpio: phandle of gpio/pwm
>
> This should use the PWM binding. It may not be a GPIO on some hosts.

pwm-gpio will go to the backlight (pwm). right now it is not used
since pwm pm8921 upstream driver is not yet implemented. Shall i
remove this pwm-gpio now and the backlight property when pwm pm8921 is
implemented?
>
>> +  pwm backlight control, this should be mapped to the backlight level
>> +  display brightness (0x51)
>> +
>> +Example:
>> +
>> +     dsi0: qcom,mdss_dsi@4700000 {
>> +             panel@0 {
>> +                     compatible = "jdi,lt070me05000";
>> +                     reg = <0>;
>> +                     pinctrl-names = "default";
>> +                     pinctrl-0 = <&dsi_panel_pinctrl>;
>> +
>> +                     power-supply = <&pm8921_lvs5>;
>> +                     vddp-supply = <&pm8921_l17>;
>> +                     dcdc_en-supply = <&pm8921_lvs7>;
>> +
>> +                     reset-gpio = <&tlmm_pinmux 54 0>;
>> +                     enable-gpio = <&pm8921_gpio 36 GPIO_ACTIVE_HIGH>;
>> +                     vcc-gpio = <&pm8921_gpio 23 GPIO_ACTIVE_HIGH>;
>> +
>> +                     pwm-gpio = <&pm8921_gpio 26 GPIO_ACTIVE_HIGH>;
>> +             };
>> +     };
>> --
>> 2.1.2
>>



-- 
Regards,

Vinay Simha.B.N.

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

* Re: [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings
@ 2016-04-22  6:55             ` Vinay Simha
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha @ 2016-04-22  6:55 UTC (permalink / raw)
  To: Rob Herring
  Cc: Thierry Reding, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Sumit.semwal, John Stultz, Archit Taneja

On Thu, Apr 21, 2016 at 9:15 PM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Wed, Apr 20, 2016 at 03:02:31PM +0530, Vinay Simha BN wrote:
>> Add documentation for lt070me05000 panel
>>
>> Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> ---
>>  .../bindings/display/panel/jdi,lt070me05000.txt    | 43 ++++++++++++++++++++++
>>  1 file changed, 43 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>>
>> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> new file mode 100644
>> index 0000000..ffe0550
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
>> @@ -0,0 +1,43 @@
>> +JDI model LT070ME05000 1200x1920 7" DSI Panel
>> +
>> +Required properties:
>> +- compatible: should be "jdi,lt070me05000"
>> +- power-supply: phandle of the regulator that provides the supply voltage
>> +  IOVCC , power supply for LCM (1.8V)
>> +- vddp-supply: phandle of the regulator that provides the supply voltage
>> +  Power IC supply (3-5V)
>> +- dcdc_en-supply: phandle of the regulator that provides the supply voltage
>> +  Power IC supply enable, High active
>> +- reset-gpio: phandle of gpio for reset line
>> +  This should be 8mA, gpio can be configured using mux and pinctrl.
>> +  XRES, Reset, Low active
>> +- enable-gpio: phandle of gpio for enable line
>> +  LED_EN, LED backlight enable, High active
>
> These should all be -gpios instead.
will implement in v3
>
>> +- vcc-gpio: phandle of regulator/gpio that provides the supply voltage
>> +  VDD, LED power supply (3-5V)
>
> Is it a regulator or gpio?
>
VDD is LED power supply, but in nexus 7 2nd gen they are using gpio 23
instead of regulator.
if we use vcc-supply, not sure we can give the gpio device tree entry
to it in nexus 7 dts. Any inputs ?
>> +
>> +Optional properties:
>> +- pwm-gpio: phandle of gpio/pwm
>
> This should use the PWM binding. It may not be a GPIO on some hosts.

pwm-gpio will go to the backlight (pwm). right now it is not used
since pwm pm8921 upstream driver is not yet implemented. Shall i
remove this pwm-gpio now and the backlight property when pwm pm8921 is
implemented?
>
>> +  pwm backlight control, this should be mapped to the backlight level
>> +  display brightness (0x51)
>> +
>> +Example:
>> +
>> +     dsi0: qcom,mdss_dsi@4700000 {
>> +             panel@0 {
>> +                     compatible = "jdi,lt070me05000";
>> +                     reg = <0>;
>> +                     pinctrl-names = "default";
>> +                     pinctrl-0 = <&dsi_panel_pinctrl>;
>> +
>> +                     power-supply = <&pm8921_lvs5>;
>> +                     vddp-supply = <&pm8921_l17>;
>> +                     dcdc_en-supply = <&pm8921_lvs7>;
>> +
>> +                     reset-gpio = <&tlmm_pinmux 54 0>;
>> +                     enable-gpio = <&pm8921_gpio 36 GPIO_ACTIVE_HIGH>;
>> +                     vcc-gpio = <&pm8921_gpio 23 GPIO_ACTIVE_HIGH>;
>> +
>> +                     pwm-gpio = <&pm8921_gpio 26 GPIO_ACTIVE_HIGH>;
>> +             };
>> +     };
>> --
>> 2.1.2
>>



-- 
Regards,

Vinay Simha.B.N.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings
@ 2016-04-22 11:59               ` Thierry Reding
  0 siblings, 0 replies; 39+ messages in thread
From: Thierry Reding @ 2016-04-22 11:59 UTC (permalink / raw)
  To: Vinay Simha
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Sumit.semwal, John Stultz, Archit Taneja

[-- Attachment #1: Type: text/plain, Size: 2871 bytes --]

On Fri, Apr 22, 2016 at 12:25:52PM +0530, Vinay Simha wrote:
> On Thu, Apr 21, 2016 at 9:15 PM, Rob Herring <robh@kernel.org> wrote:
> > On Wed, Apr 20, 2016 at 03:02:31PM +0530, Vinay Simha BN wrote:
> >> Add documentation for lt070me05000 panel
> >>
> >> Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
> >> ---
> >>  .../bindings/display/panel/jdi,lt070me05000.txt    | 43 ++++++++++++++++++++++
> >>  1 file changed, 43 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> >>
> >> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> >> new file mode 100644
> >> index 0000000..ffe0550
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> >> @@ -0,0 +1,43 @@
> >> +JDI model LT070ME05000 1200x1920 7" DSI Panel
> >> +
> >> +Required properties:
> >> +- compatible: should be "jdi,lt070me05000"
> >> +- power-supply: phandle of the regulator that provides the supply voltage
> >> +  IOVCC , power supply for LCM (1.8V)
> >> +- vddp-supply: phandle of the regulator that provides the supply voltage
> >> +  Power IC supply (3-5V)
> >> +- dcdc_en-supply: phandle of the regulator that provides the supply voltage
> >> +  Power IC supply enable, High active
> >> +- reset-gpio: phandle of gpio for reset line
> >> +  This should be 8mA, gpio can be configured using mux and pinctrl.
> >> +  XRES, Reset, Low active
> >> +- enable-gpio: phandle of gpio for enable line
> >> +  LED_EN, LED backlight enable, High active
> >
> > These should all be -gpios instead.
> will implement in v3
> >
> >> +- vcc-gpio: phandle of regulator/gpio that provides the supply voltage
> >> +  VDD, LED power supply (3-5V)
> >
> > Is it a regulator or gpio?
> >
> VDD is LED power supply, but in nexus 7 2nd gen they are using gpio 23
> instead of regulator.
> if we use vcc-supply, not sure we can give the gpio device tree entry
> to it in nexus 7 dts. Any inputs ?

You can model it as a fixed regulator that's enabled by a GPIO. See:

	Documentation/devicetree/bindings/regulator/fixed-regulator.txt

> >> +
> >> +Optional properties:
> >> +- pwm-gpio: phandle of gpio/pwm
> >
> > This should use the PWM binding. It may not be a GPIO on some hosts.
> 
> pwm-gpio will go to the backlight (pwm). right now it is not used
> since pwm pm8921 upstream driver is not yet implemented. Shall i
> remove this pwm-gpio now and the backlight property when pwm pm8921 is
> implemented?

This suggests to me that you don't have a good idea yet what the final
binding would need to look like, so it might be better to get all the
dependencies in place first so that the binding can be validated to work
properly.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings
@ 2016-04-22 11:59               ` Thierry Reding
  0 siblings, 0 replies; 39+ messages in thread
From: Thierry Reding @ 2016-04-22 11:59 UTC (permalink / raw)
  To: Vinay Simha
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Sumit.semwal, John Stultz, Archit Taneja

[-- Attachment #1: Type: text/plain, Size: 2930 bytes --]

On Fri, Apr 22, 2016 at 12:25:52PM +0530, Vinay Simha wrote:
> On Thu, Apr 21, 2016 at 9:15 PM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> > On Wed, Apr 20, 2016 at 03:02:31PM +0530, Vinay Simha BN wrote:
> >> Add documentation for lt070me05000 panel
> >>
> >> Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> >> ---
> >>  .../bindings/display/panel/jdi,lt070me05000.txt    | 43 ++++++++++++++++++++++
> >>  1 file changed, 43 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> >>
> >> diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> >> new file mode 100644
> >> index 0000000..ffe0550
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
> >> @@ -0,0 +1,43 @@
> >> +JDI model LT070ME05000 1200x1920 7" DSI Panel
> >> +
> >> +Required properties:
> >> +- compatible: should be "jdi,lt070me05000"
> >> +- power-supply: phandle of the regulator that provides the supply voltage
> >> +  IOVCC , power supply for LCM (1.8V)
> >> +- vddp-supply: phandle of the regulator that provides the supply voltage
> >> +  Power IC supply (3-5V)
> >> +- dcdc_en-supply: phandle of the regulator that provides the supply voltage
> >> +  Power IC supply enable, High active
> >> +- reset-gpio: phandle of gpio for reset line
> >> +  This should be 8mA, gpio can be configured using mux and pinctrl.
> >> +  XRES, Reset, Low active
> >> +- enable-gpio: phandle of gpio for enable line
> >> +  LED_EN, LED backlight enable, High active
> >
> > These should all be -gpios instead.
> will implement in v3
> >
> >> +- vcc-gpio: phandle of regulator/gpio that provides the supply voltage
> >> +  VDD, LED power supply (3-5V)
> >
> > Is it a regulator or gpio?
> >
> VDD is LED power supply, but in nexus 7 2nd gen they are using gpio 23
> instead of regulator.
> if we use vcc-supply, not sure we can give the gpio device tree entry
> to it in nexus 7 dts. Any inputs ?

You can model it as a fixed regulator that's enabled by a GPIO. See:

	Documentation/devicetree/bindings/regulator/fixed-regulator.txt

> >> +
> >> +Optional properties:
> >> +- pwm-gpio: phandle of gpio/pwm
> >
> > This should use the PWM binding. It may not be a GPIO on some hosts.
> 
> pwm-gpio will go to the backlight (pwm). right now it is not used
> since pwm pm8921 upstream driver is not yet implemented. Shall i
> remove this pwm-gpio now and the backlight property when pwm pm8921 is
> implemented?

This suggests to me that you don't have a good idea yet what the final
binding would need to look like, so it might be better to get all the
dependencies in place first so that the binding can be validated to work
properly.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-13  5:47 ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-13  5:47 UTC (permalink / raw)
  Cc: Vinay Simha BN, Sumit Semwal, John Stultz, Thierry Reding,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	David Airlie, Jonathan Cameron, Ralf Baechle, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Add support for the JDI lt070me05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
    https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
    git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27

Other fixes folded in were provided
by Archit Taneja <archit.taneja@gmail.com>

Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
[sumit.semwal: Ported to the drm/panel framework]
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
[jstultz: Cherry-picked to mainline, folded down other fixes
 from Vinay and Archit]
Signed-off-by: John Stultz <john.stultz@linaro.org>
---
 .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/gpu/drm/panel/Kconfig                      |  11 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
 5 files changed, 725 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
 create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644
index 0000000..35c5ac7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
@@ -0,0 +1,27 @@
+JDI model LT070ME05000 1920x1200 7" DSI Panel
+
+Basic data sheet is at:
+	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+
+This panel has video mode implemented currently in the driver.
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+
+Optional properties:
+- power-supply: phandle of the regulator that provides the supply voltage
+- reset-gpio: phandle of gpio for reset line
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+	dsi@54300000 {
+		panel: panel@0 {
+			compatible = "jdi,lt070me05000";
+			reg = <0>;
+
+			power-supply = <...>;
+			reset-gpio = <...>;
+			backlight = <...>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a580f3e..ec42bb4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -130,6 +130,7 @@ invensense	InvenSense Inc.
 isee	ISEE 2007 S.L.
 isil	Intersil
 issi	Integrated Silicon Solutions Inc.
+jdi	Japan Display Inc.
 jedec	JEDEC Solid State Technology Association
 karo	Ka-Ro electronics GmbH
 keymile	Keymile GmbH
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 1500ab9..f41690e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
 	  To compile this driver as a module, choose M here: the module
 	  will be called panel-sharp-lq101r1sx01.
 
+config DRM_PANEL_JDI_LT070ME05000
+	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
+	  command mode panel as found in Google Nexus 7 (2013) devices.
+	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+	  24 bit RGB per pixel.
+
 config DRM_PANEL_SHARP_LS043T1LE01
 	tristate "Sharp LS043T1LE01 qHD video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f277eed..e6c6fc8 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644
index 0000000..051aa1b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2015 InforceComputing
+ * Author: Vinay Simha BN <simhavcs@gmail.com>
+ *
+ * Copyright (C) 2015 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+/*
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ */
+struct jdi_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	/* TODO: Add backilght support */
+	struct backlight_device *backlight;
+	struct regulator *supply;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *vcc_gpio;
+
+	struct regulator *backlit;
+	struct regulator *lvs7;
+	struct regulator *avdd;
+	struct regulator *iovdd;
+	struct gpio_desc *pwm_gpio;
+
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct jdi_panel, base);
+}
+
+static char MCAP[2] = {0xB0, 0x00};
+static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
+static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
+
+static char interface_ID_setting[2] = {0xB4, 0x0C};
+static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
+
+static char tear_scan_line[3] = {0x44, 0x03, 0x00};
+
+/* for fps control, set fps to 60.32Hz */
+static char LTPS_timing_setting[2] = {0xC6, 0x78};
+static char sequencer_timing_control[2] = {0xD6, 0x01};
+
+/* set brightness */
+static char write_display_brightness[] = {0x51, 0xFF};
+/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
+static char write_control_display[] = {0x53, 0x24};
+/*
+ * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
+ * disable SRE(sunlight readability enhancement)
+ */
+static char write_cabc[] = {0x55, 0x00};
+/* for cabc mode 0x1(-15%) */
+static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
+/* for cabc mode 0x2(-40%) */
+static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
+/* for cabc mode 0x3(-54%) */
+static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
+/* for pwm frequency and dimming control */
+static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
+		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
+		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(10);
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_tear_on(dsi,
+					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+		if (ret < 0)
+			return ret;
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
+					     sizeof(tear_scan_line));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+		mdelay(10);
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting,
+					     sizeof(interface_setting));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		backlight_control4[18] = 0x04;
+		backlight_control4[19] = 0x00;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		MCAP[1] = 0x03;
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+	} else {
+		/*
+		 * TODO : need to verify panel settings when
+		 * dsi cmd mode supported for apq8064 - simhavcs
+		 */
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
+					     sizeof(interface_setting_cmd));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
+					     sizeof(interface_ID_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, DSI_control,
+					     sizeof(DSI_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
+					     sizeof(LTPS_timing_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
+					     sizeof(sequencer_timing_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control1,
+					     sizeof(backlight_control1));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control2,
+					     sizeof(backlight_control2));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control3,
+					     sizeof(backlight_control3));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_off(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(100);
+
+	return 0;
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (!jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("disable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = false;
+
+	return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (!jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("unprepare\n");
+
+	ret = jdi_panel_off(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+		return ret;
+	}
+
+	regulator_disable(jdi->supply);
+
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+
+	jdi->prepared = false;
+
+	return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("prepare\n");
+
+	ret = regulator_enable(jdi->iovdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->avdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->backlit);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->lvs7);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->supply);
+	if (ret < 0)
+		return ret;
+
+	msleep(20);
+
+	if (jdi->vcc_gpio) {
+		gpiod_set_value(jdi->vcc_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->reset_gpio) {
+		gpiod_set_value(jdi->reset_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->pwm_gpio) {
+		gpiod_set_value(jdi->pwm_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->enable_gpio) {
+		gpiod_set_value(jdi->enable_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	ret = jdi_panel_init(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to init panel: %d\n", ret);
+		goto poweroff;
+	}
+
+	ret = jdi_panel_on(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+		goto poweroff;
+	}
+
+	jdi->prepared = true;
+
+	return 0;
+
+poweroff:
+	regulator_disable(jdi->supply);
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("enable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_UNBLANK;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+		.clock = 155000,
+		.hdisplay = 1200,
+		.hsync_start = 1200 + 48,
+		.hsync_end = 1200 + 48 + 32,
+		.htotal = 1200 + 48 + 32 + 60,
+		.vdisplay = 1920,
+		.vsync_start = 1920 + 3,
+		.vsync_end = 1920 + 3 + 5,
+		.vtotal = 1920 + 3 + 5 + 6,
+		.vrefresh = 60,
+		.flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+		.disable = jdi_panel_disable,
+		.unprepare = jdi_panel_unprepare,
+		.prepare = jdi_panel_prepare,
+		.enable = jdi_panel_enable,
+		.get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+		{ .compatible = "jdi,lt070me05000", },
+		{ }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	jdi->mode = &default_mode;
+
+	/* lvs5 */
+	jdi->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	/* l17 */
+	jdi->backlit = devm_regulator_get(dev, "backlit");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
+	if (IS_ERR(jdi->lvs7))
+		return PTR_ERR(jdi->lvs7);
+
+	jdi->avdd = devm_regulator_get(dev, "avdd");
+	if (IS_ERR(jdi->avdd))
+		return PTR_ERR(jdi->avdd);
+
+	jdi->iovdd = devm_regulator_get(dev, "iovdd");
+	if (IS_ERR(jdi->iovdd))
+		return PTR_ERR(jdi->iovdd);
+
+	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->vcc_gpio)) {
+		dev_err(dev, "cannot get vcc-gpio %ld\n",
+			PTR_ERR(jdi->vcc_gpio));
+		jdi->vcc_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->vcc_gpio, 0);
+	}
+
+	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(jdi->reset_gpio));
+		jdi->reset_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->reset_gpio, 0);
+	}
+
+	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpio %ld\n",
+			PTR_ERR(jdi->enable_gpio));
+		jdi->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->enable_gpio, 0);
+	}
+
+	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->pwm_gpio)) {
+		dev_err(dev, "cannot get pwm-gpio %ld\n",
+			PTR_ERR(jdi->pwm_gpio));
+		jdi->pwm_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->pwm_gpio, 0);
+	}
+
+	/* we don't have backlight right now, proceed further */
+#ifdef BACKLIGHT
+	np = of_parse_phandle(dev->of_node, "backlight", 0);
+	if (np) {
+		jdi->backlight = of_find_backlight_by_node(np);
+		of_node_put(np);
+
+		if (!jdi->backlight)
+			return -EPROBE_DEFER;
+	}
+#endif
+
+	drm_panel_init(&jdi->base);
+	jdi->base.funcs = &jdi_panel_funcs;
+	jdi->base.dev = &jdi->dsi->dev;
+
+	ret = drm_panel_add(&jdi->base);
+	if (ret < 0)
+		goto put_backlight;
+
+	return 0;
+
+put_backlight:
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+
+	return ret;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+	if (jdi->base.dev)
+		drm_panel_remove(&jdi->base);
+
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi;
+	int ret;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
+			MIPI_DSI_MODE_VIDEO_HSA |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+	if (!jdi)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, jdi);
+
+	jdi->dsi = dsi;
+
+	ret = jdi_panel_add(jdi);
+	if (ret < 0)
+		return ret;
+
+	return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = jdi_panel_disable(&jdi->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+			ret);
+
+	drm_panel_detach(&jdi->base);
+	jdi_panel_del(jdi);
+
+	return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+	jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+	.driver = {
+		.name = "panel-jdi-lt070me05000",
+		.of_match_table = jdi_of_match,
+	},
+	.probe = jdi_panel_probe,
+	.remove = jdi_panel_remove,
+	.shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
+MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

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

* [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
@ 2016-04-13  5:47 ` Vinay Simha BN
  0 siblings, 0 replies; 39+ messages in thread
From: Vinay Simha BN @ 2016-04-13  5:47 UTC (permalink / raw)
  Cc: Vinay Simha BN, Sumit Semwal, John Stultz, Thierry Reding,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	David Airlie, Jonathan Cameron, Ralf Baechle, Shawn Guo,
	open list:DRM PANEL DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list

Add support for the JDI lt070me05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
    https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
    git://codeaurora.org/kernel/msm-3.10.git  LNX.LA.3.6_rb1.27

Other fixes folded in were provided
by Archit Taneja <archit.taneja-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Signed-off-by: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
[sumit.semwal: Ported to the drm/panel framework]
Signed-off-by: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
[jstultz: Cherry-picked to mainline, folded down other fixes
 from Vinay and Archit]
Signed-off-by: John Stultz <john.stultz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 .../bindings/display/panel/jdi,lt070me05000.txt    |  27 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/gpu/drm/panel/Kconfig                      |  11 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c     | 685 +++++++++++++++++++++
 5 files changed, 725 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
 create mode 100644 drivers/gpu/drm/panel/panel-jdi-lt070me05000.c

diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644
index 0000000..35c5ac7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
@@ -0,0 +1,27 @@
+JDI model LT070ME05000 1920x1200 7" DSI Panel
+
+Basic data sheet is at:
+	http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+
+This panel has video mode implemented currently in the driver.
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+
+Optional properties:
+- power-supply: phandle of the regulator that provides the supply voltage
+- reset-gpio: phandle of gpio for reset line
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+	dsi@54300000 {
+		panel: panel@0 {
+			compatible = "jdi,lt070me05000";
+			reg = <0>;
+
+			power-supply = <...>;
+			reset-gpio = <...>;
+			backlight = <...>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a580f3e..ec42bb4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -130,6 +130,7 @@ invensense	InvenSense Inc.
 isee	ISEE 2007 S.L.
 isil	Intersil
 issi	Integrated Silicon Solutions Inc.
+jdi	Japan Display Inc.
 jedec	JEDEC Solid State Technology Association
 karo	Ka-Ro electronics GmbH
 keymile	Keymile GmbH
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 1500ab9..f41690e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -61,6 +61,17 @@ config DRM_PANEL_SHARP_LQ101R1SX01
 	  To compile this driver as a module, choose M here: the module
 	  will be called panel-sharp-lq101r1sx01.
 
+config DRM_PANEL_JDI_LT070ME05000
+	tristate "JDI LT070ME05000 WUXGA DSI 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 JDI WUXGA DSI video/
+	  command mode panel as found in Google Nexus 7 (2013) devices.
+	  The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+	  24 bit RGB per pixel.
+
 config DRM_PANEL_SHARP_LS043T1LE01
 	tristate "Sharp LS043T1LE01 qHD video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f277eed..e6c6fc8 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644
index 0000000..051aa1b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2015 InforceComputing
+ * Author: Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * Copyright (C) 2015 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+/*
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ *  http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ */
+struct jdi_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	/* TODO: Add backilght support */
+	struct backlight_device *backlight;
+	struct regulator *supply;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *vcc_gpio;
+
+	struct regulator *backlit;
+	struct regulator *lvs7;
+	struct regulator *avdd;
+	struct regulator *iovdd;
+	struct gpio_desc *pwm_gpio;
+
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct jdi_panel, base);
+}
+
+static char MCAP[2] = {0xB0, 0x00};
+static char interface_setting_cmd[6] = {0xB3, 0x04, 0x08, 0x00, 0x22, 0x00};
+static char interface_setting[6] = {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00};
+
+static char interface_ID_setting[2] = {0xB4, 0x0C};
+static char DSI_control[3] = {0xB6, 0x3A, 0xD3};
+
+static char tear_scan_line[3] = {0x44, 0x03, 0x00};
+
+/* for fps control, set fps to 60.32Hz */
+static char LTPS_timing_setting[2] = {0xC6, 0x78};
+static char sequencer_timing_control[2] = {0xD6, 0x01};
+
+/* set brightness */
+static char write_display_brightness[] = {0x51, 0xFF};
+/* enable LEDPWM pin output, turn on LEDPWM output, turn off pwm dimming */
+static char write_control_display[] = {0x53, 0x24};
+/*
+ * choose cabc mode, 0x00(-0%), 0x01(-15%), 0x02(-40%), 0x03(-54%),
+ * disable SRE(sunlight readability enhancement)
+ */
+static char write_cabc[] = {0x55, 0x00};
+/* for cabc mode 0x1(-15%) */
+static char backlight_control1[] = {0xB8, 0x07, 0x87, 0x26, 0x18, 0x00, 0x32};
+/* for cabc mode 0x2(-40%) */
+static char backlight_control2[] = {0xB9, 0x07, 0x75, 0x61, 0x20, 0x16, 0x87};
+/* for cabc mode 0x3(-54%) */
+static char backlight_control3[] = {0xBA, 0x07, 0x70, 0x81, 0x20, 0x45, 0xB4};
+/* for pwm frequency and dimming control */
+static char backlight_control4[] = {0xCE, 0x7D, 0x40, 0x48, 0x56, 0x67, 0x78,
+		0x88, 0x98, 0xA7, 0xB5, 0xC3, 0xD1, 0xDE, 0xE9, 0xF2, 0xFA,
+		0xFF, 0x37, 0xF5, 0x0F, 0x0F, 0x42, 0x00};
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(10);
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x70);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_tear_on(dsi,
+					       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+		if (ret < 0)
+			return ret;
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, tear_scan_line,
+					     sizeof(tear_scan_line));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+		mdelay(10);
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting,
+					     sizeof(interface_setting));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		backlight_control4[18] = 0x04;
+		backlight_control4[19] = 0x00;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+		mdelay(20);
+
+		MCAP[1] = 0x03;
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+	} else {
+		/*
+		 * TODO : need to verify panel settings when
+		 * dsi cmd mode supported for apq8064 - simhavcs
+		 */
+		ret = mipi_dsi_dcs_soft_reset(dsi);
+		if (ret < 0)
+			return ret;
+
+		mdelay(5);
+
+		ret = mipi_dsi_generic_write(dsi, MCAP, sizeof(MCAP));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_setting_cmd,
+					     sizeof(interface_setting_cmd));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, interface_ID_setting,
+					     sizeof(interface_ID_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, DSI_control,
+					     sizeof(DSI_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, LTPS_timing_setting,
+					     sizeof(LTPS_timing_setting));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, sequencer_timing_control,
+					     sizeof(sequencer_timing_control));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_display_brightness,
+						sizeof(write_display_brightness)
+						);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_control_display,
+						sizeof(write_control_display));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_write_buffer(dsi, write_cabc,
+						sizeof(write_cabc));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control1,
+					     sizeof(backlight_control1));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control2,
+					     sizeof(backlight_control2));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control3,
+					     sizeof(backlight_control3));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_generic_write(dsi, backlight_control4,
+					     sizeof(backlight_control4));
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x04AF);
+		if (ret < 0)
+			return ret;
+
+		ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077F);
+		if (ret < 0)
+			return ret;
+		mdelay(120);
+
+		ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int jdi_panel_off(struct jdi_panel *jdi)
+{
+	struct mipi_dsi_device *dsi = jdi->dsi;
+	int ret;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_dcs_set_tear_off(dsi);
+	if (ret < 0)
+		return ret;
+
+	msleep(100);
+
+	return 0;
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (!jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("disable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_POWERDOWN;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = false;
+
+	return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (!jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("unprepare\n");
+
+	ret = jdi_panel_off(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+		return ret;
+	}
+
+	regulator_disable(jdi->supply);
+
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+
+	jdi->prepared = false;
+
+	return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+	int ret;
+
+	if (jdi->prepared)
+		return 0;
+
+	DRM_DEBUG("prepare\n");
+
+	ret = regulator_enable(jdi->iovdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->avdd);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->backlit);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->lvs7);
+	if (ret < 0)
+		return ret;
+
+	ret = regulator_enable(jdi->supply);
+	if (ret < 0)
+		return ret;
+
+	msleep(20);
+
+	if (jdi->vcc_gpio) {
+		gpiod_set_value(jdi->vcc_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->reset_gpio) {
+		gpiod_set_value(jdi->reset_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->pwm_gpio) {
+		gpiod_set_value(jdi->pwm_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	if (jdi->enable_gpio) {
+		gpiod_set_value(jdi->enable_gpio, 1);
+		usleep_range(10, 20);
+	}
+
+	ret = jdi_panel_init(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to init panel: %d\n", ret);
+		goto poweroff;
+	}
+
+	ret = jdi_panel_on(jdi);
+	if (ret) {
+		dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+		goto poweroff;
+	}
+
+	jdi->prepared = true;
+
+	return 0;
+
+poweroff:
+	regulator_disable(jdi->supply);
+	if (jdi->reset_gpio)
+		gpiod_set_value(jdi->reset_gpio, 0);
+	if (jdi->enable_gpio)
+		gpiod_set_value(jdi->enable_gpio, 0);
+	if (jdi->vcc_gpio)
+		gpiod_set_value(jdi->vcc_gpio, 0);
+
+	return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+	struct jdi_panel *jdi = to_jdi_panel(panel);
+
+	if (jdi->enabled)
+		return 0;
+
+	DRM_DEBUG("enable\n");
+
+	if (jdi->backlight) {
+		jdi->backlight->props.power = FB_BLANK_UNBLANK;
+		backlight_update_status(jdi->backlight);
+	}
+
+	jdi->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+		.clock = 155000,
+		.hdisplay = 1200,
+		.hsync_start = 1200 + 48,
+		.hsync_end = 1200 + 48 + 32,
+		.htotal = 1200 + 48 + 32 + 60,
+		.vdisplay = 1920,
+		.vsync_start = 1920 + 3,
+		.vsync_end = 1920 + 3 + 5,
+		.vtotal = 1920 + 3 + 5 + 6,
+		.vrefresh = 60,
+		.flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+		.disable = jdi_panel_disable,
+		.unprepare = jdi_panel_unprepare,
+		.prepare = jdi_panel_prepare,
+		.enable = jdi_panel_enable,
+		.get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+		{ .compatible = "jdi,lt070me05000", },
+		{ }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+	struct device *dev = &jdi->dsi->dev;
+	int ret;
+
+	jdi->mode = &default_mode;
+
+	/* lvs5 */
+	jdi->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	/* l17 */
+	jdi->backlit = devm_regulator_get(dev, "backlit");
+	if (IS_ERR(jdi->supply))
+		return PTR_ERR(jdi->supply);
+
+	jdi->lvs7 = devm_regulator_get(dev, "lvs7");
+	if (IS_ERR(jdi->lvs7))
+		return PTR_ERR(jdi->lvs7);
+
+	jdi->avdd = devm_regulator_get(dev, "avdd");
+	if (IS_ERR(jdi->avdd))
+		return PTR_ERR(jdi->avdd);
+
+	jdi->iovdd = devm_regulator_get(dev, "iovdd");
+	if (IS_ERR(jdi->iovdd))
+		return PTR_ERR(jdi->iovdd);
+
+	jdi->vcc_gpio = devm_gpiod_get(dev, "vcc", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->vcc_gpio)) {
+		dev_err(dev, "cannot get vcc-gpio %ld\n",
+			PTR_ERR(jdi->vcc_gpio));
+		jdi->vcc_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->vcc_gpio, 0);
+	}
+
+	jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(jdi->reset_gpio));
+		jdi->reset_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->reset_gpio, 0);
+	}
+
+	jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpio %ld\n",
+			PTR_ERR(jdi->enable_gpio));
+		jdi->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->enable_gpio, 0);
+	}
+
+	jdi->pwm_gpio = devm_gpiod_get(dev, "pwm", GPIOD_OUT_LOW);
+	if (IS_ERR(jdi->pwm_gpio)) {
+		dev_err(dev, "cannot get pwm-gpio %ld\n",
+			PTR_ERR(jdi->pwm_gpio));
+		jdi->pwm_gpio = NULL;
+	} else {
+		gpiod_direction_output(jdi->pwm_gpio, 0);
+	}
+
+	/* we don't have backlight right now, proceed further */
+#ifdef BACKLIGHT
+	np = of_parse_phandle(dev->of_node, "backlight", 0);
+	if (np) {
+		jdi->backlight = of_find_backlight_by_node(np);
+		of_node_put(np);
+
+		if (!jdi->backlight)
+			return -EPROBE_DEFER;
+	}
+#endif
+
+	drm_panel_init(&jdi->base);
+	jdi->base.funcs = &jdi_panel_funcs;
+	jdi->base.dev = &jdi->dsi->dev;
+
+	ret = drm_panel_add(&jdi->base);
+	if (ret < 0)
+		goto put_backlight;
+
+	return 0;
+
+put_backlight:
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+
+	return ret;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+	if (jdi->base.dev)
+		drm_panel_remove(&jdi->base);
+
+	if (jdi->backlight)
+		put_device(&jdi->backlight->dev);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi;
+	int ret;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP |
+			MIPI_DSI_MODE_VIDEO_HSA |
+			MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+	if (!jdi)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, jdi);
+
+	jdi->dsi = dsi;
+
+	ret = jdi_panel_add(jdi);
+	if (ret < 0)
+		return ret;
+
+	return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = jdi_panel_disable(&jdi->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+			ret);
+
+	drm_panel_detach(&jdi->base);
+	jdi_panel_del(jdi);
+
+	return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+	jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+	.driver = {
+		.name = "panel-jdi-lt070me05000",
+		.of_match_table = jdi_of_match,
+	},
+	.probe = jdi_panel_probe,
+	.remove = jdi_panel_remove,
+	.shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("JDI WUXGA LT070ME05000 DSI video/command mode panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

end of thread, other threads:[~2016-04-22 11:59 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-13  6:28 [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel Vinay Simha BN
2016-04-13  6:28 ` Vinay Simha BN
2016-04-13 13:49 ` Thierry Reding
2016-04-13 13:49   ` Thierry Reding
2016-04-13 15:22   ` Vinay Simha
2016-04-14 12:38   ` Vinay Simha
2016-04-14 12:38     ` Vinay Simha
2016-04-20  9:42   ` Vinay Simha
2016-04-20  9:42     ` Vinay Simha
2016-04-14 10:47 ` [RESEND][PATCH] " Vinay Simha BN
2016-04-14 10:47   ` Vinay Simha BN
2016-04-14 17:15   ` Rob Herring
2016-04-14 17:15     ` Rob Herring
2016-04-20  9:32     ` [PATCH v2 1/4] dt-bindings: Add jdi panel vendor Vinay Simha BN
2016-04-20  9:32       ` Vinay Simha BN
2016-04-20  9:32       ` [PATCH v2 2/4] dt-bindings: Add jdi lt070me05000 panel bindings Vinay Simha BN
2016-04-20  9:32         ` Vinay Simha BN
2016-04-21 15:45         ` Rob Herring
2016-04-21 15:45           ` Rob Herring
2016-04-22  6:55           ` Vinay Simha
2016-04-22  6:55             ` Vinay Simha
2016-04-22 11:59             ` Thierry Reding
2016-04-22 11:59               ` Thierry Reding
2016-04-20  9:32       ` [PATCH v2 3/4] drm/dsi: Implement set tear scanline Vinay Simha BN
2016-04-20  9:32         ` Vinay Simha BN
2016-04-20  9:53         ` kbuild test robot
2016-04-20  9:53           ` kbuild test robot
2016-04-20 10:24           ` [PATCH v3] drm/dsi: Implement set tear scanline compile fix Vinay Simha BN
2016-04-20 10:24             ` Vinay Simha BN
2016-04-20  9:32       ` [PATCH v2 4/4] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel Vinay Simha BN
2016-04-20  9:32         ` Vinay Simha BN
2016-04-21 15:33       ` [PATCH v2 1/4] dt-bindings: Add jdi panel vendor Rob Herring
2016-04-21 15:33         ` Rob Herring
2016-04-14 14:40 ` [PATCH] drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel Archit Taneja
2016-04-14 14:40   ` Archit Taneja
2016-04-20  9:46   ` Vinay Simha
2016-04-20  9:46     ` Vinay Simha
  -- strict thread matches above, loose matches on Subject: below --
2016-04-13  5:47 Vinay Simha BN
2016-04-13  5:47 ` Vinay Simha BN

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.