All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] Add TV Encoder support for ZTE DRM driver
@ 2017-01-26 15:20 Shawn Guo
  2017-01-26 15:20 ` [PATCH v2 1/5] drm: zte: add interlace mode support Shawn Guo
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Shawn Guo @ 2017-01-26 15:20 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Baoyou Xie, Jun Nie

From: Shawn Guo <shawn.guo@linaro.org>

The series updates zx_vou driver a bit as the preparation of adding
TVENC output device, and then adds the device driver.

Changes for v2:
 - Save the use of variable 'vactive' by checking interlaced case.
 - Rename mask variable for scan register to avoid naming scope
   confusion.
 - Write a proper comment to explain how vback_porch of the first
   and second field are related.
 - Embed struct drm_display_mode in zx_tvenc_mode to save function
   zx_tvenc_mode_to_drm_mode().
 - Use drm_mode_equal() to find the matching mode.
 - Use drm_mode_set_name() to name mode.
 - Fix typos in clock multiplier comment.
 - Add a sanity check in .mode_valid hook to ensure the given mode is
   valid.
 - Remove destroy() calls from .unbind hook, as those are already taken
   care of by drm_mode_config_cleanup().

Shawn Guo (5):
  drm: zte: add interlace mode support
  drm: zte: move struct vou_inf into zx_vou driver
  drm: zte: add function to configure vou_ctrl dividers
  dt: add bindings for ZTE tvenc device
  drm: zte: add tvenc driver support

 .../devicetree/bindings/display/zte,vou.txt        |  15 +
 drivers/gpu/drm/zte/Makefile                       |   1 +
 drivers/gpu/drm/zte/zx_drm_drv.c                   |   1 +
 drivers/gpu/drm/zte/zx_drm_drv.h                   |   1 +
 drivers/gpu/drm/zte/zx_hdmi.c                      |  12 +-
 drivers/gpu/drm/zte/zx_tvenc.c                     | 407 +++++++++++++++++++++
 drivers/gpu/drm/zte/zx_tvenc_regs.h                |  31 ++
 drivers/gpu/drm/zte/zx_vou.c                       | 167 ++++++++-
 drivers/gpu/drm/zte/zx_vou.h                       |  32 +-
 drivers/gpu/drm/zte/zx_vou_regs.h                  |  31 ++
 10 files changed, 672 insertions(+), 26 deletions(-)
 create mode 100644 drivers/gpu/drm/zte/zx_tvenc.c
 create mode 100644 drivers/gpu/drm/zte/zx_tvenc_regs.h

-- 
1.9.1

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

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

* [PATCH v2 1/5] drm: zte: add interlace mode support
  2017-01-26 15:20 [PATCH v2 0/5] Add TV Encoder support for ZTE DRM driver Shawn Guo
@ 2017-01-26 15:20 ` Shawn Guo
  2017-01-26 18:37   ` Sean Paul
  2017-01-26 15:20 ` [PATCH v2 2/5] drm: zte: move struct vou_inf into zx_vou driver Shawn Guo
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 8+ messages in thread
From: Shawn Guo @ 2017-01-26 15:20 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Baoyou Xie, Jun Nie

From: Shawn Guo <shawn.guo@linaro.org>

It adds interlace mode support in VOU TIMING_CTRL and channel control
block, so that VOU driver gets ready to support output device in
interlace mode like TV Encoder.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
Changes for v2:
 - Save the use of variable 'vactive' by checking interlaced case.
 - Rename mask variable for scan register to avoid naming scope
   confusion.
 - Write a proper comment to explain how vback_porch of the first
   and second field are related.

 drivers/gpu/drm/zte/zx_vou.c      | 52 +++++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/zte/zx_vou_regs.h | 15 +++++++++++
 2 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index 3056b41df518..fe066e0f3df4 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -40,6 +40,7 @@ struct zx_crtc_regs {
 	u32 fir_active;
 	u32 fir_htiming;
 	u32 fir_vtiming;
+	u32 sec_vtiming;
 	u32 timing_shift;
 	u32 timing_pi_shift;
 };
@@ -48,6 +49,7 @@ struct zx_crtc_regs {
 	.fir_active = FIR_MAIN_ACTIVE,
 	.fir_htiming = FIR_MAIN_H_TIMING,
 	.fir_vtiming = FIR_MAIN_V_TIMING,
+	.sec_vtiming = SEC_MAIN_V_TIMING,
 	.timing_shift = TIMING_MAIN_SHIFT,
 	.timing_pi_shift = TIMING_MAIN_PI_SHIFT,
 };
@@ -56,6 +58,7 @@ struct zx_crtc_regs {
 	.fir_active = FIR_AUX_ACTIVE,
 	.fir_htiming = FIR_AUX_H_TIMING,
 	.fir_vtiming = FIR_AUX_V_TIMING,
+	.sec_vtiming = SEC_AUX_V_TIMING,
 	.timing_shift = TIMING_AUX_SHIFT,
 	.timing_pi_shift = TIMING_AUX_PI_SHIFT,
 };
@@ -65,6 +68,10 @@ struct zx_crtc_bits {
 	u32 polarity_shift;
 	u32 int_frame_mask;
 	u32 tc_enable;
+	u32 sec_vactive_shift;
+	u32 sec_vactive_mask;
+	u32 interlace_select;
+	u32 pi_enable;
 };
 
 static const struct zx_crtc_bits main_crtc_bits = {
@@ -72,6 +79,10 @@ struct zx_crtc_bits {
 	.polarity_shift = MAIN_POL_SHIFT,
 	.int_frame_mask = TIMING_INT_MAIN_FRAME,
 	.tc_enable = MAIN_TC_EN,
+	.sec_vactive_shift = SEC_VACT_MAIN_SHIFT,
+	.sec_vactive_mask = SEC_VACT_MAIN_MASK,
+	.interlace_select = MAIN_INTERLACE_SEL,
+	.pi_enable = MAIN_PI_EN,
 };
 
 static const struct zx_crtc_bits aux_crtc_bits = {
@@ -79,6 +90,10 @@ struct zx_crtc_bits {
 	.polarity_shift = AUX_POL_SHIFT,
 	.int_frame_mask = TIMING_INT_AUX_FRAME,
 	.tc_enable = AUX_TC_EN,
+	.sec_vactive_shift = SEC_VACT_AUX_SHIFT,
+	.sec_vactive_mask = SEC_VACT_AUX_MASK,
+	.interlace_select = AUX_INTERLACE_SEL,
+	.pi_enable = AUX_PI_EN,
 };
 
 struct zx_crtc {
@@ -196,11 +211,13 @@ static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
 static void zx_crtc_enable(struct drm_crtc *crtc)
 {
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
 	struct zx_vou_hw *vou = zcrtc->vou;
 	const struct zx_crtc_regs *regs = zcrtc->regs;
 	const struct zx_crtc_bits *bits = zcrtc->bits;
 	struct videomode vm;
+	u32 scan_mask;
 	u32 pol = 0;
 	u32 val;
 	int ret;
@@ -208,7 +225,7 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
 	drm_display_mode_to_videomode(mode, &vm);
 
 	/* Set up timing parameters */
-	val = V_ACTIVE(vm.vactive - 1);
+	val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1);
 	val |= H_ACTIVE(vm.hactive - 1);
 	zx_writel(vou->timing + regs->fir_active, val);
 
@@ -222,6 +239,25 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
 	val |= FRONT_PORCH(vm.vfront_porch - 1);
 	zx_writel(vou->timing + regs->fir_vtiming, val);
 
+	if (interlaced) {
+		u32 shift = bits->sec_vactive_shift;
+		u32 mask = bits->sec_vactive_mask;
+
+		val = zx_readl(vou->timing + SEC_V_ACTIVE);
+		val &= ~mask;
+		val |= ((vm.vactive / 2 - 1) << shift) & mask;
+		zx_writel(vou->timing + SEC_V_ACTIVE, val);
+
+		val = SYNC_WIDE(vm.vsync_len - 1);
+		/*
+		 * The vback_porch for the second field needs to shift one on
+		 * the value for the first field.
+		 */
+		val |= BACK_PORCH(vm.vback_porch);
+		val |= FRONT_PORCH(vm.vfront_porch - 1);
+		zx_writel(vou->timing + regs->sec_vtiming, val);
+	}
+
 	/* Set up polarities */
 	if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW)
 		pol |= 1 << POL_VSYNC_SHIFT;
@@ -232,9 +268,17 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
 		       pol << bits->polarity_shift);
 
 	/* Setup SHIFT register by following what ZTE BSP does */
-	zx_writel(vou->timing + regs->timing_shift, H_SHIFT_VAL);
+	val = H_SHIFT_VAL;
+	if (interlaced)
+		val |= V_SHIFT_VAL << 16;
+	zx_writel(vou->timing + regs->timing_shift, val);
 	zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL);
 
+	/* Progressive or interlace scan select */
+	scan_mask = bits->interlace_select | bits->pi_enable;
+	zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask,
+		       interlaced ? scan_mask : 0);
+
 	/* Enable TIMING_CTRL */
 	zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable,
 		       bits->tc_enable);
@@ -245,6 +289,10 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
 	zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK,
 		       vm.vactive << CHN_SCREEN_H_SHIFT);
 
+	/* Configure channel interlace buffer control */
+	zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN,
+		       interlaced ? CHN_INTERLACE_EN : 0);
+
 	/* Update channel */
 	vou_chn_set_update(zcrtc);
 
diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
index 193c1ce01fe7..e6ed844e068a 100644
--- a/drivers/gpu/drm/zte/zx_vou_regs.h
+++ b/drivers/gpu/drm/zte/zx_vou_regs.h
@@ -75,6 +75,8 @@
 #define CHN_SCREEN_H_SHIFT		5
 #define CHN_SCREEN_H_MASK		(0x1fff << CHN_SCREEN_H_SHIFT)
 #define CHN_UPDATE			0x08
