All of lore.kernel.org
 help / color / mirror / Atom feed
From: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
To: airlied-cv59FeDIM0c@public.gmane.org,
	mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org
Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Mark Yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>,
	Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
Subject: [PATCH RFC v2 07/12] drm/rockchip: Add support for Rockchip Soc LVDS
Date: Wed,  1 Apr 2015 12:09:41 +0200	[thread overview]
Message-ID: <1427882986-19110-8-git-send-email-heiko@sntech.de> (raw)
In-Reply-To: <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>

From: Mark Yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

This adds support for Rockchip soc lvds found on rk3288

Signed-off-by: Mark Yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
This still needs to address Laurent's comment about trying to get the
lvds settings from the panel/connected device instead of encoding them
as separate properties in the devicetree.

 drivers/gpu/drm/rockchip/Kconfig         |   9 +
 drivers/gpu/drm/rockchip/Makefile        |   1 +
 drivers/gpu/drm/rockchip/rockchip_lvds.c | 640 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.h | 107 ++++++
 4 files changed, 757 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..845f953 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,12 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_LVDS
+	tristate "Rockchip lvds support"
+	depends on DRM_ROCKCHIP
+	help
+	  Choose this option to enable support for Rockchip LVDS controllers.
+	  Rockchip rk3288 SoC has LVDS TX Controller can be used, and it
+	  support lvds, rgb, dual lvds output mode. say Y to enable its
+	  driver.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index f3d8a19..8541304 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,5 +6,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
new file mode 100644
index 0000000..657609e
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_of.h>
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#include <video/display_timing.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+#include "rockchip_lvds.h"
+
+#define DISPLAY_OUTPUT_RGB		0
+#define DISPLAY_OUTPUT_LVDS		1
+#define DISPLAY_OUTPUT_DUAL_LVDS	2
+
+#define connector_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, connector)
+
+#define encoder_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, encoder)
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
+ */
+struct rockchip_lvds_soc_data {
+	int grf_soc_con6;
+	int grf_soc_con7;
+};
+
+struct rockchip_lvds {
+	void *base;
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct clk *pclk;
+	const struct rockchip_lvds_soc_data *soc_data;
+
+	struct regulator_bulk_data supplies[3];
+
+	int output;
+	int format;
+
+	struct drm_device *drm_dev;
+	struct drm_panel *panel;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	struct mutex suspend_lock;
+	int suspend;
+};
+
+static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val)
+{
+	writel_relaxed(val, lvds->regs + offset);
+	writel_relaxed(val, lvds->regs + offset + 0x100);
+}
+
+static inline int lvds_name_to_format(const char *s)
+{
+	if (!s)
+		return -EINVAL;
+
+	if (strncmp(s, "jeida", 6) == 0)
+		return LVDS_FORMAT_JEIDA;
+	else if (strncmp(s, "vesa", 6) == 0)
+		return LVDS_FORMAT_VESA;
+
+	return -EINVAL;
+}
+
+static inline int lvds_name_to_output(const char *s)
+{
+	if (!s)
+		return -EINVAL;
+
+	if (strncmp(s, "rgb", 3) == 0)
+		return DISPLAY_OUTPUT_RGB;
+	else if (strncmp(s, "lvds", 4) == 0)
+		return DISPLAY_OUTPUT_LVDS;
+	else if (strncmp(s, "duallvds", 8) == 0)
+		return DISPLAY_OUTPUT_DUAL_LVDS;
+
+	return -EINVAL;
+}
+
+static int rockchip_lvds_poweron(struct rockchip_lvds *lvds)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(lvds->supplies),
+				    lvds->supplies);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(lvds->pclk);
+	if (ret < 0) {
+		dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret);
+		return ret;
+	}
+
+	writel(RK3288_LVDS_CFG_REGC_PLL_ENABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REGC);
+	writel(RK3288_LVDS_CFG_REG21_TX_ENABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REG21);
+
+	return 0;
+}
+
+static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds)
+{
+	int ret;
+
+	ret = regmap_write(lvds->grf,
+			   lvds->soc_data->grf_soc_con7, 0xffff8000);
+	if (ret != 0)
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+
+	writel(RK3288_LVDS_CFG_REG21_TX_DISABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REG21);
+	writel(RK3288_LVDS_CFG_REGC_PLL_DISABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REGC);
+
+	clk_disable(lvds->pclk);
+
+	regulator_bulk_disable(ARRAY_SIZE(lvds->supplies),
+			       lvds->supplies);
+}
+
+static enum drm_connector_status
+rockchip_lvds_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void rockchip_lvds_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs rockchip_lvds_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rockchip_lvds_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rockchip_lvds_connector_destroy,
+};
+
+static int rockchip_lvds_connector_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_lvds *lvds = connector_to_lvds(connector);
+	struct drm_panel *panel = lvds->panel;
+
+	return panel->funcs->get_modes(panel);
+}
+
+static struct drm_encoder *
+rockchip_lvds_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_lvds *lvds = connector_to_lvds(connector);
+
+	return &lvds->encoder;
+}
+
+static enum drm_mode_status rockchip_lvds_connector_mode_valid(
+		struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static
+struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = {
+	.get_modes = rockchip_lvds_connector_get_modes,
+	.mode_valid = rockchip_lvds_connector_mode_valid,
+	.best_encoder = rockchip_lvds_connector_best_encoder,
+};
+
+static void rockchip_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	int ret;
+
+	mutex_lock(&lvds->suspend_lock);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (!lvds->suspend)
+			goto out;
+
+		drm_panel_prepare(lvds->panel);
+		ret = rockchip_lvds_poweron(lvds);
+		if (ret < 0) {
+			drm_panel_unprepare(lvds->panel);
+			return;
+		}
+		drm_panel_enable(lvds->panel);
+
+		lvds->suspend = false;
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		if (lvds->suspend)
+			goto out;
+
+		drm_panel_disable(lvds->panel);
+		rockchip_lvds_poweroff(lvds);
+		drm_panel_unprepare(lvds->panel);
+
+		lvds->suspend = true;
+		break;
+	default:
+		break;
+	}
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static bool
+rockchip_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	u32 h_bp = mode->htotal - mode->hsync_start;
+	u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
+	u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
+	u32 val;
+	int ret;
+
+	val = lvds->format;
+	if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS)
+		val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
+	else if (lvds->output == DISPLAY_OUTPUT_LVDS)
+		val |= LVDS_CH0_EN;
+	else if (lvds->output == DISPLAY_OUTPUT_RGB)
+		val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
+
+	if (h_bp & 0x01)
+		val |= LVDS_START_PHASE_RST_1;
+
+	val |= (pin_dclk << 8) | (pin_hsync << 9);
+	val |= (0xffff << 16);
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	if (lvds->output == DISPLAY_OUTPUT_RGB) {
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG0,
+					RK3288_LVDS_CH0_REG0_TTL_EN |
+					RK3288_LVDS_CH0_REG0_LANECK_EN |
+					RK3288_LVDS_CH0_REG0_LANE4_EN |
+					RK3288_LVDS_CH0_REG0_LANE3_EN |
+					RK3288_LVDS_CH0_REG0_LANE2_EN |
+					RK3288_LVDS_CH0_REG0_LANE1_EN |
+					RK3288_LVDS_CH0_REG0_LANE0_EN);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
+					RK3288_LVDS_PLL_FBDIV_REG2(0x46));
+
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG3,
+					RK3288_LVDS_PLL_FBDIV_REG3(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG4,
+					RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG5,
+					RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REGD,
+					RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG20,
+					RK3288_LVDS_CH0_REG20_LSB);
+	} else {
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG0,
+					RK3288_LVDS_CH0_REG0_LVDS_EN |
+					RK3288_LVDS_CH0_REG0_LANECK_EN |
+					RK3288_LVDS_CH0_REG0_LANE4_EN |
+					RK3288_LVDS_CH0_REG0_LANE3_EN |
+					RK3288_LVDS_CH0_REG0_LANE2_EN |
+					RK3288_LVDS_CH0_REG0_LANE1_EN |
+					RK3288_LVDS_CH0_REG0_LANE0_EN);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG1,
+					RK3288_LVDS_CH0_REG1_LANECK_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE4_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE3_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE2_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE1_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE0_BIAS);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
+					RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
+					RK3288_LVDS_PLL_FBDIV_REG2(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG3,
+					RK3288_LVDS_PLL_FBDIV_REG3(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REGD,
+					RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG20,
+					RK3288_LVDS_CH0_REG20_LSB);
+	}
+
+	dsb();
+}
+
+static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	u32 val;
+	int ret;
+
+	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
+						lvds->connector.connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_drm_encoder_get_mux_id(lvds->dev->of_node, encoder);
+	if (ret < 0)
+		return;
+
+	if (ret)
+		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
+		      (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
+	else
+		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+}
+
+static void rockchip_lvds_encoder_commit(struct drm_encoder *encoder)
+{
+	rockchip_lvds_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder)
+{
+	rockchip_lvds_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = {
+	.dpms = rockchip_lvds_encoder_dpms,
+	.mode_fixup = rockchip_lvds_encoder_mode_fixup,
+	.mode_set = rockchip_lvds_encoder_mode_set,
+	.prepare = rockchip_lvds_encoder_prepare,
+	.commit = rockchip_lvds_encoder_commit,
+	.disable = rockchip_lvds_encoder_disable,
+};
+
+static void rockchip_lvds_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
+	.destroy = rockchip_lvds_encoder_destroy,
+};
+
+static struct rockchip_lvds_soc_data rk3288_lvds_data = {
+	.grf_soc_con6 = 0x025c,
+	.grf_soc_con7 = 0x0260,
+};
+
+static const struct of_device_id rockchip_lvds_dt_ids[] = {
+	{
+		.compatible = "rockchip,rk3288-lvds",
+		.data = &rk3288_lvds_data
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
+
+static int rockchip_lvds_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	int ret;
+
+	lvds->drm_dev = drm_dev;
+
+	encoder = &lvds->encoder;
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_lvds_encoder_funcs,
+			       DRM_MODE_ENCODER_LVDS);
+	if (ret < 0) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs);
+
+	connector = &lvds->connector;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &rockchip_lvds_connector_funcs,
+				 DRM_MODE_CONNECTOR_LVDS);
+	if (ret < 0) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rockchip_lvds_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret < 0) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_panel_attach(lvds->panel, connector);
+	if (ret < 0) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_lvds_unbind(struct device *dev, struct device *master,
+				void *data)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+
+	if (lvds->panel) {
+		rockchip_lvds_encoder_dpms(&lvds->encoder, DRM_MODE_DPMS_OFF);
+
+		drm_panel_detach(lvds->panel);
+
+		drm_connector_cleanup(&lvds->connector);
+		drm_encoder_cleanup(&lvds->encoder);
+	}
+}
+static const struct component_ops rockchip_lvds_component_ops = {
+	.bind = rockchip_lvds_bind,
+	.unbind = rockchip_lvds_unbind,
+};
+
+static int rockchip_lvds_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_lvds *lvds;
+	struct device_node *port, *output_node = NULL;
+	const struct of_device_id *match;
+	struct resource *res;
+	const char *name;
+	int i, ret;
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+	if (!lvds)
+		return -ENOMEM;
+
+	lvds->dev = dev;
+	lvds->suspend = true;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lvds->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(lvds->regs))
+		return PTR_ERR(lvds->regs);
+
+	lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+						    "rockchip,grf");
+	if (IS_ERR(lvds->grf)) {
+		dev_err(dev, "missing rockchip,grf property\n");
+		return PTR_ERR(lvds->grf);
+	}
+
+	lvds->supplies[0].supply = "avdd1v0";
+	lvds->supplies[1].supply = "avdd1v8";
+	lvds->supplies[2].supply = "avdd3v3";
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(lvds->supplies),
+				      lvds->supplies);
+	if (ret < 0)
+		return ret;
+
+	lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
+	if (IS_ERR(lvds->pclk)) {
+		dev_err(dev, "could not get pclk_lvds\n");
+		return PTR_ERR(lvds->pclk);
+	}
+
+	ret = clk_prepare(lvds->pclk);
+	if (ret < 0) {
+		dev_err(dev, "failed to prepare pclk_lvds\n");
+		return ret;
+	}
+
+	match = of_match_node(rockchip_lvds_dt_ids, dev->of_node);
+	lvds->soc_data = match->data;
+
+	dev_set_drvdata(dev, lvds);
+	mutex_init(&lvds->suspend_lock);
+
+	if (of_property_read_string(dev->of_node, "rockchip,output", &name))
+		/* default set it as output rgb */
+		lvds->output = DISPLAY_OUTPUT_RGB;
+	else
+		lvds->output = lvds_name_to_output(name);
+
+	if (of_property_read_string(dev->of_node, "rockchip,data-mapping",
+				    &name))
+		/* default set it as format jeida */
+		lvds->format = LVDS_FORMAT_JEIDA;
+	else
+		lvds->format = lvds_name_to_format(name);
+
+	if (of_property_read_u32(dev->of_node, "rockchip,data-width", &i)) {
+		lvds->format |= LVDS_24BIT;
+	} else {
+		if (i == 24) {
+			lvds->format |= LVDS_24BIT;
+		} else if (i == 18) {
+			lvds->format |= LVDS_18BIT;
+		} else {
+			dev_err(&pdev->dev,
+				"rockchip-lvds unsupport data-width[%d]\n", i);
+			ret = -EINVAL;
+			goto err_unprepare_pclk;
+		}
+	}
+
+	port = of_graph_get_port_by_id(dev->of_node, 1);
+	if (port) {
+		struct device_node *endpoint;
+
+		endpoint = of_get_child_by_name(port, "endpoint");
+		if (endpoint) {
+			output_node = of_graph_get_remote_port_parent(endpoint);
+			of_node_put(endpoint);
+		}
+	}
+
+	if (!output_node) {
+		dev_err(&pdev->dev, "no output defined\n");
+		return -EINVAL;
+	}
+
+	lvds->panel = of_drm_find_panel(output_node);
+	of_node_put(output_node);
+	if (!lvds->panel) {
+		dev_err(&pdev->dev, "panel not found\n");
+		return -EPROBE_DEFER;
+	}
+
+	return component_add(&pdev->dev, &rockchip_lvds_component_ops);
+
+err_unprepare_pclk:
+	clk_unprepare(lvds->pclk);
+	return ret;
+}
+
+static int rockchip_lvds_remove(struct platform_device *pdev)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
+
+	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+
+	clk_unprepare(lvds->pclk);
+
+	return 0;
+}
+
+struct platform_driver rockchip_lvds_driver = {
+	.probe = rockchip_lvds_probe,
+	.remove = rockchip_lvds_remove,
+	.driver = {
+		   .name = "rockchip-lvds",
+		   .of_match_table = of_match_ptr(rockchip_lvds_dt_ids),
+	},
+};
+module_platform_driver(rockchip_lvds_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
+MODULE_AUTHOR("Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>");
+MODULE_DESCRIPTION("ROCKCHIP LVDS Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h
new file mode 100644
index 0000000..9b59d87
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _ROCKCHIP_LVDS_
+#define _ROCKCHIP_LVDS_
+
+#define RK3288_LVDS_CH0_REG0			0x00
+#define RK3288_LVDS_CH0_REG0_LVDS_EN		BIT(7)
+#define RK3288_LVDS_CH0_REG0_TTL_EN		BIT(6)
+#define RK3288_LVDS_CH0_REG0_LANECK_EN		BIT(5)
+#define RK3288_LVDS_CH0_REG0_LANE4_EN		BIT(4)
+#define RK3288_LVDS_CH0_REG0_LANE3_EN		BIT(3)
+#define RK3288_LVDS_CH0_REG0_LANE2_EN		BIT(2)
+#define RK3288_LVDS_CH0_REG0_LANE1_EN		BIT(1)
+#define RK3288_LVDS_CH0_REG0_LANE0_EN		BIT(0)
+
+#define RK3288_LVDS_CH0_REG1			0x04
+#define RK3288_LVDS_CH0_REG1_LANECK_BIAS	BIT(5)
+#define RK3288_LVDS_CH0_REG1_LANE4_BIAS		BIT(4)
+#define RK3288_LVDS_CH0_REG1_LANE3_BIAS		BIT(3)
+#define RK3288_LVDS_CH0_REG1_LANE2_BIAS		BIT(2)
+#define RK3288_LVDS_CH0_REG1_LANE1_BIAS		BIT(1)
+#define RK3288_LVDS_CH0_REG1_LANE0_BIAS		BIT(0)
+
+#define RK3288_LVDS_CH0_REG2			0x08
+#define RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE	BIT(6)
+#define RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE	BIT(5)
+#define RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE	BIT(4)
+#define RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE	BIT(3)
+#define RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE	BIT(2)
+#define RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE	BIT(1)
+#define RK3288_LVDS_CH0_REG2_PLL_FBDIV8		BIT(0)
+
+#define RK3288_LVDS_CH0_REG3			0x0c
+#define RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK	0xff
+
+#define RK3288_LVDS_CH0_REG4			0x10
+#define RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE	BIT(5)
+#define RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE	BIT(4)
+#define RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE	BIT(3)
+#define RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE	BIT(2)
+#define RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE	BIT(1)
+#define RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE	BIT(0)
+
+#define RK3288_LVDS_CH0_REG5			0x14
+#define RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA	BIT(5)
+#define RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA	BIT(4)
+#define RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA	BIT(3)
+#define RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA	BIT(2)
+#define RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA	BIT(1)
+#define RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA	BIT(0)
+
+#define RK3288_LVDS_CFG_REGC			0x30
+#define RK3288_LVDS_CFG_REGC_PLL_ENABLE		0x00
+#define RK3288_LVDS_CFG_REGC_PLL_DISABLE	0xff
+
+#define RK3288_LVDS_CH0_REGD			0x34
+#define RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK	0x1f
+
+#define RK3288_LVDS_CH0_REG20			0x80
+#define RK3288_LVDS_CH0_REG20_MSB		0x45
+#define RK3288_LVDS_CH0_REG20_LSB		0x44
+
+#define RK3288_LVDS_CFG_REG21			0x84
+#define RK3288_LVDS_CFG_REG21_TX_ENABLE		0x92
+#define RK3288_LVDS_CFG_REG21_TX_DISABLE	0x00
+
+/* fbdiv value is split over 2 registers, with bit8 in reg2 */
+#define RK3288_LVDS_PLL_FBDIV_REG2(_fbd) \
+		(_fbd & BIT(8) ? RK3288_LVDS_CH0_REG2_PLL_FBDIV8 : 0)
+#define RK3288_LVDS_PLL_FBDIV_REG3(_fbd) \
+		(_fbd & RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK)
+#define RK3288_LVDS_PLL_PREDIV_REGD(_pd) \
+		(_pd & RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK)
+
+#define RK3288_LVDS_SOC_CON6_SEL_VOP_LIT	BIT(3)
+
+#define LVDS_FMT_MASK			(0x07 << 16)
+#define LVDS_MSB			(0x01 << 3)
+#define LVDS_DUAL			(0x01 << 4)
+#define LVDS_FMT_1			(0x01 << 5)
+#define LVDS_TTL_EN			(0x01 << 6)
+#define LVDS_START_PHASE_RST_1		(0x01 << 7)
+#define LVDS_DCLK_INV			(0x01 << 8)
+#define LVDS_CH0_EN			(0x01 << 11)
+#define LVDS_CH1_EN			(0x01 << 12)
+#define LVDS_PWRDN			(0x01 << 15)
+
+#define LVDS_24BIT		(0 << 1)
+#define LVDS_18BIT		(1 << 1)
+#define LVDS_FORMAT_VESA	(0 << 0)
+#define LVDS_FORMAT_JEIDA	(1 << 0)
+
+#endif /* _ROCKCHIP_LVDS_ */
-- 
2.1.4

--
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

WARNING: multiple messages have this Message-ID (diff)
From: heiko@sntech.de (Heiko Stuebner)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH RFC v2 07/12] drm/rockchip: Add support for Rockchip Soc LVDS
Date: Wed,  1 Apr 2015 12:09:41 +0200	[thread overview]
Message-ID: <1427882986-19110-8-git-send-email-heiko@sntech.de> (raw)
In-Reply-To: <1427882986-19110-1-git-send-email-heiko@sntech.de>

From: Mark Yao <yzq@rock-chips.com>

This adds support for Rockchip soc lvds found on rk3288

Signed-off-by: Mark Yao <yzq@rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
This still needs to address Laurent's comment about trying to get the
lvds settings from the panel/connected device instead of encoding them
as separate properties in the devicetree.

 drivers/gpu/drm/rockchip/Kconfig         |   9 +
 drivers/gpu/drm/rockchip/Makefile        |   1 +
 drivers/gpu/drm/rockchip/rockchip_lvds.c | 640 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.h | 107 ++++++
 4 files changed, 757 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..845f953 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,12 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_LVDS
+	tristate "Rockchip lvds support"
+	depends on DRM_ROCKCHIP
+	help
+	  Choose this option to enable support for Rockchip LVDS controllers.
+	  Rockchip rk3288 SoC has LVDS TX Controller can be used, and it
+	  support lvds, rgb, dual lvds output mode. say Y to enable its
+	  driver.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index f3d8a19..8541304 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,5 +6,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
new file mode 100644
index 0000000..657609e
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_of.h>
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#include <video/display_timing.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+#include "rockchip_lvds.h"
+
+#define DISPLAY_OUTPUT_RGB		0
+#define DISPLAY_OUTPUT_LVDS		1
+#define DISPLAY_OUTPUT_DUAL_LVDS	2
+
+#define connector_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, connector)
+
+#define encoder_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, encoder)
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
+ */
+struct rockchip_lvds_soc_data {
+	int grf_soc_con6;
+	int grf_soc_con7;
+};
+
+struct rockchip_lvds {
+	void *base;
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct clk *pclk;
+	const struct rockchip_lvds_soc_data *soc_data;
+
+	struct regulator_bulk_data supplies[3];
+
+	int output;
+	int format;
+
+	struct drm_device *drm_dev;
+	struct drm_panel *panel;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	struct mutex suspend_lock;
+	int suspend;
+};
+
+static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val)
+{
+	writel_relaxed(val, lvds->regs + offset);
+	writel_relaxed(val, lvds->regs + offset + 0x100);
+}
+
+static inline int lvds_name_to_format(const char *s)
+{
+	if (!s)
+		return -EINVAL;
+
+	if (strncmp(s, "jeida", 6) == 0)
+		return LVDS_FORMAT_JEIDA;
+	else if (strncmp(s, "vesa", 6) == 0)
+		return LVDS_FORMAT_VESA;
+
+	return -EINVAL;
+}
+
+static inline int lvds_name_to_output(const char *s)
+{
+	if (!s)
+		return -EINVAL;
+
+	if (strncmp(s, "rgb", 3) == 0)
+		return DISPLAY_OUTPUT_RGB;
+	else if (strncmp(s, "lvds", 4) == 0)
+		return DISPLAY_OUTPUT_LVDS;
+	else if (strncmp(s, "duallvds", 8) == 0)
+		return DISPLAY_OUTPUT_DUAL_LVDS;
+
+	return -EINVAL;
+}
+
+static int rockchip_lvds_poweron(struct rockchip_lvds *lvds)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(lvds->supplies),
+				    lvds->supplies);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(lvds->pclk);
+	if (ret < 0) {
+		dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret);
+		return ret;
+	}
+
+	writel(RK3288_LVDS_CFG_REGC_PLL_ENABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REGC);
+	writel(RK3288_LVDS_CFG_REG21_TX_ENABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REG21);
+
+	return 0;
+}
+
+static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds)
+{
+	int ret;
+
+	ret = regmap_write(lvds->grf,
+			   lvds->soc_data->grf_soc_con7, 0xffff8000);
+	if (ret != 0)
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+
+	writel(RK3288_LVDS_CFG_REG21_TX_DISABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REG21);
+	writel(RK3288_LVDS_CFG_REGC_PLL_DISABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REGC);
+
+	clk_disable(lvds->pclk);
+
+	regulator_bulk_disable(ARRAY_SIZE(lvds->supplies),
+			       lvds->supplies);
+}
+
+static enum drm_connector_status
+rockchip_lvds_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void rockchip_lvds_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs rockchip_lvds_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rockchip_lvds_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rockchip_lvds_connector_destroy,
+};
+
+static int rockchip_lvds_connector_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_lvds *lvds = connector_to_lvds(connector);
+	struct drm_panel *panel = lvds->panel;
+
+	return panel->funcs->get_modes(panel);
+}
+
+static struct drm_encoder *
+rockchip_lvds_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_lvds *lvds = connector_to_lvds(connector);
+
+	return &lvds->encoder;
+}
+
+static enum drm_mode_status rockchip_lvds_connector_mode_valid(
+		struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static
+struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = {
+	.get_modes = rockchip_lvds_connector_get_modes,
+	.mode_valid = rockchip_lvds_connector_mode_valid,
+	.best_encoder = rockchip_lvds_connector_best_encoder,
+};
+
+static void rockchip_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	int ret;
+
+	mutex_lock(&lvds->suspend_lock);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (!lvds->suspend)
+			goto out;
+
+		drm_panel_prepare(lvds->panel);
+		ret = rockchip_lvds_poweron(lvds);
+		if (ret < 0) {
+			drm_panel_unprepare(lvds->panel);
+			return;
+		}
+		drm_panel_enable(lvds->panel);
+
+		lvds->suspend = false;
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		if (lvds->suspend)
+			goto out;
+
+		drm_panel_disable(lvds->panel);
+		rockchip_lvds_poweroff(lvds);
+		drm_panel_unprepare(lvds->panel);
+
+		lvds->suspend = true;
+		break;
+	default:
+		break;
+	}
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static bool
+rockchip_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	u32 h_bp = mode->htotal - mode->hsync_start;
+	u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
+	u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
+	u32 val;
+	int ret;
+
+	val = lvds->format;
+	if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS)
+		val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
+	else if (lvds->output == DISPLAY_OUTPUT_LVDS)
+		val |= LVDS_CH0_EN;
+	else if (lvds->output == DISPLAY_OUTPUT_RGB)
+		val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
+
+	if (h_bp & 0x01)
+		val |= LVDS_START_PHASE_RST_1;
+
+	val |= (pin_dclk << 8) | (pin_hsync << 9);
+	val |= (0xffff << 16);
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	if (lvds->output == DISPLAY_OUTPUT_RGB) {
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG0,
+					RK3288_LVDS_CH0_REG0_TTL_EN |
+					RK3288_LVDS_CH0_REG0_LANECK_EN |
+					RK3288_LVDS_CH0_REG0_LANE4_EN |
+					RK3288_LVDS_CH0_REG0_LANE3_EN |
+					RK3288_LVDS_CH0_REG0_LANE2_EN |
+					RK3288_LVDS_CH0_REG0_LANE1_EN |
+					RK3288_LVDS_CH0_REG0_LANE0_EN);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
+					RK3288_LVDS_PLL_FBDIV_REG2(0x46));
+
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG3,
+					RK3288_LVDS_PLL_FBDIV_REG3(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG4,
+					RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG5,
+					RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REGD,
+					RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG20,
+					RK3288_LVDS_CH0_REG20_LSB);
+	} else {
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG0,
+					RK3288_LVDS_CH0_REG0_LVDS_EN |
+					RK3288_LVDS_CH0_REG0_LANECK_EN |
+					RK3288_LVDS_CH0_REG0_LANE4_EN |
+					RK3288_LVDS_CH0_REG0_LANE3_EN |
+					RK3288_LVDS_CH0_REG0_LANE2_EN |
+					RK3288_LVDS_CH0_REG0_LANE1_EN |
+					RK3288_LVDS_CH0_REG0_LANE0_EN);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG1,
+					RK3288_LVDS_CH0_REG1_LANECK_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE4_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE3_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE2_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE1_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE0_BIAS);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
+					RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
+					RK3288_LVDS_PLL_FBDIV_REG2(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG3,
+					RK3288_LVDS_PLL_FBDIV_REG3(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REGD,
+					RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG20,
+					RK3288_LVDS_CH0_REG20_LSB);
+	}
+
+	dsb();
+}
+
+static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	u32 val;
+	int ret;
+
+	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
+						lvds->connector.connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_drm_encoder_get_mux_id(lvds->dev->of_node, encoder);
+	if (ret < 0)
+		return;
+
+	if (ret)
+		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
+		      (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
+	else
+		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+}
+
+static void rockchip_lvds_encoder_commit(struct drm_encoder *encoder)
+{
+	rockchip_lvds_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder)
+{
+	rockchip_lvds_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = {
+	.dpms = rockchip_lvds_encoder_dpms,
+	.mode_fixup = rockchip_lvds_encoder_mode_fixup,
+	.mode_set = rockchip_lvds_encoder_mode_set,
+	.prepare = rockchip_lvds_encoder_prepare,
+	.commit = rockchip_lvds_encoder_commit,
+	.disable = rockchip_lvds_encoder_disable,
+};
+
+static void rockchip_lvds_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
+	.destroy = rockchip_lvds_encoder_destroy,
+};
+
+static struct rockchip_lvds_soc_data rk3288_lvds_data = {
+	.grf_soc_con6 = 0x025c,
+	.grf_soc_con7 = 0x0260,
+};
+
+static const struct of_device_id rockchip_lvds_dt_ids[] = {
+	{
+		.compatible = "rockchip,rk3288-lvds",
+		.data = &rk3288_lvds_data
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
+
+static int rockchip_lvds_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	int ret;
+
+	lvds->drm_dev = drm_dev;
+
+	encoder = &lvds->encoder;
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_lvds_encoder_funcs,
+			       DRM_MODE_ENCODER_LVDS);
+	if (ret < 0) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs);
+
+	connector = &lvds->connector;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &rockchip_lvds_connector_funcs,
+				 DRM_MODE_CONNECTOR_LVDS);
+	if (ret < 0) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rockchip_lvds_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret < 0) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_panel_attach(lvds->panel, connector);
+	if (ret < 0) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_lvds_unbind(struct device *dev, struct device *master,
+				void *data)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+
+	if (lvds->panel) {
+		rockchip_lvds_encoder_dpms(&lvds->encoder, DRM_MODE_DPMS_OFF);
+
+		drm_panel_detach(lvds->panel);
+
+		drm_connector_cleanup(&lvds->connector);
+		drm_encoder_cleanup(&lvds->encoder);
+	}
+}
+static const struct component_ops rockchip_lvds_component_ops = {
+	.bind = rockchip_lvds_bind,
+	.unbind = rockchip_lvds_unbind,
+};
+
+static int rockchip_lvds_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_lvds *lvds;
+	struct device_node *port, *output_node = NULL;
+	const struct of_device_id *match;
+	struct resource *res;
+	const char *name;
+	int i, ret;
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+	if (!lvds)
+		return -ENOMEM;
+
+	lvds->dev = dev;
+	lvds->suspend = true;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lvds->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(lvds->regs))
+		return PTR_ERR(lvds->regs);
+
+	lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+						    "rockchip,grf");
+	if (IS_ERR(lvds->grf)) {
+		dev_err(dev, "missing rockchip,grf property\n");
+		return PTR_ERR(lvds->grf);
+	}
+
+	lvds->supplies[0].supply = "avdd1v0";
+	lvds->supplies[1].supply = "avdd1v8";
+	lvds->supplies[2].supply = "avdd3v3";
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(lvds->supplies),
+				      lvds->supplies);
+	if (ret < 0)
+		return ret;
+
+	lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
+	if (IS_ERR(lvds->pclk)) {
+		dev_err(dev, "could not get pclk_lvds\n");
+		return PTR_ERR(lvds->pclk);
+	}
+
+	ret = clk_prepare(lvds->pclk);
+	if (ret < 0) {
+		dev_err(dev, "failed to prepare pclk_lvds\n");
+		return ret;
+	}
+
+	match = of_match_node(rockchip_lvds_dt_ids, dev->of_node);
+	lvds->soc_data = match->data;
+
+	dev_set_drvdata(dev, lvds);
+	mutex_init(&lvds->suspend_lock);
+
+	if (of_property_read_string(dev->of_node, "rockchip,output", &name))
+		/* default set it as output rgb */
+		lvds->output = DISPLAY_OUTPUT_RGB;
+	else
+		lvds->output = lvds_name_to_output(name);
+
+	if (of_property_read_string(dev->of_node, "rockchip,data-mapping",
+				    &name))
+		/* default set it as format jeida */
+		lvds->format = LVDS_FORMAT_JEIDA;
+	else
+		lvds->format = lvds_name_to_format(name);
+
+	if (of_property_read_u32(dev->of_node, "rockchip,data-width", &i)) {
+		lvds->format |= LVDS_24BIT;
+	} else {
+		if (i == 24) {
+			lvds->format |= LVDS_24BIT;
+		} else if (i == 18) {
+			lvds->format |= LVDS_18BIT;
+		} else {
+			dev_err(&pdev->dev,
+				"rockchip-lvds unsupport data-width[%d]\n", i);
+			ret = -EINVAL;
+			goto err_unprepare_pclk;
+		}
+	}
+
+	port = of_graph_get_port_by_id(dev->of_node, 1);
+	if (port) {
+		struct device_node *endpoint;
+
+		endpoint = of_get_child_by_name(port, "endpoint");
+		if (endpoint) {
+			output_node = of_graph_get_remote_port_parent(endpoint);
+			of_node_put(endpoint);
+		}
+	}
+
+	if (!output_node) {
+		dev_err(&pdev->dev, "no output defined\n");
+		return -EINVAL;
+	}
+
+	lvds->panel = of_drm_find_panel(output_node);
+	of_node_put(output_node);
+	if (!lvds->panel) {
+		dev_err(&pdev->dev, "panel not found\n");
+		return -EPROBE_DEFER;
+	}
+
+	return component_add(&pdev->dev, &rockchip_lvds_component_ops);
+
+err_unprepare_pclk:
+	clk_unprepare(lvds->pclk);
+	return ret;
+}
+
+static int rockchip_lvds_remove(struct platform_device *pdev)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
+
+	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+
+	clk_unprepare(lvds->pclk);
+
+	return 0;
+}
+
+struct platform_driver rockchip_lvds_driver = {
+	.probe = rockchip_lvds_probe,
+	.remove = rockchip_lvds_remove,
+	.driver = {
+		   .name = "rockchip-lvds",
+		   .of_match_table = of_match_ptr(rockchip_lvds_dt_ids),
+	},
+};
+module_platform_driver(rockchip_lvds_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("ROCKCHIP LVDS Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h
new file mode 100644
index 0000000..9b59d87
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc@rock-chips.com>
+ *      mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _ROCKCHIP_LVDS_
+#define _ROCKCHIP_LVDS_
+
+#define RK3288_LVDS_CH0_REG0			0x00
+#define RK3288_LVDS_CH0_REG0_LVDS_EN		BIT(7)
+#define RK3288_LVDS_CH0_REG0_TTL_EN		BIT(6)
+#define RK3288_LVDS_CH0_REG0_LANECK_EN		BIT(5)
+#define RK3288_LVDS_CH0_REG0_LANE4_EN		BIT(4)
+#define RK3288_LVDS_CH0_REG0_LANE3_EN		BIT(3)
+#define RK3288_LVDS_CH0_REG0_LANE2_EN		BIT(2)
+#define RK3288_LVDS_CH0_REG0_LANE1_EN		BIT(1)
+#define RK3288_LVDS_CH0_REG0_LANE0_EN		BIT(0)
+
+#define RK3288_LVDS_CH0_REG1			0x04
+#define RK3288_LVDS_CH0_REG1_LANECK_BIAS	BIT(5)
+#define RK3288_LVDS_CH0_REG1_LANE4_BIAS		BIT(4)
+#define RK3288_LVDS_CH0_REG1_LANE3_BIAS		BIT(3)
+#define RK3288_LVDS_CH0_REG1_LANE2_BIAS		BIT(2)
+#define RK3288_LVDS_CH0_REG1_LANE1_BIAS		BIT(1)
+#define RK3288_LVDS_CH0_REG1_LANE0_BIAS		BIT(0)
+
+#define RK3288_LVDS_CH0_REG2			0x08
+#define RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE	BIT(6)
+#define RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE	BIT(5)
+#define RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE	BIT(4)
+#define RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE	BIT(3)
+#define RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE	BIT(2)
+#define RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE	BIT(1)
+#define RK3288_LVDS_CH0_REG2_PLL_FBDIV8		BIT(0)
+
+#define RK3288_LVDS_CH0_REG3			0x0c
+#define RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK	0xff
+
+#define RK3288_LVDS_CH0_REG4			0x10
+#define RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE	BIT(5)
+#define RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE	BIT(4)
+#define RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE	BIT(3)
+#define RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE	BIT(2)
+#define RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE	BIT(1)
+#define RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE	BIT(0)
+
+#define RK3288_LVDS_CH0_REG5			0x14
+#define RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA	BIT(5)
+#define RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA	BIT(4)
+#define RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA	BIT(3)
+#define RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA	BIT(2)
+#define RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA	BIT(1)
+#define RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA	BIT(0)
+
+#define RK3288_LVDS_CFG_REGC			0x30
+#define RK3288_LVDS_CFG_REGC_PLL_ENABLE		0x00
+#define RK3288_LVDS_CFG_REGC_PLL_DISABLE	0xff
+
+#define RK3288_LVDS_CH0_REGD			0x34
+#define RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK	0x1f
+
+#define RK3288_LVDS_CH0_REG20			0x80
+#define RK3288_LVDS_CH0_REG20_MSB		0x45
+#define RK3288_LVDS_CH0_REG20_LSB		0x44
+
+#define RK3288_LVDS_CFG_REG21			0x84
+#define RK3288_LVDS_CFG_REG21_TX_ENABLE		0x92
+#define RK3288_LVDS_CFG_REG21_TX_DISABLE	0x00
+
+/* fbdiv value is split over 2 registers, with bit8 in reg2 */
+#define RK3288_LVDS_PLL_FBDIV_REG2(_fbd) \
+		(_fbd & BIT(8) ? RK3288_LVDS_CH0_REG2_PLL_FBDIV8 : 0)
+#define RK3288_LVDS_PLL_FBDIV_REG3(_fbd) \
+		(_fbd & RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK)
+#define RK3288_LVDS_PLL_PREDIV_REGD(_pd) \
+		(_pd & RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK)
+
+#define RK3288_LVDS_SOC_CON6_SEL_VOP_LIT	BIT(3)
+
+#define LVDS_FMT_MASK			(0x07 << 16)
+#define LVDS_MSB			(0x01 << 3)
+#define LVDS_DUAL			(0x01 << 4)
+#define LVDS_FMT_1			(0x01 << 5)
+#define LVDS_TTL_EN			(0x01 << 6)
+#define LVDS_START_PHASE_RST_1		(0x01 << 7)
+#define LVDS_DCLK_INV			(0x01 << 8)
+#define LVDS_CH0_EN			(0x01 << 11)
+#define LVDS_CH1_EN			(0x01 << 12)
+#define LVDS_PWRDN			(0x01 << 15)
+
+#define LVDS_24BIT		(0 << 1)
+#define LVDS_18BIT		(1 << 1)
+#define LVDS_FORMAT_VESA	(0 << 0)
+#define LVDS_FORMAT_JEIDA	(1 << 0)
+
+#endif /* _ROCKCHIP_LVDS_ */
-- 
2.1.4

  parent reply	other threads:[~2015-04-01 10:09 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
2015-04-01 10:09 ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 01/12] drm/encoder: add functionality to register encoders to a global list Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 04/12] drm/components: add generic vga encoder driver Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
     [not found]   ` <1427882986-19110-5-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2015-04-01 10:27     ` Russell King - ARM Linux
2015-04-01 10:27       ` Russell King - ARM Linux
2015-04-01 10:40       ` Heiko Stübner
2015-04-01 10:40         ` Heiko Stübner
2015-04-01 10:09 ` [PATCH RFC v2 06/12] dt-bindings: Add documentation for rockchip lvds Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
     [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2015-04-01 10:09   ` [PATCH RFC v2 02/12] drm/connector: add functionality to register connectors to a global list Heiko Stuebner
2015-04-01 10:09     ` Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 03/12] drm: add components subdirectory and infrastructure Heiko Stuebner
2015-04-01 10:09     ` Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 05/12] drm/components: add generic vga connector driver Heiko Stuebner
2015-04-01 10:09     ` Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner [this message]
2015-04-01 10:09     ` [PATCH RFC v2 07/12] drm/rockchip: Add support for Rockchip Soc LVDS Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 08/12] drm/rockchip: lvds: register a bridge when no panel is set Heiko Stuebner
2015-04-01 10:09     ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 09/12] drm/rockchip: enable rgb output of vops for all other connectors Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 10/12] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 11/12] ARM: dts: rockchip: add rk3288 lvds node Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 12/12] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly Heiko Stuebner
2015-04-01 10:09   ` Heiko Stuebner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1427882986-19110-8-git-send-email-heiko@sntech.de \
    --to=heiko-4mtyjxux2i+zqb+pc5nmwq@public.gmane.org \
    --cc=airlied-cv59FeDIM0c@public.gmane.org \
    --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org \
    --cc=dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org \
    --cc=laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org \
    --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org \
    --cc=robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.