All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Add TCON support to the R-Car DU driver
@ 2016-04-28 21:00 Sergei Shtylyov
  2016-04-28 21:02 ` [PATCH 1/4] drm_mode: add TCON encoder/connector Sergei Shtylyov
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Sergei Shtylyov @ 2016-04-28 21:00 UTC (permalink / raw)
  To: laurent.pinchart, airlied, dri-devel, linux-renesas-soc

Hello.

   Here's the set of 4 patches against David Airlie's 'linux.git' repo's
'drm-next' branch. We're adding support for the TCON (timing controller) that
 is a part of the R-Car SoC and can directly drive a LCD.

[1/4] drm_mode: add-TCON encoder connector
[2/4] rcar-du: add TCON encoder driver
[3/4] rcar-du: add TCON connector driver
[4/4] rcar-du: add R8A7794 TCON support

MBR, Sergei

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

* [PATCH 1/4] drm_mode: add TCON encoder/connector
  2016-04-28 21:00 [PATCH 0/4] Add TCON support to the R-Car DU driver Sergei Shtylyov
@ 2016-04-28 21:02 ` Sergei Shtylyov
  2016-04-29  9:13     ` Daniel Vetter
  2016-04-28 21:03 ` [PATCH 2/4] rcar-du: add TCON encoder driver Sergei Shtylyov
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Sergei Shtylyov @ 2016-04-28 21:02 UTC (permalink / raw)
  To: airlied, dri-devel; +Cc: laurent.pinchart, linux-renesas-soc

TCON (Timing  Controller) usually means  a chip that drives a LCD panel.
In  our case, such controller  is a part of the Renesas R-Car SoCs. Add
the TCON encoder/connector #define's  to be used by the TCON support code
in the Renesas R-Car Display Unit (DU) driver.

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

---
 include/uapi/drm/drm_mode.h |    2 ++
 1 file changed, 2 insertions(+)