+#define CHN_INTERLACE_BUF_CTRL		0x24
+#define CHN_INTERLACE_EN		BIT(2)
 
 /* TIMING_CTRL registers */
 #define TIMING_TC_ENABLE		0x04
@@ -117,6 +119,19 @@
 #define TIMING_MAIN_SHIFT		0x2c
 #define TIMING_AUX_SHIFT		0x30
 #define H_SHIFT_VAL			0x0048
+#define V_SHIFT_VAL			0x0001
+#define SCAN_CTRL			0x34
+#define AUX_PI_EN			BIT(19)
+#define MAIN_PI_EN			BIT(18)
+#define AUX_INTERLACE_SEL		BIT(1)
+#define MAIN_INTERLACE_SEL		BIT(0)
+#define SEC_V_ACTIVE			0x38
+#define SEC_VACT_MAIN_SHIFT		0
+#define SEC_VACT_MAIN_MASK		(0xffff << SEC_VACT_MAIN_SHIFT)
+#define SEC_VACT_AUX_SHIFT		16
+#define SEC_VACT_AUX_MASK		(0xffff << SEC_VACT_AUX_SHIFT)
+#define SEC_MAIN_V_TIMING		0x3c
+#define SEC_AUX_V_TIMING		0x40
 #define TIMING_MAIN_PI_SHIFT		0x68
 #define TIMING_AUX_PI_SHIFT		0x6c
 #define H_PI_SHIFT_VAL			0x000f
-- 
1.9.1

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

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

* [PATCH v2 2/5] drm: zte: move struct vou_inf into zx_vou driver
  2017-01-26 15:20 [PATCH v2 0/5] Add TV Encoder support for ZTE DRM driver Shawn Guo
  2017-01-26 15:20 ` [PATCH v2 1/5] drm: zte: add interlace mode support Shawn Guo
@ 2017-01-26 15:20 ` Shawn Guo
  2017-01-26 15:20 ` [PATCH v2 3/5] drm: zte: add function to configure vou_ctrl dividers Shawn Guo
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Shawn Guo @ 2017-01-26 15:20 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Baoyou Xie, Jun Nie

From: Shawn Guo <shawn.guo@linaro.org>

Although data in struct vou_inf is defined per output device, it doesn't
belong to the device itself but VOU control module.  All these data can
just be defined in VOU driver, and output device driver only needs to
invoke VOU driver function with device ID to enable/disable specific
output device.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/zte/zx_hdmi.c | 12 ++----------
 drivers/gpu/drm/zte/zx_vou.c  | 31 ++++++++++++++++++++++++-------
 drivers/gpu/drm/zte/zx_vou.h  | 11 ++---------
 3 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/zte/zx_hdmi.c b/drivers/gpu/drm/zte/zx_hdmi.c
index 6bf6c364811e..2f1e278ab50e 100644
--- a/drivers/gpu/drm/zte/zx_hdmi.c
+++ b/drivers/gpu/drm/zte/zx_hdmi.c
@@ -53,13 +53,6 @@ struct zx_hdmi {
 
 #define to_zx_hdmi(x) container_of(x, struct zx_hdmi, x)
 
-static const struct vou_inf vou_inf_hdmi = {
-	.id = VOU_HDMI,
-	.data_sel = VOU_YUV444,
-	.clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
-	.clocks_sel_bits = BIT(13) | BIT(2),
-};
-
 static inline u8 hdmi_readb(struct zx_hdmi *hdmi, u16 offset)
 {
 	return readl_relaxed(hdmi->mmio + offset * 4);
@@ -238,14 +231,14 @@ static void zx_hdmi_encoder_enable(struct drm_encoder *encoder)
 
 	zx_hdmi_hw_enable(hdmi);
 
-	vou_inf_enable(hdmi->inf, encoder->crtc);
+	vou_inf_enable(VOU_HDMI, encoder->crtc);
 }
 
 static void zx_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
 	struct zx_hdmi *hdmi = to_zx_hdmi(encoder);
 
-	vou_inf_disable(hdmi->inf, encoder->crtc);
+	vou_inf_disable(VOU_HDMI, encoder->crtc);
 
 	zx_hdmi_hw_disable(hdmi);
 
@@ -523,7 +516,6 @@ static int zx_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	hdmi->dev = dev;
 	hdmi->drm = drm;
-	hdmi->inf = &vou_inf_hdmi;
 
 	dev_set_drvdata(dev, hdmi);
 
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index fe066e0f3df4..a82ba294c7e1 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -158,6 +158,21 @@ struct zx_vou_hw {
 	struct zx_crtc *aux_crtc;
 };
 
+struct vou_inf {
+	enum vou_inf_id id;
+	enum vou_inf_data_sel data_sel;
+	u32 clocks_en_bits;
+	u32 clocks_sel_bits;
+};
+
+static struct vou_inf vou_infs[] = {
+	[VOU_HDMI] = {
+		.data_sel = VOU_YUV444,
+		.clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
+		.clocks_sel_bits = BIT(13) | BIT(2),
+	},
+};
+
 static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
 {
 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
@@ -165,20 +180,21 @@ static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
 	return zcrtc->vou;
 }
 
-void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc)
+void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc)
 {
 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
 	struct zx_vou_hw *vou = zcrtc->vou;
+	struct vou_inf *inf = &vou_infs[id];
 	bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
-	u32 data_sel_shift = inf->id << 1;
+	u32 data_sel_shift = id << 1;
 
 	/* Select data format */
 	zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
 		       inf->data_sel << data_sel_shift);
 
 	/* Select channel */
-	zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << inf->id,
-		       zcrtc->chn_type << inf->id);
+	zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << id,
+		       zcrtc->chn_type << id);
 
 	/* Select interface clocks */
 	zx_writel_mask(vou->vouctl + VOU_CLK_SEL, inf->clocks_sel_bits,
@@ -189,15 +205,16 @@ void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc)
 		       inf->clocks_en_bits);
 
 	/* Enable the device */
-	zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << inf->id, 1 << inf->id);
+	zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 1 << id);
 }
 
-void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc)
+void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc)
 {
 	struct zx_vou_hw *vou = crtc_to_vou(crtc);
+	struct vou_inf *inf = &vou_infs[id];
 
 	/* Disable the device */
-	zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << inf->id, 0);
+	zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 0);
 
 	/* Disable interface clocks */
 	zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 0);
diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h
index 4b4339be641b..a41a0ad49857 100644
--- a/drivers/gpu/drm/zte/zx_vou.h
+++ b/drivers/gpu/drm/zte/zx_vou.h
@@ -30,15 +30,8 @@ enum vou_inf_data_sel {
 	VOU_RGB_666	= 3,
 };
 
-struct vou_inf {
-	enum vou_inf_id id;
-	enum vou_inf_data_sel data_sel;
-	u32 clocks_en_bits;
-	u32 clocks_sel_bits;
-};
-
-void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc);
-void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc);
+void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc);
+void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc);
 
 int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe);
 void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe);
-- 
1.9.1

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

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

* [PATCH v2 3/5] drm: zte: add function to configure vou_ctrl dividers
  2017-01-26 15:20 [PATCH v2 0/5] Add TV Encoder support for ZTE DRM driver Shawn Guo
  2017-01-26 15:20 ` [PATCH v2 1/5] drm: zte: add interlace mode support Shawn Guo
  2017-01-26 15:20 ` [PATCH v2 2/5] drm: zte: move struct vou_inf into zx_vou driver Shawn Guo
@ 2017-01-26 15:20 ` Shawn Guo
  2017-01-26 15:20 ` [PATCH v2 4/5] dt: add bindings for ZTE tvenc device Shawn Guo
  2017-01-26 15:20 ` [PATCH v2 5/5] drm: zte: add tvenc driver support Shawn Guo
  4 siblings, 0 replies; 8+ messages in thread
From: Shawn Guo @ 2017-01-26 15:20 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Baoyou Xie, Jun Nie

From: Shawn Guo <shawn.guo@linaro.org>

The clock control module (CRM) cannot always provide desired frequency
for all VOU output devices.  That's why VOU integrates a few dividers
to further divide the clocks from CRM.  Let's add an interface for
configuring these dividers.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
---
 drivers/gpu/drm/zte/zx_vou.c      | 79 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/zte/zx_vou.h      | 25 +++++++++++++
 drivers/gpu/drm/zte/zx_vou_regs.h | 16 ++++++++
 3 files changed, 120 insertions(+)

diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index a82ba294c7e1..823b74ea758a 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -72,6 +72,13 @@ struct zx_crtc_bits {
 	u32 sec_vactive_mask;
 	u32 interlace_select;
 	u32 pi_enable;
+	u32 div_vga_shift;
+	u32 div_pic_shift;
+	u32 div_tvenc_shift;
+	u32 div_hdmi_pnx_shift;
+	u32 div_hdmi_shift;
+	u32 div_inf_shift;
+	u32 div_layer_shift;
 };
 
 static const struct zx_crtc_bits main_crtc_bits = {
@@ -83,6 +90,13 @@ struct zx_crtc_bits {
 	.sec_vactive_mask = SEC_VACT_MAIN_MASK,
 	.interlace_select = MAIN_INTERLACE_SEL,
 	.pi_enable = MAIN_PI_EN,
+	.div_vga_shift = VGA_MAIN_DIV_SHIFT,
+	.div_pic_shift = PIC_MAIN_DIV_SHIFT,
+	.div_tvenc_shift = TVENC_MAIN_DIV_SHIFT,
+	.div_hdmi_pnx_shift = HDMI_MAIN_PNX_DIV_SHIFT,
+	.div_hdmi_shift = HDMI_MAIN_DIV_SHIFT,
+	.div_inf_shift = INF_MAIN_DIV_SHIFT,
+	.div_layer_shift = LAYER_MAIN_DIV_SHIFT,
 };
 
 static const struct zx_crtc_bits aux_crtc_bits = {
@@ -94,6 +108,13 @@ struct zx_crtc_bits {
 	.sec_vactive_mask = SEC_VACT_AUX_MASK,
 	.interlace_select = AUX_INTERLACE_SEL,
 	.pi_enable = AUX_PI_EN,
+	.div_vga_shift = VGA_AUX_DIV_SHIFT,
+	.div_pic_shift = PIC_AUX_DIV_SHIFT,
+	.div_tvenc_shift = TVENC_AUX_DIV_SHIFT,
+	.div_hdmi_pnx_shift = HDMI_AUX_PNX_DIV_SHIFT,
+	.div_hdmi_shift = HDMI_AUX_DIV_SHIFT,
+	.div_inf_shift = INF_AUX_DIV_SHIFT,
+	.div_layer_shift = LAYER_AUX_DIV_SHIFT,
 };
 
 struct zx_crtc {
@@ -220,6 +241,64 @@ void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc)
 	zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 0);
 }
 
+void zx_vou_config_dividers(struct drm_crtc *crtc,
+			    struct vou_div_config *configs, int num)
+{
+	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
+	struct zx_vou_hw *vou = zcrtc->vou;
+	const struct zx_crtc_bits *bits = zcrtc->bits;
+	int i;
+
+	/* Clear update flag bit */
+	zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, 0);
+
+	for (i = 0; i < num; i++) {
+		struct vou_div_config *cfg = configs + i;
+		u32 reg, shift;
+
+		switch (cfg->id) {
+		case VOU_DIV_VGA:
+			reg = VOU_CLK_SEL;
+			shift = bits->div_vga_shift;
+			break;
+		case VOU_DIV_PIC:
+			reg = VOU_CLK_SEL;
+			shift = bits->div_pic_shift;
+			break;
+		case VOU_DIV_TVENC:
+			reg = VOU_DIV_PARA;
+			shift = bits->div_tvenc_shift;
+			break;
+		case VOU_DIV_HDMI_PNX:
+			reg = VOU_DIV_PARA;
+			shift = bits->div_hdmi_pnx_shift;
+			break;
+		case VOU_DIV_HDMI:
+			reg = VOU_DIV_PARA;
+			shift = bits->div_hdmi_shift;
+			break;
+		case VOU_DIV_INF:
+			reg = VOU_DIV_PARA;
+			shift = bits->div_inf_shift;
+			break;
+		case VOU_DIV_LAYER:
+			reg = VOU_DIV_PARA;
+			shift = bits->div_layer_shift;
+			break;
+		default:
+			continue;
+		}
+
+		/* Each divider occupies 3 bits */
+		zx_writel_mask(vou->vouctl + reg, 0x7 << shift,
+			       cfg->val << shift);
+	}
+
+	/* Set update flag bit to get dividers effected */
+	zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE,
+		       DIV_PARA_UPDATE);
+}
+
 static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
 {
 	zx_writel(zcrtc->chnreg + CHN_UPDATE, 1);
diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h
index a41a0ad49857..0dae4faefac4 100644
--- a/drivers/gpu/drm/zte/zx_vou.h
+++ b/drivers/gpu/drm/zte/zx_vou.h
@@ -33,6 +33,31 @@ enum vou_inf_data_sel {
 void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc);
 void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc);
 
+enum vou_div_id {
+	VOU_DIV_VGA,
+	VOU_DIV_PIC,
+	VOU_DIV_TVENC,
+	VOU_DIV_HDMI_PNX,
+	VOU_DIV_HDMI,
+	VOU_DIV_INF,
+	VOU_DIV_LAYER,
+};
+
+enum vou_div_val {
+	VOU_DIV_1 = 0,
+	VOU_DIV_2 = 1,
+	VOU_DIV_4 = 3,
+	VOU_DIV_8 = 7,
+};
+
+struct vou_div_config {
+	enum vou_div_id id;
+	enum vou_div_val val;
+};
+
+void zx_vou_config_dividers(struct drm_crtc *crtc,
+			    struct vou_div_config *configs, int num);
+
 int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe);
 void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe);
 
diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
index e6ed844e068a..552772137cf0 100644
--- a/drivers/gpu/drm/zte/zx_vou_regs.h
+++ b/drivers/gpu/drm/zte/zx_vou_regs.h
@@ -176,11 +176,27 @@
 #define VOU_INF_DATA_SEL		0x08
 #define VOU_SOFT_RST			0x14
 #define VOU_CLK_SEL			0x18
+#define VGA_AUX_DIV_SHIFT		29
+#define VGA_MAIN_DIV_SHIFT		26
+#define PIC_MAIN_DIV_SHIFT		23
+#define PIC_AUX_DIV_SHIFT		20
 #define VOU_CLK_VL2_SEL			BIT(8)
 #define VOU_CLK_VL1_SEL			BIT(7)
 #define VOU_CLK_VL0_SEL			BIT(6)
 #define VOU_CLK_GL1_SEL			BIT(5)
 #define VOU_CLK_GL0_SEL			BIT(4)
+#define VOU_DIV_PARA			0x1c
+#define DIV_PARA_UPDATE			BIT(31)
+#define TVENC_AUX_DIV_SHIFT		28
+#define HDMI_AUX_PNX_DIV_SHIFT		25
+#define HDMI_MAIN_PNX_DIV_SHIFT		22
+#define HDMI_AUX_DIV_SHIFT		19
+#define HDMI_MAIN_DIV_SHIFT		16
+#define TVENC_MAIN_DIV_SHIFT		13
+#define INF_AUX_DIV_SHIFT		9
+#define INF_MAIN_DIV_SHIFT		6
+#define LAYER_AUX_DIV_SHIFT		3
+#define LAYER_MAIN_DIV_SHIFT		0
 #define VOU_CLK_REQEN			0x20
 #define VOU_CLK_EN			0x24
 
-- 
1.9.1

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

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

* [PATCH v2 4/5] dt: add bindings for ZTE tvenc device
  2017-01-26 15:20 [PATCH v2 0/5] Add TV Encoder support for ZTE DRM driver Shawn Guo
                   ` (2 preceding siblings ...)
  2017-01-26 15:20 ` [PATCH v2 3/5] drm: zte: add function to configure vou_ctrl dividers Shawn Guo
@ 2017-01-26 15:20 ` Shawn Guo
  2017-01-26 15:20 ` [PATCH v2 5/5] drm: zte: add tvenc driver support Shawn Guo
  4 siblings, 0 replies; 8+ messages in thread
From: Shawn Guo @ 2017-01-26 15:20 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Baoyou Xie, Jun Nie

From: Shawn Guo <shawn.guo@linaro.org>

It adds bindings doc for ZTE VOU TV Encoder device.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Acked-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/display/zte,vou.txt | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/zte,vou.txt b/Documentation/devicetree/bindings/display/zte,vou.txt
index 740e5bd2e4f7..9c356284232b 100644
--- a/Documentation/devicetree/bindings/display/zte,vou.txt
+++ b/Documentation/devicetree/bindings/display/zte,vou.txt
@@ -49,6 +49,15 @@ Required properties:
 	"osc_clk"
 	"xclk"
 
+* TV Encoder output device
+
+Required properties:
+ - compatible: should be "zte,zx296718-tvenc"
+ - reg: Physical base address and length of the TVENC device IO region
+ - zte,tvenc-power-control: the phandle to SYSCTRL block followed by two
+   integer cells.  The first cell is the offset of SYSCTRL register used
+   to control TV Encoder DAC power, and the second cell is the bit mask.
+
 Example:
 
 vou: vou@1440000 {
@@ -81,4 +90,10 @@ vou: vou@1440000 {
 			 <&topcrm HDMI_XCLK>;
 		clock-names = "osc_cec", "osc_clk", "xclk";
 	};
+
+	tvenc: tvenc@2000 {
+		compatible = "zte,zx296718-tvenc";
+		reg = <0x2000 0x1000>;
+		zte,tvenc-power-control = <&sysctrl 0x170 0x10>;
+	};
 };
-- 
1.9.1

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

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

* [PATCH v2 5/5] drm: zte: add tvenc driver support
  2017-01-26 15:20 [PATCH v2 0/5] Add TV Encoder support for ZTE DRM driver Shawn Guo
                   ` (3 preceding siblings ...)
  2017-01-26 15:20 ` [PATCH v2 4/5] dt: add bindings for ZTE tvenc device Shawn Guo
@ 2017-01-26 15:20 ` Shawn Guo
  2017-01-26 18:43   ` Sean Paul
  4 siblings, 1 reply; 8+ messages in thread
From: Shawn Guo @ 2017-01-26 15:20 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Baoyou Xie, Jun Nie

From: Shawn Guo <shawn.guo@linaro.org>

It adds the TV Encoder driver to support video output in PAL and NTSC
format.  The driver uses syscon/regmap interface to configure register
bit sitting in SYSCTRL module for DAC power control.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
Changes for v2:
 - Embed struct drm_display_mode in zx_tvenc_mode to save function
   zx_tvenc_mode_to_drm_mode().
 - Use drm_mode_equal() to find the matching mode.
 - Use drm_mode_set_name() to name mode.
 - Fix typos in clock multiplier comment.
 - Add a sanity check in .mode_valid hook to ensure the given mode is
   valid.
 - Remove destroy() calls from .unbind hook, as those are already taken
   care of by drm_mode_config_cleanup().

 drivers/gpu/drm/zte/Makefile        |   1 +
 drivers/gpu/drm/zte/zx_drm_drv.c    |   1 +
 drivers/gpu/drm/zte/zx_drm_drv.h    |   1 +
 drivers/gpu/drm/zte/zx_tvenc.c      | 407 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/zte/zx_tvenc_regs.h |  31 +++
 drivers/gpu/drm/zte/zx_vou.c        |   5 +
 6 files changed, 446 insertions(+)
 create mode 100644 drivers/gpu/drm/zte/zx_tvenc.c
 create mode 100644 drivers/gpu/drm/zte/zx_tvenc_regs.h