Index: linux/include/uapi/drm/drm_mode.h
===================================================================
--- linux.orig/include/uapi/drm/drm_mode.h
+++ linux/include/uapi/drm/drm_mode.h
@@ -202,6 +202,7 @@ struct drm_mode_get_plane_res {
 #define DRM_MODE_ENCODER_VIRTUAL 5
 #define DRM_MODE_ENCODER_DSI	6
 #define DRM_MODE_ENCODER_DPMST	7
+#define DRM_MODE_ENCODER_TCON	8
 
 struct drm_mode_get_encoder {
 	__u32 encoder_id;
@@ -241,6 +242,7 @@ struct drm_mode_get_encoder {
 #define DRM_MODE_CONNECTOR_eDP		14
 #define DRM_MODE_CONNECTOR_VIRTUAL      15
 #define DRM_MODE_CONNECTOR_DSI		16
+#define DRM_MODE_CONNECTOR_TCON		17
 
 struct drm_mode_get_connector {
 

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

* [PATCH 2/4] rcar-du: add TCON encoder driver
  2016-04-28 21:00 [PATCH 0/4] Add TCON support to the R-Car DU driver Sergei Shtylyov
  2016-04-28 21:02 ` [PATCH 1/4] drm_mode: add TCON encoder/connector Sergei Shtylyov
@ 2016-04-28 21:03 ` Sergei Shtylyov
  2016-05-27 21:28     ` Laurent Pinchart
  2016-04-28 21:04 ` [PATCH 3/4] rcar-du: add TCON connector driver Sergei Shtylyov
  2016-04-28 21:05 ` [PATCH 4/4] rcar-du: add R8A7794 TCON support Sergei Shtylyov
  3 siblings, 1 reply; 11+ messages in thread
From: Sergei Shtylyov @ 2016-04-28 21:03 UTC (permalink / raw)
  To: laurent.pinchart, airlied, dri-devel; +Cc: linux-renesas-soc

Renesas  R-Car SoCs  include the timing controller (TCON) that can directly
drive LCDs. It receives  the H/VSYNC, etc. from the Display Unit (DU)  and
converts  them to the set of signals  that a LCD panel can understand (the
RBG  signals are effectively passed thru).  TCON has a set of registers
that we need to  program based on the video mode timings, so we're adding
a DU encoder driver doing that...

Based on a large patch by Andrey Gusakov.

Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

---
 drivers/gpu/drm/rcar-du/Kconfig           |    6 
 drivers/gpu/drm/rcar-du/Makefile          |    1 
 drivers/gpu/drm/rcar-du/rcar_du_drv.h     |    4 
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c |    9 +
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h |    3 
 drivers/gpu/drm/rcar-du/rcar_du_kms.c     |    4 
 drivers/gpu/drm/rcar-du/rcar_du_tconenc.c |  184 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/rcar-du/rcar_du_tconenc.h |   37 ++++++
 drivers/gpu/drm/rcar-du/rcar_tcon_regs.h  |   70 +++++++++++
 9 files changed, 318 insertions(+)

Index: linux/drivers/gpu/drm/rcar-du/Kconfig
===================================================================
--- linux.orig/drivers/gpu/drm/rcar-du/Kconfig
+++ linux/drivers/gpu/drm/rcar-du/Kconfig
@@ -24,6 +24,12 @@ config DRM_RCAR_LVDS
 	help
 	  Enable support for the R-Car Display Unit embedded LVDS encoders.
 
+config DRM_RCAR_TCON
+	bool "R-Car DU TCON Encoder Support"
+	depends on DRM_RCAR_DU
+	help
+	  Enable support for the R-Car Display Unit embedded TCON encoders.
+
 config DRM_RCAR_VSP
 	bool "R-Car DU VSP Compositor Support"
 	depends on DRM_RCAR_DU
Index: linux/drivers/gpu/drm/rcar-du/Makefile
===================================================================
--- linux.orig/drivers/gpu/drm/rcar-du/Makefile
+++ linux/drivers/gpu/drm/rcar-du/Makefile
@@ -10,6 +10,7 @@ rcar-du-drm-y := rcar_du_crtc.o \
 rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI)	+= rcar_du_hdmicon.o \
 					   rcar_du_hdmienc.o
 rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)	+= rcar_du_lvdsenc.o
+rcar-du-drm-$(CONFIG_DRM_RCAR_TCON)	+= rcar_du_tconenc.o
 
 rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)	+= rcar_du_vsp.o
 
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_drv.h
===================================================================
--- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -59,6 +59,7 @@ struct rcar_du_output_routing {
  * @num_crtcs: total number of CRTCs
  * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
  * @num_lvds: number of internal LVDS encoders
+ * @num_tcon: number of internal TCON encoders
  */
 struct rcar_du_device_info {
 	unsigned int gen;
@@ -67,11 +68,13 @@ struct rcar_du_device_info {
 	unsigned int num_crtcs;
 	struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
 	unsigned int num_lvds;
+	unsigned int num_tcon;
 };
 
 #define RCAR_DU_MAX_CRTCS		4
 #define RCAR_DU_MAX_GROUPS		DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
 #define RCAR_DU_MAX_LVDS		2
+#define RCAR_DU_MAX_TCON		1
 #define RCAR_DU_MAX_VSPS		4
 
 struct rcar_du_device {
@@ -99,6 +102,7 @@ struct rcar_du_device {
 	unsigned int vspd1_sink;
 
 	struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
+	struct rcar_du_tconenc *tcon[RCAR_DU_MAX_TCON];
 
 	struct {
 		wait_queue_head_t wait;
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
===================================================================
--- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -24,6 +24,7 @@
 #include "rcar_du_kms.h"
 #include "rcar_du_lvdscon.h"
 #include "rcar_du_lvdsenc.h"
+#include "rcar_du_tconenc.h"
 #include "rcar_du_vgacon.h"
 
 /* -----------------------------------------------------------------------------
@@ -48,6 +49,8 @@ static void rcar_du_encoder_disable(stru
 
 	if (renc->lvds)
 		rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
+	if (renc->tcon)
+		rcar_du_tconenc_enable(renc->tcon, encoder->crtc, false);
 }
 
 static void rcar_du_encoder_enable(struct drm_encoder *encoder)
@@ -56,6 +59,8 @@ static void rcar_du_encoder_enable(struc
 
 	if (renc->lvds)
 		rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
+	if (renc->tcon)
+		rcar_du_tconenc_enable(renc->tcon, encoder->crtc, true);
 }
 
 static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
@@ -142,6 +147,10 @@ int rcar_du_encoder_init(struct rcar_du_
 		renc->lvds = rcdu->lvds[1];
 		break;
 
+	case RCAR_DU_OUTPUT_TCON:
+		renc->tcon = rcdu->tcon[0];
+		break;
+
 	default:
 		break;
 	}
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
===================================================================
--- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -20,6 +20,7 @@
 struct rcar_du_device;
 struct rcar_du_hdmienc;
 struct rcar_du_lvdsenc;
+struct rcar_du_tconenc;
 
 enum rcar_du_encoder_type {
 	RCAR_DU_ENCODER_UNUSED = 0,
@@ -27,6 +28,7 @@ enum rcar_du_encoder_type {
 	RCAR_DU_ENCODER_VGA,
 	RCAR_DU_ENCODER_LVDS,
 	RCAR_DU_ENCODER_HDMI,
+	RCAR_DU_ENCODER_TCON,
 };
 
 struct rcar_du_encoder {
@@ -34,6 +36,7 @@ struct rcar_du_encoder {
 	enum rcar_du_output output;
 	struct rcar_du_hdmienc *hdmi;
 	struct rcar_du_lvdsenc *lvds;
+	struct rcar_du_tconenc *tcon;
 };
 
 #define to_rcar_encoder(e) \
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_kms.c
===================================================================
--- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -27,6 +27,7 @@
 #include "rcar_du_encoder.h"
 #include "rcar_du_kms.h"
 #include "rcar_du_lvdsenc.h"
+#include "rcar_du_tconenc.h"
 #include "rcar_du_regs.h"
 #include "rcar_du_vsp.h"
 
@@ -619,6 +620,9 @@ int rcar_du_modeset_init(struct rcar_du_
 	ret = rcar_du_lvdsenc_init(rcdu);
 	if (ret < 0)
 		return ret;
+	ret = rcar_du_tconenc_init(rcdu);
+	if (ret < 0)
+		return ret;
 
 	ret = rcar_du_encoders_init(rcdu);
 	if (ret < 0)
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.c
===================================================================
--- /dev/null
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.c
@@ -0,0 +1,184 @@
+/*
+ * rcar_du_tconenc.c -- R-Car Display Unit TCON Encoder
+ *
+ * Copyright (C) 2015-2016 Cogent Embedded, Inc. <source@cogentembedded.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_encoder.h"
+#include "rcar_du_tconenc.h"
+#include "rcar_tcon_regs.h"
+
+struct rcar_du_tconenc {
+	struct rcar_du_device *dev;
+
+	unsigned int index;
+	void __iomem *mmio;
+	struct clk *clock;
+	bool enabled;
+};
+
+static void rcar_tcon_write(struct rcar_du_tconenc *tcon, u32 reg, u32 data)
+{
+	iowrite32(data, tcon->mmio + reg);
+}
+
+static int rcar_du_tconenc_start(struct rcar_du_tconenc *tcon,
+				 struct rcar_du_crtc *rcrtc)
+{
+	const struct drm_display_mode *mode = &rcrtc->crtc.mode;
+	int ret;
+
+	if (tcon->enabled)
+		return 0;
+
+	ret = clk_prepare_enable(tcon->clock);
+	if (ret < 0)
+		return ret;
+
+	/* Update */
+	rcar_tcon_write(tcon, OUT_V_LATCH, OUTCNT_VEN);
+	rcar_tcon_write(tcon, TCON_V_LATCH, TCON_VEN);
+
+	/* Signals:
+	 * R-Car	Display
+	 * QCLK		SSC (Source Driver Clock Input)
+	 * QSTH		SSP (Source Scanning Signal Left/Right)
+	 * QSTB		SOE (Source Driver Output Enable)
+	 * QCPV		GOE (Gate Driver Output Enable)
+	 * QPOLA	POL (Polarity Control Signal)
+	 * QPOLB	GSC (Gate Driver Scanning Clock)
+	 * QSTVA	GSP (Gate Scanning Start Signal Up/Down)
+	 * QSTVB	nope
+	 */
+	/* Setup timings */
+	rcar_tcon_write(tcon, TCON_TIM, TCON_TIMING(50, 0));
+	/* Horizontal timings */
+	rcar_tcon_write(tcon, TCON_TIM_STH1, TCON_TIMING(mode->htotal -
+							 mode->hsync_start - 1,
+							 1));
+	rcar_tcon_write(tcon, TCON_TIM_STH2, TCON_SEL_STH_SP_HS);
+	rcar_tcon_write(tcon, TCON_TIM_STB1, TCON_TIMING(mode->htotal -
+							 mode->hsync_start +
+							 mode->hdisplay + 6,
+							 3));
+	rcar_tcon_write(tcon, TCON_TIM_STB2, TCON_SEL_STB_LP_HE);
+	rcar_tcon_write(tcon, TCON_TIM_POLB1,
+			TCON_TIMING(mode->htotal - mode->hsync_start +
+				    mode->hdisplay - 8, mode->htotal / 2));
+	rcar_tcon_write(tcon, TCON_TIM_POLB2,
+			TCON_SEL_POLB | TCON_INV | TCON_MD_NORM);
+	rcar_tcon_write(tcon, TCON_TIM_CPV1,
+			TCON_TIMING(mode->htotal - mode->hsync_start +
+				    mode->hdisplay - 33, 50));
+	rcar_tcon_write(tcon, TCON_TIM_CPV2, TCON_SEL_CPV_GCK);
+	rcar_tcon_write(tcon, TCON_TIM_POLA1,
+			TCON_TIMING(mode->htotal - mode->hsync_start +
+				    mode->hdisplay + 1, 39));
+	rcar_tcon_write(tcon, TCON_TIM_POLA2,
+			TCON_SEL_POLA | TCON_INV | TCON_MD_1X1);
+
+	/* Vertical timings */
+	rcar_tcon_write(tcon, TCON_TIM_STVA1,
+			TCON_TIMING(mode->vtotal - mode->vsync_start, 1));
+	rcar_tcon_write(tcon, TCON_TIM_STVA2, TCON_SEL_STVA_VS);
+	rcar_tcon_write(tcon, TCON_TIM_STVB1, TCON_TIMING(0, 0));
+	rcar_tcon_write(tcon, TCON_TIM_STVB2, TCON_SEL_STVB_VE);
+
+	/* Data clocked out on the falling edge of QCLK, latched in LCD on
+	 * the rising edge
+	 */
+	rcar_tcon_write(tcon, OUT_CLK_PHASE, OUTCNT_LCD_EDGE);
+
+	/* Update */
+	rcar_tcon_write(tcon, OUT_V_LATCH, OUTCNT_VEN);
+	rcar_tcon_write(tcon, TCON_V_LATCH, TCON_VEN);
+
+	tcon->enabled = true;
+
+	return 0;
+}
+
+static void rcar_du_tconenc_stop(struct rcar_du_tconenc *tcon)
+{
+	if (!tcon->enabled)
+		return;
+
+	clk_disable_unprepare(tcon->clock);
+
+	tcon->enabled = false;
+}
+
+int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon, struct drm_crtc *crtc,
+			   bool enable)
+{
+	if (!enable) {
+		rcar_du_tconenc_stop(tcon);
+		return 0;
+	} else if (crtc) {
+		struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+		return rcar_du_tconenc_start(tcon, rcrtc);
+	} else
+		return -EINVAL;
+}
+
+static int rcar_du_tconenc_get_resources(struct rcar_du_tconenc *tcon,
+					 struct platform_device *pdev)
+{
+	struct resource *mem;
+	char name[7];
+
+	sprintf(name, "tcon.%u", tcon->index);
+
+	mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+	tcon->mmio = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(tcon->mmio))
+		return PTR_ERR(tcon->mmio);
+
+	tcon->clock = devm_clk_get(&pdev->dev, name);
+	if (IS_ERR(tcon->clock)) {
+		dev_err(&pdev->dev, "failed to get clock for %s\n", name);
+		return PTR_ERR(tcon->clock);
+	}
+
+	return 0;
+}
+
+int rcar_du_tconenc_init(struct rcar_du_device *rcdu)
+{
+	struct platform_device *pdev = to_platform_device(rcdu->dev);
+	struct rcar_du_tconenc *tcon;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < rcdu->info->num_tcon; ++i) {
+		tcon = devm_kzalloc(&pdev->dev, sizeof(*tcon), GFP_KERNEL);
+		if (tcon == NULL)
+			return -ENOMEM;
+
+		tcon->dev = rcdu;
+		tcon->index = i;
+		tcon->enabled = false;
+
+		ret = rcar_du_tconenc_get_resources(tcon, pdev);
+		if (ret < 0)
+			return ret;
+
+		rcdu->tcon[i] = tcon;
+	}
+
+	return 0;
+}
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.h
===================================================================
--- /dev/null
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.h
@@ -0,0 +1,37 @@
+/*
+ * rcar_du_tconenc.h -- R-Car Display Unit TCON Encoder
+ *
+ * Copyright (C) 2015-2016 CogentEmbedded, Inc. <source@cogentembedded.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_TCONENC_H__
+#define __RCAR_DU_TCONENC_H__
+
+#include <linux/io.h>
+#include <linux/module.h>
+
+struct rcar_drm_crtc;
+struct rcar_du_tconenc;
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_TCON)
+int rcar_du_tconenc_init(struct rcar_du_device *rcdu);
+int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon,
+			   struct drm_crtc *crtc, bool enable);
+#else
+static inline int rcar_du_tconenc_init(struct rcar_du_device *rcdu)
+{
+	return 0;
+}
+static inline int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon,
+					 struct drm_crtc *crtc, bool enable)
+{
+	return 0;
+}
+#endif
+
+#endif /* __RCAR_DU_TCONENC_H__ */
Index: linux/drivers/gpu/drm/rcar-du/rcar_tcon_regs.h
===================================================================
--- /dev/null
+++ linux/drivers/gpu/drm/rcar-du/rcar_tcon_regs.h
@@ -0,0 +1,70 @@
+/*
+ * rcar_tcon_regs.h -- R-Car TCON Registers Definitions
+ *
+ * Copyright (C) 2015-2016 CogentEmbedded, Inc. <source@cogentembedded.com>
+ *
+ * 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.
+ */
+
+#ifndef __RCAR_TCON_REGS_H__
+#define __RCAR_TCON_REGS_H__
+
+#define OUT_V_LATCH		0x0000
+#define OUTCNT_VEN		(1 << 0)
+
+#define OUT_CLK_PHASE		0x0024
+#define OUTCNT_LCD_EDGE		(1 << 8)	/* If set, LCDOUT[23:0] are */
+						/* output on the falling edge */
+						/* of QCLK */
+
+#define TCON_V_LATCH		0x0280
+#define TCON_VEN		(1 << 0)
+
+#define TCON_TIM		0x0284
+
+/* Synced to VSYNC */
+#define TCON_TIM_STVA1		0x0288
+#define TCON_TIM_STVA2		0x028c
+#define TCON_TIM_STVB1		0x0290
+#define TCON_TIM_STVB2		0x0294
+
+/* Synced to HSYNC */
+#define TCON_TIM_STH1		0x0298
+#define TCON_TIM_STH2		0x029c
+#define TCON_TIM_STB1		0x02a0
+#define TCON_TIM_STB2		0x02a4
+#define TCON_TIM_CPV1		0x02a8
+#define TCON_TIM_CPV2		0x02ac
+#define TCON_TIM_POLA1		0x02b0
+#define TCON_TIM_POLA2		0x02b4
+#define TCON_TIM_POLB1		0x02b8
+#define TCON_TIM_POLB2		0x02bc
+#define TCON_TIM_DE		0x02c0
+
+/* Common definitions for all TCON_TIM_*1 registers */
+#define TCON_TIMING(start, width) (((start) << 16) | ((width) << 0))
+
+/* Common definitions for all TCON_TIM_*2 registers */
+#define TCON_INV		(1 << 4)
+/* Output signal select */
+#define TCON_SEL_STVA_VS	0
+#define TCON_SEL_STVB_VE	1
+#define TCON_SEL_STH_SP_HS	2
+#define TCON_SEL_STB_LP_HE	3
+#define TCON_SEL_CPV_GCK	4
+#define TCON_SEL_POLA		5
+#define TCON_SEL_POLB		6
+#define TCON_SEL_DE		7
+/* Definitions for HSYNC-related TIM registers */
+#define TCON_HS_SEL		(1 << 8)	/* If set, horizontal sync    */
+						/* signal reference after the */
+						/* offset */
+/* Polarity generation mode */
+#define TCON_MD_NORM		(0 << 12)
+#define TCON_MD_1X1		(1 << 12)
+#define TCON_MD_1X2		(2 << 12)
+#define TCON_MD_2X2		(3 << 12)
+
+#endif /* __RCAR_TCON_REGS_H__ */

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

* [PATCH 3/4] rcar-du: add TCON connector driver
  2016-04-28 21:00 [PATCH 0/4] Add TCON support to the R-Car DU driver Sergei Shtylyov
  2016-04-28 21:02 ` [PATCH 1/4] drm_mode: add TCON encoder/connector Sergei Shtylyov
  2016-04-28 21:03 ` [PATCH 2/4] rcar-du: add TCON encoder driver Sergei Shtylyov
@ 2016-04-28 21:04 ` Sergei Shtylyov
  2016-04-28 21:05 ` [PATCH 4/4] rcar-du: add R8A7794 TCON support Sergei Shtylyov
  3 siblings, 0 replies; 11+ messages in thread
From: Sergei Shtylyov @ 2016-04-28 21:04 UTC (permalink / raw)
  To: laurent.pinchart, airlied, dri-devel; +Cc: linux-renesas-soc

Renesas  R-Car SoCs include  the timing controller (TCON) that can directly
drive LCDs. It receives the  H/VSYNC, etc. from the Display Unit (DU)  and
converts  them to the set of signals  that a LCD panel can understand (the
RBG signals are  effectively passed thru). Add the DRM connector driver for
the LCDs connected via the TCON interface...

Based on a large patch by Andrey Gusakov.

Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

---
 drivers/gpu/drm/rcar-du/Makefile          |    1 
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c |    5 +
 drivers/gpu/drm/rcar-du/rcar_du_tconcon.c |  125 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/rcar-du/rcar_du_tconcon.h |   23 +++++
 4 files changed, 154 insertions(+)

Index: linux/drivers/gpu/drm/rcar-du/Makefile
===================================================================
--- linux.orig/drivers/gpu/drm/rcar-du/Makefile
+++ linux/drivers/gpu/drm/rcar-du/Makefile
@@ -4,6 +4,7 @@ rcar-du-drm-y := rcar_du_crtc.o \
 		 rcar_du_group.o \
 		 rcar_du_kms.o \
 		 rcar_du_lvdscon.o \
+		 rcar_du_tconcon.o \
 		 rcar_du_plane.o \
 		 rcar_du_vgacon.o
 
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
===================================================================
--- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -24,6 +24,7 @@
 #include "rcar_du_kms.h"
 #include "rcar_du_lvdscon.h"
 #include "rcar_du_lvdsenc.h"
+#include "rcar_du_tconcon.h"
 #include "rcar_du_tconenc.h"
 #include "rcar_du_vgacon.h"
 
@@ -194,6 +195,10 @@ int rcar_du_encoder_init(struct rcar_du_
 		ret = rcar_du_vga_connector_init(rcdu, renc);
 		break;
 
+	case RCAR_DU_ENCODER_TCON:
+		ret = rcar_du_tcon_connector_init(rcdu, renc, con_node);
+		break;
+
 	case DRM_MODE_ENCODER_TMDS:
 		ret = rcar_du_hdmi_connector_init(rcdu, renc);
 		break;
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_tconcon.c
===================================================================
--- /dev/null
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_tconcon.c
@@ -0,0 +1,125 @@
+/*
+ * rcar_du_tconcon.c -- R-Car Display Unit TCON Connector
+ *
+ * Copyright (C) 2015-2016 Cogent Embedded, Inc. <source@cogentembedded.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_encoder.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_tconcon.h"
+
+struct rcar_du_tcon_connector {
+	struct rcar_du_connector connector;
+
+	struct {
+		unsigned int width_mm;		/* Panel width in mm */
+		unsigned int height_mm;		/* Panel height in mm */
+		struct videomode mode;
+	} panel;
+};
+
+#define to_rcar_tcon_connector(c) \
+	container_of(c, struct rcar_du_tcon_connector, connector.connector)
+
+static int rcar_du_tcon_connector_get_modes(struct drm_connector *connector)
+{
+	struct rcar_du_tcon_connector *tconcon =
+		to_rcar_tcon_connector(connector);
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_create(connector->dev);
+	if (mode == NULL)
+		return 0;
+
+	mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+
+	drm_display_mode_from_videomode(&tconcon->panel.mode, mode);
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = rcar_du_tcon_connector_get_modes,
+	.best_encoder = rcar_du_connector_best_encoder,
+};
+
+static enum drm_connector_status
+rcar_du_tcon_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
+	.detect = rcar_du_tcon_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+int rcar_du_tcon_connector_init(struct rcar_du_device *rcdu,
+				struct rcar_du_encoder *renc,
+				/* TODO const */ struct device_node *np)
+{
+	struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
+	struct rcar_du_tcon_connector *tconcon;
+	struct drm_connector *connector;
+	struct display_timing timing;
+	int ret;
+
+	tconcon = devm_kzalloc(rcdu->dev, sizeof(*tconcon), GFP_KERNEL);
+	if (tconcon == NULL)
+		return -ENOMEM;
+
+	ret = of_get_display_timing(np, "panel-timing", &timing);
+	if (ret < 0)
+		return ret;
+
+	videomode_from_timing(&timing, &tconcon->panel.mode);
+
+	of_property_read_u32(np, "width-mm", &tconcon->panel.width_mm);
+	of_property_read_u32(np, "height-mm", &tconcon->panel.height_mm);
+
+	connector = &tconcon->connector.connector;
+	connector->display_info.width_mm = tconcon->panel.width_mm;
+	connector->display_info.height_mm = tconcon->panel.height_mm;
+
+	ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+				 DRM_MODE_CONNECTOR_TCON);
+	if (ret < 0)
+		return ret;
+
+	drm_connector_helper_add(connector, &connector_helper_funcs);
+
+	connector->dpms = DRM_MODE_DPMS_OFF;
+	drm_object_property_set_value(&connector->base,
+		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret < 0)
+		return ret;
+
+	tconcon->connector.encoder = renc;
+
+	return 0;
+}
Index: linux/drivers/gpu/drm/rcar-du/rcar_du_tconcon.h
===================================================================
--- /dev/null
+++ linux/drivers/gpu/drm/rcar-du/rcar_du_tconcon.h
@@ -0,0 +1,23 @@
+/*
+ * rcar_du_tconcon.h -- R-Car Display Unit LVDS Connector
+ *
+ * Copyright (C) 2015-2016 CogentEmbedded, Inc. <source@cogentembedded.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_TCONCON_H__
+#define __RCAR_DU_TCONCON_H__
+
+struct rcar_du_device;
+struct rcar_du_encoder;
+struct rcar_du_panel_data;
+
+int rcar_du_tcon_connector_init(struct rcar_du_device *rcdu,
+				struct rcar_du_encoder *renc,
+				struct device_node *np);
+
+#endif /* __RCAR_DU_TCONCON_H__ */

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

* [PATCH 4/4] rcar-du: add R8A7794 TCON support
  2016-04-28 21:00 [PATCH 0/4] Add TCON support to the R-Car DU driver Sergei Shtylyov
                   ` (2 preceding siblings ...)
  2016-04-28 21:04 ` [PATCH 3/4] rcar-du: add TCON connector driver Sergei Shtylyov
@ 2016-04-28 21:05 ` Sergei Shtylyov
  2016-05-27 21:33     ` Laurent Pinchart
  3 siblings, 1 reply; 11+ messages in thread
From: Sergei Shtylyov @ 2016-04-28 21:05 UTC (permalink / raw)
  To: laurent.pinchart, airlied, dri-devel; +Cc: linux-renesas-soc

Now that we have the TCON encoder driver, we can start enabling TCON support
for the  R-Car SoCs. We have only tested the code on R8A7794 so far, so  let
it  be the first supported SoC...

Based on a large patch by Andrey Gusakov.

Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

---
 drivers/gpu/drm/rcar-du/rcar_du_drv.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

Index: renesas/drivers/gpu/drm/rcar-du/rcar_du_drv.c
===================================================================
--- renesas.orig/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ renesas/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -116,9 +116,7 @@ static const struct rcar_du_device_info
 		  | RCAR_DU_FEATURE_EXT_CTRL_REGS,
 	.num_crtcs = 2,
 	.routes = {
-		/* R8A7794 has two RGB outputs and one (currently unsupported)
-		 * TCON output.
-		 */
+		/* R8A7794 has two RGB outputs and one TCON output. */
 		[RCAR_DU_OUTPUT_DPAD0] = {
 			.possible_crtcs = BIT(0),
 			.encoder_type = DRM_MODE_ENCODER_NONE,
@@ -129,8 +127,14 @@ static const struct rcar_du_device_info
 			.encoder_type = DRM_MODE_ENCODER_NONE,
 			.port = 1,
 		},
+		[RCAR_DU_OUTPUT_TCON] = {
+			.possible_crtcs = BIT(0),
+			.encoder_type = DRM_MODE_ENCODER_TCON,
+			.port = 2,
+		},
 	},
 	.num_lvds = 0,
+	.num_tcon = 1,
 };
 
 static const struct rcar_du_device_info rcar_du_r8a7795_info = {

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

* Re: [PATCH 1/4] drm_mode: add TCON encoder/connector
  2016-04-28 21:02 ` [PATCH 1/4] drm_mode: add TCON encoder/connector Sergei Shtylyov
@ 2016-04-29  9:13     ` Daniel Vetter
  0 siblings, 0 replies; 11+ messages in thread
From: Daniel Vetter @ 2016-04-29  9:13 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: airlied, dri-devel, linux-renesas-soc, laurent.pinchart

On Fri, Apr 29, 2016 at 12:02:15AM +0300, Sergei Shtylyov wrote:
> TCON (Timing  Controller) usually means  a chip that drives a LCD panel.
> In  our case, such controller  is a part of the Renesas R-Car SoCs. Add
> the TCON encoder/connector #define's  to be used by the TCON support code
> in the Renesas R-Car Display Unit (DU) driver.
> 
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
> ---
>  include/uapi/drm/drm_mode.h |    2 ++
>  1 file changed, 2 insertions(+)
> 
> Index: linux/include/uapi/drm/drm_mode.h
> ===================================================================
> --- linux.orig/include/uapi/drm/drm_mode.h
> +++ linux/include/uapi/drm/drm_mode.h
> @@ -202,6 +202,7 @@ struct drm_mode_get_plane_res {
>  #define DRM_MODE_ENCODER_VIRTUAL 5
>  #define DRM_MODE_ENCODER_DSI	6
>  #define DRM_MODE_ENCODER_DPMST	7
> +#define DRM_MODE_ENCODER_TCON	8
>  
>  struct drm_mode_get_encoder {
>  	__u32 encoder_id;
> @@ -241,6 +242,7 @@ struct drm_mode_get_encoder {
>  #define DRM_MODE_CONNECTOR_eDP		14
>  #define DRM_MODE_CONNECTOR_VIRTUAL      15
>  #define DRM_MODE_CONNECTOR_DSI		16
> +#define DRM_MODE_CONNECTOR_TCON		17
>  
>  struct drm_mode_get_connector {

The trouble with adding more here is that everytime you do this all the
userspace needs to be extended to print something reasonable for this type
of connector. For external connectors that makes sense (since users need
to know whether it's the hdmi or DP plug), but for internal panels I'd
honestly just go with lvds for anything that's not a more complex standard
like eDP or DSI.

Just my 2 cents really, not strong opinion, and we'll not run out of
connector type numbers anytime soon ;-)
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH 1/4] drm_mode: add TCON encoder/connector
@ 2016-04-29  9:13     ` Daniel Vetter
  0 siblings, 0 replies; 11+ messages in thread
From: Daniel Vetter @ 2016-04-29  9:13 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: linux-renesas-soc, laurent.pinchart, dri-devel

On Fri, Apr 29, 2016 at 12:02:15AM +0300, Sergei Shtylyov wrote:
> TCON (Timing  Controller) usually means  a chip that drives a LCD panel.
> In  our case, such controller  is a part of the Renesas R-Car SoCs. Add
> the TCON encoder/connector #define's  to be used by the TCON support code
> in the Renesas R-Car Display Unit (DU) driver.
> 
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
> ---
>  include/uapi/drm/drm_mode.h |    2 ++
>  1 file changed, 2 insertions(+)
> 
> Index: linux/include/uapi/drm/drm_mode.h
> ===================================================================
> --- linux.orig/include/uapi/drm/drm_mode.h
> +++ linux/include/uapi/drm/drm_mode.h
> @@ -202,6 +202,7 @@ struct drm_mode_get_plane_res {
>  #define DRM_MODE_ENCODER_VIRTUAL 5
>  #define DRM_MODE_ENCODER_DSI	6
>  #define DRM_MODE_ENCODER_DPMST	7
> +#define DRM_MODE_ENCODER_TCON	8
>  
>  struct drm_mode_get_encoder {
>  	__u32 encoder_id;
> @@ -241,6 +242,7 @@ struct drm_mode_get_encoder {
>  #define DRM_MODE_CONNECTOR_eDP		14
>  #define DRM_MODE_CONNECTOR_VIRTUAL      15
>  #define DRM_MODE_CONNECTOR_DSI		16
> +#define DRM_MODE_CONNECTOR_TCON		17
>  
>  struct drm_mode_get_connector {

The trouble with adding more here is that everytime you do this all the
userspace needs to be extended to print something reasonable for this type
of connector. For external connectors that makes sense (since users need
to know whether it's the hdmi or DP plug), but for internal panels I'd
honestly just go with lvds for anything that's not a more complex standard
like eDP or DSI.

Just my 2 cents really, not strong opinion, and we'll not run out of
connector type numbers anytime soon ;-)
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 2/4] rcar-du: add TCON encoder driver
  2016-04-28 21:03 ` [PATCH 2/4] rcar-du: add TCON encoder driver Sergei Shtylyov
@ 2016-05-27 21:28     ` Laurent Pinchart
  0 siblings, 0 replies; 11+ messages in thread
From: Laurent Pinchart @ 2016-05-27 21:28 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: airlied, dri-devel, linux-renesas-soc

Hi Sergei,

Thank you for the patch.

On Friday 29 Apr 2016 00:03:29 Sergei Shtylyov wrote:
> Renesas  R-Car SoCs  include the timing controller (TCON) that can directly
> drive LCDs. It receives  the H/VSYNC, etc. from the Display Unit (DU)  and
> converts  them to the set of signals  that a LCD panel can understand (the
> RBG  signals are effectively passed thru).  TCON has a set of registers
> that we need to  program based on the video mode timings, so we're adding
> a DU encoder driver doing that...
> 
> Based on a large patch by Andrey Gusakov.
> 
> Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
> ---
>  drivers/gpu/drm/rcar-du/Kconfig           |    6
>  drivers/gpu/drm/rcar-du/Makefile          |    1
>  drivers/gpu/drm/rcar-du/rcar_du_drv.h     |    4
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c |    9 +
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.h |    3
>  drivers/gpu/drm/rcar-du/rcar_du_kms.c     |    4
>  drivers/gpu/drm/rcar-du/rcar_du_tconenc.c |  184 ++++++++++++++++++++++++++
>  drivers/gpu/drm/rcar-du/rcar_du_tconenc.h |   37 ++++++
>  drivers/gpu/drm/rcar-du/rcar_tcon_regs.h  |   70 +++++++++++
>  9 files changed, 318 insertions(+)
> 
> Index: linux/drivers/gpu/drm/rcar-du/Kconfig
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/Kconfig
> +++ linux/drivers/gpu/drm/rcar-du/Kconfig
> @@ -24,6 +24,12 @@ config DRM_RCAR_LVDS
>  	help
>  	  Enable support for the R-Car Display Unit embedded LVDS encoders.
> 
> +config DRM_RCAR_TCON
> +	bool "R-Car DU TCON Encoder Support"
> +	depends on DRM_RCAR_DU
> +	help
> +	  Enable support for the R-Car Display Unit embedded TCON encoders.
> +
>  config DRM_RCAR_VSP
>  	bool "R-Car DU VSP Compositor Support"
>  	depends on DRM_RCAR_DU
> Index: linux/drivers/gpu/drm/rcar-du/Makefile
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/Makefile
> +++ linux/drivers/gpu/drm/rcar-du/Makefile
> @@ -10,6 +10,7 @@ rcar-du-drm-y := rcar_du_crtc.o \
>  rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI)	+= rcar_du_hdmicon.o \
>  					   rcar_du_hdmienc.o
>  rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)	+= rcar_du_lvdsenc.o
> +rcar-du-drm-$(CONFIG_DRM_RCAR_TCON)	+= rcar_du_tconenc.o
> 
>  rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)	+= rcar_du_vsp.o
> 
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> @@ -59,6 +59,7 @@ struct rcar_du_output_routing {
>   * @num_crtcs: total number of CRTCs
>   * @routes: array of CRTC to output routes, indexed by output
> (RCAR_DU_OUTPUT_*) * @num_lvds: number of internal LVDS encoders
> + * @num_tcon: number of internal TCON encoders
>   */
>  struct rcar_du_device_info {
>  	unsigned int gen;
> @@ -67,11 +68,13 @@ struct rcar_du_device_info {
>  	unsigned int num_crtcs;
>  	struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
>  	unsigned int num_lvds;
> +	unsigned int num_tcon;
>  };
> 
>  #define RCAR_DU_MAX_CRTCS		4
>  #define RCAR_DU_MAX_GROUPS		DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
>  #define RCAR_DU_MAX_LVDS		2
> +#define RCAR_DU_MAX_TCON		1
>  #define RCAR_DU_MAX_VSPS		4
> 
>  struct rcar_du_device {
> @@ -99,6 +102,7 @@ struct rcar_du_device {
>  	unsigned int vspd1_sink;
> 
>  	struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
> +	struct rcar_du_tconenc *tcon[RCAR_DU_MAX_TCON];
> 
>  	struct {
>  		wait_queue_head_t wait;
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> @@ -24,6 +24,7 @@
>  #include "rcar_du_kms.h"
>  #include "rcar_du_lvdscon.h"
>  #include "rcar_du_lvdsenc.h"
> +#include "rcar_du_tconenc.h"
>  #include "rcar_du_vgacon.h"
> 
>  /* ------------------------------------------------------------------------
> @@ -48,6 +49,8 @@ static void rcar_du_encoder_disable(stru
> 
>  	if (renc->lvds)
>  		rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
> +	if (renc->tcon)
> +		rcar_du_tconenc_enable(renc->tcon, encoder->crtc, false);
>  }
> 
>  static void rcar_du_encoder_enable(struct drm_encoder *encoder)
> @@ -56,6 +59,8 @@ static void rcar_du_encoder_enable(struc
> 
>  	if (renc->lvds)
>  		rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
> +	if (renc->tcon)
> +		rcar_du_tconenc_enable(renc->tcon, encoder->crtc, true);
>  }
> 
>  static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
> @@ -142,6 +147,10 @@ int rcar_du_encoder_init(struct rcar_du_
>  		renc->lvds = rcdu->lvds[1];
>  		break;
> 
> +	case RCAR_DU_OUTPUT_TCON:
> +		renc->tcon = rcdu->tcon[0];
> +		break;
> +
>  	default:
>  		break;
>  	}
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> @@ -20,6 +20,7 @@
>  struct rcar_du_device;
>  struct rcar_du_hdmienc;
>  struct rcar_du_lvdsenc;
> +struct rcar_du_tconenc;
> 
>  enum rcar_du_encoder_type {
>  	RCAR_DU_ENCODER_UNUSED = 0,
> @@ -27,6 +28,7 @@ enum rcar_du_encoder_type {
>  	RCAR_DU_ENCODER_VGA,
>  	RCAR_DU_ENCODER_LVDS,
>  	RCAR_DU_ENCODER_HDMI,
> +	RCAR_DU_ENCODER_TCON,
>  };
> 
>  struct rcar_du_encoder {
> @@ -34,6 +36,7 @@ struct rcar_du_encoder {
>  	enum rcar_du_output output;
>  	struct rcar_du_hdmienc *hdmi;
>  	struct rcar_du_lvdsenc *lvds;
> +	struct rcar_du_tconenc *tcon;
>  };
> 
>  #define to_rcar_encoder(e) \
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> @@ -27,6 +27,7 @@
>  #include "rcar_du_encoder.h"
>  #include "rcar_du_kms.h"
>  #include "rcar_du_lvdsenc.h"
> +#include "rcar_du_tconenc.h"
>  #include "rcar_du_regs.h"
>  #include "rcar_du_vsp.h"
> 
> @@ -619,6 +620,9 @@ int rcar_du_modeset_init(struct rcar_du_
>  	ret = rcar_du_lvdsenc_init(rcdu);
>  	if (ret < 0)
>  		return ret;
> +	ret = rcar_du_tconenc_init(rcdu);
> +	if (ret < 0)
> +		return ret;
> 
>  	ret = rcar_du_encoders_init(rcdu);
>  	if (ret < 0)
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.c
> ===================================================================
> --- /dev/null
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.c
> @@ -0,0 +1,184 @@
> +/*
> + * rcar_du_tconenc.c -- R-Car Display Unit TCON Encoder
> + *
> + * Copyright (C) 2015-2016 Cogent Embedded, Inc.
> <source@cogentembedded.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <drm/drm_encoder_slave.h>
> +
> +#include "rcar_du_drv.h"
> +#include "rcar_du_encoder.h"
> +#include "rcar_du_tconenc.h"
> +#include "rcar_tcon_regs.h"
> +
> +struct rcar_du_tconenc {
> +	struct rcar_du_device *dev;
> +
> +	unsigned int index;
> +	void __iomem *mmio;
> +	struct clk *clock;
> +	bool enabled;
> +};
> +
> +static void rcar_tcon_write(struct rcar_du_tconenc *tcon, u32 reg, u32
> data)
> +{
> +	iowrite32(data, tcon->mmio + reg);
> +}
> +
> +static int rcar_du_tconenc_start(struct rcar_du_tconenc *tcon,
> +				 struct rcar_du_crtc *rcrtc)
> +{
> +	const struct drm_display_mode *mode = &rcrtc->crtc.mode;
> +	int ret;
> +
> +	if (tcon->enabled)

Now that the DU driver implements the atomic API the DRM/KMS core is supposed 
to only enable/disable CRTCs and encoders when needed. Can this still happen ? 
Same comment for the stop function.

> +		return 0;
> +
> +	ret = clk_prepare_enable(tcon->clock);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Update */
> +	rcar_tcon_write(tcon, OUT_V_LATCH, OUTCNT_VEN);
> +	rcar_tcon_write(tcon, TCON_V_LATCH, TCON_VEN);

Aren't you supposed to set those bits after modifying the registers only, not 
before ?

> +	/* Signals:
> +	 * R-Car	Display
> +	 * QCLK		SSC (Source Driver Clock Input)
> +	 * QSTH		SSP (Source Scanning Signal Left/Right)
> +	 * QSTB		SOE (Source Driver Output Enable)
> +	 * QCPV		GOE (Gate Driver Output Enable)
> +	 * QPOLA	POL (Polarity Control Signal)
> +	 * QPOLB	GSC (Gate Driver Scanning Clock)
> +	 * QSTVA	GSP (Gate Scanning Start Signal Up/Down)
> +	 * QSTVB	nope
> +	 */
> +	/* Setup timings */
> +	rcar_tcon_write(tcon, TCON_TIM, TCON_TIMING(50, 0));
> +	/* Horizontal timings */
> +	rcar_tcon_write(tcon, TCON_TIM_STH1, TCON_TIMING(mode->htotal -
> +							 mode->hsync_start - 1,
> +							 1));
> +	rcar_tcon_write(tcon, TCON_TIM_STH2, TCON_SEL_STH_SP_HS);
> +	rcar_tcon_write(tcon, TCON_TIM_STB1, TCON_TIMING(mode->htotal -
> +							 mode->hsync_start +
> +							 mode->hdisplay + 6,
> +							 3));
> +	rcar_tcon_write(tcon, TCON_TIM_STB2, TCON_SEL_STB_LP_HE);
> +	rcar_tcon_write(tcon, TCON_TIM_POLB1,
> +			TCON_TIMING(mode->htotal - mode->hsync_start +
> +				    mode->hdisplay - 8, mode->htotal / 2));
> +	rcar_tcon_write(tcon, TCON_TIM_POLB2,
> +			TCON_SEL_POLB | TCON_INV | TCON_MD_NORM);
> +	rcar_tcon_write(tcon, TCON_TIM_CPV1,
> +			TCON_TIMING(mode->htotal - mode->hsync_start +
> +				    mode->hdisplay - 33, 50));
> +	rcar_tcon_write(tcon, TCON_TIM_CPV2, TCON_SEL_CPV_GCK);
> +	rcar_tcon_write(tcon, TCON_TIM_POLA1,
> +			TCON_TIMING(mode->htotal - mode->hsync_start +
> +				    mode->hdisplay + 1, 39));
> +	rcar_tcon_write(tcon, TCON_TIM_POLA2,
> +			TCON_SEL_POLA | TCON_INV | TCON_MD_1X1);
> +
> +	/* Vertical timings */
> +	rcar_tcon_write(tcon, TCON_TIM_STVA1,
> +			TCON_TIMING(mode->vtotal - mode->vsync_start, 1));
> +	rcar_tcon_write(tcon, TCON_TIM_STVA2, TCON_SEL_STVA_VS);
> +	rcar_tcon_write(tcon, TCON_TIM_STVB1, TCON_TIMING(0, 0));
> +	rcar_tcon_write(tcon, TCON_TIM_STVB2, TCON_SEL_STVB_VE);
> +
> +	/* Data clocked out on the falling edge of QCLK, latched in LCD on
> +	 * the rising edge
> +	 */
> +	rcar_tcon_write(tcon, OUT_CLK_PHASE, OUTCNT_LCD_EDGE);

I can't verify all those settings as I have no idea how TCON operates. 
However, it looks like we have lots of magic numbers, and I have a feeling 
those actually depend on the panel model. Am I wrong ?

> +	/* Update */
> +	rcar_tcon_write(tcon, OUT_V_LATCH, OUTCNT_VEN);
> +	rcar_tcon_write(tcon, TCON_V_LATCH, TCON_VEN);
> +
> +	tcon->enabled = true;
> +
> +	return 0;
> +}
> +
> +static void rcar_du_tconenc_stop(struct rcar_du_tconenc *tcon)
> +{
> +	if (!tcon->enabled)
> +		return;
> +
> +	clk_disable_unprepare(tcon->clock);
> +
> +	tcon->enabled = false;
> +}
> +
> +int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon, struct drm_crtc
> *crtc,
> +			   bool enable)
> +{
> +	if (!enable) {
> +		rcar_du_tconenc_stop(tcon);
> +		return 0;
> +	} else if (crtc) {
> +		struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
> +
> +		return rcar_du_tconenc_start(tcon, rcrtc);
> +	} else
> +		return -EINVAL;

Missing { } around the last branch. Is this needed though, can enable be true 
and crtc NULL ? If so, under which circumstances ?

> +}
> +
> +static int rcar_du_tconenc_get_resources(struct rcar_du_tconenc *tcon,
> +					 struct platform_device *pdev)
> +{
> +	struct resource *mem;
> +	char name[7];
> +
> +	sprintf(name, "tcon.%u", tcon->index);
> +
> +	mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
> +	tcon->mmio = devm_ioremap_resource(&pdev->dev, mem);
> +	if (IS_ERR(tcon->mmio))
> +		return PTR_ERR(tcon->mmio);
> +
> +	tcon->clock = devm_clk_get(&pdev->dev, name);
> +	if (IS_ERR(tcon->clock)) {
> +		dev_err(&pdev->dev, "failed to get clock for %s\n", name);
> +		return PTR_ERR(tcon->clock);
> +	}

I wasn't very proud of my very similar implementation of the LVDS encoder 
code. It's a separate IP core, it should have been modeled by a separate DT 
node. I can't really ask you to do so for TCON given that I haven't done it 
for LVDS though, but I suspect we'll pay the price at some point (for instance 
when we'll have an SoC with the DU and TCON in separate power domains). If you 
have a clever idea to enhance this, please let me know.

> +	return 0;
> +}
> +
> +int rcar_du_tconenc_init(struct rcar_du_device *rcdu)
> +{
> +	struct platform_device *pdev = to_platform_device(rcdu->dev);
> +	struct rcar_du_tconenc *tcon;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < rcdu->info->num_tcon; ++i) {
> +		tcon = devm_kzalloc(&pdev->dev, sizeof(*tcon), GFP_KERNEL);
> +		if (tcon == NULL)
> +			return -ENOMEM;
> +
> +		tcon->dev = rcdu;
> +		tcon->index = i;
> +		tcon->enabled = false;
> +
> +		ret = rcar_du_tconenc_get_resources(tcon, pdev);
> +		if (ret < 0)
> +			return ret;
> +
> +		rcdu->tcon[i] = tcon;
> +	}
> +
> +	return 0;
> +}
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.h
> ===================================================================
> --- /dev/null
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.h
> @@ -0,0 +1,37 @@
> +/*
> + * rcar_du_tconenc.h -- R-Car Display Unit TCON Encoder
> + *
> + * Copyright (C) 2015-2016 CogentEmbedded, Inc. <source@cogentembedded.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef __RCAR_DU_TCONENC_H__
> +#define __RCAR_DU_TCONENC_H__
> +
> +#include <linux/io.h>
> +#include <linux/module.h>
> +
> +struct rcar_drm_crtc;
> +struct rcar_du_tconenc;
> +
> +#if IS_ENABLED(CONFIG_DRM_RCAR_TCON)
> +int rcar_du_tconenc_init(struct rcar_du_device *rcdu);
> +int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon,
> +			   struct drm_crtc *crtc, bool enable);
> +#else
> +static inline int rcar_du_tconenc_init(struct rcar_du_device *rcdu)
> +{
> +	return 0;
> +}
> +static inline int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon,
> +					 struct drm_crtc *crtc, bool enable)
> +{
> +	return 0;
> +}
> +#endif
> +
> +#endif /* __RCAR_DU_TCONENC_H__ */
> Index: linux/drivers/gpu/drm/rcar-du/rcar_tcon_regs.h
> ===================================================================
> --- /dev/null
> +++ linux/drivers/gpu/drm/rcar-du/rcar_tcon_regs.h
> @@ -0,0 +1,70 @@
> +/*
> + * rcar_tcon_regs.h -- R-Car TCON Registers Definitions
> + *
> + * Copyright (C) 2015-2016 CogentEmbedded, Inc. <source@cogentembedded.com>
> + *
> + * 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.
> + */
> +
> +#ifndef __RCAR_TCON_REGS_H__
> +#define __RCAR_TCON_REGS_H__
> +
> +#define OUT_V_LATCH		0x0000
> +#define OUTCNT_VEN		(1 << 0)
> +
> +#define OUT_CLK_PHASE		0x0024
> +#define OUTCNT_LCD_EDGE		(1 << 8)	/* If set, LCDOUT[23:0] are */
> +						/* output on the falling edge */
> +						/* of QCLK */
> +
> +#define TCON_V_LATCH		0x0280
> +#define TCON_VEN		(1 << 0)
> +
> +#define TCON_TIM		0x0284
> +
> +/* Synced to VSYNC */
> +#define TCON_TIM_STVA1		0x0288
> +#define TCON_TIM_STVA2		0x028c
> +#define TCON_TIM_STVB1		0x0290
> +#define TCON_TIM_STVB2		0x0294
> +
> +/* Synced to HSYNC */
> +#define TCON_TIM_STH1		0x0298
> +#define TCON_TIM_STH2		0x029c
> +#define TCON_TIM_STB1		0x02a0
> +#define TCON_TIM_STB2		0x02a4
> +#define TCON_TIM_CPV1		0x02a8
> +#define TCON_TIM_CPV2		0x02ac
> +#define TCON_TIM_POLA1		0x02b0
> +#define TCON_TIM_POLA2		0x02b4
> +#define TCON_TIM_POLB1		0x02b8
> +#define TCON_TIM_POLB2		0x02bc
> +#define TCON_TIM_DE		0x02c0

For the sake of completeness, could you define the TCON_DE_INV bit ?

> +
> +/* Common definitions for all TCON_TIM_*1 registers */
> +#define TCON_TIMING(start, width) (((start) << 16) | ((width) << 0))
> +
> +/* Common definitions for all TCON_TIM_*2 registers */
> +#define TCON_INV		(1 << 4)
> +/* Output signal select */
> +#define TCON_SEL_STVA_VS	0

Could you write this as (0 << 0) (and some for the other bits below) to make 
it clearer that those are bit values ?

> +#define TCON_SEL_STVB_VE	1
> +#define TCON_SEL_STH_SP_HS	2
> +#define TCON_SEL_STB_LP_HE	3
> +#define TCON_SEL_CPV_GCK	4
> +#define TCON_SEL_POLA		5
> +#define TCON_SEL_POLB		6
> +#define TCON_SEL_DE		7

I'd add a blank line here.

> +/* Definitions for HSYNC-related TIM registers */

I'd list the registers explicitly here, or would add a short comment after 
each of them above to tell what they control.

> +#define TCON_HS_SEL		(1 << 8)	/* If set, horizontal sync    */
> +						/* signal reference after the */
> +						/* offset */

And a blank linke here too.

> +/* Polarity generation mode */

Could you mention that this is applicable to POLA2 and POLB2 only ?

> +#define TCON_MD_NORM		(0 << 12)
> +#define TCON_MD_1X1		(1 << 12)
> +#define TCON_MD_1X2		(2 << 12)
> +#define TCON_MD_2X2		(3 << 12)
> +
> +#endif /* __RCAR_TCON_REGS_H__ */

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 2/4] rcar-du: add TCON encoder driver
@ 2016-05-27 21:28     ` Laurent Pinchart
  0 siblings, 0 replies; 11+ messages in thread
From: Laurent Pinchart @ 2016-05-27 21:28 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: linux-renesas-soc, dri-devel

Hi Sergei,

Thank you for the patch.

On Friday 29 Apr 2016 00:03:29 Sergei Shtylyov wrote:
> Renesas  R-Car SoCs  include the timing controller (TCON) that can directly
> drive LCDs. It receives  the H/VSYNC, etc. from the Display Unit (DU)  and
> converts  them to the set of signals  that a LCD panel can understand (the
> RBG  signals are effectively passed thru).  TCON has a set of registers
> that we need to  program based on the video mode timings, so we're adding
> a DU encoder driver doing that...
> 
> Based on a large patch by Andrey Gusakov.
> 
> Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
> ---
>  drivers/gpu/drm/rcar-du/Kconfig           |    6
>  drivers/gpu/drm/rcar-du/Makefile          |    1
>  drivers/gpu/drm/rcar-du/rcar_du_drv.h     |    4
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c |    9 +
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.h |    3
>  drivers/gpu/drm/rcar-du/rcar_du_kms.c     |    4
>  drivers/gpu/drm/rcar-du/rcar_du_tconenc.c |  184 ++++++++++++++++++++++++++
>  drivers/gpu/drm/rcar-du/rcar_du_tconenc.h |   37 ++++++
>  drivers/gpu/drm/rcar-du/rcar_tcon_regs.h  |   70 +++++++++++
>  9 files changed, 318 insertions(+)
> 
> Index: linux/drivers/gpu/drm/rcar-du/Kconfig
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/Kconfig
> +++ linux/drivers/gpu/drm/rcar-du/Kconfig
> @@ -24,6 +24,12 @@ config DRM_RCAR_LVDS
>  	help
>  	  Enable support for the R-Car Display Unit embedded LVDS encoders.
> 
> +config DRM_RCAR_TCON
> +	bool "R-Car DU TCON Encoder Support"
> +	depends on DRM_RCAR_DU
> +	help
> +	  Enable support for the R-Car Display Unit embedded TCON encoders.
> +
>  config DRM_RCAR_VSP
>  	bool "R-Car DU VSP Compositor Support"
>  	depends on DRM_RCAR_DU
> Index: linux/drivers/gpu/drm/rcar-du/Makefile
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/Makefile
> +++ linux/drivers/gpu/drm/rcar-du/Makefile
> @@ -10,6 +10,7 @@ rcar-du-drm-y := rcar_du_crtc.o \
>  rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI)	+= rcar_du_hdmicon.o \
>  					   rcar_du_hdmienc.o
>  rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)	+= rcar_du_lvdsenc.o
> +rcar-du-drm-$(CONFIG_DRM_RCAR_TCON)	+= rcar_du_tconenc.o
> 
>  rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)	+= rcar_du_vsp.o
> 
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> @@ -59,6 +59,7 @@ struct rcar_du_output_routing {
>   * @num_crtcs: total number of CRTCs
>   * @routes: array of CRTC to output routes, indexed by output
> (RCAR_DU_OUTPUT_*) * @num_lvds: number of internal LVDS encoders
> + * @num_tcon: number of internal TCON encoders
>   */
>  struct rcar_du_device_info {
>  	unsigned int gen;
> @@ -67,11 +68,13 @@ struct rcar_du_device_info {
>  	unsigned int num_crtcs;
>  	struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
>  	unsigned int num_lvds;
> +	unsigned int num_tcon;
>  };
> 
>  #define RCAR_DU_MAX_CRTCS		4
>  #define RCAR_DU_MAX_GROUPS		DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
>  #define RCAR_DU_MAX_LVDS		2
> +#define RCAR_DU_MAX_TCON		1
>  #define RCAR_DU_MAX_VSPS		4
> 
>  struct rcar_du_device {
> @@ -99,6 +102,7 @@ struct rcar_du_device {
>  	unsigned int vspd1_sink;
> 
>  	struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
> +	struct rcar_du_tconenc *tcon[RCAR_DU_MAX_TCON];
> 
>  	struct {
>  		wait_queue_head_t wait;
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> @@ -24,6 +24,7 @@
>  #include "rcar_du_kms.h"
>  #include "rcar_du_lvdscon.h"
>  #include "rcar_du_lvdsenc.h"
> +#include "rcar_du_tconenc.h"
>  #include "rcar_du_vgacon.h"
> 
>  /* ------------------------------------------------------------------------
> @@ -48,6 +49,8 @@ static void rcar_du_encoder_disable(stru
> 
>  	if (renc->lvds)
>  		rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
> +	if (renc->tcon)
> +		rcar_du_tconenc_enable(renc->tcon, encoder->crtc, false);
>  }
> 
>  static void rcar_du_encoder_enable(struct drm_encoder *encoder)
> @@ -56,6 +59,8 @@ static void rcar_du_encoder_enable(struc
> 
>  	if (renc->lvds)
>  		rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
> +	if (renc->tcon)
> +		rcar_du_tconenc_enable(renc->tcon, encoder->crtc, true);
>  }
> 
>  static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
> @@ -142,6 +147,10 @@ int rcar_du_encoder_init(struct rcar_du_
>  		renc->lvds = rcdu->lvds[1];
>  		break;
> 
> +	case RCAR_DU_OUTPUT_TCON:
> +		renc->tcon = rcdu->tcon[0];
> +		break;
> +
>  	default:
>  		break;
>  	}
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> @@ -20,6 +20,7 @@
>  struct rcar_du_device;
>  struct rcar_du_hdmienc;
>  struct rcar_du_lvdsenc;
> +struct rcar_du_tconenc;
> 
>  enum rcar_du_encoder_type {
>  	RCAR_DU_ENCODER_UNUSED = 0,
> @@ -27,6 +28,7 @@ enum rcar_du_encoder_type {
>  	RCAR_DU_ENCODER_VGA,
>  	RCAR_DU_ENCODER_LVDS,
>  	RCAR_DU_ENCODER_HDMI,
> +	RCAR_DU_ENCODER_TCON,
>  };
> 
>  struct rcar_du_encoder {
> @@ -34,6 +36,7 @@ struct rcar_du_encoder {
>  	enum rcar_du_output output;
>  	struct rcar_du_hdmienc *hdmi;
>  	struct rcar_du_lvdsenc *lvds;
> +	struct rcar_du_tconenc *tcon;
>  };
> 
>  #define to_rcar_encoder(e) \
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> ===================================================================
> --- linux.orig/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> @@ -27,6 +27,7 @@
>  #include "rcar_du_encoder.h"
>  #include "rcar_du_kms.h"
>  #include "rcar_du_lvdsenc.h"
> +#include "rcar_du_tconenc.h"
>  #include "rcar_du_regs.h"
>  #include "rcar_du_vsp.h"
> 
> @@ -619,6 +620,9 @@ int rcar_du_modeset_init(struct rcar_du_
>  	ret = rcar_du_lvdsenc_init(rcdu);
>  	if (ret < 0)
>  		return ret;
> +	ret = rcar_du_tconenc_init(rcdu);
> +	if (ret < 0)
> +		return ret;
> 
>  	ret = rcar_du_encoders_init(rcdu);
>  	if (ret < 0)
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.c
> ===================================================================
> --- /dev/null
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.c
> @@ -0,0 +1,184 @@
> +/*
> + * rcar_du_tconenc.c -- R-Car Display Unit TCON Encoder
> + *
> + * Copyright (C) 2015-2016 Cogent Embedded, Inc.
> <source@cogentembedded.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <drm/drm_encoder_slave.h>
> +
> +#include "rcar_du_drv.h"
> +#include "rcar_du_encoder.h"
> +#include "rcar_du_tconenc.h"
> +#include "rcar_tcon_regs.h"
> +
> +struct rcar_du_tconenc {
> +	struct rcar_du_device *dev;
> +
> +	unsigned int index;
> +	void __iomem *mmio;
> +	struct clk *clock;
> +	bool enabled;
> +};
> +
> +static void rcar_tcon_write(struct rcar_du_tconenc *tcon, u32 reg, u32
> data)
> +{
> +	iowrite32(data, tcon->mmio + reg);
> +}
> +
> +static int rcar_du_tconenc_start(struct rcar_du_tconenc *tcon,
> +				 struct rcar_du_crtc *rcrtc)
> +{
> +	const struct drm_display_mode *mode = &rcrtc->crtc.mode;
> +	int ret;
> +
> +	if (tcon->enabled)

Now that the DU driver implements the atomic API the DRM/KMS core is supposed 
to only enable/disable CRTCs and encoders when needed. Can this still happen ? 
Same comment for the stop function.

> +		return 0;
> +
> +	ret = clk_prepare_enable(tcon->clock);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Update */
> +	rcar_tcon_write(tcon, OUT_V_LATCH, OUTCNT_VEN);
> +	rcar_tcon_write(tcon, TCON_V_LATCH, TCON_VEN);

Aren't you supposed to set those bits after modifying the registers only, not 
before ?

> +	/* Signals:
> +	 * R-Car	Display
> +	 * QCLK		SSC (Source Driver Clock Input)
> +	 * QSTH		SSP (Source Scanning Signal Left/Right)
> +	 * QSTB		SOE (Source Driver Output Enable)
> +	 * QCPV		GOE (Gate Driver Output Enable)
> +	 * QPOLA	POL (Polarity Control Signal)
> +	 * QPOLB	GSC (Gate Driver Scanning Clock)
> +	 * QSTVA	GSP (Gate Scanning Start Signal Up/Down)
> +	 * QSTVB	nope
> +	 */
> +	/* Setup timings */
> +	rcar_tcon_write(tcon, TCON_TIM, TCON_TIMING(50, 0));
> +	/* Horizontal timings */
> +	rcar_tcon_write(tcon, TCON_TIM_STH1, TCON_TIMING(mode->htotal -
> +							 mode->hsync_start - 1,
> +							 1));
> +	rcar_tcon_write(tcon, TCON_TIM_STH2, TCON_SEL_STH_SP_HS);
> +	rcar_tcon_write(tcon, TCON_TIM_STB1, TCON_TIMING(mode->htotal -
> +							 mode->hsync_start +
> +							 mode->hdisplay + 6,
> +							 3));
> +	rcar_tcon_write(tcon, TCON_TIM_STB2, TCON_SEL_STB_LP_HE);
> +	rcar_tcon_write(tcon, TCON_TIM_POLB1,
> +			TCON_TIMING(mode->htotal - mode->hsync_start +
> +				    mode->hdisplay - 8, mode->htotal / 2));
> +	rcar_tcon_write(tcon, TCON_TIM_POLB2,
> +			TCON_SEL_POLB | TCON_INV | TCON_MD_NORM);
> +	rcar_tcon_write(tcon, TCON_TIM_CPV1,
> +			TCON_TIMING(mode->htotal - mode->hsync_start +
> +				    mode->hdisplay - 33, 50));
> +	rcar_tcon_write(tcon, TCON_TIM_CPV2, TCON_SEL_CPV_GCK);
> +	rcar_tcon_write(tcon, TCON_TIM_POLA1,
> +			TCON_TIMING(mode->htotal - mode->hsync_start +
> +				    mode->hdisplay + 1, 39));
> +	rcar_tcon_write(tcon, TCON_TIM_POLA2,
> +			TCON_SEL_POLA | TCON_INV | TCON_MD_1X1);
> +
> +	/* Vertical timings */
> +	rcar_tcon_write(tcon, TCON_TIM_STVA1,
> +			TCON_TIMING(mode->vtotal - mode->vsync_start, 1));
> +	rcar_tcon_write(tcon, TCON_TIM_STVA2, TCON_SEL_STVA_VS);
> +	rcar_tcon_write(tcon, TCON_TIM_STVB1, TCON_TIMING(0, 0));
> +	rcar_tcon_write(tcon, TCON_TIM_STVB2, TCON_SEL_STVB_VE);
> +
> +	/* Data clocked out on the falling edge of QCLK, latched in LCD on
> +	 * the rising edge
> +	 */
> +	rcar_tcon_write(tcon, OUT_CLK_PHASE, OUTCNT_LCD_EDGE);

I can't verify all those settings as I have no idea how TCON operates. 
However, it looks like we have lots of magic numbers, and I have a feeling 
those actually depend on the panel model. Am I wrong ?

> +	/* Update */
> +	rcar_tcon_write(tcon, OUT_V_LATCH, OUTCNT_VEN);
> +	rcar_tcon_write(tcon, TCON_V_LATCH, TCON_VEN);
> +
> +	tcon->enabled = true;
> +
> +	return 0;
> +}
> +
> +static void rcar_du_tconenc_stop(struct rcar_du_tconenc *tcon)
> +{
> +	if (!tcon->enabled)
> +		return;
> +
> +	clk_disable_unprepare(tcon->clock);
> +
> +	tcon->enabled = false;
> +}
> +
> +int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon, struct drm_crtc
> *crtc,
> +			   bool enable)
> +{
> +	if (!enable) {
> +		rcar_du_tconenc_stop(tcon);
> +		return 0;
> +	} else if (crtc) {
> +		struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
> +
> +		return rcar_du_tconenc_start(tcon, rcrtc);
> +	} else
> +		return -EINVAL;

Missing { } around the last branch. Is this needed though, can enable be true 
and crtc NULL ? If so, under which circumstances ?

> +}
> +
> +static int rcar_du_tconenc_get_resources(struct rcar_du_tconenc *tcon,
> +					 struct platform_device *pdev)
> +{
> +	struct resource *mem;
> +	char name[7];
> +
> +	sprintf(name, "tcon.%u", tcon->index);
> +
> +	mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
> +	tcon->mmio = devm_ioremap_resource(&pdev->dev, mem);
> +	if (IS_ERR(tcon->mmio))
> +		return PTR_ERR(tcon->mmio);
> +
> +	tcon->clock = devm_clk_get(&pdev->dev, name);
> +	if (IS_ERR(tcon->clock)) {
> +		dev_err(&pdev->dev, "failed to get clock for %s\n", name);
> +		return PTR_ERR(tcon->clock);
> +	}

I wasn't very proud of my very similar implementation of the LVDS encoder 
code. It's a separate IP core, it should have been modeled by a separate DT 
node. I can't really ask you to do so for TCON given that I haven't done it 
for LVDS though, but I suspect we'll pay the price at some point (for instance 
when we'll have an SoC with the DU and TCON in separate power domains). If you 
have a clever idea to enhance this, please let me know.

> +	return 0;
> +}
> +
> +int rcar_du_tconenc_init(struct rcar_du_device *rcdu)
> +{
> +	struct platform_device *pdev = to_platform_device(rcdu->dev);
> +	struct rcar_du_tconenc *tcon;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < rcdu->info->num_tcon; ++i) {
> +		tcon = devm_kzalloc(&pdev->dev, sizeof(*tcon), GFP_KERNEL);
> +		if (tcon == NULL)
> +			return -ENOMEM;
> +
> +		tcon->dev = rcdu;
> +		tcon->index = i;
> +		tcon->enabled = false;
> +
> +		ret = rcar_du_tconenc_get_resources(tcon, pdev);
> +		if (ret < 0)
> +			return ret;
> +
> +		rcdu->tcon[i] = tcon;
> +	}
> +
> +	return 0;
> +}
> Index: linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.h
> ===================================================================
> --- /dev/null
> +++ linux/drivers/gpu/drm/rcar-du/rcar_du_tconenc.h
> @@ -0,0 +1,37 @@
> +/*
> + * rcar_du_tconenc.h -- R-Car Display Unit TCON Encoder
> + *
> + * Copyright (C) 2015-2016 CogentEmbedded, Inc. <source@cogentembedded.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef __RCAR_DU_TCONENC_H__
> +#define __RCAR_DU_TCONENC_H__
> +
> +#include <linux/io.h>
> +#include <linux/module.h>
> +
> +struct rcar_drm_crtc;
> +struct rcar_du_tconenc;
> +
> +#if IS_ENABLED(CONFIG_DRM_RCAR_TCON)
> +int rcar_du_tconenc_init(struct rcar_du_device *rcdu);
> +int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon,
> +			   struct drm_crtc *crtc, bool enable);
> +#else
> +static inline int rcar_du_tconenc_init(struct rcar_du_device *rcdu)
> +{
> +	return 0;
> +}
> +static inline int rcar_du_tconenc_enable(struct rcar_du_tconenc *tcon,
> +					 struct drm_crtc *crtc, bool enable)
> +{
> +	return 0;
> +}
> +#endif
> +
> +#endif /* __RCAR_DU_TCONENC_H__ */
> Index: linux/drivers/gpu/drm/rcar-du/rcar_tcon_regs.h
> ===================================================================
> --- /dev/null
> +++ linux/drivers/gpu/drm/rcar-du/rcar_tcon_regs.h
> @@ -0,0 +1,70 @@
> +/*
> + * rcar_tcon_regs.h -- R-Car TCON Registers Definitions
> + *
> + * Copyright (C) 2015-2016 CogentEmbedded, Inc. <source@cogentembedded.com>
> + *
> + * 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.
> + */
> +
> +#ifndef __RCAR_TCON_REGS_H__
> +#define __RCAR_TCON_REGS_H__
> +
> +#define OUT_V_LATCH		0x0000
> +#define OUTCNT_VEN		(1 << 0)
> +
> +#define OUT_CLK_PHASE		0x0024
> +#define OUTCNT_LCD_EDGE		(1 << 8)	/* If set, LCDOUT[23:0] are */
> +						/* output on the falling edge */
> +						/* of QCLK */
> +
> +#define TCON_V_LATCH		0x0280
> +#define TCON_VEN		(1 << 0)
> +
> +#define TCON_TIM		0x0284
> +
> +/* Synced to VSYNC */
> +#define TCON_TIM_STVA1		0x0288
> +#define TCON_TIM_STVA2		0x028c
> +#define TCON_TIM_STVB1		0x0290
> +#define TCON_TIM_STVB2		0x0294
> +
> +/* Synced to HSYNC */
> +#define TCON_TIM_STH1		0x0298
> +#define TCON_TIM_STH2		0x029c
> +#define TCON_TIM_STB1		0x02a0
> +#define TCON_TIM_STB2		0x02a4
> +#define TCON_TIM_CPV1		0x02a8
> +#define TCON_TIM_CPV2		0x02ac
> +#define TCON_TIM_POLA1		0x02b0
> +#define TCON_TIM_POLA2		0x02b4
> +#define TCON_TIM_POLB1		0x02b8
> +#define TCON_TIM_POLB2		0x02bc
> +#define TCON_TIM_DE		0x02c0

For the sake of completeness, could you define the TCON_DE_INV bit ?

> +
> +/* Common definitions for all TCON_TIM_*1 registers */
> +#define TCON_TIMING(start, width) (((start) << 16) | ((width) << 0))
> +
> +/* Common definitions for all TCON_TIM_*2 registers */
> +#define TCON_INV		(1 << 4)
> +/* Output signal select */
> +#define TCON_SEL_STVA_VS	0

Could you write this as (0 << 0) (and some for the other bits below) to make 
it clearer that those are bit values ?

> +#define TCON_SEL_STVB_VE	1
> +#define TCON_SEL_STH_SP_HS	2
> +#define TCON_SEL_STB_LP_HE	3
> +#define TCON_SEL_CPV_GCK	4
> +#define TCON_SEL_POLA		5
> +#define TCON_SEL_POLB		6
> +#define TCON_SEL_DE		7

I'd add a blank line here.

> +/* Definitions for HSYNC-related TIM registers */

I'd list the registers explicitly here, or would add a short comment after 
each of them above to tell what they control.

> +#define TCON_HS_SEL		(1 << 8)	/* If set, horizontal sync    */
> +						/* signal reference after the */
> +						/* offset */

And a blank linke here too.

> +/* Polarity generation mode */

Could you mention that this is applicable to POLA2 and POLB2 only ?

> +#define TCON_MD_NORM		(0 << 12)
> +#define TCON_MD_1X1		(1 << 12)
> +#define TCON_MD_1X2		(2 << 12)
> +#define TCON_MD_2X2		(3 << 12)
> +
> +#endif /* __RCAR_TCON_REGS_H__ */

-- 
Regards,

Laurent Pinchart

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

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

* Re: [PATCH 4/4] rcar-du: add R8A7794 TCON support
  2016-04-28 21:05 ` [PATCH 4/4] rcar-du: add R8A7794 TCON support Sergei Shtylyov
@ 2016-05-27 21:33     ` Laurent Pinchart
  0 siblings, 0 replies; 11+ messages in thread
From: Laurent Pinchart @ 2016-05-27 21:33 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: airlied, dri-devel, linux-renesas-soc

Hi Sergei,

Thank you for the patch.

On Friday 29 Apr 2016 00:05:33 Sergei Shtylyov wrote:
> Now that we have the TCON encoder driver, we can start enabling TCON support
> for the  R-Car SoCs. We have only tested the code on R8A7794 so far, so 
> let it  be the first supported SoC...

Please also update the DT bindings.

> Based on a large patch by Andrey Gusakov.
> 
> Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
> ---
>  drivers/gpu/drm/rcar-du/rcar_du_drv.c |   10 +++++++---
>  1 file changed, 7 insertions(+), 3 deletions(-)
> 
> Index: renesas/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> ===================================================================
> --- renesas.orig/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> +++ renesas/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> @@ -116,9 +116,7 @@ static const struct rcar_du_device_info
> 
>  		  | RCAR_DU_FEATURE_EXT_CTRL_REGS,
> 
>  	.num_crtcs = 2,
>  	.routes = {
> -		/* R8A7794 has two RGB outputs and one (currently unsupported)
> -		 * TCON output.
> -		 */
> +		/* R8A7794 has two RGB outputs and one TCON output. */
>  		[RCAR_DU_OUTPUT_DPAD0] = {
>  			.possible_crtcs = BIT(0),
>  			.encoder_type = DRM_MODE_ENCODER_NONE,
> @@ -129,8 +127,14 @@ static const struct rcar_du_device_info
>  			.encoder_type = DRM_MODE_ENCODER_NONE,
>  			.port = 1,
>  		},
> +		[RCAR_DU_OUTPUT_TCON] = {
> +			.possible_crtcs = BIT(0),

According to the datasheet TCON can be connected to both DU0 and DU1.

> +			.encoder_type = DRM_MODE_ENCODER_TCON,
> +			.port = 2,
> +		},
>  	},
>  	.num_lvds = 0,
> +	.num_tcon = 1,
>  };
> 
>  static const struct rcar_du_device_info rcar_du_r8a7795_info = {

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 4/4] rcar-du: add R8A7794 TCON support
@ 2016-05-27 21:33     ` Laurent Pinchart
  0 siblings, 0 replies; 11+ messages in thread
From: Laurent Pinchart @ 2016-05-27 21:33 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: linux-renesas-soc, dri-devel

Hi Sergei,

Thank you for the patch.

On Friday 29 Apr 2016 00:05:33 Sergei Shtylyov wrote:
> Now that we have the TCON encoder driver, we can start enabling TCON support
> for the  R-Car SoCs. We have only tested the code on R8A7794 so far, so 
> let it  be the first supported SoC...

Please also update the DT bindings.

> Based on a large patch by Andrey Gusakov.
> 
> Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
> ---
>  drivers/gpu/drm/rcar-du/rcar_du_drv.c |   10 +++++++---
>  1 file changed, 7 insertions(+), 3 deletions(-)
> 
> Index: renesas/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> ===================================================================
> --- renesas.orig/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> +++ renesas/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> @@ -116,9 +116,7 @@ static const struct rcar_du_device_info
> 
>  		  | RCAR_DU_FEATURE_EXT_CTRL_REGS,
> 
>  	.num_crtcs = 2,
>  	.routes = {
> -		/* R8A7794 has two RGB outputs and one (currently unsupported)
> -		 * TCON output.
> -		 */
> +		/* R8A7794 has two RGB outputs and one TCON output. */
>  		[RCAR_DU_OUTPUT_DPAD0] = {
>  			.possible_crtcs = BIT(0),
>  			.encoder_type = DRM_MODE_ENCODER_NONE,
> @@ -129,8 +127,14 @@ static const struct rcar_du_device_info
>  			.encoder_type = DRM_MODE_ENCODER_NONE,
>  			.port = 1,
>  		},
> +		[RCAR_DU_OUTPUT_TCON] = {
> +			.possible_crtcs = BIT(0),

According to the datasheet TCON can be connected to both DU0 and DU1.

> +			.encoder_type = DRM_MODE_ENCODER_TCON,
> +			.port = 2,
> +		},
>  	},
>  	.num_lvds = 0,
> +	.num_tcon = 1,
>  };
> 
>  static const struct rcar_du_device_info rcar_du_r8a7795_info = {

-- 
Regards,

Laurent Pinchart

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

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

end of thread, other threads:[~2016-05-27 21:33 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-28 21:00 [PATCH 0/4] Add TCON support to the R-Car DU driver Sergei Shtylyov
2016-04-28 21:02 ` [PATCH 1/4] drm_mode: add TCON encoder/connector Sergei Shtylyov
2016-04-29  9:13   ` Daniel Vetter
2016-04-29  9:13     ` Daniel Vetter
2016-04-28 21:03 ` [PATCH 2/4] rcar-du: add TCON encoder driver Sergei Shtylyov
2016-05-27 21:28   ` Laurent Pinchart
2016-05-27 21:28     ` Laurent Pinchart
2016-04-28 21:04 ` [PATCH 3/4] rcar-du: add TCON connector driver Sergei Shtylyov
2016-04-28 21:05 ` [PATCH 4/4] rcar-du: add R8A7794 TCON support Sergei Shtylyov
2016-05-27 21:33   ` Laurent Pinchart
2016-05-27 21:33     ` Laurent Pinchart

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.