diff --git a/drivers/gpu/drm/zte/Makefile b/drivers/gpu/drm/zte/Makefile
index 699180bfd57c..01352b56c418 100644
--- a/drivers/gpu/drm/zte/Makefile
+++ b/drivers/gpu/drm/zte/Makefile
@@ -2,6 +2,7 @@ zxdrm-y := \
 	zx_drm_drv.o \
 	zx_hdmi.o \
 	zx_plane.o \
+	zx_tvenc.o \
 	zx_vou.o
 
 obj-$(CONFIG_DRM_ZTE) += zxdrm.o
diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
index 3e76f72c92ff..13081fed902d 100644
--- a/drivers/gpu/drm/zte/zx_drm_drv.c
+++ b/drivers/gpu/drm/zte/zx_drm_drv.c
@@ -247,6 +247,7 @@ static int zx_drm_remove(struct platform_device *pdev)
 static struct platform_driver *drivers[] = {
 	&zx_crtc_driver,
 	&zx_hdmi_driver,
+	&zx_tvenc_driver,
 	&zx_drm_platform_driver,
 };
 
diff --git a/drivers/gpu/drm/zte/zx_drm_drv.h b/drivers/gpu/drm/zte/zx_drm_drv.h
index e65cd18a6cba..5ca035b079c7 100644
--- a/drivers/gpu/drm/zte/zx_drm_drv.h
+++ b/drivers/gpu/drm/zte/zx_drm_drv.h
@@ -13,6 +13,7 @@
 
 extern struct platform_driver zx_crtc_driver;
 extern struct platform_driver zx_hdmi_driver;
+extern struct platform_driver zx_tvenc_driver;
 
 static inline u32 zx_readl(void __iomem *reg)
 {
diff --git a/drivers/gpu/drm/zte/zx_tvenc.c b/drivers/gpu/drm/zte/zx_tvenc.c
new file mode 100644
index 000000000000..b56dc69843fc
--- /dev/null
+++ b/drivers/gpu/drm/zte/zx_tvenc.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2017 Linaro Ltd.
+ * Copyright 2017 ZTE Corporation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drmP.h>
+
+#include "zx_drm_drv.h"
+#include "zx_tvenc_regs.h"
+#include "zx_vou.h"
+
+struct zx_tvenc_pwrctrl {
+	struct regmap *regmap;
+	u32 reg;
+	u32 mask;
+};
+
+struct zx_tvenc {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct device *dev;
+	void __iomem *mmio;
+	const struct vou_inf *inf;
+	struct zx_tvenc_pwrctrl pwrctrl;
+};
+
+#define to_zx_tvenc(x) container_of(x, struct zx_tvenc, x)
+
+struct zx_tvenc_mode {
+	struct drm_display_mode mode;
+	u32 video_info;
+	u32 video_res;
+	u32 field1_param;
+	u32 field2_param;
+	u32 burst_line_odd1;
+	u32 burst_line_even1;
+	u32 burst_line_odd2;
+	u32 burst_line_even2;
+	u32 line_timing_param;
+	u32 weight_value;
+	u32 blank_black_level;
+	u32 burst_level;
+	u32 control_param;
+	u32 sub_carrier_phase1;
+	u32 phase_line_incr_cvbs;
+};
+
+/*
+ * The CRM cannot directly provide a suitable frequency, and we have to
+ * ask a multiplied rate from CRM and use the divider in VOU to get the
+ * desired one.
+ */
+#define TVENC_CLOCK_MULTIPLIER	4
+
+static const struct zx_tvenc_mode tvenc_mode_pal = {
+	.mode = {
+		.clock = 13500 * TVENC_CLOCK_MULTIPLIER,
+		.hdisplay = 720,
+		.hsync_start = 720 + 12,
+		.hsync_end = 720 + 12 + 2,
+		.htotal = 720 + 12 + 2 + 130,
+		.vdisplay = 576,
+		.vsync_start = 576 + 2,
+		.vsync_end = 576 + 2 + 2,
+		.vtotal = 576 + 2 + 2 + 20,
+		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+			 DRM_MODE_FLAG_INTERLACE,
+	},
+	.video_info = 0x00040040,
+	.video_res = 0x05a9c760,
+	.field1_param = 0x0004d416,
+	.field2_param = 0x0009b94f,
+	.burst_line_odd1 = 0x0004d406,
+	.burst_line_even1 = 0x0009b53e,
+	.burst_line_odd2 = 0x0004d805,
+	.burst_line_even2 = 0x0009b93f,
+	.line_timing_param = 0x06a96fdf,
+	.weight_value = 0x00c188a0,
+	.blank_black_level = 0x0000fcfc,
+	.burst_level = 0x00001595,
+	.control_param = 0x00000001,
+	.sub_carrier_phase1 = 0x1504c566,
+	.phase_line_incr_cvbs = 0xc068db8c,
+};
+
+static const struct zx_tvenc_mode tvenc_mode_ntsc = {
+	.mode = {
+		.clock = 13500 * TVENC_CLOCK_MULTIPLIER,
+		.hdisplay = 720,
+		.hsync_start = 720 + 16,
+		.hsync_end = 720 + 16 + 2,
+		.htotal = 720 + 16 + 2 + 120,
+		.vdisplay = 480,
+		.vsync_start = 480 + 3,
+		.vsync_end = 480 + 3 + 2,
+		.vtotal = 480 + 3 + 2 + 17,
+		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+			 DRM_MODE_FLAG_INTERLACE,
+	},
+	.video_info = 0x00040080,
+	.video_res = 0x05a8375a,
+	.field1_param = 0x00041817,
+	.field2_param = 0x0008351e,
+	.burst_line_odd1 = 0x00041006,
+	.burst_line_even1 = 0x0008290d,
+	.burst_line_odd2 = 0x00000000,
+	.burst_line_even2 = 0x00000000,
+	.line_timing_param = 0x06a8ef9e,
+	.weight_value = 0x00b68197,
+	.blank_black_level = 0x0000f0f0,
+	.burst_level = 0x0000009c,
+	.control_param = 0x00000001,
+	.sub_carrier_phase1 = 0x10f83e10,
+	.phase_line_incr_cvbs = 0x80000000,
+};
+
+static const struct zx_tvenc_mode *tvenc_modes[] = {
+	&tvenc_mode_pal,
+	&tvenc_mode_ntsc,
+};
+
+static const struct zx_tvenc_mode *
+zx_tvenc_find_zmode(struct drm_display_mode *mode)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) {
+		const struct zx_tvenc_mode *zmode = tvenc_modes[i];
+
+		if (drm_mode_equal(mode, &zmode->mode))
+			return zmode;
+	}
+
+	return NULL;
+}
+
+static void zx_tvenc_encoder_mode_set(struct drm_encoder *encoder,
+				      struct drm_display_mode *mode,
+				      struct drm_display_mode *adj_mode)
+{
+	struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
+	const struct zx_tvenc_mode *zmode;
+	struct vou_div_config configs[] = {
+		{ VOU_DIV_INF,   VOU_DIV_4 },
+		{ VOU_DIV_TVENC, VOU_DIV_1 },
+		{ VOU_DIV_LAYER, VOU_DIV_2 },
+	};
+
+	zx_vou_config_dividers(encoder->crtc, configs, ARRAY_SIZE(configs));
+
+	zmode = zx_tvenc_find_zmode(mode);
+	if (!zmode) {
+		DRM_DEV_ERROR(tvenc->dev, "failed to find zmode\n");
+		return;
+	}
+
+	zx_writel(tvenc->mmio + VENC_VIDEO_INFO, zmode->video_info);
+	zx_writel(tvenc->mmio + VENC_VIDEO_RES, zmode->video_res);
+	zx_writel(tvenc->mmio + VENC_FIELD1_PARAM, zmode->field1_param);
+	zx_writel(tvenc->mmio + VENC_FIELD2_PARAM, zmode->field2_param);
+	zx_writel(tvenc->mmio + VENC_LINE_O_1, zmode->burst_line_odd1);
+	zx_writel(tvenc->mmio + VENC_LINE_E_1, zmode->burst_line_even1);
+	zx_writel(tvenc->mmio + VENC_LINE_O_2, zmode->burst_line_odd2);
+	zx_writel(tvenc->mmio + VENC_LINE_E_2, zmode->burst_line_even2);
+	zx_writel(tvenc->mmio + VENC_LINE_TIMING_PARAM,
+		  zmode->line_timing_param);
+	zx_writel(tvenc->mmio + VENC_WEIGHT_VALUE, zmode->weight_value);
+	zx_writel(tvenc->mmio + VENC_BLANK_BLACK_LEVEL,
+		  zmode->blank_black_level);
+	zx_writel(tvenc->mmio + VENC_BURST_LEVEL, zmode->burst_level);
+	zx_writel(tvenc->mmio + VENC_CONTROL_PARAM, zmode->control_param);
+	zx_writel(tvenc->mmio + VENC_SUB_CARRIER_PHASE1,
+		  zmode->sub_carrier_phase1);
+	zx_writel(tvenc->mmio + VENC_PHASE_LINE_INCR_CVBS,
+		  zmode->phase_line_incr_cvbs);
+}
+
+static void zx_tvenc_encoder_enable(struct drm_encoder *encoder)
+{
+	struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
+	struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
+
+	/* Set bit to power up TVENC DAC */
+	regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask,
+			   pwrctrl->mask);
+
+	vou_inf_enable(VOU_TV_ENC, encoder->crtc);
+
+	zx_writel(tvenc->mmio + VENC_ENABLE, 1);
+}
+
+static void zx_tvenc_encoder_disable(struct drm_encoder *encoder)
+{
+	struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
+	struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
+
+	zx_writel(tvenc->mmio + VENC_ENABLE, 0);
+
+	vou_inf_disable(VOU_TV_ENC, encoder->crtc);
+
+	/* Clear bit to power down TVENC DAC */
+	regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0);
+}
+
+static const struct drm_encoder_helper_funcs zx_tvenc_encoder_helper_funcs = {
+	.enable	= zx_tvenc_encoder_enable,
+	.disable = zx_tvenc_encoder_disable,
+	.mode_set = zx_tvenc_encoder_mode_set,
+};
+
+static const struct drm_encoder_funcs zx_tvenc_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int zx_tvenc_connector_get_modes(struct drm_connector *connector)
+{
+	struct zx_tvenc *tvenc = to_zx_tvenc(connector);
+	struct device *dev = tvenc->dev;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) {
+		const struct zx_tvenc_mode *zmode = tvenc_modes[i];
+		struct drm_display_mode *mode;
+
+		mode = drm_mode_duplicate(connector->dev, &zmode->mode);
+		if (!mode) {
+			DRM_DEV_ERROR(dev, "failed to duplicate drm mode\n");
+			continue;
+		}
+
+		drm_mode_set_name(mode);
+		drm_mode_probed_add(connector, mode);
+	}
+
+	return i;
+}
+
+static enum drm_mode_status
+zx_tvenc_connector_mode_valid(struct drm_connector *connector,
+			      struct drm_display_mode *mode)
+{
+	struct zx_tvenc *tvenc = to_zx_tvenc(connector);
+	const struct zx_tvenc_mode *zmode;
+
+	zmode = zx_tvenc_find_zmode(mode);
+	if (!zmode) {
+		DRM_DEV_ERROR(tvenc->dev, "unsupported mode: %s\n", mode->name);
+		return MODE_NOMODE;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs zx_tvenc_connector_helper_funcs = {
+	.get_modes = zx_tvenc_connector_get_modes,
+	.mode_valid = zx_tvenc_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs zx_tvenc_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int zx_tvenc_register(struct drm_device *drm, struct zx_tvenc *tvenc)
+{
+	struct drm_encoder *encoder = &tvenc->encoder;
+	struct drm_connector *connector = &tvenc->connector;
+
+	/*
+	 * The tvenc is designed to use aux channel, as there is a deflicker
+	 * block for the channel.
+	 */
+	encoder->possible_crtcs = BIT(1);
+
+	drm_encoder_init(drm, encoder, &zx_tvenc_encoder_funcs,
+			 DRM_MODE_ENCODER_TVDAC, NULL);
+	drm_encoder_helper_add(encoder, &zx_tvenc_encoder_helper_funcs);
+
+	connector->interlace_allowed = true;
+
+	drm_connector_init(drm, connector, &zx_tvenc_connector_funcs,
+			   DRM_MODE_CONNECTOR_Composite);
+	drm_connector_helper_add(connector, &zx_tvenc_connector_helper_funcs);
+
+	drm_mode_connector_attach_encoder(connector, encoder);
+
+	return 0;
+}
+
+static int zx_tvenc_pwrctrl_init(struct zx_tvenc *tvenc)
+{
+	struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
+	struct device *dev = tvenc->dev;
+	struct of_phandle_args out_args;
+	struct regmap *regmap;
+	int ret;
+
+	ret = of_parse_phandle_with_fixed_args(dev->of_node,
+				"zte,tvenc-power-control", 2, 0, &out_args);
+	if (ret)
+		return ret;
+
+	regmap = syscon_node_to_regmap(out_args.np);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		goto out;
+	}
+
+	pwrctrl->regmap = regmap;
+	pwrctrl->reg = out_args.args[0];
+	pwrctrl->mask = out_args.args[1];
+
+out:
+	of_node_put(out_args.np);
+	return ret;
+}
+
+static int zx_tvenc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct resource *res;
+	struct zx_tvenc *tvenc;
+	int ret;
+
+	tvenc = devm_kzalloc(dev, sizeof(*tvenc), GFP_KERNEL);
+	if (!tvenc)
+		return -ENOMEM;
+
+	tvenc->dev = dev;
+	dev_set_drvdata(dev, tvenc);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	tvenc->mmio = devm_ioremap_resource(dev, res);
+	if (IS_ERR(tvenc->mmio)) {
+		ret = PTR_ERR(tvenc->mmio);
+		DRM_DEV_ERROR(dev, "failed to remap tvenc region: %d\n", ret);
+		return ret;
+	}
+
+	ret = zx_tvenc_pwrctrl_init(tvenc);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret);
+		return ret;
+	}
+
+	ret = zx_tvenc_register(drm, tvenc);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to register tvenc: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void zx_tvenc_unbind(struct device *dev, struct device *master,
+			    void *data)
+{
+	/* Nothing to do */
+}
+
+static const struct component_ops zx_tvenc_component_ops = {
+	.bind = zx_tvenc_bind,
+	.unbind = zx_tvenc_unbind,
+};
+
+static int zx_tvenc_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &zx_tvenc_component_ops);
+}
+
+static int zx_tvenc_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &zx_tvenc_component_ops);
+	return 0;
+}
+
+static const struct of_device_id zx_tvenc_of_match[] = {
+	{ .compatible = "zte,zx296718-tvenc", },
+	{ /* end */ },
+};
+MODULE_DEVICE_TABLE(of, zx_tvenc_of_match);
+
+struct platform_driver zx_tvenc_driver = {
+	.probe = zx_tvenc_probe,
+	.remove = zx_tvenc_remove,
+	.driver	= {
+		.name = "zx-tvenc",
+		.of_match_table	= zx_tvenc_of_match,
+	},
+};
diff --git a/drivers/gpu/drm/zte/zx_tvenc_regs.h b/drivers/gpu/drm/zte/zx_tvenc_regs.h
new file mode 100644
index 000000000000..bd91f5dcc1f3
--- /dev/null
+++ b/drivers/gpu/drm/zte/zx_tvenc_regs.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Linaro Ltd.
+ * Copyright 2017 ZTE Corporation.
+ *
+ * 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 __ZX_TVENC_REGS_H__
+#define __ZX_TVENC_REGS_H__
+
+#define VENC_VIDEO_INFO			0x04
+#define VENC_VIDEO_RES			0x08
+#define VENC_FIELD1_PARAM		0x10
+#define VENC_FIELD2_PARAM		0x14
+#define VENC_LINE_O_1			0x18
+#define VENC_LINE_E_1			0x1c
+#define VENC_LINE_O_2			0x20
+#define VENC_LINE_E_2			0x24
+#define VENC_LINE_TIMING_PARAM		0x28
+#define VENC_WEIGHT_VALUE		0x2c
+#define VENC_BLANK_BLACK_LEVEL		0x30
+#define VENC_BURST_LEVEL		0x34
+#define VENC_CONTROL_PARAM		0x3c
+#define VENC_SUB_CARRIER_PHASE1		0x40
+#define VENC_PHASE_LINE_INCR_CVBS	0x48
+#define VENC_ENABLE			0xa8
+
+#endif /* __ZX_TVENC_REGS_H__ */
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index 823b74ea758a..71aaf8f2b071 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -192,6 +192,11 @@ struct vou_inf {
 		.clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
 		.clocks_sel_bits = BIT(13) | BIT(2),
 	},
+	[VOU_TV_ENC] = {
+		.data_sel = VOU_YUV444,
+		.clocks_en_bits = BIT(15),
+		.clocks_sel_bits = BIT(11) | BIT(0),
+	},
 };
 
 static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
-- 
1.9.1

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

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

* Re: [PATCH v2 1/5] drm: zte: add interlace mode support
  2017-01-26 15:20 ` [PATCH v2 1/5] drm: zte: add interlace mode support Shawn Guo
@ 2017-01-26 18:37   ` Sean Paul
  0 siblings, 0 replies; 8+ messages in thread
From: Sean Paul @ 2017-01-26 18:37 UTC (permalink / raw)
  To: Shawn Guo; +Cc: Daniel Vetter, Jun Nie, Baoyou Xie, dri-devel

On Thu, Jan 26, 2017 at 11:20:49PM +0800, Shawn Guo wrote:
> From: Shawn Guo <shawn.guo@linaro.org>
> 
> It adds interlace mode support in VOU TIMING_CTRL and channel control
> block, so that VOU driver gets ready to support output device in
> interlace mode like TV Encoder.
> 

Reviewed-by: Sean Paul <seanpaul@chromium.org>

> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
> Changes for v2:
>  - Save the use of variable 'vactive' by checking interlaced case.
>  - Rename mask variable for scan register to avoid naming scope
>    confusion.
>  - Write a proper comment to explain how vback_porch of the first
>    and second field are related.
> 
>  drivers/gpu/drm/zte/zx_vou.c      | 52 +++++++++++++++++++++++++++++++++++++--
>  drivers/gpu/drm/zte/zx_vou_regs.h | 15 +++++++++++
>  2 files changed, 65 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
> index 3056b41df518..fe066e0f3df4 100644
> --- a/drivers/gpu/drm/zte/zx_vou.c
> +++ b/drivers/gpu/drm/zte/zx_vou.c
> @@ -40,6 +40,7 @@ struct zx_crtc_regs {
>  	u32 fir_active;
>  	u32 fir_htiming;
>  	u32 fir_vtiming;
> +	u32 sec_vtiming;
>  	u32 timing_shift;
>  	u32 timing_pi_shift;
>  };
> @@ -48,6 +49,7 @@ struct zx_crtc_regs {
>  	.fir_active = FIR_MAIN_ACTIVE,
>  	.fir_htiming = FIR_MAIN_H_TIMING,
>  	.fir_vtiming = FIR_MAIN_V_TIMING,
> +	.sec_vtiming = SEC_MAIN_V_TIMING,
>  	.timing_shift = TIMING_MAIN_SHIFT,
>  	.timing_pi_shift = TIMING_MAIN_PI_SHIFT,
>  };
> @@ -56,6 +58,7 @@ struct zx_crtc_regs {
>  	.fir_active = FIR_AUX_ACTIVE,
>  	.fir_htiming = FIR_AUX_H_TIMING,
>  	.fir_vtiming = FIR_AUX_V_TIMING,
> +	.sec_vtiming = SEC_AUX_V_TIMING,
>  	.timing_shift = TIMING_AUX_SHIFT,
>  	.timing_pi_shift = TIMING_AUX_PI_SHIFT,
>  };
> @@ -65,6 +68,10 @@ struct zx_crtc_bits {
>  	u32 polarity_shift;
>  	u32 int_frame_mask;
>  	u32 tc_enable;
> +	u32 sec_vactive_shift;
> +	u32 sec_vactive_mask;
> +	u32 interlace_select;
> +	u32 pi_enable;
>  };
>  
>  static const struct zx_crtc_bits main_crtc_bits = {
> @@ -72,6 +79,10 @@ struct zx_crtc_bits {
>  	.polarity_shift = MAIN_POL_SHIFT,
>  	.int_frame_mask = TIMING_INT_MAIN_FRAME,
>  	.tc_enable = MAIN_TC_EN,
> +	.sec_vactive_shift = SEC_VACT_MAIN_SHIFT,
> +	.sec_vactive_mask = SEC_VACT_MAIN_MASK,
> +	.interlace_select = MAIN_INTERLACE_SEL,
> +	.pi_enable = MAIN_PI_EN,
>  };
>  
>  static const struct zx_crtc_bits aux_crtc_bits = {
> @@ -79,6 +90,10 @@ struct zx_crtc_bits {
>  	.polarity_shift = AUX_POL_SHIFT,
>  	.int_frame_mask = TIMING_INT_AUX_FRAME,
>  	.tc_enable = AUX_TC_EN,
> +	.sec_vactive_shift = SEC_VACT_AUX_SHIFT,
> +	.sec_vactive_mask = SEC_VACT_AUX_MASK,
> +	.interlace_select = AUX_INTERLACE_SEL,
> +	.pi_enable = AUX_PI_EN,
>  };
>  
>  struct zx_crtc {
> @@ -196,11 +211,13 @@ static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
>  static void zx_crtc_enable(struct drm_crtc *crtc)
>  {
>  	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +	bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
>  	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
>  	struct zx_vou_hw *vou = zcrtc->vou;
>  	const struct zx_crtc_regs *regs = zcrtc->regs;
>  	const struct zx_crtc_bits *bits = zcrtc->bits;
>  	struct videomode vm;
> +	u32 scan_mask;
>  	u32 pol = 0;
>  	u32 val;
>  	int ret;
> @@ -208,7 +225,7 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
>  	drm_display_mode_to_videomode(mode, &vm);
>  
>  	/* Set up timing parameters */
> -	val = V_ACTIVE(vm.vactive - 1);
> +	val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1);
>  	val |= H_ACTIVE(vm.hactive - 1);
>  	zx_writel(vou->timing + regs->fir_active, val);
>  
> @@ -222,6 +239,25 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
>  	val |= FRONT_PORCH(vm.vfront_porch - 1);
>  	zx_writel(vou->timing + regs->fir_vtiming, val);
>  
> +	if (interlaced) {
> +		u32 shift = bits->sec_vactive_shift;
> +		u32 mask = bits->sec_vactive_mask;
> +
> +		val = zx_readl(vou->timing + SEC_V_ACTIVE);
> +		val &= ~mask;
> +		val |= ((vm.vactive / 2 - 1) << shift) & mask;
> +		zx_writel(vou->timing + SEC_V_ACTIVE, val);
> +
> +		val = SYNC_WIDE(vm.vsync_len - 1);
> +		/*
> +		 * The vback_porch for the second field needs to shift one on
> +		 * the value for the first field.
> +		 */
> +		val |= BACK_PORCH(vm.vback_porch);
> +		val |= FRONT_PORCH(vm.vfront_porch - 1);
> +		zx_writel(vou->timing + regs->sec_vtiming, val);
> +	}
> +
>  	/* Set up polarities */
>  	if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW)
>  		pol |= 1 << POL_VSYNC_SHIFT;
> @@ -232,9 +268,17 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
>  		       pol << bits->polarity_shift);
>  
>  	/* Setup SHIFT register by following what ZTE BSP does */
> -	zx_writel(vou->timing + regs->timing_shift, H_SHIFT_VAL);
> +	val = H_SHIFT_VAL;
> +	if (interlaced)
> +		val |= V_SHIFT_VAL << 16;
> +	zx_writel(vou->timing + regs->timing_shift, val);
>  	zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL);
>  
> +	/* Progressive or interlace scan select */
> +	scan_mask = bits->interlace_select | bits->pi_enable;
> +	zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask,
> +		       interlaced ? scan_mask : 0);
> +
>  	/* Enable TIMING_CTRL */
>  	zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable,
>  		       bits->tc_enable);
> @@ -245,6 +289,10 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
>  	zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK,
>  		       vm.vactive << CHN_SCREEN_H_SHIFT);
>  
> +	/* Configure channel interlace buffer control */
> +	zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN,
> +		       interlaced ? CHN_INTERLACE_EN : 0);
> +
>  	/* Update channel */
>  	vou_chn_set_update(zcrtc);
>  
> diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
> index 193c1ce01fe7..e6ed844e068a 100644
> --- a/drivers/gpu/drm/zte/zx_vou_regs.h
> +++ b/drivers/gpu/drm/zte/zx_vou_regs.h
> @@ -75,6 +75,8 @@
>  #define CHN_SCREEN_H_SHIFT		5
>  #define CHN_SCREEN_H_MASK		(0x1fff << CHN_SCREEN_H_SHIFT)
>  #define CHN_UPDATE			0x08
> +#define CHN_INTERLACE_BUF_CTRL		0x24
> +#define CHN_INTERLACE_EN		BIT(2)
>  
>  /* TIMING_CTRL registers */
>  #define TIMING_TC_ENABLE		0x04
> @@ -117,6 +119,19 @@
>  #define TIMING_MAIN_SHIFT		0x2c
>  #define TIMING_AUX_SHIFT		0x30
>  #define H_SHIFT_VAL			0x0048
> +#define V_SHIFT_VAL			0x0001
> +#define SCAN_CTRL			0x34
> +#define AUX_PI_EN			BIT(19)
> +#define MAIN_PI_EN			BIT(18)
> +#define AUX_INTERLACE_SEL		BIT(1)
> +#define MAIN_INTERLACE_SEL		BIT(0)
> +#define SEC_V_ACTIVE			0x38
> +#define SEC_VACT_MAIN_SHIFT		0
> +#define SEC_VACT_MAIN_MASK		(0xffff << SEC_VACT_MAIN_SHIFT)
> +#define SEC_VACT_AUX_SHIFT		16
> +#define SEC_VACT_AUX_MASK		(0xffff << SEC_VACT_AUX_SHIFT)
> +#define SEC_MAIN_V_TIMING		0x3c
> +#define SEC_AUX_V_TIMING		0x40
>  #define TIMING_MAIN_PI_SHIFT		0x68
>  #define TIMING_AUX_PI_SHIFT		0x6c
>  #define H_PI_SHIFT_VAL			0x000f
> -- 
> 1.9.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Sean Paul, Software Engineer, Google / Chromium OS
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v2 5/5] drm: zte: add tvenc driver support
  2017-01-26 15:20 ` [PATCH v2 5/5] drm: zte: add tvenc driver support Shawn Guo
@ 2017-01-26 18:43   ` Sean Paul
  0 siblings, 0 replies; 8+ messages in thread
From: Sean Paul @ 2017-01-26 18:43 UTC (permalink / raw)
  To: Shawn Guo; +Cc: Daniel Vetter, Jun Nie, Baoyou Xie, dri-devel

On Thu, Jan 26, 2017 at 11:20:53PM +0800, Shawn Guo wrote:
> From: Shawn Guo <shawn.guo@linaro.org>
> 
> It adds the TV Encoder driver to support video output in PAL and NTSC
> format.  The driver uses syscon/regmap interface to configure register
> bit sitting in SYSCTRL module for DAC power control.
> 

Reviewed-by: Sean Paul <seanpaul@chromium.org>

> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
> Changes for v2:
>  - Embed struct drm_display_mode in zx_tvenc_mode to save function
>    zx_tvenc_mode_to_drm_mode().
>  - Use drm_mode_equal() to find the matching mode.
>  - Use drm_mode_set_name() to name mode.
>  - Fix typos in clock multiplier comment.
>  - Add a sanity check in .mode_valid hook to ensure the given mode is
>    valid.
>  - Remove destroy() calls from .unbind hook, as those are already taken
>    care of by drm_mode_config_cleanup().
> 
>  drivers/gpu/drm/zte/Makefile        |   1 +
>  drivers/gpu/drm/zte/zx_drm_drv.c    |   1 +
>  drivers/gpu/drm/zte/zx_drm_drv.h    |   1 +
>  drivers/gpu/drm/zte/zx_tvenc.c      | 407 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/zte/zx_tvenc_regs.h |  31 +++
>  drivers/gpu/drm/zte/zx_vou.c        |   5 +
>  6 files changed, 446 insertions(+)
>  create mode 100644 drivers/gpu/drm/zte/zx_tvenc.c
>  create mode 100644 drivers/gpu/drm/zte/zx_tvenc_regs.h
> 
> diff --git a/drivers/gpu/drm/zte/Makefile b/drivers/gpu/drm/zte/Makefile
> index 699180bfd57c..01352b56c418 100644
> --- a/drivers/gpu/drm/zte/Makefile
> +++ b/drivers/gpu/drm/zte/Makefile
> @@ -2,6 +2,7 @@ zxdrm-y := \
>  	zx_drm_drv.o \
>  	zx_hdmi.o \
>  	zx_plane.o \
> +	zx_tvenc.o \
>  	zx_vou.o
>  
>  obj-$(CONFIG_DRM_ZTE) += zxdrm.o
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
> index 3e76f72c92ff..13081fed902d 100644
> --- a/drivers/gpu/drm/zte/zx_drm_drv.c
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.c
> @@ -247,6 +247,7 @@ static int zx_drm_remove(struct platform_device *pdev)
>  static struct platform_driver *drivers[] = {
>  	&zx_crtc_driver,
>  	&zx_hdmi_driver,
> +	&zx_tvenc_driver,
>  	&zx_drm_platform_driver,
>  };
>  
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.h b/drivers/gpu/drm/zte/zx_drm_drv.h
> index e65cd18a6cba..5ca035b079c7 100644
> --- a/drivers/gpu/drm/zte/zx_drm_drv.h
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.h
> @@ -13,6 +13,7 @@
>  
>  extern struct platform_driver zx_crtc_driver;
>  extern struct platform_driver zx_hdmi_driver;
> +extern struct platform_driver zx_tvenc_driver;
>  
>  static inline u32 zx_readl(void __iomem *reg)
>  {
> diff --git a/drivers/gpu/drm/zte/zx_tvenc.c b/drivers/gpu/drm/zte/zx_tvenc.c
> new file mode 100644
> index 000000000000..b56dc69843fc
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_tvenc.c
> @@ -0,0 +1,407 @@
> +/*
> + * Copyright 2017 Linaro Ltd.
> + * Copyright 2017 ZTE Corporation.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drmP.h>
> +
> +#include "zx_drm_drv.h"
> +#include "zx_tvenc_regs.h"
> +#include "zx_vou.h"
> +
> +struct zx_tvenc_pwrctrl {
> +	struct regmap *regmap;
> +	u32 reg;
> +	u32 mask;
> +};
> +
> +struct zx_tvenc {
> +	struct drm_connector connector;
> +	struct drm_encoder encoder;
> +	struct device *dev;
> +	void __iomem *mmio;
> +	const struct vou_inf *inf;
> +	struct zx_tvenc_pwrctrl pwrctrl;
> +};
> +
> +#define to_zx_tvenc(x) container_of(x, struct zx_tvenc, x)
> +
> +struct zx_tvenc_mode {
> +	struct drm_display_mode mode;
> +	u32 video_info;
> +	u32 video_res;
> +	u32 field1_param;
> +	u32 field2_param;
> +	u32 burst_line_odd1;
> +	u32 burst_line_even1;
> +	u32 burst_line_odd2;
> +	u32 burst_line_even2;
> +	u32 line_timing_param;
> +	u32 weight_value;
> +	u32 blank_black_level;
> +	u32 burst_level;
> +	u32 control_param;
> +	u32 sub_carrier_phase1;
> +	u32 phase_line_incr_cvbs;
> +};
> +
> +/*
> + * The CRM cannot directly provide a suitable frequency, and we have to
> + * ask a multiplied rate from CRM and use the divider in VOU to get the
> + * desired one.
> + */
> +#define TVENC_CLOCK_MULTIPLIER	4
> +
> +static const struct zx_tvenc_mode tvenc_mode_pal = {
> +	.mode = {
> +		.clock = 13500 * TVENC_CLOCK_MULTIPLIER,
> +		.hdisplay = 720,
> +		.hsync_start = 720 + 12,
> +		.hsync_end = 720 + 12 + 2,
> +		.htotal = 720 + 12 + 2 + 130,
> +		.vdisplay = 576,
> +		.vsync_start = 576 + 2,
> +		.vsync_end = 576 + 2 + 2,
> +		.vtotal = 576 + 2 + 2 + 20,
> +		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
> +			 DRM_MODE_FLAG_INTERLACE,
> +	},
> +	.video_info = 0x00040040,
> +	.video_res = 0x05a9c760,
> +	.field1_param = 0x0004d416,
> +	.field2_param = 0x0009b94f,
> +	.burst_line_odd1 = 0x0004d406,
> +	.burst_line_even1 = 0x0009b53e,
> +	.burst_line_odd2 = 0x0004d805,
> +	.burst_line_even2 = 0x0009b93f,
> +	.line_timing_param = 0x06a96fdf,
> +	.weight_value = 0x00c188a0,
> +	.blank_black_level = 0x0000fcfc,
> +	.burst_level = 0x00001595,
> +	.control_param = 0x00000001,
> +	.sub_carrier_phase1 = 0x1504c566,
> +	.phase_line_incr_cvbs = 0xc068db8c,
> +};
> +
> +static const struct zx_tvenc_mode tvenc_mode_ntsc = {
> +	.mode = {
> +		.clock = 13500 * TVENC_CLOCK_MULTIPLIER,
> +		.hdisplay = 720,
> +		.hsync_start = 720 + 16,
> +		.hsync_end = 720 + 16 + 2,
> +		.htotal = 720 + 16 + 2 + 120,
> +		.vdisplay = 480,
> +		.vsync_start = 480 + 3,
> +		.vsync_end = 480 + 3 + 2,
> +		.vtotal = 480 + 3 + 2 + 17,
> +		.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
> +			 DRM_MODE_FLAG_INTERLACE,
> +	},
> +	.video_info = 0x00040080,
> +	.video_res = 0x05a8375a,
> +	.field1_param = 0x00041817,
> +	.field2_param = 0x0008351e,
> +	.burst_line_odd1 = 0x00041006,
> +	.burst_line_even1 = 0x0008290d,
> +	.burst_line_odd2 = 0x00000000,
> +	.burst_line_even2 = 0x00000000,
> +	.line_timing_param = 0x06a8ef9e,
> +	.weight_value = 0x00b68197,
> +	.blank_black_level = 0x0000f0f0,
> +	.burst_level = 0x0000009c,
> +	.control_param = 0x00000001,
> +	.sub_carrier_phase1 = 0x10f83e10,
> +	.phase_line_incr_cvbs = 0x80000000,
> +};
> +
> +static const struct zx_tvenc_mode *tvenc_modes[] = {
> +	&tvenc_mode_pal,
> +	&tvenc_mode_ntsc,
> +};
> +
> +static const struct zx_tvenc_mode *
> +zx_tvenc_find_zmode(struct drm_display_mode *mode)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) {
> +		const struct zx_tvenc_mode *zmode = tvenc_modes[i];
> +
> +		if (drm_mode_equal(mode, &zmode->mode))
> +			return zmode;
> +	}
> +
> +	return NULL;
> +}
> +
> +static void zx_tvenc_encoder_mode_set(struct drm_encoder *encoder,
> +				      struct drm_display_mode *mode,
> +				      struct drm_display_mode *adj_mode)
> +{
> +	struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
> +	const struct zx_tvenc_mode *zmode;
> +	struct vou_div_config configs[] = {
> +		{ VOU_DIV_INF,   VOU_DIV_4 },
> +		{ VOU_DIV_TVENC, VOU_DIV_1 },
> +		{ VOU_DIV_LAYER, VOU_DIV_2 },
> +	};
> +
> +	zx_vou_config_dividers(encoder->crtc, configs, ARRAY_SIZE(configs));
> +
> +	zmode = zx_tvenc_find_zmode(mode);
> +	if (!zmode) {
> +		DRM_DEV_ERROR(tvenc->dev, "failed to find zmode\n");
> +		return;
> +	}
> +
> +	zx_writel(tvenc->mmio + VENC_VIDEO_INFO, zmode->video_info);
> +	zx_writel(tvenc->mmio + VENC_VIDEO_RES, zmode->video_res);
> +	zx_writel(tvenc->mmio + VENC_FIELD1_PARAM, zmode->field1_param);
> +	zx_writel(tvenc->mmio + VENC_FIELD2_PARAM, zmode->field2_param);
> +	zx_writel(tvenc->mmio + VENC_LINE_O_1, zmode->burst_line_odd1);
> +	zx_writel(tvenc->mmio + VENC_LINE_E_1, zmode->burst_line_even1);
> +	zx_writel(tvenc->mmio + VENC_LINE_O_2, zmode->burst_line_odd2);
> +	zx_writel(tvenc->mmio + VENC_LINE_E_2, zmode->burst_line_even2);
> +	zx_writel(tvenc->mmio + VENC_LINE_TIMING_PARAM,
> +		  zmode->line_timing_param);
> +	zx_writel(tvenc->mmio + VENC_WEIGHT_VALUE, zmode->weight_value);
> +	zx_writel(tvenc->mmio + VENC_BLANK_BLACK_LEVEL,
> +		  zmode->blank_black_level);
> +	zx_writel(tvenc->mmio + VENC_BURST_LEVEL, zmode->burst_level);
> +	zx_writel(tvenc->mmio + VENC_CONTROL_PARAM, zmode->control_param);
> +	zx_writel(tvenc->mmio + VENC_SUB_CARRIER_PHASE1,
> +		  zmode->sub_carrier_phase1);
> +	zx_writel(tvenc->mmio + VENC_PHASE_LINE_INCR_CVBS,
> +		  zmode->phase_line_incr_cvbs);
> +}
> +
> +static void zx_tvenc_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
> +	struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
> +
> +	/* Set bit to power up TVENC DAC */
> +	regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask,
> +			   pwrctrl->mask);
> +
> +	vou_inf_enable(VOU_TV_ENC, encoder->crtc);
> +
> +	zx_writel(tvenc->mmio + VENC_ENABLE, 1);
> +}
> +
> +static void zx_tvenc_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
> +	struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
> +
> +	zx_writel(tvenc->mmio + VENC_ENABLE, 0);
> +
> +	vou_inf_disable(VOU_TV_ENC, encoder->crtc);
> +
> +	/* Clear bit to power down TVENC DAC */
> +	regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0);
> +}
> +
> +static const struct drm_encoder_helper_funcs zx_tvenc_encoder_helper_funcs = {
> +	.enable	= zx_tvenc_encoder_enable,
> +	.disable = zx_tvenc_encoder_disable,
> +	.mode_set = zx_tvenc_encoder_mode_set,
> +};
> +
> +static const struct drm_encoder_funcs zx_tvenc_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +static int zx_tvenc_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct zx_tvenc *tvenc = to_zx_tvenc(connector);
> +	struct device *dev = tvenc->dev;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) {
> +		const struct zx_tvenc_mode *zmode = tvenc_modes[i];
> +		struct drm_display_mode *mode;
> +
> +		mode = drm_mode_duplicate(connector->dev, &zmode->mode);
> +		if (!mode) {
> +			DRM_DEV_ERROR(dev, "failed to duplicate drm mode\n");
> +			continue;
> +		}
> +
> +		drm_mode_set_name(mode);
> +		drm_mode_probed_add(connector, mode);
> +	}
> +
> +	return i;
> +}
> +
> +static enum drm_mode_status
> +zx_tvenc_connector_mode_valid(struct drm_connector *connector,
> +			      struct drm_display_mode *mode)
> +{
> +	struct zx_tvenc *tvenc = to_zx_tvenc(connector);
> +	const struct zx_tvenc_mode *zmode;
> +
> +	zmode = zx_tvenc_find_zmode(mode);
> +	if (!zmode) {
> +		DRM_DEV_ERROR(tvenc->dev, "unsupported mode: %s\n", mode->name);
> +		return MODE_NOMODE;
> +	}
> +
> +	return MODE_OK;
> +}
> +
> +static struct drm_connector_helper_funcs zx_tvenc_connector_helper_funcs = {
> +	.get_modes = zx_tvenc_connector_get_modes,
> +	.mode_valid = zx_tvenc_connector_mode_valid,
> +};
> +
> +static const struct drm_connector_funcs zx_tvenc_connector_funcs = {
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = drm_connector_cleanup,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int zx_tvenc_register(struct drm_device *drm, struct zx_tvenc *tvenc)
> +{
> +	struct drm_encoder *encoder = &tvenc->encoder;
> +	struct drm_connector *connector = &tvenc->connector;
> +
> +	/*
> +	 * The tvenc is designed to use aux channel, as there is a deflicker
> +	 * block for the channel.
> +	 */
> +	encoder->possible_crtcs = BIT(1);
> +
> +	drm_encoder_init(drm, encoder, &zx_tvenc_encoder_funcs,
> +			 DRM_MODE_ENCODER_TVDAC, NULL);
> +	drm_encoder_helper_add(encoder, &zx_tvenc_encoder_helper_funcs);
> +
> +	connector->interlace_allowed = true;
> +
> +	drm_connector_init(drm, connector, &zx_tvenc_connector_funcs,
> +			   DRM_MODE_CONNECTOR_Composite);
> +	drm_connector_helper_add(connector, &zx_tvenc_connector_helper_funcs);
> +
> +	drm_mode_connector_attach_encoder(connector, encoder);
> +
> +	return 0;
> +}
> +
> +static int zx_tvenc_pwrctrl_init(struct zx_tvenc *tvenc)
> +{
> +	struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
> +	struct device *dev = tvenc->dev;
> +	struct of_phandle_args out_args;
> +	struct regmap *regmap;
> +	int ret;
> +
> +	ret = of_parse_phandle_with_fixed_args(dev->of_node,
> +				"zte,tvenc-power-control", 2, 0, &out_args);
> +	if (ret)
> +		return ret;
> +
> +	regmap = syscon_node_to_regmap(out_args.np);
> +	if (IS_ERR(regmap)) {
> +		ret = PTR_ERR(regmap);
> +		goto out;
> +	}
> +
> +	pwrctrl->regmap = regmap;
> +	pwrctrl->reg = out_args.args[0];
> +	pwrctrl->mask = out_args.args[1];
> +
> +out:
> +	of_node_put(out_args.np);
> +	return ret;
> +}
> +
> +static int zx_tvenc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct resource *res;
> +	struct zx_tvenc *tvenc;
> +	int ret;
> +
> +	tvenc = devm_kzalloc(dev, sizeof(*tvenc), GFP_KERNEL);
> +	if (!tvenc)
> +		return -ENOMEM;
> +
> +	tvenc->dev = dev;
> +	dev_set_drvdata(dev, tvenc);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	tvenc->mmio = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(tvenc->mmio)) {
> +		ret = PTR_ERR(tvenc->mmio);
> +		DRM_DEV_ERROR(dev, "failed to remap tvenc region: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = zx_tvenc_pwrctrl_init(tvenc);
> +	if (ret) {
> +		DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = zx_tvenc_register(drm, tvenc);
> +	if (ret) {
> +		DRM_DEV_ERROR(dev, "failed to register tvenc: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void zx_tvenc_unbind(struct device *dev, struct device *master,
> +			    void *data)
> +{
> +	/* Nothing to do */
> +}
> +
> +static const struct component_ops zx_tvenc_component_ops = {
> +	.bind = zx_tvenc_bind,
> +	.unbind = zx_tvenc_unbind,
> +};
> +
> +static int zx_tvenc_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &zx_tvenc_component_ops);
> +}
> +
> +static int zx_tvenc_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &zx_tvenc_component_ops);
> +	return 0;
> +}
> +
> +static const struct of_device_id zx_tvenc_of_match[] = {
> +	{ .compatible = "zte,zx296718-tvenc", },
> +	{ /* end */ },
> +};
> +MODULE_DEVICE_TABLE(of, zx_tvenc_of_match);
> +
> +struct platform_driver zx_tvenc_driver = {
> +	.probe = zx_tvenc_probe,
> +	.remove = zx_tvenc_remove,
> +	.driver	= {
> +		.name = "zx-tvenc",
> +		.of_match_table	= zx_tvenc_of_match,
> +	},
> +};
> diff --git a/drivers/gpu/drm/zte/zx_tvenc_regs.h b/drivers/gpu/drm/zte/zx_tvenc_regs.h
> new file mode 100644
> index 000000000000..bd91f5dcc1f3
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_tvenc_regs.h
> @@ -0,0 +1,31 @@
> +/*
> + * Copyright 2017 Linaro Ltd.
> + * Copyright 2017 ZTE Corporation.
> + *
> + * 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 __ZX_TVENC_REGS_H__
> +#define __ZX_TVENC_REGS_H__
> +
> +#define VENC_VIDEO_INFO			0x04
> +#define VENC_VIDEO_RES			0x08
> +#define VENC_FIELD1_PARAM		0x10
> +#define VENC_FIELD2_PARAM		0x14
> +#define VENC_LINE_O_1			0x18
> +#define VENC_LINE_E_1			0x1c
> +#define VENC_LINE_O_2			0x20
> +#define VENC_LINE_E_2			0x24
> +#define VENC_LINE_TIMING_PARAM		0x28
> +#define VENC_WEIGHT_VALUE		0x2c
> +#define VENC_BLANK_BLACK_LEVEL		0x30
> +#define VENC_BURST_LEVEL		0x34
> +#define VENC_CONTROL_PARAM		0x3c
> +#define VENC_SUB_CARRIER_PHASE1		0x40
> +#define VENC_PHASE_LINE_INCR_CVBS	0x48
> +#define VENC_ENABLE			0xa8
> +
> +#endif /* __ZX_TVENC_REGS_H__ */
> diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
> index 823b74ea758a..71aaf8f2b071 100644
> --- a/drivers/gpu/drm/zte/zx_vou.c
> +++ b/drivers/gpu/drm/zte/zx_vou.c
> @@ -192,6 +192,11 @@ struct vou_inf {
>  		.clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
>  		.clocks_sel_bits = BIT(13) | BIT(2),
>  	},
> +	[VOU_TV_ENC] = {
> +		.data_sel = VOU_YUV444,
> +		.clocks_en_bits = BIT(15),
> +		.clocks_sel_bits = BIT(11) | BIT(0),
> +	},
>  };
>  
>  static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
> -- 
> 1.9.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Sean Paul, Software Engineer, Google / Chromium OS
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

end of thread, other threads:[~2017-01-26 18:43 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-26 15:20 [PATCH v2 0/5] Add TV Encoder support for ZTE DRM driver Shawn Guo
2017-01-26 15:20 ` [PATCH v2 1/5] drm: zte: add interlace mode support Shawn Guo
2017-01-26 18:37   ` Sean Paul
2017-01-26 15:20 ` [PATCH v2 2/5] drm: zte: move struct vou_inf into zx_vou driver Shawn Guo
2017-01-26 15:20 ` [PATCH v2 3/5] drm: zte: add function to configure vou_ctrl dividers Shawn Guo
2017-01-26 15:20 ` [PATCH v2 4/5] dt: add bindings for ZTE tvenc device Shawn Guo
2017-01-26 15:20 ` [PATCH v2 5/5] drm: zte: add tvenc driver support Shawn Guo
2017-01-26 18:43   ` Sean Paul

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.