All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Add PSR function support for Analogix/Rockchip DP
@ 2016-07-01  9:18 ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:18 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Stéphane Marchesin, Sean Paul, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, emil.l.velikov, Yakir Yang, linux-kernel,
	dri-devel, linux-samsung-soc, linux-rockchip

Hi all,

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make a lots
of sense to save the power consumption.

This v3 version have splited an common PSR driver for Rockchip, which is
biggest changes from v2.

This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP
thread[1].

[0]: https://patchwork.kernel.org/patch/8886041/
[1]: https://patchwork.kernel.org/patch/9204497/

Thanks,
- Yakir


Changes in v3:
- Remove the notify for waiting line_flag event (Daniel)
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- split the psr flow into an common abstracted PSR driver
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- remove psr_active() callback which introduce in v2
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- Introduce in v2, split VOP line flag changes out
- introduce in v2, splite the common Analogix DP changes out
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

Yakir Yang (4):
  drm/rockchip: vop: export line flag function
  drm/rockchip: add an common abstracted PSR driver
  drm/bridge: analogix_dp: add the PSR function support
  drm/rockchip: analogix_dp: implement PSR function

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c |  64 +++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |   4 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  54 ++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  |  28 +++
 drivers/gpu/drm/rockchip/Makefile                  |   2 +-
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  52 ++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h        |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c         |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c        | 200 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h        |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        | 127 +++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   3 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   4 +
 include/drm/bridge/analogix_dp.h                   |   3 +
 14 files changed, 567 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

-- 
1.9.1

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

* [PATCH v3 0/4] Add PSR function support for Analogix/Rockchip DP
@ 2016-07-01  9:18 ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:18 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip,
	linux-kernel, emil.l.velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Stéphane Marchesin,
	Thierry Reding

Hi all,

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make a lots
of sense to save the power consumption.

This v3 version have splited an common PSR driver for Rockchip, which is
biggest changes from v2.

This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP
thread[1].

[0]: https://patchwork.kernel.org/patch/8886041/
[1]: https://patchwork.kernel.org/patch/9204497/

Thanks,
- Yakir


Changes in v3:
- Remove the notify for waiting line_flag event (Daniel)
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- split the psr flow into an common abstracted PSR driver
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- remove psr_active() callback which introduce in v2
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- Introduce in v2, split VOP line flag changes out
- introduce in v2, splite the common Analogix DP changes out
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

Yakir Yang (4):
  drm/rockchip: vop: export line flag function
  drm/rockchip: add an common abstracted PSR driver
  drm/bridge: analogix_dp: add the PSR function support
  drm/rockchip: analogix_dp: implement PSR function

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c |  64 +++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |   4 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  54 ++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  |  28 +++
 drivers/gpu/drm/rockchip/Makefile                  |   2 +-
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  52 ++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h        |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c         |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c        | 200 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h        |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        | 127 +++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   3 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   4 +
 include/drm/bridge/analogix_dp.h                   |   3 +
 14 files changed, 567 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.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] 35+ messages in thread

* [PATCH v3 1/4] drm/rockchip: vop: export line flag function
  2016-07-01  9:18 ` Yakir Yang
@ 2016-07-01  9:19   ` Yakir Yang
  -1 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:19 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Stéphane Marchesin, Sean Paul, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, emil.l.velikov, Yakir Yang, linux-kernel,
	dri-devel, linux-samsung-soc, linux-rockchip

VOP have integrated a hardware counter which indicate the exact display
line that vop is scanning. And if we're interested in a specific line,
we can set the line number to vop line_flag register, and then vop would
generate a line_flag interrupt for it.

For example eDP PSR function is interested in the vertical blanking
period, then driver could set the line number to zero.

This patch have exported a symbol that allow other driver to listen the
line flag event with given timeout limit:
-  rockchip_drm_wait_line_flag()

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v3:
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- Remove the notify for waiting line_flag event (Daniel)

Changes in v2:
- Introduce in v2, split VOP line flag changes out

 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
 4 files changed, 113 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index ea39329..239b830 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 				   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout);
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c8a62a8..cd3cac5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -121,6 +121,8 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	struct completion line_flag_completion;
+
 	const struct vop_data *data;
 
 	uint32_t *regsbak;
@@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
+ *                |                         |
+ *                v                         v
+ *                | Vsync | Vbp |  Vactive  | Vfp |
+ *                        ^     ^           ^     ^
+ *                        |     |           |     |
+ *                        |     |           |     |
+ * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
+ */
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_CTRL_SET(vop, line_flag_num_0, line_num);
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+	vop_cfg_done(vop);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
+	if (active_irqs & LINE_FLAG_INTR) {
+		if (!completion_done(&vop->line_flag_completion))
+			complete(&vop->line_flag_completion);
+		active_irqs &= ~LINE_FLAG_INTR;
+		ret = IRQ_HANDLED;
+	}
+
 	if (active_irqs & FS_INTR) {
 		drm_crtc_handle_vblank(crtc);
 		vop_handle_vblank(vop);
@@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
 
 	init_completion(&vop->dsp_hold_completion);
 	init_completion(&vop->wait_update_complete);
+	init_completion(&vop->line_flag_completion);
 	crtc->port = port;
 	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
@@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
 	}
 }
 
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout)
+{
+	struct vop *vop = to_vop(crtc);
+	unsigned long jiffies_left;
+
+	if (!crtc || !vop->is_enabled)
+		return -ENODEV;
+
+	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+		return -EINVAL;
+
+	reinit_completion(&vop->line_flag_completion);
+	vop_line_flag_irq_enable(vop, line_num);
+
+	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+						   msecs_to_jiffies(mstimeout));
+	vop_line_flag_irq_disable(vop);
+
+	if (jiffies_left == 0) {
+		dev_err(vop->dev, "Timeout waiting for IRQ\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..34fcd03 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -61,6 +61,9 @@ struct vop_ctrl {
 	struct vop_reg hpost_st_end;
 	struct vop_reg vpost_st_end;
 
+	struct vop_reg line_flag_num_0;
+	struct vop_reg line_flag_num_1;
+
 	struct vop_reg cfg_done;
 };
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 6f42e56..e9211c9 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
 	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
 	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
 	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
 	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
 	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
+	.line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
 	.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
 };
 
-- 
1.9.1

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

* [PATCH v3 1/4] drm/rockchip: vop: export line flag function
@ 2016-07-01  9:19   ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:19 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip,
	linux-kernel, emil.l.velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Stéphane Marchesin,
	Thierry Reding

VOP have integrated a hardware counter which indicate the exact display
line that vop is scanning. And if we're interested in a specific line,
we can set the line number to vop line_flag register, and then vop would
generate a line_flag interrupt for it.

For example eDP PSR function is interested in the vertical blanking
period, then driver could set the line number to zero.

This patch have exported a symbol that allow other driver to listen the
line flag event with given timeout limit:
-  rockchip_drm_wait_line_flag()

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v3:
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- Remove the notify for waiting line_flag event (Daniel)

Changes in v2:
- Introduce in v2, split VOP line flag changes out

 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
 4 files changed, 113 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index ea39329..239b830 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 				   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout);
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c8a62a8..cd3cac5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -121,6 +121,8 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	struct completion line_flag_completion;
+
 	const struct vop_data *data;
 
 	uint32_t *regsbak;
@@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
+ *                |                         |
+ *                v                         v
+ *                | Vsync | Vbp |  Vactive  | Vfp |
+ *                        ^     ^           ^     ^
+ *                        |     |           |     |
+ *                        |     |           |     |
+ * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
+ */
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_CTRL_SET(vop, line_flag_num_0, line_num);
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+	vop_cfg_done(vop);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
+	if (active_irqs & LINE_FLAG_INTR) {
+		if (!completion_done(&vop->line_flag_completion))
+			complete(&vop->line_flag_completion);
+		active_irqs &= ~LINE_FLAG_INTR;
+		ret = IRQ_HANDLED;
+	}
+
 	if (active_irqs & FS_INTR) {
 		drm_crtc_handle_vblank(crtc);
 		vop_handle_vblank(vop);
@@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
 
 	init_completion(&vop->dsp_hold_completion);
 	init_completion(&vop->wait_update_complete);
+	init_completion(&vop->line_flag_completion);
 	crtc->port = port;
 	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
@@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
 	}
 }
 
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout)
+{
+	struct vop *vop = to_vop(crtc);
+	unsigned long jiffies_left;
+
+	if (!crtc || !vop->is_enabled)
+		return -ENODEV;
+
+	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+		return -EINVAL;
+
+	reinit_completion(&vop->line_flag_completion);
+	vop_line_flag_irq_enable(vop, line_num);
+
+	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+						   msecs_to_jiffies(mstimeout));
+	vop_line_flag_irq_disable(vop);
+
+	if (jiffies_left == 0) {
+		dev_err(vop->dev, "Timeout waiting for IRQ\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..34fcd03 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -61,6 +61,9 @@ struct vop_ctrl {
 	struct vop_reg hpost_st_end;
 	struct vop_reg vpost_st_end;
 
+	struct vop_reg line_flag_num_0;
+	struct vop_reg line_flag_num_1;
+
 	struct vop_reg cfg_done;
 };
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 6f42e56..e9211c9 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
 	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
 	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
 	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
 	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
 	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
+	.line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
 	.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
 };
 
-- 
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] 35+ messages in thread

* [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
  2016-07-01  9:18 ` Yakir Yang
@ 2016-07-01  9:19   ` Yakir Yang
  -1 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:19 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Stéphane Marchesin, Sean Paul, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, emil.l.velikov, Yakir Yang, linux-kernel,
	dri-devel, linux-samsung-soc, linux-rockchip

The PSR driver have exported four symbols for specific device driver:
- rockchip_drm_psr_register()
- rockchip_drm_psr_unregister()
- rockchip_drm_psr_enable()
- rockchip_drm_psr_disable()
- rockchip_drm_psr_flush()

Encoder driver should call the register/unregister interfaces to hook
itself into common PSR driver, encoder have implement the 'psr_set'
callback which use the set PSR state in hardware side.

Crtc driver would call the enable/disable interfaces when vblank is
enable/disable, after that the common PSR driver would call the encoder
registered callback to set the PSR state.

Fb driver would call the flush interface in 'fb->dirty' callback, this
helper function would force all PSR enabled encoders to exit from PSR
for 3 seconds.

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v3:
- split the psr flow into an common abstracted PSR driver
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- remove psr_active() callback which introduce in v2

Changes in v2: None

 drivers/gpu/drm/rockchip/Makefile           |   2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
 5 files changed, 249 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..9746365 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -3,7 +3,7 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o
+		rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 20f12bc..0fec18f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -21,6 +21,7 @@
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
+#include "rockchip_drm_psr.h"
 
 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
 
@@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
 				     rockchip_fb->obj[0], handle);
 }
 
+static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
+				 struct drm_file *file,
+				 unsigned int flags, unsigned int color,
+				 struct drm_clip_rect *clips,
+				 unsigned int num_clips)
+{
+	rockchip_drm_psr_flush();
+	return 0;
+}
+
 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
 	.destroy	= rockchip_drm_fb_destroy,
 	.create_handle	= rockchip_drm_fb_create_handle,
+	.dirty		= rockchip_drm_fb_dirty,
 };
 
 static struct rockchip_drm_fb *
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
new file mode 100644
index 0000000..c044443
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
@@ -0,0 +1,200 @@
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_psr.h"
+
+#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
+
+static LIST_HEAD(psr_list);
+static DEFINE_MUTEX(psr_list_mutex);
+
+enum psr_state {
+	PSR_FLUSH,
+	PSR_ENABLE,
+	PSR_DISABLE,
+};
+
+struct psr_drv {
+	struct list_head list;
+	enum psr_state state;
+	struct mutex state_mutex;
+
+	struct timer_list flush_timer;
+
+	struct drm_encoder *encoder;
+	int (*set)(struct drm_encoder *encoder, bool enable);
+};
+
+static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->encoder->crtc == crtc) {
+			mutex_unlock(&psr_list_mutex);
+			return psr;
+		}
+	}
+	mutex_unlock(&psr_list_mutex);
+
+	return ERR_PTR(-ENODEV);
+}
+
+static void psr_enable(struct psr_drv *psr)
+{
+	if (psr->state == PSR_ENABLE)
+		return;
+
+	mutex_lock(&psr->state_mutex);
+	psr->state = PSR_ENABLE;
+	psr->set(psr->encoder, true);
+	mutex_unlock(&psr->state_mutex);
+}
+
+static void psr_disable(struct psr_drv *psr)
+{
+	if (psr->state == PSR_DISABLE)
+		return;
+
+	mutex_lock(&psr->state_mutex);
+	psr->state = PSR_DISABLE;
+	psr->set(psr->encoder, false);
+	mutex_unlock(&psr->state_mutex);
+}
+
+static void psr_flush_handler(unsigned long data)
+{
+	struct psr_drv *psr = (struct psr_drv *)data;
+
+	if (!psr || psr->state != PSR_FLUSH)
+		return;
+
+	psr_enable(psr);
+}
+
+/**
+ * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_enable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_enable(psr);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_enable);
+
+/**
+ * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_disable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_disable(psr);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_disable);
+
+/**
+ * rockchip_drm_psr_flush - force to flush all registered PSR encoders
+ *
+ * Disable the PSR function for all registered encoders, and then enable the
+ * PSR function back after 5 second. If encoder PSR state have been changed
+ * during flush time, then keep the state no change after flush timeout.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_flush(void)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->state == PSR_DISABLE)
+			continue;
+
+		mod_timer(&psr->flush_timer,
+			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
+
+		psr_disable(psr);
+		psr->state = PSR_FLUSH;
+	}
+	mutex_unlock(&psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_flush);
+
+/**
+ * rockchip_drm_psr_register - register encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			      int (*psr_set)(struct drm_encoder *, bool enable))
+{
+	struct psr_drv *psr;
+
+	if (!encoder || !psr_set)
+		return -EINVAL;
+
+	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
+	if (!psr)
+		return -ENOMEM;
+
+	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
+
+	mutex_init(&psr->state_mutex);
+
+	psr->state = PSR_DISABLE;
+	psr->encoder = encoder;
+	psr->set = psr_set;
+
+	mutex_lock(&psr_list_mutex);
+	list_add_tail(&psr->list, &psr_list);
+	mutex_unlock(&psr_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_register);
+
+/**
+ * rockchip_drm_psr_unregister - unregister encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->encoder == encoder) {
+			del_timer(&psr->flush_timer);
+			list_del(&psr->list);
+			kfree(psr);
+		}
+	}
+	mutex_unlock(&psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_unregister);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
new file mode 100644
index 0000000..622f605
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
@@ -0,0 +1,12 @@
+#ifndef __ROCKCHIP_DRM_PSR___
+#define __ROCKCHIP_DRM_PSR___
+
+void rockchip_drm_psr_flush(void);
+int rockchip_drm_psr_enable(struct drm_crtc *crtc);
+int rockchip_drm_psr_disable(struct drm_crtc *crtc);
+
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			    int (*psr_set)(struct drm_encoder *, bool enable));
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
+
+#endif /* __ROCKCHIP_DRM_PSR__ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index cd3cac5..3c6dfc5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -34,6 +34,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
@@ -121,6 +122,9 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	bool psr_enabled;
+	struct delayed_work psr_work;
+
 	struct completion line_flag_completion;
 
 	const struct vop_data *data;
@@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
 	.atomic_destroy_state = vop_atomic_plane_destroy_state,
 };
 
+static void vop_psr_work(struct work_struct *work)
+{
+	struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
+
+	if (vop->psr_enabled)
+		rockchip_drm_psr_enable(&vop->crtc);
+	else
+		rockchip_drm_psr_disable(&vop->crtc);
+}
+
 static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 
+	vop->psr_enabled = false;
+	schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
+
 	return 0;
 }
 
@@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
 	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	vop->psr_enabled = true;
+	schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
 }
 
 static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
@@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	pm_runtime_enable(&pdev->dev);
+
+	vop->psr_enabled = false;
+	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
+
 	return 0;
 }
 
-- 
1.9.1

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

* [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
@ 2016-07-01  9:19   ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:19 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip,
	linux-kernel, emil.l.velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Stéphane Marchesin,
	Thierry Reding

The PSR driver have exported four symbols for specific device driver:
- rockchip_drm_psr_register()
- rockchip_drm_psr_unregister()
- rockchip_drm_psr_enable()
- rockchip_drm_psr_disable()
- rockchip_drm_psr_flush()

Encoder driver should call the register/unregister interfaces to hook
itself into common PSR driver, encoder have implement the 'psr_set'
callback which use the set PSR state in hardware side.

Crtc driver would call the enable/disable interfaces when vblank is
enable/disable, after that the common PSR driver would call the encoder
registered callback to set the PSR state.

Fb driver would call the flush interface in 'fb->dirty' callback, this
helper function would force all PSR enabled encoders to exit from PSR
for 3 seconds.

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v3:
- split the psr flow into an common abstracted PSR driver
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- remove psr_active() callback which introduce in v2

Changes in v2: None

 drivers/gpu/drm/rockchip/Makefile           |   2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
 5 files changed, 249 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..9746365 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -3,7 +3,7 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o
+		rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 20f12bc..0fec18f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -21,6 +21,7 @@
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
+#include "rockchip_drm_psr.h"
 
 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
 
@@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
 				     rockchip_fb->obj[0], handle);
 }
 
+static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
+				 struct drm_file *file,
+				 unsigned int flags, unsigned int color,
+				 struct drm_clip_rect *clips,
+				 unsigned int num_clips)
+{
+	rockchip_drm_psr_flush();
+	return 0;
+}
+
 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
 	.destroy	= rockchip_drm_fb_destroy,
 	.create_handle	= rockchip_drm_fb_create_handle,
+	.dirty		= rockchip_drm_fb_dirty,
 };
 
 static struct rockchip_drm_fb *
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
new file mode 100644
index 0000000..c044443
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
@@ -0,0 +1,200 @@
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_psr.h"
+
+#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
+
+static LIST_HEAD(psr_list);
+static DEFINE_MUTEX(psr_list_mutex);
+
+enum psr_state {
+	PSR_FLUSH,
+	PSR_ENABLE,
+	PSR_DISABLE,
+};
+
+struct psr_drv {
+	struct list_head list;
+	enum psr_state state;
+	struct mutex state_mutex;
+
+	struct timer_list flush_timer;
+
+	struct drm_encoder *encoder;
+	int (*set)(struct drm_encoder *encoder, bool enable);
+};
+
+static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->encoder->crtc == crtc) {
+			mutex_unlock(&psr_list_mutex);
+			return psr;
+		}
+	}
+	mutex_unlock(&psr_list_mutex);
+
+	return ERR_PTR(-ENODEV);
+}
+
+static void psr_enable(struct psr_drv *psr)
+{
+	if (psr->state == PSR_ENABLE)
+		return;
+
+	mutex_lock(&psr->state_mutex);
+	psr->state = PSR_ENABLE;
+	psr->set(psr->encoder, true);
+	mutex_unlock(&psr->state_mutex);
+}
+
+static void psr_disable(struct psr_drv *psr)
+{
+	if (psr->state == PSR_DISABLE)
+		return;
+
+	mutex_lock(&psr->state_mutex);
+	psr->state = PSR_DISABLE;
+	psr->set(psr->encoder, false);
+	mutex_unlock(&psr->state_mutex);
+}
+
+static void psr_flush_handler(unsigned long data)
+{
+	struct psr_drv *psr = (struct psr_drv *)data;
+
+	if (!psr || psr->state != PSR_FLUSH)
+		return;
+
+	psr_enable(psr);
+}
+
+/**
+ * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_enable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_enable(psr);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_enable);
+
+/**
+ * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_disable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_disable(psr);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_disable);
+
+/**
+ * rockchip_drm_psr_flush - force to flush all registered PSR encoders
+ *
+ * Disable the PSR function for all registered encoders, and then enable the
+ * PSR function back after 5 second. If encoder PSR state have been changed
+ * during flush time, then keep the state no change after flush timeout.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_flush(void)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->state == PSR_DISABLE)
+			continue;
+
+		mod_timer(&psr->flush_timer,
+			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
+
+		psr_disable(psr);
+		psr->state = PSR_FLUSH;
+	}
+	mutex_unlock(&psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_flush);
+
+/**
+ * rockchip_drm_psr_register - register encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			      int (*psr_set)(struct drm_encoder *, bool enable))
+{
+	struct psr_drv *psr;
+
+	if (!encoder || !psr_set)
+		return -EINVAL;
+
+	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
+	if (!psr)
+		return -ENOMEM;
+
+	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
+
+	mutex_init(&psr->state_mutex);
+
+	psr->state = PSR_DISABLE;
+	psr->encoder = encoder;
+	psr->set = psr_set;
+
+	mutex_lock(&psr_list_mutex);
+	list_add_tail(&psr->list, &psr_list);
+	mutex_unlock(&psr_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_register);
+
+/**
+ * rockchip_drm_psr_unregister - unregister encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->encoder == encoder) {
+			del_timer(&psr->flush_timer);
+			list_del(&psr->list);
+			kfree(psr);
+		}
+	}
+	mutex_unlock(&psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_unregister);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
new file mode 100644
index 0000000..622f605
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
@@ -0,0 +1,12 @@
+#ifndef __ROCKCHIP_DRM_PSR___
+#define __ROCKCHIP_DRM_PSR___
+
+void rockchip_drm_psr_flush(void);
+int rockchip_drm_psr_enable(struct drm_crtc *crtc);
+int rockchip_drm_psr_disable(struct drm_crtc *crtc);
+
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			    int (*psr_set)(struct drm_encoder *, bool enable));
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
+
+#endif /* __ROCKCHIP_DRM_PSR__ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index cd3cac5..3c6dfc5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -34,6 +34,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
@@ -121,6 +122,9 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	bool psr_enabled;
+	struct delayed_work psr_work;
+
 	struct completion line_flag_completion;
 
 	const struct vop_data *data;
@@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
 	.atomic_destroy_state = vop_atomic_plane_destroy_state,
 };
 
+static void vop_psr_work(struct work_struct *work)
+{
+	struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
+
+	if (vop->psr_enabled)
+		rockchip_drm_psr_enable(&vop->crtc);
+	else
+		rockchip_drm_psr_disable(&vop->crtc);
+}
+
 static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 
+	vop->psr_enabled = false;
+	schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
+
 	return 0;
 }
 
@@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
 	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	vop->psr_enabled = true;
+	schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
 }
 
 static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
@@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	pm_runtime_enable(&pdev->dev);
+
+	vop->psr_enabled = false;
+	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
+
 	return 0;
 }
 
-- 
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] 35+ messages in thread

* [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
  2016-07-01  9:18 ` Yakir Yang
@ 2016-07-01  9:19   ` Yakir Yang
  -1 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:19 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Stéphane Marchesin, Sean Paul, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, emil.l.velikov, Yakir Yang, linux-kernel,
	dri-devel, linux-samsung-soc, linux-rockchip

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make lots of
sense to save the power consumption.

This patch have exported two symbols for platform driver to implement
the PSR function in hardware side:
- analogix_dp_active_psr()
- analogix_dp_inactive_psr()

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v3:
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments

Changes in v2:
- introduce in v2, splite the common Analogix DP changes out

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 ++++++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 ++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
 include/drm/bridge/analogix_dp.h                   |  3 +
 5 files changed, 153 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..b557097 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 	return 0;
 }
 
+int analogix_dp_active_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
+				 EDP_VSC_PSR_CRC_VALUES_VALID);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
+
+int analogix_dp_inactive_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	analogix_dp_send_psr_spd(dp, 0);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
+
+static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_version;
+
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
+	dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
+
+	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
+}
+
+static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_en;
+
+	/* Disable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en &= ~DP_PSR_ENABLE;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Main-Link transmitter remains active during PSR active states */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Enable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
+		 DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	analogix_dp_enable_psr_crc(dp);
+
+	return 0;
+}
+
 static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 {
 	int i;
@@ -921,6 +981,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 
 	/* Enable video */
 	analogix_dp_start_video(dp);
+
+	dp->psr_support = analogix_dp_detect_sink_psr(dp);
+	if (dp->psr_support)
+		analogix_dp_enable_sink_psr(dp);
 }
 
 int analogix_dp_get_modes(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index b456380..6ca5dde 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -177,6 +177,7 @@ struct analogix_dp_device {
 	int			hpd_gpio;
 	bool                    force_hpd;
 	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
+	bool			psr_support;
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
+
 #endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 48030f0..e8372c7 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
 	reg |= SCRAMBLING_DISABLE;
 	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
 }
+
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
+{
+	writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
+	       dp->reg_base + ANALOGIX_DP_CRC_CON);
+
+	usleep_range(10, 20);
+
+	writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
+}
+
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
+{
+	unsigned int val;
+
+	/* don't send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val &= ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* configure single frame update mode */
+	writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+
+	/* configure VSC HB0 ~ HB3 */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
+	writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
+	writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
+	writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
+
+	/* configure VSC HB0 ~ HB3 */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
+	writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
+	writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
+	writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
+
+	/* configure DB0 / DB1 values */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+	writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+
+	/* set reuse spd inforframe */
+	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	val |= REUSE_SPD_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+
+	/* mark info frame update */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = (val | IF_UP) & ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val |= IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index cdcc6c5..a2698e4 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -22,6 +22,8 @@
 #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10		0x44
 
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
+
 #define ANALOGIX_DP_PLL_REG_1			0xfc
 #define ANALOGIX_DP_PLL_REG_2			0x9e4
 #define ANALOGIX_DP_PLL_REG_3			0x9e8
@@ -30,6 +32,21 @@
 
 #define ANALOGIX_DP_PD				0x12c
 
+#define ANALOGIX_DP_IF_TYPE			0x244
+#define ANALOGIX_DP_IF_PKT_DB1			0x254
+#define ANALOGIX_DP_IF_PKT_DB2			0x258
+#define ANALOGIX_DP_SPD_HB0			0x2F8
+#define ANALOGIX_DP_SPD_HB1			0x2FC
+#define ANALOGIX_DP_SPD_HB2			0x300
+#define ANALOGIX_DP_SPD_HB3			0x304
+#define ANALOGIX_DP_SPD_PB0			0x308
+#define ANALOGIX_DP_SPD_PB1			0x30C
+#define ANALOGIX_DP_SPD_PB2			0x310
+#define ANALOGIX_DP_SPD_PB3			0x314
+#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
+#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
+#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
+
 #define ANALOGIX_DP_LANE_MAP			0x35C
 
 #define ANALOGIX_DP_ANALOG_CTL_1		0x370
@@ -103,6 +120,8 @@
 
 #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
 
+#define ANALOGIX_DP_CRC_CON			0x890
+
 /* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
 
@@ -151,6 +170,7 @@
 #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
 #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+#define REUSE_SPD_EN				(0x1 << 3)
 
 /* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
@@ -376,4 +396,12 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
+/* ANALOGIX_DP_PKT_SEND_CTL */
+#define IF_UP					(0x1 << 4)
+#define IF_EN					(0x1 << 0)
+
+/* ANALOGIX_DP_CRC_CON */
+#define PSR_VID_CRC_FLUSH			(0x1 << 2)
+#define PSR_VID_CRC_ENABLE			(0x1 << 0)
+
 #endif /* _ANALOGIX_DP_REG_H */
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index 261b86d..183a336 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
 			 struct drm_connector *);
 };
 
+int analogix_dp_active_psr(struct device *dev);
+int analogix_dp_inactive_psr(struct device *dev);
+
 int analogix_dp_resume(struct device *dev);
 int analogix_dp_suspend(struct device *dev);
 
-- 
1.9.1

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

* [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
@ 2016-07-01  9:19   ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:19 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip,
	linux-kernel, emil.l.velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Stéphane Marchesin,
	Thierry Reding

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make lots of
sense to save the power consumption.

This patch have exported two symbols for platform driver to implement
the PSR function in hardware side:
- analogix_dp_active_psr()
- analogix_dp_inactive_psr()

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v3:
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments

Changes in v2:
- introduce in v2, splite the common Analogix DP changes out

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 ++++++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 ++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
 include/drm/bridge/analogix_dp.h                   |  3 +
 5 files changed, 153 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..b557097 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 	return 0;
 }
 
+int analogix_dp_active_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
+				 EDP_VSC_PSR_CRC_VALUES_VALID);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
+
+int analogix_dp_inactive_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	analogix_dp_send_psr_spd(dp, 0);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
+
+static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_version;
+
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
+	dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
+
+	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
+}
+
+static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_en;
+
+	/* Disable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en &= ~DP_PSR_ENABLE;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Main-Link transmitter remains active during PSR active states */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Enable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
+		 DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	analogix_dp_enable_psr_crc(dp);
+
+	return 0;
+}
+
 static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 {
 	int i;
@@ -921,6 +981,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 
 	/* Enable video */
 	analogix_dp_start_video(dp);
+
+	dp->psr_support = analogix_dp_detect_sink_psr(dp);
+	if (dp->psr_support)
+		analogix_dp_enable_sink_psr(dp);
 }
 
 int analogix_dp_get_modes(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index b456380..6ca5dde 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -177,6 +177,7 @@ struct analogix_dp_device {
 	int			hpd_gpio;
 	bool                    force_hpd;
 	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
+	bool			psr_support;
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
+
 #endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 48030f0..e8372c7 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
 	reg |= SCRAMBLING_DISABLE;
 	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
 }
+
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
+{
+	writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
+	       dp->reg_base + ANALOGIX_DP_CRC_CON);
+
+	usleep_range(10, 20);
+
+	writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
+}
+
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
+{
+	unsigned int val;
+
+	/* don't send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val &= ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* configure single frame update mode */
+	writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+
+	/* configure VSC HB0 ~ HB3 */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
+	writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
+	writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
+	writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
+
+	/* configure VSC HB0 ~ HB3 */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
+	writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
+	writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
+	writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
+
+	/* configure DB0 / DB1 values */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+	writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+
+	/* set reuse spd inforframe */
+	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	val |= REUSE_SPD_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+
+	/* mark info frame update */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = (val | IF_UP) & ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val |= IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index cdcc6c5..a2698e4 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -22,6 +22,8 @@
 #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10		0x44
 
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
+
 #define ANALOGIX_DP_PLL_REG_1			0xfc
 #define ANALOGIX_DP_PLL_REG_2			0x9e4
 #define ANALOGIX_DP_PLL_REG_3			0x9e8
@@ -30,6 +32,21 @@
 
 #define ANALOGIX_DP_PD				0x12c
 
+#define ANALOGIX_DP_IF_TYPE			0x244
+#define ANALOGIX_DP_IF_PKT_DB1			0x254
+#define ANALOGIX_DP_IF_PKT_DB2			0x258
+#define ANALOGIX_DP_SPD_HB0			0x2F8
+#define ANALOGIX_DP_SPD_HB1			0x2FC
+#define ANALOGIX_DP_SPD_HB2			0x300
+#define ANALOGIX_DP_SPD_HB3			0x304
+#define ANALOGIX_DP_SPD_PB0			0x308
+#define ANALOGIX_DP_SPD_PB1			0x30C
+#define ANALOGIX_DP_SPD_PB2			0x310
+#define ANALOGIX_DP_SPD_PB3			0x314
+#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
+#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
+#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
+
 #define ANALOGIX_DP_LANE_MAP			0x35C
 
 #define ANALOGIX_DP_ANALOG_CTL_1		0x370
@@ -103,6 +120,8 @@
 
 #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
 
+#define ANALOGIX_DP_CRC_CON			0x890
+
 /* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
 
@@ -151,6 +170,7 @@
 #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
 #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+#define REUSE_SPD_EN				(0x1 << 3)
 
 /* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
@@ -376,4 +396,12 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
+/* ANALOGIX_DP_PKT_SEND_CTL */
+#define IF_UP					(0x1 << 4)
+#define IF_EN					(0x1 << 0)
+
+/* ANALOGIX_DP_CRC_CON */
+#define PSR_VID_CRC_FLUSH			(0x1 << 2)
+#define PSR_VID_CRC_ENABLE			(0x1 << 0)
+
 #endif /* _ANALOGIX_DP_REG_H */
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index 261b86d..183a336 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
 			 struct drm_connector *);
 };
 
+int analogix_dp_active_psr(struct device *dev);
+int analogix_dp_inactive_psr(struct device *dev);
+
 int analogix_dp_resume(struct device *dev);
 int analogix_dp_suspend(struct device *dev);
 
-- 
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] 35+ messages in thread

* [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function
  2016-07-01  9:18 ` Yakir Yang
@ 2016-07-01  9:19   ` Yakir Yang
  -1 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:19 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Stéphane Marchesin, Sean Paul, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, emil.l.velikov, Yakir Yang, linux-kernel,
	dri-devel, linux-samsung-soc, linux-rockchip

Alway enable the PSR function for Rockchip analogix_dp driver. If panel
don't support PSR, then the core analogix_dp would ignore this setting.

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v3:
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 52 +++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index e81e19a..80a60a6 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -32,6 +32,7 @@
 #include <drm/bridge/analogix_dp.h>
 
 #include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define RK3288_GRF_SOC_CON6		0x25c
@@ -68,11 +69,53 @@ struct rockchip_dp_device {
 	struct regmap            *grf;
 	struct reset_control     *rst;
 
+	struct delayed_work	 psr_work;
+	unsigned int		 psr_state;
+
 	const struct rockchip_dp_chip_data *data;
 
 	struct analogix_dp_plat_data plat_data;
 };
 
+static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
+{
+	struct rockchip_dp_device *dp = to_dp(encoder);
+
+	dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
+
+	if (enabled)
+		dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
+	else
+		dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+
+	schedule_delayed_work(&dp->psr_work, msecs_to_jiffies(10));
+
+	return 0;
+}
+
+static void analogix_dp_psr_work(struct work_struct *work)
+{
+	struct rockchip_dp_device *dp =
+				container_of(work, typeof(*dp), psr_work.work);
+	struct drm_crtc *crtc = dp->encoder.crtc;
+	int psr_state = dp->psr_state;
+	int vact_end;
+	int ret;
+
+	if (!crtc)
+		return;
+
+	vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
+
+	ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, 100);
+	if (ret == 0) {
+		if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+			analogix_dp_active_psr(dp->dev);
+		else
+			analogix_dp_inactive_psr(dp->dev);
+	}
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -340,12 +383,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 	dp->plat_data.power_off = rockchip_dp_powerdown;
 	dp->plat_data.get_modes = rockchip_dp_get_modes;
 
+	dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+	INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
+
+	rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
+
 	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
 }
 
 static void rockchip_dp_unbind(struct device *dev, struct device *master,
 			       void *data)
 {
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	rockchip_drm_psr_unregister(&dp->encoder);
+
 	return analogix_dp_unbind(dev, master, data);
 }
 
-- 
1.9.1

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

* [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function
@ 2016-07-01  9:19   ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-01  9:19 UTC (permalink / raw)
  To: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip,
	linux-kernel, emil.l.velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Stéphane Marchesin,
	Thierry Reding

Alway enable the PSR function for Rockchip analogix_dp driver. If panel
don't support PSR, then the core analogix_dp would ignore this setting.

Signed-off-by: Yakir Yang <ykk@rock-chips.com>
---
Changes in v3:
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 52 +++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index e81e19a..80a60a6 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -32,6 +32,7 @@
 #include <drm/bridge/analogix_dp.h>
 
 #include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define RK3288_GRF_SOC_CON6		0x25c
@@ -68,11 +69,53 @@ struct rockchip_dp_device {
 	struct regmap            *grf;
 	struct reset_control     *rst;
 
+	struct delayed_work	 psr_work;
+	unsigned int		 psr_state;
+
 	const struct rockchip_dp_chip_data *data;
 
 	struct analogix_dp_plat_data plat_data;
 };
 
+static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
+{
+	struct rockchip_dp_device *dp = to_dp(encoder);
+
+	dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
+
+	if (enabled)
+		dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
+	else
+		dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+
+	schedule_delayed_work(&dp->psr_work, msecs_to_jiffies(10));
+
+	return 0;
+}
+
+static void analogix_dp_psr_work(struct work_struct *work)
+{
+	struct rockchip_dp_device *dp =
+				container_of(work, typeof(*dp), psr_work.work);
+	struct drm_crtc *crtc = dp->encoder.crtc;
+	int psr_state = dp->psr_state;
+	int vact_end;
+	int ret;
+
+	if (!crtc)
+		return;
+
+	vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
+
+	ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, 100);
+	if (ret == 0) {
+		if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+			analogix_dp_active_psr(dp->dev);
+		else
+			analogix_dp_inactive_psr(dp->dev);
+	}
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -340,12 +383,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 	dp->plat_data.power_off = rockchip_dp_powerdown;
 	dp->plat_data.get_modes = rockchip_dp_get_modes;
 
+	dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+	INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
+
+	rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
+
 	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
 }
 
 static void rockchip_dp_unbind(struct device *dev, struct device *master,
 			       void *data)
 {
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	rockchip_drm_psr_unregister(&dp->encoder);
+
 	return analogix_dp_unbind(dev, master, data);
 }
 
-- 
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] 35+ messages in thread

* Re: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
@ 2016-07-01 15:30     ` Sean Paul
  0 siblings, 0 replies; 35+ messages in thread
From: Sean Paul @ 2016-07-01 15:30 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> VOP have integrated a hardware counter which indicate the exact display
> line that vop is scanning. And if we're interested in a specific line,
> we can set the line number to vop line_flag register, and then vop would
> generate a line_flag interrupt for it.
>
> For example eDP PSR function is interested in the vertical blanking
> period, then driver could set the line number to zero.
>
> This patch have exported a symbol that allow other driver to listen the
> line flag event with given timeout limit:
> -  rockchip_drm_wait_line_flag()
>
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> ---
> Changes in v3:
> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
> - Add 'line_flag_num_0' for RK3288/RK3036
> - Remove the notify for waiting line_flag event (Daniel)
>
> Changes in v2:
> - Introduce in v2, split VOP line flag changes out
>
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>  4 files changed, 113 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index ea39329..239b830 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>                                    struct device *dev);
>  void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>                                     struct device *dev);
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +                               unsigned int mstimeout);
> +
>  #endif /* _ROCKCHIP_DRM_DRV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index c8a62a8..cd3cac5 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -121,6 +121,8 @@ struct vop {
>         /* protected by dev->event_lock */
>         struct drm_pending_vblank_event *event;
>
> +       struct completion line_flag_completion;
> +
>         const struct vop_data *data;
>
>         uint32_t *regsbak;
> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>  }
>
> +/*
> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
> + *     the "FRAME_SYNC" interrupt.
> + * (2) the active data region of each frame ends at dsp_vact_end
> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
> + *
> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
> + * Interrupts
> + * LINE_FLAG -------------------------------+
> + * FRAME_SYNC ----+                         |
> + *                |                         |
> + *                v                         v
> + *                | Vsync | Vbp |  Vactive  | Vfp |
> + *                        ^     ^           ^     ^
> + *                        |     |           |     |
> + *                        |     |           |     |
> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
> + */
> +
> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
> +{
> +       unsigned long flags;
> +
> +       if (WARN_ON(!vop->is_enabled))
> +               return;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
> +       vop_cfg_done(vop);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
> +static void vop_line_flag_irq_disable(struct vop *vop)
> +{
> +       unsigned long flags;
> +
> +       if (WARN_ON(!vop->is_enabled))
> +               return;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
>  static void vop_enable(struct drm_crtc *crtc)
>  {
>         struct vop *vop = to_vop(crtc);
> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>                 ret = IRQ_HANDLED;
>         }
>
> +       if (active_irqs & LINE_FLAG_INTR) {
> +               if (!completion_done(&vop->line_flag_completion))
> +                       complete(&vop->line_flag_completion);

I think there's potential to miss flags here if the timing is just
right because the completion_done and complete are not atomic wrt the
wait_line_flag function. I think you need some locking around the
completion operations to ensure they're properly sequenced.

FWIW, if you're reinitializing the completion every time in
drm_wait_line_flag, you should be able to just call complete (or
complete_all) without checking if there are any waiters.


> +               active_irqs &= ~LINE_FLAG_INTR;
> +               ret = IRQ_HANDLED;
> +       }
> +
>         if (active_irqs & FS_INTR) {
>                 drm_crtc_handle_vblank(crtc);
>                 vop_handle_vblank(vop);
> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>
>         init_completion(&vop->dsp_hold_completion);
>         init_completion(&vop->wait_update_complete);
> +       init_completion(&vop->line_flag_completion);
>         crtc->port = port;
>         rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>
> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>         }
>  }
>
> +/**
> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
> + * @crtc: CRTC to enable line flag
> + * @line_num: interested line number
> + * @mstimeout: millisecond for timeout
> + *
> + * Driver would hold here until the interested line flag interrupt have
> + * happened or timeout to wait.
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +                               unsigned int mstimeout)
> +{
> +       struct vop *vop = to_vop(crtc);
> +       unsigned long jiffies_left;
> +
> +       if (!crtc || !vop->is_enabled)
> +               return -ENODEV;
> +
> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
> +               return -EINVAL;
> +
> +       reinit_completion(&vop->line_flag_completion);
> +       vop_line_flag_irq_enable(vop, line_num);
> +

This will only work for one waiter per vop. Is it worth enforcing this
explicitly to avoid weird behavior when there are more than one?

> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
> +                                                  msecs_to_jiffies(mstimeout));
> +       vop_line_flag_irq_disable(vop);
> +
> +       if (jiffies_left == 0) {
> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
> +
>  static int vop_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct platform_device *pdev = to_platform_device(dev);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..34fcd03 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -61,6 +61,9 @@ struct vop_ctrl {
>         struct vop_reg hpost_st_end;
>         struct vop_reg vpost_st_end;
>
> +       struct vop_reg line_flag_num_0;
> +       struct vop_reg line_flag_num_1;
> +

nit: you could make this an array:

struct vop_reg line_flag_num[2];


>         struct vop_reg cfg_done;
>  };
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 6f42e56..e9211c9 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>         .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>         .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>         .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>         .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>  };
>
> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>         .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>         .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>         .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>         .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>  };
>
> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>         .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>         .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>         .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>         .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>  };
>
> --
> 1.9.1
>
>

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

* Re: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
@ 2016-07-01 15:30     ` Sean Paul
  0 siblings, 0 replies; 35+ messages in thread
From: Sean Paul @ 2016-07-01 15:30 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Krzysztof Kozlowski, linux-samsung-soc, Heiko Stuebner,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Yao,
	Daniel Vetter, David Airlie, Emil Velikov, Doug Anderson,
	dri-devel, Tomasz Figa, Inki Dae, Javier Martinez Canillas,
	Jingoo Han, Stéphane Marchesin, Thierry Reding,
	Linux Kernel Mailing List

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
> VOP have integrated a hardware counter which indicate the exact display
> line that vop is scanning. And if we're interested in a specific line,
> we can set the line number to vop line_flag register, and then vop would
> generate a line_flag interrupt for it.
>
> For example eDP PSR function is interested in the vertical blanking
> period, then driver could set the line number to zero.
>
> This patch have exported a symbol that allow other driver to listen the
> line flag event with given timeout limit:
> -  rockchip_drm_wait_line_flag()
>
> Signed-off-by: Yakir Yang <ykk-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> ---
> Changes in v3:
> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
> - Add 'line_flag_num_0' for RK3288/RK3036
> - Remove the notify for waiting line_flag event (Daniel)
>
> Changes in v2:
> - Introduce in v2, split VOP line flag changes out
>
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>  4 files changed, 113 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index ea39329..239b830 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>                                    struct device *dev);
>  void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>                                     struct device *dev);
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +                               unsigned int mstimeout);
> +
>  #endif /* _ROCKCHIP_DRM_DRV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index c8a62a8..cd3cac5 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -121,6 +121,8 @@ struct vop {
>         /* protected by dev->event_lock */
>         struct drm_pending_vblank_event *event;
>
> +       struct completion line_flag_completion;
> +
>         const struct vop_data *data;
>
>         uint32_t *regsbak;
> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>  }
>
> +/*
> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
> + *     the "FRAME_SYNC" interrupt.
> + * (2) the active data region of each frame ends at dsp_vact_end
> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
> + *
> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
> + * Interrupts
> + * LINE_FLAG -------------------------------+
> + * FRAME_SYNC ----+                         |
> + *                |                         |
> + *                v                         v
> + *                | Vsync | Vbp |  Vactive  | Vfp |
> + *                        ^     ^           ^     ^
> + *                        |     |           |     |
> + *                        |     |           |     |
> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
> + */
> +
> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
> +{
> +       unsigned long flags;
> +
> +       if (WARN_ON(!vop->is_enabled))
> +               return;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
> +       vop_cfg_done(vop);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
> +static void vop_line_flag_irq_disable(struct vop *vop)
> +{
> +       unsigned long flags;
> +
> +       if (WARN_ON(!vop->is_enabled))
> +               return;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
>  static void vop_enable(struct drm_crtc *crtc)
>  {
>         struct vop *vop = to_vop(crtc);
> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>                 ret = IRQ_HANDLED;
>         }
>
> +       if (active_irqs & LINE_FLAG_INTR) {
> +               if (!completion_done(&vop->line_flag_completion))
> +                       complete(&vop->line_flag_completion);

I think there's potential to miss flags here if the timing is just
right because the completion_done and complete are not atomic wrt the
wait_line_flag function. I think you need some locking around the
completion operations to ensure they're properly sequenced.

FWIW, if you're reinitializing the completion every time in
drm_wait_line_flag, you should be able to just call complete (or
complete_all) without checking if there are any waiters.


> +               active_irqs &= ~LINE_FLAG_INTR;
> +               ret = IRQ_HANDLED;
> +       }
> +
>         if (active_irqs & FS_INTR) {
>                 drm_crtc_handle_vblank(crtc);
>                 vop_handle_vblank(vop);
> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>
>         init_completion(&vop->dsp_hold_completion);
>         init_completion(&vop->wait_update_complete);
> +       init_completion(&vop->line_flag_completion);
>         crtc->port = port;
>         rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>
> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>         }
>  }
>
> +/**
> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
> + * @crtc: CRTC to enable line flag
> + * @line_num: interested line number
> + * @mstimeout: millisecond for timeout
> + *
> + * Driver would hold here until the interested line flag interrupt have
> + * happened or timeout to wait.
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +                               unsigned int mstimeout)
> +{
> +       struct vop *vop = to_vop(crtc);
> +       unsigned long jiffies_left;
> +
> +       if (!crtc || !vop->is_enabled)
> +               return -ENODEV;
> +
> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
> +               return -EINVAL;
> +
> +       reinit_completion(&vop->line_flag_completion);
> +       vop_line_flag_irq_enable(vop, line_num);
> +

This will only work for one waiter per vop. Is it worth enforcing this
explicitly to avoid weird behavior when there are more than one?

> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
> +                                                  msecs_to_jiffies(mstimeout));
> +       vop_line_flag_irq_disable(vop);
> +
> +       if (jiffies_left == 0) {
> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
> +
>  static int vop_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct platform_device *pdev = to_platform_device(dev);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..34fcd03 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -61,6 +61,9 @@ struct vop_ctrl {
>         struct vop_reg hpost_st_end;
>         struct vop_reg vpost_st_end;
>
> +       struct vop_reg line_flag_num_0;
> +       struct vop_reg line_flag_num_1;
> +

nit: you could make this an array:

struct vop_reg line_flag_num[2];


>         struct vop_reg cfg_done;
>  };
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 6f42e56..e9211c9 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>         .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>         .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>         .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>         .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>  };
>
> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>         .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>         .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>         .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>         .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>  };
>
> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>         .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>         .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>         .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>         .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>  };
>
> --
> 1.9.1
>
>

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

* Re: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
@ 2016-07-01 15:32       ` Sean Paul
  0 siblings, 0 replies; 35+ messages in thread
From: Sean Paul @ 2016-07-01 15:32 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

On Fri, Jul 1, 2016 at 11:30 AM, Sean Paul <seanpaul@chromium.org> wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>> VOP have integrated a hardware counter which indicate the exact display
>> line that vop is scanning. And if we're interested in a specific line,
>> we can set the line number to vop line_flag register, and then vop would
>> generate a line_flag interrupt for it.
>>
>> For example eDP PSR function is interested in the vertical blanking
>> period, then driver could set the line number to zero.
>>
>> This patch have exported a symbol that allow other driver to listen the
>> line flag event with given timeout limit:
>> -  rockchip_drm_wait_line_flag()
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> ---
>> Changes in v3:
>> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
>> - Add 'line_flag_num_0' for RK3288/RK3036
>> - Remove the notify for waiting line_flag event (Daniel)
>>
>> Changes in v2:
>> - Introduce in v2, split VOP line flag changes out
>>
>>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>>  4 files changed, 113 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index ea39329..239b830 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>>                                    struct device *dev);
>>  void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>>                                     struct device *dev);
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +                               unsigned int mstimeout);
>> +
>>  #endif /* _ROCKCHIP_DRM_DRV_H_ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index c8a62a8..cd3cac5 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -121,6 +121,8 @@ struct vop {
>>         /* protected by dev->event_lock */
>>         struct drm_pending_vblank_event *event;
>>
>> +       struct completion line_flag_completion;
>> +
>>         const struct vop_data *data;
>>
>>         uint32_t *regsbak;
>> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>>  }
>>
>> +/*
>> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
>> + *     the "FRAME_SYNC" interrupt.
>> + * (2) the active data region of each frame ends at dsp_vact_end
>> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
>> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
>> + *
>> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
>> + * Interrupts
>> + * LINE_FLAG -------------------------------+
>> + * FRAME_SYNC ----+                         |
>> + *                |                         |
>> + *                v                         v
>> + *                | Vsync | Vbp |  Vactive  | Vfp |
>> + *                        ^     ^           ^     ^
>> + *                        |     |           |     |
>> + *                        |     |           |     |
>> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
>> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
>> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
>> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
>> + */
>> +
>> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
>> +{
>> +       unsigned long flags;
>> +
>> +       if (WARN_ON(!vop->is_enabled))
>> +               return;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
>> +       vop_cfg_done(vop);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>> +static void vop_line_flag_irq_disable(struct vop *vop)
>> +{
>> +       unsigned long flags;
>> +
>> +       if (WARN_ON(!vop->is_enabled))
>> +               return;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>>  static void vop_enable(struct drm_crtc *crtc)
>>  {
>>         struct vop *vop = to_vop(crtc);
>> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>>                 ret = IRQ_HANDLED;
>>         }
>>
>> +       if (active_irqs & LINE_FLAG_INTR) {
>> +               if (!completion_done(&vop->line_flag_completion))
>> +                       complete(&vop->line_flag_completion);
>
> I think there's potential to miss flags here if the timing is just
> right because the completion_done and complete are not atomic wrt the
> wait_line_flag function. I think you need some locking around the
> completion operations to ensure they're properly sequenced.
>
> FWIW, if you're reinitializing the completion every time in
> drm_wait_line_flag, you should be able to just call complete (or
> complete_all) without checking if there are any waiters.
>

I should add, if you just call complete/complete_all() here, you
shouldn't need to add locking (assuming only one waiter).

>
>> +               active_irqs &= ~LINE_FLAG_INTR;
>> +               ret = IRQ_HANDLED;
>> +       }
>> +
>>         if (active_irqs & FS_INTR) {
>>                 drm_crtc_handle_vblank(crtc);
>>                 vop_handle_vblank(vop);
>> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>>
>>         init_completion(&vop->dsp_hold_completion);
>>         init_completion(&vop->wait_update_complete);
>> +       init_completion(&vop->line_flag_completion);
>>         crtc->port = port;
>>         rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>>
>> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>>         }
>>  }
>>
>> +/**
>> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
>> + * @crtc: CRTC to enable line flag
>> + * @line_num: interested line number
>> + * @mstimeout: millisecond for timeout
>> + *
>> + * Driver would hold here until the interested line flag interrupt have
>> + * happened or timeout to wait.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +                               unsigned int mstimeout)
>> +{
>> +       struct vop *vop = to_vop(crtc);
>> +       unsigned long jiffies_left;
>> +
>> +       if (!crtc || !vop->is_enabled)
>> +               return -ENODEV;
>> +
>> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
>> +               return -EINVAL;
>> +
>> +       reinit_completion(&vop->line_flag_completion);
>> +       vop_line_flag_irq_enable(vop, line_num);
>> +
>
> This will only work for one waiter per vop. Is it worth enforcing this
> explicitly to avoid weird behavior when there are more than one?
>
>> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
>> +                                                  msecs_to_jiffies(mstimeout));
>> +       vop_line_flag_irq_disable(vop);
>> +
>> +       if (jiffies_left == 0) {
>> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
>> +               return -ETIMEDOUT;
>> +       }
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
>> +
>>  static int vop_bind(struct device *dev, struct device *master, void *data)
>>  {
>>         struct platform_device *pdev = to_platform_device(dev);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> index ff4f52e..34fcd03 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -61,6 +61,9 @@ struct vop_ctrl {
>>         struct vop_reg hpost_st_end;
>>         struct vop_reg vpost_st_end;
>>
>> +       struct vop_reg line_flag_num_0;
>> +       struct vop_reg line_flag_num_1;
>> +
>
> nit: you could make this an array:
>
> struct vop_reg line_flag_num[2];
>
>
>>         struct vop_reg cfg_done;
>>  };
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> index 6f42e56..e9211c9 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>>         .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>>         .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>>         .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>>         .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>>         .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>         .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>         .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>>         .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>         .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>         .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>         .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
>> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>>         .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> --
>> 1.9.1
>>
>>

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

* Re: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
@ 2016-07-01 15:32       ` Sean Paul
  0 siblings, 0 replies; 35+ messages in thread
From: Sean Paul @ 2016-07-01 15:32 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Krzysztof Kozlowski, linux-samsung-soc, Heiko Stuebner,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Yao,
	Daniel Vetter, David Airlie, Emil Velikov, Doug Anderson,
	dri-devel, Tomasz Figa, Inki Dae, Javier Martinez Canillas,
	Jingoo Han, Stéphane Marchesin, Thierry Reding,
	Linux Kernel Mailing List

On Fri, Jul 1, 2016 at 11:30 AM, Sean Paul <seanpaul-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
>> VOP have integrated a hardware counter which indicate the exact display
>> line that vop is scanning. And if we're interested in a specific line,
>> we can set the line number to vop line_flag register, and then vop would
>> generate a line_flag interrupt for it.
>>
>> For example eDP PSR function is interested in the vertical blanking
>> period, then driver could set the line number to zero.
>>
>> This patch have exported a symbol that allow other driver to listen the
>> line flag event with given timeout limit:
>> -  rockchip_drm_wait_line_flag()
>>
>> Signed-off-by: Yakir Yang <ykk-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> ---
>> Changes in v3:
>> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
>> - Add 'line_flag_num_0' for RK3288/RK3036
>> - Remove the notify for waiting line_flag event (Daniel)
>>
>> Changes in v2:
>> - Introduce in v2, split VOP line flag changes out
>>
>>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>>  4 files changed, 113 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index ea39329..239b830 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>>                                    struct device *dev);
>>  void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>>                                     struct device *dev);
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +                               unsigned int mstimeout);
>> +
>>  #endif /* _ROCKCHIP_DRM_DRV_H_ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index c8a62a8..cd3cac5 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -121,6 +121,8 @@ struct vop {
>>         /* protected by dev->event_lock */
>>         struct drm_pending_vblank_event *event;
>>
>> +       struct completion line_flag_completion;
>> +
>>         const struct vop_data *data;
>>
>>         uint32_t *regsbak;
>> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>>  }
>>
>> +/*
>> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
>> + *     the "FRAME_SYNC" interrupt.
>> + * (2) the active data region of each frame ends at dsp_vact_end
>> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
>> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
>> + *
>> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
>> + * Interrupts
>> + * LINE_FLAG -------------------------------+
>> + * FRAME_SYNC ----+                         |
>> + *                |                         |
>> + *                v                         v
>> + *                | Vsync | Vbp |  Vactive  | Vfp |
>> + *                        ^     ^           ^     ^
>> + *                        |     |           |     |
>> + *                        |     |           |     |
>> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
>> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
>> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
>> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
>> + */
>> +
>> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
>> +{
>> +       unsigned long flags;
>> +
>> +       if (WARN_ON(!vop->is_enabled))
>> +               return;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
>> +       vop_cfg_done(vop);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>> +static void vop_line_flag_irq_disable(struct vop *vop)
>> +{
>> +       unsigned long flags;
>> +
>> +       if (WARN_ON(!vop->is_enabled))
>> +               return;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>>  static void vop_enable(struct drm_crtc *crtc)
>>  {
>>         struct vop *vop = to_vop(crtc);
>> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>>                 ret = IRQ_HANDLED;
>>         }
>>
>> +       if (active_irqs & LINE_FLAG_INTR) {
>> +               if (!completion_done(&vop->line_flag_completion))
>> +                       complete(&vop->line_flag_completion);
>
> I think there's potential to miss flags here if the timing is just
> right because the completion_done and complete are not atomic wrt the
> wait_line_flag function. I think you need some locking around the
> completion operations to ensure they're properly sequenced.
>
> FWIW, if you're reinitializing the completion every time in
> drm_wait_line_flag, you should be able to just call complete (or
> complete_all) without checking if there are any waiters.
>

I should add, if you just call complete/complete_all() here, you
shouldn't need to add locking (assuming only one waiter).

>
>> +               active_irqs &= ~LINE_FLAG_INTR;
>> +               ret = IRQ_HANDLED;
>> +       }
>> +
>>         if (active_irqs & FS_INTR) {
>>                 drm_crtc_handle_vblank(crtc);
>>                 vop_handle_vblank(vop);
>> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>>
>>         init_completion(&vop->dsp_hold_completion);
>>         init_completion(&vop->wait_update_complete);
>> +       init_completion(&vop->line_flag_completion);
>>         crtc->port = port;
>>         rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>>
>> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>>         }
>>  }
>>
>> +/**
>> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
>> + * @crtc: CRTC to enable line flag
>> + * @line_num: interested line number
>> + * @mstimeout: millisecond for timeout
>> + *
>> + * Driver would hold here until the interested line flag interrupt have
>> + * happened or timeout to wait.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +                               unsigned int mstimeout)
>> +{
>> +       struct vop *vop = to_vop(crtc);
>> +       unsigned long jiffies_left;
>> +
>> +       if (!crtc || !vop->is_enabled)
>> +               return -ENODEV;
>> +
>> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
>> +               return -EINVAL;
>> +
>> +       reinit_completion(&vop->line_flag_completion);
>> +       vop_line_flag_irq_enable(vop, line_num);
>> +
>
> This will only work for one waiter per vop. Is it worth enforcing this
> explicitly to avoid weird behavior when there are more than one?
>
>> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
>> +                                                  msecs_to_jiffies(mstimeout));
>> +       vop_line_flag_irq_disable(vop);
>> +
>> +       if (jiffies_left == 0) {
>> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
>> +               return -ETIMEDOUT;
>> +       }
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
>> +
>>  static int vop_bind(struct device *dev, struct device *master, void *data)
>>  {
>>         struct platform_device *pdev = to_platform_device(dev);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> index ff4f52e..34fcd03 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -61,6 +61,9 @@ struct vop_ctrl {
>>         struct vop_reg hpost_st_end;
>>         struct vop_reg vpost_st_end;
>>
>> +       struct vop_reg line_flag_num_0;
>> +       struct vop_reg line_flag_num_1;
>> +
>
> nit: you could make this an array:
>
> struct vop_reg line_flag_num[2];
>
>
>>         struct vop_reg cfg_done;
>>  };
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> index 6f42e56..e9211c9 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>>         .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>>         .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>>         .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>>         .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>>         .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>         .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>         .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>>         .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>         .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>         .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>         .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
>> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>>         .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> --
>> 1.9.1
>>
>>

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
  2016-07-01  9:19   ` Yakir Yang
  (?)
@ 2016-07-01 18:00   ` Sean Paul
  2016-07-08  2:12       ` Yakir Yang
  2016-07-12 12:38       ` Daniel Vetter
  -1 siblings, 2 replies; 35+ messages in thread
From: Sean Paul @ 2016-07-01 18:00 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> The PSR driver have exported four symbols for specific device driver:
> - rockchip_drm_psr_register()
> - rockchip_drm_psr_unregister()
> - rockchip_drm_psr_enable()
> - rockchip_drm_psr_disable()
> - rockchip_drm_psr_flush()
>
> Encoder driver should call the register/unregister interfaces to hook
> itself into common PSR driver, encoder have implement the 'psr_set'
> callback which use the set PSR state in hardware side.
>
> Crtc driver would call the enable/disable interfaces when vblank is
> enable/disable, after that the common PSR driver would call the encoder
> registered callback to set the PSR state.
>

This feels overly complicated. It seems like you could cut out a bunch
of code by just coding the psr functions into vop and
analogix_dp-rockchip. I suppose the only reason to keep it abstracted
would be if you plan on supporting psr in a different encoder or crtc
in rockchip, or if you're planning on moving this into drm core.

Perhaps others will disagree with this sentiment and this is the right
thing to do.

> Fb driver would call the flush interface in 'fb->dirty' callback, this
> helper function would force all PSR enabled encoders to exit from PSR
> for 3 seconds.
>
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> ---
> Changes in v3:
> - split the psr flow into an common abstracted PSR driver
> - implement the 'fb->dirty' callback function (Daniel)
> - avoid to use notify to acqiure for vact event (Daniel)
> - remove psr_active() callback which introduce in v2
>
> Changes in v2: None
>
>  drivers/gpu/drm/rockchip/Makefile           |   2 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
>  5 files changed, 249 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index 05d0713..9746365 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -3,7 +3,7 @@
>  # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>
>  rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> -               rockchip_drm_gem.o rockchip_drm_vop.o
> +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>
>  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 20f12bc..0fec18f 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -21,6 +21,7 @@
>
>  #include "rockchip_drm_drv.h"
>  #include "rockchip_drm_gem.h"
> +#include "rockchip_drm_psr.h"
>
>  #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>
> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>                                      rockchip_fb->obj[0], handle);
>  }
>
> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
> +                                struct drm_file *file,
> +                                unsigned int flags, unsigned int color,
> +                                struct drm_clip_rect *clips,
> +                                unsigned int num_clips)
> +{
> +       rockchip_drm_psr_flush();
> +       return 0;
> +}
> +
>  static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>         .destroy        = rockchip_drm_fb_destroy,
>         .create_handle  = rockchip_drm_fb_create_handle,
> +       .dirty          = rockchip_drm_fb_dirty,
>  };
>
>  static struct rockchip_drm_fb *
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> new file mode 100644
> index 0000000..c044443
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -0,0 +1,200 @@
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_psr.h"
> +
> +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
> +
> +static LIST_HEAD(psr_list);
> +static DEFINE_MUTEX(psr_list_mutex);

I'm not crazy about these globals. Perhaps you can initialize them
with the rockchip driver and tuck them in a driver-level struct
(rockchip_drm_private or something).


> +
> +enum psr_state {
> +       PSR_FLUSH,
> +       PSR_ENABLE,
> +       PSR_DISABLE,
> +};
> +
> +struct psr_drv {
> +       struct list_head list;
> +       enum psr_state state;
> +       struct mutex state_mutex;
> +
> +       struct timer_list flush_timer;
> +
> +       struct drm_encoder *encoder;
> +       int (*set)(struct drm_encoder *encoder, bool enable);
> +};
> +
> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
> +{
> +       struct psr_drv *psr;
> +
> +       mutex_lock(&psr_list_mutex);
> +       list_for_each_entry(psr, &psr_list, list) {
> +               if (psr->encoder->crtc == crtc) {
> +                       mutex_unlock(&psr_list_mutex);
> +                       return psr;
> +               }
> +       }
> +       mutex_unlock(&psr_list_mutex);
> +
> +       return ERR_PTR(-ENODEV);
> +}
> +
> +static void psr_enable(struct psr_drv *psr)
> +{
> +       if (psr->state == PSR_ENABLE)
> +               return;

Should you be worried about races by accessing this outside of the state mutex?

> +
> +       mutex_lock(&psr->state_mutex);
> +       psr->state = PSR_ENABLE;
> +       psr->set(psr->encoder, true);
> +       mutex_unlock(&psr->state_mutex);
> +}
> +
> +static void psr_disable(struct psr_drv *psr)
> +{
> +       if (psr->state == PSR_DISABLE)
> +               return;
> +
> +       mutex_lock(&psr->state_mutex);
> +       psr->state = PSR_DISABLE;
> +       psr->set(psr->encoder, false);
> +       mutex_unlock(&psr->state_mutex);
> +}
> +
> +static void psr_flush_handler(unsigned long data)
> +{
> +       struct psr_drv *psr = (struct psr_drv *)data;
> +
> +       if (!psr || psr->state != PSR_FLUSH)
> +               return;
> +
> +       psr_enable(psr);
> +}
> +
> +/**
> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
> + * @crtc: CRTC to obtain the PSR encoder
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
> +{
> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> +
> +       if (IS_ERR(psr))
> +               return PTR_ERR(psr);
> +
> +       psr_enable(psr);
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
> +
> +/**
> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
> + * @crtc: CRTC to obtain the PSR encoder
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
> +{
> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> +
> +       if (IS_ERR(psr))
> +               return PTR_ERR(psr);
> +
> +       psr_disable(psr);
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
> +
> +/**
> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
> + *
> + * Disable the PSR function for all registered encoders, and then enable the
> + * PSR function back after 5 second. If encoder PSR state have been changed

s/5 second/PSR_FLUSH_TIMEOUT/

> + * during flush time, then keep the state no change after flush timeout.
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +void rockchip_drm_psr_flush(void)
> +{
> +       struct psr_drv *psr;
> +
> +       mutex_lock(&psr_list_mutex);
> +       list_for_each_entry(psr, &psr_list, list) {
> +               if (psr->state == PSR_DISABLE)
> +                       continue;
> +
> +               mod_timer(&psr->flush_timer,
> +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
> +
> +               psr_disable(psr);
> +               psr->state = PSR_FLUSH;

This is set outside of state_mutex, is that intentional?

> +       }
> +       mutex_unlock(&psr_list_mutex);
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
> +
> +/**
> + * rockchip_drm_psr_register - register encoder to psr driver
> + * @encoder: encoder that obtain the PSR function
> + * @psr_set: call back to set PSR state
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> +                             int (*psr_set)(struct drm_encoder *, bool enable))
> +{
> +       struct psr_drv *psr;
> +
> +       if (!encoder || !psr_set)
> +               return -EINVAL;
> +
> +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
> +       if (!psr)
> +               return -ENOMEM;
> +
> +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
> +
> +       mutex_init(&psr->state_mutex);
> +
> +       psr->state = PSR_DISABLE;
> +       psr->encoder = encoder;
> +       psr->set = psr_set;
> +
> +       mutex_lock(&psr_list_mutex);
> +       list_add_tail(&psr->list, &psr_list);
> +       mutex_unlock(&psr_list_mutex);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_register);
> +
> +/**
> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
> + * @encoder: encoder that obtain the PSR function
> + * @psr_set: call back to set PSR state
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
> +{
> +       struct psr_drv *psr;
> +
> +       mutex_lock(&psr_list_mutex);
> +       list_for_each_entry(psr, &psr_list, list) {
> +               if (psr->encoder == encoder) {
> +                       del_timer(&psr->flush_timer);
> +                       list_del(&psr->list);
> +                       kfree(psr);
> +               }
> +       }
> +       mutex_unlock(&psr_list_mutex);
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> new file mode 100644
> index 0000000..622f605
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> @@ -0,0 +1,12 @@
> +#ifndef __ROCKCHIP_DRM_PSR___
> +#define __ROCKCHIP_DRM_PSR___
> +
> +void rockchip_drm_psr_flush(void);
> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
> +
> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> +                           int (*psr_set)(struct drm_encoder *, bool enable));
> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
> +
> +#endif /* __ROCKCHIP_DRM_PSR__ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index cd3cac5..3c6dfc5 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -34,6 +34,7 @@
>  #include "rockchip_drm_drv.h"
>  #include "rockchip_drm_gem.h"
>  #include "rockchip_drm_fb.h"
> +#include "rockchip_drm_psr.h"
>  #include "rockchip_drm_vop.h"
>
>  #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
> @@ -121,6 +122,9 @@ struct vop {
>         /* protected by dev->event_lock */
>         struct drm_pending_vblank_event *event;
>
> +       bool psr_enabled;
> +       struct delayed_work psr_work;
> +
>         struct completion line_flag_completion;
>
>         const struct vop_data *data;
> @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>         .atomic_destroy_state = vop_atomic_plane_destroy_state,
>  };
>
> +static void vop_psr_work(struct work_struct *work)
> +{
> +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
> +
> +       if (vop->psr_enabled)
> +               rockchip_drm_psr_enable(&vop->crtc);
> +       else
> +               rockchip_drm_psr_disable(&vop->crtc);
> +}
> +
>  static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>  {
>         struct vop *vop = to_vop(crtc);
> @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>
>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>
> +       vop->psr_enabled = false;
> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
> +
>         return 0;
>  }
>
> @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>         VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>
>         spin_unlock_irqrestore(&vop->irq_lock, flags);
> +
> +       vop->psr_enabled = true;
> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>  }
>
>  static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
> @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>                 return ret;
>
>         pm_runtime_enable(&pdev->dev);
> +
> +       vop->psr_enabled = false;
> +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
> +
>         return 0;
>  }
>
> --
> 1.9.1
>
>

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

* Re: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
  2016-07-01  9:19   ` Yakir Yang
@ 2016-07-01 19:46     ` Sean Paul
  -1 siblings, 0 replies; 35+ messages in thread
From: Sean Paul @ 2016-07-01 19:46 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> The full name of PSR is Panel Self Refresh, panel device could refresh
> itself with the hardware framebuffer in panel, this would make lots of
> sense to save the power consumption.
>
> This patch have exported two symbols for platform driver to implement
> the PSR function in hardware side:
> - analogix_dp_active_psr()
> - analogix_dp_inactive_psr()
>
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> ---
> Changes in v3:
> - split analogix_dp_enable_psr(), make it more clearly
>     analogix_dp_detect_sink_psr()
>     analogix_dp_enable_sink_psr()
> - remove some nosie register setting comments
>
> Changes in v2:
> - introduce in v2, splite the common Analogix DP changes out
>
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 ++++++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 ++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>  include/drm/bridge/analogix_dp.h                   |  3 +
>  5 files changed, 153 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..b557097 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
>         return 0;
>  }
>
> +int analogix_dp_active_psr(struct device *dev)
> +{
> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +
> +       if (!dp->psr_support)
> +               return -EINVAL;
> +
> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
> +                                EDP_VSC_PSR_CRC_VALUES_VALID);
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
> +
> +int analogix_dp_inactive_psr(struct device *dev)
> +{
> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +
> +       if (!dp->psr_support)
> +               return -EINVAL;
> +
> +       analogix_dp_send_psr_spd(dp, 0);
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
> +
> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
> +{
> +       unsigned char psr_version;
> +
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
> +

This info message is likely to be spammy since it's printed everytime
the panel toggle on. Perhaps downgrade to debug level.

> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
> +}
> +
> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)

Return type is int, but the function never fails and you don't check
the return value when calling it. Seems like this should be void.

> +{
> +       unsigned char psr_en;
> +
> +       /* Disable psr function */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en &= ~DP_PSR_ENABLE;
> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       /* Main-Link transmitter remains active during PSR active states */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;

Why read psr_en if you're just going to overwrite it? Perhaps you meant |= here.

> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       /* Enable psr function */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
> +                DP_PSR_CRC_VERIFICATION;

Again, no need to read if you're just overwriting.

> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       analogix_dp_enable_psr_crc(dp);
> +
> +       return 0;
> +}
> +
>  static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
>  {
>         int i;
> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
>
>         /* Enable video */
>         analogix_dp_start_video(dp);
> +
> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
> +       if (dp->psr_support)
> +               analogix_dp_enable_sink_psr(dp);
>  }
>
>  int analogix_dp_get_modes(struct drm_connector *connector)
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> index b456380..6ca5dde 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>         int                     hpd_gpio;
>         bool                    force_hpd;
>         unsigned char           edid[EDID_BLOCK_LENGTH * 2];
> +       bool                    psr_support;
>
>         struct analogix_dp_plat_data *plat_data;
>  };
> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
>  void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
>  void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>  void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
> +
>  #endif /* _ANALOGIX_DP_CORE_H */
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> index 48030f0..e8372c7 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
>         reg |= SCRAMBLING_DISABLE;
>         writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>  }
> +
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
> +{
> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
> +
> +       usleep_range(10, 20);

Is this sleep arbitrary, or documented somewhere? Could you add a
comment explaining how this was arrived at?

> +
> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
> +}
> +
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
> +{
> +       unsigned int val;
> +
> +       /* don't send info frame */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val &= ~IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +       /* configure single frame update mode */
> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
> +
> +       /* configure VSC HB0 ~ HB3 */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
> +
> +       /* configure VSC HB0 ~ HB3 */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
> +
> +       /* configure DB0 / DB1 values */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);

Lots of hardcoded values here. I think this could be cleaned up.

> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
> +
> +       /* set reuse spd inforframe */
> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +       val |= REUSE_SPD_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +
> +       /* mark info frame update */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val = (val | IF_UP) & ~IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +       /* send info frame */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val |= IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +}
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> index cdcc6c5..a2698e4 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> @@ -22,6 +22,8 @@
>  #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>  #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>
> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
> +
>  #define ANALOGIX_DP_PLL_REG_1                  0xfc
>  #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>  #define ANALOGIX_DP_PLL_REG_3                  0x9e8
> @@ -30,6 +32,21 @@
>
>  #define ANALOGIX_DP_PD                         0x12c
>
> +#define ANALOGIX_DP_IF_TYPE                    0x244
> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
> +#define ANALOGIX_DP_SPD_HB2                    0x300
> +#define ANALOGIX_DP_SPD_HB3                    0x304
> +#define ANALOGIX_DP_SPD_PB0                    0x308
> +#define ANALOGIX_DP_SPD_PB1                    0x30C
> +#define ANALOGIX_DP_SPD_PB2                    0x310
> +#define ANALOGIX_DP_SPD_PB3                    0x314
> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
> +
>  #define ANALOGIX_DP_LANE_MAP                   0x35C
>
>  #define ANALOGIX_DP_ANALOG_CTL_1               0x370
> @@ -103,6 +120,8 @@
>
>  #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>
> +#define ANALOGIX_DP_CRC_CON                    0x890
> +
>  /* ANALOGIX_DP_TX_SW_RESET */
>  #define RESET_DP_TX                            (0x1 << 0)
>
> @@ -151,6 +170,7 @@
>  #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>  #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>  #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
> +#define REUSE_SPD_EN                           (0x1 << 3)
>
>  /* ANALOGIX_DP_VIDEO_CTL_8 */
>  #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
> @@ -376,4 +396,12 @@
>  #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>  #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>
> +/* ANALOGIX_DP_PKT_SEND_CTL */
> +#define IF_UP                                  (0x1 << 4)
> +#define IF_EN                                  (0x1 << 0)
> +
> +/* ANALOGIX_DP_CRC_CON */
> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
> +
>  #endif /* _ANALOGIX_DP_REG_H */
> diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
> index 261b86d..183a336 100644
> --- a/include/drm/bridge/analogix_dp.h
> +++ b/include/drm/bridge/analogix_dp.h
> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>                          struct drm_connector *);
>  };
>
> +int analogix_dp_active_psr(struct device *dev);
> +int analogix_dp_inactive_psr(struct device *dev);

Why active/inactive instead of enable/disable, which is used everywhere else?

> +
>  int analogix_dp_resume(struct device *dev);
>  int analogix_dp_suspend(struct device *dev);
>
> --
> 1.9.1
>
>

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

* Re: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
@ 2016-07-01 19:46     ` Sean Paul
  0 siblings, 0 replies; 35+ messages in thread
From: Sean Paul @ 2016-07-01 19:46 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip, Mark Yao,
	Daniel Vetter, Emil Velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Jingoo Han,
	Stéphane Marchesin, Thierry Reding,
	Linux Kernel Mailing List

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> The full name of PSR is Panel Self Refresh, panel device could refresh
> itself with the hardware framebuffer in panel, this would make lots of
> sense to save the power consumption.
>
> This patch have exported two symbols for platform driver to implement
> the PSR function in hardware side:
> - analogix_dp_active_psr()
> - analogix_dp_inactive_psr()
>
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> ---
> Changes in v3:
> - split analogix_dp_enable_psr(), make it more clearly
>     analogix_dp_detect_sink_psr()
>     analogix_dp_enable_sink_psr()
> - remove some nosie register setting comments
>
> Changes in v2:
> - introduce in v2, splite the common Analogix DP changes out
>
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 ++++++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 ++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>  include/drm/bridge/analogix_dp.h                   |  3 +
>  5 files changed, 153 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..b557097 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
>         return 0;
>  }
>
> +int analogix_dp_active_psr(struct device *dev)
> +{
> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +
> +       if (!dp->psr_support)
> +               return -EINVAL;
> +
> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
> +                                EDP_VSC_PSR_CRC_VALUES_VALID);
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
> +
> +int analogix_dp_inactive_psr(struct device *dev)
> +{
> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +
> +       if (!dp->psr_support)
> +               return -EINVAL;
> +
> +       analogix_dp_send_psr_spd(dp, 0);
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
> +
> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
> +{
> +       unsigned char psr_version;
> +
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
> +

This info message is likely to be spammy since it's printed everytime
the panel toggle on. Perhaps downgrade to debug level.

> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
> +}
> +
> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)

Return type is int, but the function never fails and you don't check
the return value when calling it. Seems like this should be void.

> +{
> +       unsigned char psr_en;
> +
> +       /* Disable psr function */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en &= ~DP_PSR_ENABLE;
> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       /* Main-Link transmitter remains active during PSR active states */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;

Why read psr_en if you're just going to overwrite it? Perhaps you meant |= here.

> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       /* Enable psr function */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
> +                DP_PSR_CRC_VERIFICATION;

Again, no need to read if you're just overwriting.

> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       analogix_dp_enable_psr_crc(dp);
> +
> +       return 0;
> +}
> +
>  static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
>  {
>         int i;
> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
>
>         /* Enable video */
>         analogix_dp_start_video(dp);
> +
> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
> +       if (dp->psr_support)
> +               analogix_dp_enable_sink_psr(dp);
>  }
>
>  int analogix_dp_get_modes(struct drm_connector *connector)
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> index b456380..6ca5dde 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>         int                     hpd_gpio;
>         bool                    force_hpd;
>         unsigned char           edid[EDID_BLOCK_LENGTH * 2];
> +       bool                    psr_support;
>
>         struct analogix_dp_plat_data *plat_data;
>  };
> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
>  void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
>  void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>  void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
> +
>  #endif /* _ANALOGIX_DP_CORE_H */
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> index 48030f0..e8372c7 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
>         reg |= SCRAMBLING_DISABLE;
>         writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>  }
> +
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
> +{
> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
> +
> +       usleep_range(10, 20);

Is this sleep arbitrary, or documented somewhere? Could you add a
comment explaining how this was arrived at?

> +
> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
> +}
> +
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
> +{
> +       unsigned int val;
> +
> +       /* don't send info frame */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val &= ~IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +       /* configure single frame update mode */
> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
> +
> +       /* configure VSC HB0 ~ HB3 */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
> +
> +       /* configure VSC HB0 ~ HB3 */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
> +
> +       /* configure DB0 / DB1 values */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);

Lots of hardcoded values here. I think this could be cleaned up.

> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
> +
> +       /* set reuse spd inforframe */
> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +       val |= REUSE_SPD_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +
> +       /* mark info frame update */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val = (val | IF_UP) & ~IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +       /* send info frame */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val |= IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +}
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> index cdcc6c5..a2698e4 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> @@ -22,6 +22,8 @@
>  #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>  #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>
> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
> +
>  #define ANALOGIX_DP_PLL_REG_1                  0xfc
>  #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>  #define ANALOGIX_DP_PLL_REG_3                  0x9e8
> @@ -30,6 +32,21 @@
>
>  #define ANALOGIX_DP_PD                         0x12c
>
> +#define ANALOGIX_DP_IF_TYPE                    0x244
> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
> +#define ANALOGIX_DP_SPD_HB2                    0x300
> +#define ANALOGIX_DP_SPD_HB3                    0x304
> +#define ANALOGIX_DP_SPD_PB0                    0x308
> +#define ANALOGIX_DP_SPD_PB1                    0x30C
> +#define ANALOGIX_DP_SPD_PB2                    0x310
> +#define ANALOGIX_DP_SPD_PB3                    0x314
> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
> +
>  #define ANALOGIX_DP_LANE_MAP                   0x35C
>
>  #define ANALOGIX_DP_ANALOG_CTL_1               0x370
> @@ -103,6 +120,8 @@
>
>  #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>
> +#define ANALOGIX_DP_CRC_CON                    0x890
> +
>  /* ANALOGIX_DP_TX_SW_RESET */
>  #define RESET_DP_TX                            (0x1 << 0)
>
> @@ -151,6 +170,7 @@
>  #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>  #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>  #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
> +#define REUSE_SPD_EN                           (0x1 << 3)
>
>  /* ANALOGIX_DP_VIDEO_CTL_8 */
>  #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
> @@ -376,4 +396,12 @@
>  #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>  #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>
> +/* ANALOGIX_DP_PKT_SEND_CTL */
> +#define IF_UP                                  (0x1 << 4)
> +#define IF_EN                                  (0x1 << 0)
> +
> +/* ANALOGIX_DP_CRC_CON */
> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
> +
>  #endif /* _ANALOGIX_DP_REG_H */
> diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
> index 261b86d..183a336 100644
> --- a/include/drm/bridge/analogix_dp.h
> +++ b/include/drm/bridge/analogix_dp.h
> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>                          struct drm_connector *);
>  };
>
> +int analogix_dp_active_psr(struct device *dev);
> +int analogix_dp_inactive_psr(struct device *dev);

Why active/inactive instead of enable/disable, which is used everywhere else?

> +
>  int analogix_dp_resume(struct device *dev);
>  int analogix_dp_suspend(struct device *dev);
>
> --
> 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] 35+ messages in thread

* Re: [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function
  2016-07-01  9:19   ` Yakir Yang
  (?)
@ 2016-07-01 20:05   ` Sean Paul
  2016-07-08  2:32       ` Yakir Yang
  -1 siblings, 1 reply; 35+ messages in thread
From: Sean Paul @ 2016-07-01 20:05 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> Alway enable the PSR function for Rockchip analogix_dp driver. If panel
> don't support PSR, then the core analogix_dp would ignore this setting.
>
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> ---
> Changes in v3:
> - split the common psr logic into a seperate driver, make this to a
>   simple sub-psr device driver.
>
> Changes in v2:
> - remove vblank notify out (Daniel)
> - create a psr_active() callback in vop data struct.
>
>  drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 52 +++++++++++++++++++++++++
>  1 file changed, 52 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> index e81e19a..80a60a6 100644
> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> @@ -32,6 +32,7 @@
>  #include <drm/bridge/analogix_dp.h>
>
>  #include "rockchip_drm_drv.h"
> +#include "rockchip_drm_psr.h"
>  #include "rockchip_drm_vop.h"
>
>  #define RK3288_GRF_SOC_CON6            0x25c
> @@ -68,11 +69,53 @@ struct rockchip_dp_device {
>         struct regmap            *grf;
>         struct reset_control     *rst;
>
> +       struct delayed_work      psr_work;
> +       unsigned int             psr_state;
> +
>         const struct rockchip_dp_chip_data *data;
>
>         struct analogix_dp_plat_data plat_data;
>  };
>
> +static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)

Again, this function doesn't fail, but its return type is int.
Fortunately you don't check the return in rockchip_drm_psr.c, so this
also seems like a good void candidate.

> +{
> +       struct rockchip_dp_device *dp = to_dp(encoder);
> +
> +       dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
> +
> +       if (enabled)
> +               dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
> +       else
> +               dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
> +
> +       schedule_delayed_work(&dp->psr_work, msecs_to_jiffies(10));

Pull 10 out into a #define

> +
> +       return 0;
> +}
> +
> +static void analogix_dp_psr_work(struct work_struct *work)
> +{
> +       struct rockchip_dp_device *dp =
> +                               container_of(work, typeof(*dp), psr_work.work);
> +       struct drm_crtc *crtc = dp->encoder.crtc;
> +       int psr_state = dp->psr_state;
> +       int vact_end;
> +       int ret;
> +
> +       if (!crtc)
> +               return;
> +
> +       vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
> +
> +       ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, 100);


Pull 100 out into a #define

> +       if (ret == 0) {

if (ret) {
    dev_err(... "line flag interrupt did not arrive");
    return;
}

> +               if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
> +                       analogix_dp_active_psr(dp->dev);
> +               else
> +                       analogix_dp_inactive_psr(dp->dev);
> +       }
> +}
> +
>  static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
>  {
>         reset_control_assert(dp->rst);
> @@ -340,12 +383,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
>         dp->plat_data.power_off = rockchip_dp_powerdown;
>         dp->plat_data.get_modes = rockchip_dp_get_modes;
>
> +       dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
> +       INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
> +
> +       rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
> +
>         return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
>  }
>
>  static void rockchip_dp_unbind(struct device *dev, struct device *master,
>                                void *data)
>  {
> +       struct rockchip_dp_device *dp = dev_get_drvdata(dev);
> +
> +       rockchip_drm_psr_unregister(&dp->encoder);
> +
>         return analogix_dp_unbind(dev, master, data);
>  }
>
> --
> 1.9.1
>
>

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
  2016-07-01 18:00   ` Sean Paul
@ 2016-07-08  2:12       ` Yakir Yang
  2016-07-12 12:38       ` Daniel Vetter
  1 sibling, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-08  2:12 UTC (permalink / raw)
  To: Sean Paul
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

Sean,

On 07/02/2016 02:00 AM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>> The PSR driver have exported four symbols for specific device driver:
>> - rockchip_drm_psr_register()
>> - rockchip_drm_psr_unregister()
>> - rockchip_drm_psr_enable()
>> - rockchip_drm_psr_disable()
>> - rockchip_drm_psr_flush()
>>
>> Encoder driver should call the register/unregister interfaces to hook
>> itself into common PSR driver, encoder have implement the 'psr_set'
>> callback which use the set PSR state in hardware side.
>>
>> Crtc driver would call the enable/disable interfaces when vblank is
>> enable/disable, after that the common PSR driver would call the encoder
>> registered callback to set the PSR state.
>>
> This feels overly complicated. It seems like you could cut out a bunch
> of code by just coding the psr functions into vop and
> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> would be if you plan on supporting psr in a different encoder or crtc
> in rockchip, or if you're planning on moving this into drm core.

I split the psr code into a separate driver, cause it seems make things 
more clearly.

'rockchip_drm_vop.c' need to call the PSR enable/disable interfaces, and 
'rockchip_drm_drv.c' need to call the PSR flush interface. If I put the 
whole psr code into analogix_dp-rockchip driver, then the Rockchip drm 
core driver would tie to analogix_dp-rockchip driver very deeply, and 
hard to expand if there are two PSR device in further.

> Perhaps others will disagree with this sentiment and this is the right
> thing to do.
>
>> Fb driver would call the flush interface in 'fb->dirty' callback, this
>> helper function would force all PSR enabled encoders to exit from PSR
>> for 3 seconds.
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> ---
>> Changes in v3:
>> - split the psr flow into an common abstracted PSR driver
>> - implement the 'fb->dirty' callback function (Daniel)
>> - avoid to use notify to acqiure for vact event (Daniel)
>> - remove psr_active() callback which introduce in v2
>>
>> Changes in v2: None
>>
>>   drivers/gpu/drm/rockchip/Makefile           |   2 +-
>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
>>   5 files changed, 249 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index 05d0713..9746365 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -3,7 +3,7 @@
>>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>>
>>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>> -               rockchip_drm_gem.o rockchip_drm_vop.o
>> +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>
>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> index 20f12bc..0fec18f 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> @@ -21,6 +21,7 @@
>>
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_psr.h"
>>
>>   #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>>
>> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>>                                       rockchip_fb->obj[0], handle);
>>   }
>>
>> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
>> +                                struct drm_file *file,
>> +                                unsigned int flags, unsigned int color,
>> +                                struct drm_clip_rect *clips,
>> +                                unsigned int num_clips)
>> +{
>> +       rockchip_drm_psr_flush();
>> +       return 0;
>> +}
>> +
>>   static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>>          .destroy        = rockchip_drm_fb_destroy,
>>          .create_handle  = rockchip_drm_fb_create_handle,
>> +       .dirty          = rockchip_drm_fb_dirty,
>>   };
>>
>>   static struct rockchip_drm_fb *
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> new file mode 100644
>> index 0000000..c044443
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> @@ -0,0 +1,200 @@
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_psr.h"
>> +
>> +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
>> +
>> +static LIST_HEAD(psr_list);
>> +static DEFINE_MUTEX(psr_list_mutex);
> I'm not crazy about these globals. Perhaps you can initialize them
> with the rockchip driver and tuck them in a driver-level struct
> (rockchip_drm_private or something).

Okay, done.

>
>> +
>> +enum psr_state {
>> +       PSR_FLUSH,
>> +       PSR_ENABLE,
>> +       PSR_DISABLE,
>> +};
>> +
>> +struct psr_drv {
>> +       struct list_head list;
>> +       enum psr_state state;
>> +       struct mutex state_mutex;
>> +
>> +       struct timer_list flush_timer;
>> +
>> +       struct drm_encoder *encoder;
>> +       int (*set)(struct drm_encoder *encoder, bool enable);
>> +};
>> +
>> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->encoder->crtc == crtc) {
>> +                       mutex_unlock(&psr_list_mutex);
>> +                       return psr;
>> +               }
>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +
>> +       return ERR_PTR(-ENODEV);
>> +}
>> +
>> +static void psr_enable(struct psr_drv *psr)
>> +{
>> +       if (psr->state == PSR_ENABLE)
>> +               return;
> Should you be worried about races by accessing this outside of the state mutex?
>

Yes, this should under the protect of state_mutex.

>> +
>> +       mutex_lock(&psr->state_mutex);
>> +       psr->state = PSR_ENABLE;
>> +       psr->set(psr->encoder, true);
>> +       mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_disable(struct psr_drv *psr)
>> +{
>> +       if (psr->state == PSR_DISABLE)
>> +               return;
>> +
>> +       mutex_lock(&psr->state_mutex);
>> +       psr->state = PSR_DISABLE;
>> +       psr->set(psr->encoder, false);
>> +       mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_flush_handler(unsigned long data)
>> +{
>> +       struct psr_drv *psr = (struct psr_drv *)data;
>> +
>> +       if (!psr || psr->state != PSR_FLUSH)
>> +               return;
>> +
>> +       psr_enable(psr);
>> +}
>> +
>> +/**
>> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +       if (IS_ERR(psr))
>> +               return PTR_ERR(psr);
>> +
>> +       psr_enable(psr);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
>> +
>> +/**
>> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +       if (IS_ERR(psr))
>> +               return PTR_ERR(psr);
>> +
>> +       psr_disable(psr);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
>> +
>> +/**
>> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
>> + *
>> + * Disable the PSR function for all registered encoders, and then enable the
>> + * PSR function back after 5 second. If encoder PSR state have been changed
> s/5 second/PSR_FLUSH_TIMEOUT/

Done

>
>> + * during flush time, then keep the state no change after flush timeout.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_flush(void)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->state == PSR_DISABLE)
>> +                       continue;
>> +
>> +               mod_timer(&psr->flush_timer,
>> +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
>> +
>> +               psr_disable(psr);
>> +               psr->state = PSR_FLUSH;
> This is set outside of state_mutex, is that intentional?

No, I just don't want to create another interface like psr_flush(). But 
it's a bad idea to let 'state' change outside the protect of 'state_mutex'.

Thanks,
- Yakir

>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
>> +
>> +/**
>> + * rockchip_drm_psr_register - register encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +                             int (*psr_set)(struct drm_encoder *, bool enable))
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       if (!encoder || !psr_set)
>> +               return -EINVAL;
>> +
>> +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
>> +       if (!psr)
>> +               return -ENOMEM;
>> +
>> +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
>> +
>> +       mutex_init(&psr->state_mutex);
>> +
>> +       psr->state = PSR_DISABLE;
>> +       psr->encoder = encoder;
>> +       psr->set = psr_set;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_add_tail(&psr->list, &psr_list);
>> +       mutex_unlock(&psr_list_mutex);
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_register);
>> +
>> +/**
>> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->encoder == encoder) {
>> +                       del_timer(&psr->flush_timer);
>> +                       list_del(&psr->list);
>> +                       kfree(psr);
>> +               }
>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> new file mode 100644
>> index 0000000..622f605
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> @@ -0,0 +1,12 @@
>> +#ifndef __ROCKCHIP_DRM_PSR___
>> +#define __ROCKCHIP_DRM_PSR___
>> +
>> +void rockchip_drm_psr_flush(void);
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
>> +
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +                           int (*psr_set)(struct drm_encoder *, bool enable));
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
>> +
>> +#endif /* __ROCKCHIP_DRM_PSR__ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index cd3cac5..3c6dfc5 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -34,6 +34,7 @@
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>>   #include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>
>>   #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
>> @@ -121,6 +122,9 @@ struct vop {
>>          /* protected by dev->event_lock */
>>          struct drm_pending_vblank_event *event;
>>
>> +       bool psr_enabled;
>> +       struct delayed_work psr_work;
>> +
>>          struct completion line_flag_completion;
>>
>>          const struct vop_data *data;
>> @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>          .atomic_destroy_state = vop_atomic_plane_destroy_state,
>>   };
>>
>> +static void vop_psr_work(struct work_struct *work)
>> +{
>> +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
>> +
>> +       if (vop->psr_enabled)
>> +               rockchip_drm_psr_enable(&vop->crtc);
>> +       else
>> +               rockchip_drm_psr_disable(&vop->crtc);
>> +}
>> +
>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>   {
>>          struct vop *vop = to_vop(crtc);
>> @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>
>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>
>> +       vop->psr_enabled = false;
>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>> +
>>          return 0;
>>   }
>>
>> @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>>          VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>>
>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +
>> +       vop->psr_enabled = true;
>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>   }
>>
>>   static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
>> @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>>                  return ret;
>>
>>          pm_runtime_enable(&pdev->dev);
>> +
>> +       vop->psr_enabled = false;
>> +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>> +
>>          return 0;
>>   }
>>
>> --
>> 1.9.1
>>
>>
>
>

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
@ 2016-07-08  2:12       ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-08  2:12 UTC (permalink / raw)
  To: Sean Paul
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip, Mark Yao,
	Daniel Vetter, Emil Velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Jingoo Han,
	Stéphane Marchesin, Thierry Reding,
	Linux Kernel Mailing List

Sean,

On 07/02/2016 02:00 AM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>> The PSR driver have exported four symbols for specific device driver:
>> - rockchip_drm_psr_register()
>> - rockchip_drm_psr_unregister()
>> - rockchip_drm_psr_enable()
>> - rockchip_drm_psr_disable()
>> - rockchip_drm_psr_flush()
>>
>> Encoder driver should call the register/unregister interfaces to hook
>> itself into common PSR driver, encoder have implement the 'psr_set'
>> callback which use the set PSR state in hardware side.
>>
>> Crtc driver would call the enable/disable interfaces when vblank is
>> enable/disable, after that the common PSR driver would call the encoder
>> registered callback to set the PSR state.
>>
> This feels overly complicated. It seems like you could cut out a bunch
> of code by just coding the psr functions into vop and
> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> would be if you plan on supporting psr in a different encoder or crtc
> in rockchip, or if you're planning on moving this into drm core.

I split the psr code into a separate driver, cause it seems make things 
more clearly.

'rockchip_drm_vop.c' need to call the PSR enable/disable interfaces, and 
'rockchip_drm_drv.c' need to call the PSR flush interface. If I put the 
whole psr code into analogix_dp-rockchip driver, then the Rockchip drm 
core driver would tie to analogix_dp-rockchip driver very deeply, and 
hard to expand if there are two PSR device in further.

> Perhaps others will disagree with this sentiment and this is the right
> thing to do.
>
>> Fb driver would call the flush interface in 'fb->dirty' callback, this
>> helper function would force all PSR enabled encoders to exit from PSR
>> for 3 seconds.
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> ---
>> Changes in v3:
>> - split the psr flow into an common abstracted PSR driver
>> - implement the 'fb->dirty' callback function (Daniel)
>> - avoid to use notify to acqiure for vact event (Daniel)
>> - remove psr_active() callback which introduce in v2
>>
>> Changes in v2: None
>>
>>   drivers/gpu/drm/rockchip/Makefile           |   2 +-
>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
>>   5 files changed, 249 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index 05d0713..9746365 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -3,7 +3,7 @@
>>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>>
>>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>> -               rockchip_drm_gem.o rockchip_drm_vop.o
>> +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>
>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> index 20f12bc..0fec18f 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> @@ -21,6 +21,7 @@
>>
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_psr.h"
>>
>>   #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>>
>> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>>                                       rockchip_fb->obj[0], handle);
>>   }
>>
>> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
>> +                                struct drm_file *file,
>> +                                unsigned int flags, unsigned int color,
>> +                                struct drm_clip_rect *clips,
>> +                                unsigned int num_clips)
>> +{
>> +       rockchip_drm_psr_flush();
>> +       return 0;
>> +}
>> +
>>   static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>>          .destroy        = rockchip_drm_fb_destroy,
>>          .create_handle  = rockchip_drm_fb_create_handle,
>> +       .dirty          = rockchip_drm_fb_dirty,
>>   };
>>
>>   static struct rockchip_drm_fb *
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> new file mode 100644
>> index 0000000..c044443
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> @@ -0,0 +1,200 @@
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_psr.h"
>> +
>> +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
>> +
>> +static LIST_HEAD(psr_list);
>> +static DEFINE_MUTEX(psr_list_mutex);
> I'm not crazy about these globals. Perhaps you can initialize them
> with the rockchip driver and tuck them in a driver-level struct
> (rockchip_drm_private or something).

Okay, done.

>
>> +
>> +enum psr_state {
>> +       PSR_FLUSH,
>> +       PSR_ENABLE,
>> +       PSR_DISABLE,
>> +};
>> +
>> +struct psr_drv {
>> +       struct list_head list;
>> +       enum psr_state state;
>> +       struct mutex state_mutex;
>> +
>> +       struct timer_list flush_timer;
>> +
>> +       struct drm_encoder *encoder;
>> +       int (*set)(struct drm_encoder *encoder, bool enable);
>> +};
>> +
>> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->encoder->crtc == crtc) {
>> +                       mutex_unlock(&psr_list_mutex);
>> +                       return psr;
>> +               }
>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +
>> +       return ERR_PTR(-ENODEV);
>> +}
>> +
>> +static void psr_enable(struct psr_drv *psr)
>> +{
>> +       if (psr->state == PSR_ENABLE)
>> +               return;
> Should you be worried about races by accessing this outside of the state mutex?
>

Yes, this should under the protect of state_mutex.

>> +
>> +       mutex_lock(&psr->state_mutex);
>> +       psr->state = PSR_ENABLE;
>> +       psr->set(psr->encoder, true);
>> +       mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_disable(struct psr_drv *psr)
>> +{
>> +       if (psr->state == PSR_DISABLE)
>> +               return;
>> +
>> +       mutex_lock(&psr->state_mutex);
>> +       psr->state = PSR_DISABLE;
>> +       psr->set(psr->encoder, false);
>> +       mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_flush_handler(unsigned long data)
>> +{
>> +       struct psr_drv *psr = (struct psr_drv *)data;
>> +
>> +       if (!psr || psr->state != PSR_FLUSH)
>> +               return;
>> +
>> +       psr_enable(psr);
>> +}
>> +
>> +/**
>> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +       if (IS_ERR(psr))
>> +               return PTR_ERR(psr);
>> +
>> +       psr_enable(psr);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
>> +
>> +/**
>> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +       if (IS_ERR(psr))
>> +               return PTR_ERR(psr);
>> +
>> +       psr_disable(psr);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
>> +
>> +/**
>> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
>> + *
>> + * Disable the PSR function for all registered encoders, and then enable the
>> + * PSR function back after 5 second. If encoder PSR state have been changed
> s/5 second/PSR_FLUSH_TIMEOUT/

Done

>
>> + * during flush time, then keep the state no change after flush timeout.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_flush(void)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->state == PSR_DISABLE)
>> +                       continue;
>> +
>> +               mod_timer(&psr->flush_timer,
>> +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
>> +
>> +               psr_disable(psr);
>> +               psr->state = PSR_FLUSH;
> This is set outside of state_mutex, is that intentional?

No, I just don't want to create another interface like psr_flush(). But 
it's a bad idea to let 'state' change outside the protect of 'state_mutex'.

Thanks,
- Yakir

>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
>> +
>> +/**
>> + * rockchip_drm_psr_register - register encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +                             int (*psr_set)(struct drm_encoder *, bool enable))
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       if (!encoder || !psr_set)
>> +               return -EINVAL;
>> +
>> +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
>> +       if (!psr)
>> +               return -ENOMEM;
>> +
>> +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
>> +
>> +       mutex_init(&psr->state_mutex);
>> +
>> +       psr->state = PSR_DISABLE;
>> +       psr->encoder = encoder;
>> +       psr->set = psr_set;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_add_tail(&psr->list, &psr_list);
>> +       mutex_unlock(&psr_list_mutex);
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_register);
>> +
>> +/**
>> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->encoder == encoder) {
>> +                       del_timer(&psr->flush_timer);
>> +                       list_del(&psr->list);
>> +                       kfree(psr);
>> +               }
>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> new file mode 100644
>> index 0000000..622f605
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> @@ -0,0 +1,12 @@
>> +#ifndef __ROCKCHIP_DRM_PSR___
>> +#define __ROCKCHIP_DRM_PSR___
>> +
>> +void rockchip_drm_psr_flush(void);
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
>> +
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +                           int (*psr_set)(struct drm_encoder *, bool enable));
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
>> +
>> +#endif /* __ROCKCHIP_DRM_PSR__ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index cd3cac5..3c6dfc5 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -34,6 +34,7 @@
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>>   #include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>
>>   #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
>> @@ -121,6 +122,9 @@ struct vop {
>>          /* protected by dev->event_lock */
>>          struct drm_pending_vblank_event *event;
>>
>> +       bool psr_enabled;
>> +       struct delayed_work psr_work;
>> +
>>          struct completion line_flag_completion;
>>
>>          const struct vop_data *data;
>> @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>          .atomic_destroy_state = vop_atomic_plane_destroy_state,
>>   };
>>
>> +static void vop_psr_work(struct work_struct *work)
>> +{
>> +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
>> +
>> +       if (vop->psr_enabled)
>> +               rockchip_drm_psr_enable(&vop->crtc);
>> +       else
>> +               rockchip_drm_psr_disable(&vop->crtc);
>> +}
>> +
>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>   {
>>          struct vop *vop = to_vop(crtc);
>> @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>
>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>
>> +       vop->psr_enabled = false;
>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>> +
>>          return 0;
>>   }
>>
>> @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>>          VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>>
>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +
>> +       vop->psr_enabled = true;
>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>   }
>>
>>   static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
>> @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>>                  return ret;
>>
>>          pm_runtime_enable(&pdev->dev);
>> +
>> +       vop->psr_enabled = false;
>> +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>> +
>>          return 0;
>>   }
>>
>> --
>> 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] 35+ messages in thread

* Re: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
  2016-07-01 19:46     ` Sean Paul
  (?)
@ 2016-07-08  2:26     ` Yakir Yang
  2016-07-08  2:39       ` Yakir Yang
  2016-07-12 15:29       ` Sean Paul
  -1 siblings, 2 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-08  2:26 UTC (permalink / raw)
  To: Sean Paul
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

Sean,

Thanks for your review.

On 07/02/2016 03:46 AM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>> The full name of PSR is Panel Self Refresh, panel device could refresh
>> itself with the hardware framebuffer in panel, this would make lots of
>> sense to save the power consumption.
>>
>> This patch have exported two symbols for platform driver to implement
>> the PSR function in hardware side:
>> - analogix_dp_active_psr()
>> - analogix_dp_inactive_psr()
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> ---
>> Changes in v3:
>> - split analogix_dp_enable_psr(), make it more clearly
>>      analogix_dp_detect_sink_psr()
>>      analogix_dp_enable_sink_psr()
>> - remove some nosie register setting comments
>>
>> Changes in v2:
>> - introduce in v2, splite the common Analogix DP changes out
>>
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 ++++++++++++++++++++++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 ++++++++++++++++++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>>   include/drm/bridge/analogix_dp.h                   |  3 +
>>   5 files changed, 153 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> index 32715da..b557097 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
>>          return 0;
>>   }
>>
>> +int analogix_dp_active_psr(struct device *dev)
>> +{
>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +       if (!dp->psr_support)
>> +               return -EINVAL;
>> +
>> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
>> +                                EDP_VSC_PSR_CRC_VALUES_VALID);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
>> +
>> +int analogix_dp_inactive_psr(struct device *dev)
>> +{
>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +       if (!dp->psr_support)
>> +               return -EINVAL;
>> +
>> +       analogix_dp_send_psr_spd(dp, 0);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
>> +
>> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
>> +{
>> +       unsigned char psr_version;
>> +
>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
>> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
>> +
> This info message is likely to be spammy since it's printed everytime
> the panel toggle on. Perhaps downgrade to debug level.

Okay, done.

>> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
>> +}
>> +
>> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
> Return type is int, but the function never fails and you don't check
> the return value when calling it. Seems like this should be void.

Done.

>> +{
>> +       unsigned char psr_en;
>> +
>> +       /* Disable psr function */
>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>> +       psr_en &= ~DP_PSR_ENABLE;
>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +       /* Main-Link transmitter remains active during PSR active states */
>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
> Why read psr_en if you're just going to overwrite it? Perhaps you meant |= here.
>

Yes, it's my mistaken, no need to read the DP_PSR_EN_CFG, just configure 
it directly is enough.

>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +       /* Enable psr function */
>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
>> +                DP_PSR_CRC_VERIFICATION;
> Again, no need to read if you're just overwriting.

Yes, ditto

>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +       analogix_dp_enable_psr_crc(dp);
>> +
>> +       return 0;
>> +}
>> +
>>   static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
>>   {
>>          int i;
>> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
>>
>>          /* Enable video */
>>          analogix_dp_start_video(dp);
>> +
>> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
>> +       if (dp->psr_support)
>> +               analogix_dp_enable_sink_psr(dp);
>>   }
>>
>>   int analogix_dp_get_modes(struct drm_connector *connector)
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> index b456380..6ca5dde 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>>          int                     hpd_gpio;
>>          bool                    force_hpd;
>>          unsigned char           edid[EDID_BLOCK_LENGTH * 2];
>> +       bool                    psr_support;
>>
>>          struct analogix_dp_plat_data *plat_data;
>>   };
>> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
>>   void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
>>   void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>>   void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
>> +
>>   #endif /* _ANALOGIX_DP_CORE_H */
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> index 48030f0..e8372c7 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
>>          reg |= SCRAMBLING_DISABLE;
>>          writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>>   }
>> +
>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
>> +{
>> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
>> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
>> +
>> +       usleep_range(10, 20);
> Is this sleep arbitrary, or documented somewhere? Could you add a
> comment explaining how this was arrived at?
>

Yes, this sleep is arbitrary, there is no document about the

>> +
>> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
>> +}
>> +
>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
>> +{
>> +       unsigned int val;
>> +
>> +       /* don't send info frame */
>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +       val &= ~IF_EN;
>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +
>> +       /* configure single frame update mode */
>> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
>> +
>> +       /* configure VSC HB0 ~ HB3 */
>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
>> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
>> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
>> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
>> +
>> +       /* configure VSC HB0 ~ HB3 */
>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
>> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
>> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
>> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
>> +
>> +       /* configure DB0 / DB1 values */
>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
> Lots of hardcoded values here. I think this could be cleaned up.

For "HB0~HB3", "PB0~PB3" and "DB1", I don't understand very well. Those 
seems to be a kind of head number, I got those magic values from our IC 
side. So I think those should be okay to keep the hardcode values here :-D

But for the "0x3" in "ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL", I would fix it 
with suitable macro.

>> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
>> +
>> +       /* set reuse spd inforframe */
>> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>> +       val |= REUSE_SPD_EN;
>> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>> +
>> +       /* mark info frame update */
>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +       val = (val | IF_UP) & ~IF_EN;
>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +
>> +       /* send info frame */
>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +       val |= IF_EN;
>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +}
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> index cdcc6c5..a2698e4 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> @@ -22,6 +22,8 @@
>>   #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>>   #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>>
>> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
>> +
>>   #define ANALOGIX_DP_PLL_REG_1                  0xfc
>>   #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>>   #define ANALOGIX_DP_PLL_REG_3                  0x9e8
>> @@ -30,6 +32,21 @@
>>
>>   #define ANALOGIX_DP_PD                         0x12c
>>
>> +#define ANALOGIX_DP_IF_TYPE                    0x244
>> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
>> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
>> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
>> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
>> +#define ANALOGIX_DP_SPD_HB2                    0x300
>> +#define ANALOGIX_DP_SPD_HB3                    0x304
>> +#define ANALOGIX_DP_SPD_PB0                    0x308
>> +#define ANALOGIX_DP_SPD_PB1                    0x30C
>> +#define ANALOGIX_DP_SPD_PB2                    0x310
>> +#define ANALOGIX_DP_SPD_PB3                    0x314
>> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
>> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
>> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
>> +
>>   #define ANALOGIX_DP_LANE_MAP                   0x35C
>>
>>   #define ANALOGIX_DP_ANALOG_CTL_1               0x370
>> @@ -103,6 +120,8 @@
>>
>>   #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>>
>> +#define ANALOGIX_DP_CRC_CON                    0x890
>> +
>>   /* ANALOGIX_DP_TX_SW_RESET */
>>   #define RESET_DP_TX                            (0x1 << 0)
>>
>> @@ -151,6 +170,7 @@
>>   #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>>   #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>>   #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
>> +#define REUSE_SPD_EN                           (0x1 << 3)
>>
>>   /* ANALOGIX_DP_VIDEO_CTL_8 */
>>   #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
>> @@ -376,4 +396,12 @@
>>   #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>>   #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>>
>> +/* ANALOGIX_DP_PKT_SEND_CTL */
>> +#define IF_UP                                  (0x1 << 4)
>> +#define IF_EN                                  (0x1 << 0)
>> +
>> +/* ANALOGIX_DP_CRC_CON */
>> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
>> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
>> +
>>   #endif /* _ANALOGIX_DP_REG_H */
>> diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
>> index 261b86d..183a336 100644
>> --- a/include/drm/bridge/analogix_dp.h
>> +++ b/include/drm/bridge/analogix_dp.h
>> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>>                           struct drm_connector *);
>>   };
>>
>> +int analogix_dp_active_psr(struct device *dev);
>> +int analogix_dp_inactive_psr(struct device *dev);
> Why active/inactive instead of enable/disable, which is used everywhere else?

Done

Thanks,
- Yakir

>> +
>>   int analogix_dp_resume(struct device *dev);
>>   int analogix_dp_suspend(struct device *dev);
>>
>> --
>> 1.9.1
>>
>>
>
>

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

* Re: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
  2016-07-01 15:32       ` Sean Paul
@ 2016-07-08  2:30         ` Yakir Yang
  -1 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-08  2:30 UTC (permalink / raw)
  To: Sean Paul
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

Sean,

On 07/01/2016 11:32 PM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 11:30 AM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>>> VOP have integrated a hardware counter which indicate the exact display
>>> line that vop is scanning. And if we're interested in a specific line,
>>> we can set the line number to vop line_flag register, and then vop would
>>> generate a line_flag interrupt for it.
>>>
>>> For example eDP PSR function is interested in the vertical blanking
>>> period, then driver could set the line number to zero.
>>>
>>> This patch have exported a symbol that allow other driver to listen the
>>> line flag event with given timeout limit:
>>> -  rockchip_drm_wait_line_flag()
>>>
>>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
>>> - Add 'line_flag_num_0' for RK3288/RK3036
>>> - Remove the notify for waiting line_flag event (Daniel)
>>>
>>> Changes in v2:
>>> - Introduce in v2, split VOP line flag changes out
>>>
>>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>>>   4 files changed, 113 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> index ea39329..239b830 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>>>                                     struct device *dev);
>>>   void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>>>                                      struct device *dev);
>>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>>> +                               unsigned int mstimeout);
>>> +
>>>   #endif /* _ROCKCHIP_DRM_DRV_H_ */
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> index c8a62a8..cd3cac5 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> @@ -121,6 +121,8 @@ struct vop {
>>>          /* protected by dev->event_lock */
>>>          struct drm_pending_vblank_event *event;
>>>
>>> +       struct completion line_flag_completion;
>>> +
>>>          const struct vop_data *data;
>>>
>>>          uint32_t *regsbak;
>>> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>>   }
>>>
>>> +/*
>>> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
>>> + *     the "FRAME_SYNC" interrupt.
>>> + * (2) the active data region of each frame ends at dsp_vact_end
>>> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
>>> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
>>> + *
>>> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
>>> + * Interrupts
>>> + * LINE_FLAG -------------------------------+
>>> + * FRAME_SYNC ----+                         |
>>> + *                |                         |
>>> + *                v                         v
>>> + *                | Vsync | Vbp |  Vactive  | Vfp |
>>> + *                        ^     ^           ^     ^
>>> + *                        |     |           |     |
>>> + *                        |     |           |     |
>>> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
>>> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
>>> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
>>> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
>>> + */
>>> +
>>> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
>>> +{
>>> +       unsigned long flags;
>>> +
>>> +       if (WARN_ON(!vop->is_enabled))
>>> +               return;
>>> +
>>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>>> +
>>> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
>>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
>>> +       vop_cfg_done(vop);
>>> +
>>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +}
>>> +
>>> +static void vop_line_flag_irq_disable(struct vop *vop)
>>> +{
>>> +       unsigned long flags;
>>> +
>>> +       if (WARN_ON(!vop->is_enabled))
>>> +               return;
>>> +
>>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>>> +
>>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
>>> +
>>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +}
>>> +
>>>   static void vop_enable(struct drm_crtc *crtc)
>>>   {
>>>          struct vop *vop = to_vop(crtc);
>>> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>>>                  ret = IRQ_HANDLED;
>>>          }
>>>
>>> +       if (active_irqs & LINE_FLAG_INTR) {
>>> +               if (!completion_done(&vop->line_flag_completion))
>>> +                       complete(&vop->line_flag_completion);
>> I think there's potential to miss flags here if the timing is just
>> right because the completion_done and complete are not atomic wrt the
>> wait_line_flag function. I think you need some locking around the
>> completion operations to ensure they're properly sequenced.
>>
>> FWIW, if you're reinitializing the completion every time in
>> drm_wait_line_flag, you should be able to just call complete (or
>> complete_all) without checking if there are any waiters.
>>
> I should add, if you just call complete/complete_all() here, you
> shouldn't need to add locking (assuming only one waiter).

Yes, only one waiter.

>>> +               active_irqs &= ~LINE_FLAG_INTR;
>>> +               ret = IRQ_HANDLED;
>>> +       }
>>> +
>>>          if (active_irqs & FS_INTR) {
>>>                  drm_crtc_handle_vblank(crtc);
>>>                  vop_handle_vblank(vop);
>>> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>>>
>>>          init_completion(&vop->dsp_hold_completion);
>>>          init_completion(&vop->wait_update_complete);
>>> +       init_completion(&vop->line_flag_completion);
>>>          crtc->port = port;
>>>          rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>>>
>>> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>>>          }
>>>   }
>>>
>>> +/**
>>> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
>>> + * @crtc: CRTC to enable line flag
>>> + * @line_num: interested line number
>>> + * @mstimeout: millisecond for timeout
>>> + *
>>> + * Driver would hold here until the interested line flag interrupt have
>>> + * happened or timeout to wait.
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>>> +                               unsigned int mstimeout)
>>> +{
>>> +       struct vop *vop = to_vop(crtc);
>>> +       unsigned long jiffies_left;
>>> +
>>> +       if (!crtc || !vop->is_enabled)
>>> +               return -ENODEV;
>>> +
>>> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
>>> +               return -EINVAL;
>>> +
>>> +       reinit_completion(&vop->line_flag_completion);
>>> +       vop_line_flag_irq_enable(vop, line_num);
>>> +
>> This will only work for one waiter per vop. Is it worth enforcing this
>> explicitly to avoid weird behavior when there are more than one?

Yes, you're right, need to avoid the weird behavior here.

>>
>>> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
>>> +                                                  msecs_to_jiffies(mstimeout));
>>> +       vop_line_flag_irq_disable(vop);
>>> +
>>> +       if (jiffies_left == 0) {
>>> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
>>> +               return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
>>> +
>>>   static int vop_bind(struct device *dev, struct device *master, void *data)
>>>   {
>>>          struct platform_device *pdev = to_platform_device(dev);
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> index ff4f52e..34fcd03 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> @@ -61,6 +61,9 @@ struct vop_ctrl {
>>>          struct vop_reg hpost_st_end;
>>>          struct vop_reg vpost_st_end;
>>>
>>> +       struct vop_reg line_flag_num_0;
>>> +       struct vop_reg line_flag_num_1;
>>> +
>> nit: you could make this an array:
>>
>> struct vop_reg line_flag_num[2];

Done,

>>
>>>          struct vop_reg cfg_done;
>>>   };
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> index 6f42e56..e9211c9 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>>>          .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>>>          .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>>>          .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>>>          .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>>>          .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>>          .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>>          .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>>>          .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>>          .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>>          .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>>          .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
>>> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>>>          .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> --
>>> 1.9.1
>>>
>>>
>
>

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

* Re: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
@ 2016-07-08  2:30         ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-08  2:30 UTC (permalink / raw)
  To: Sean Paul
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip, Mark Yao,
	Daniel Vetter, Emil Velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Jingoo Han,
	Stéphane Marchesin, Thierry Reding,
	Linux Kernel Mailing List

Sean,

On 07/01/2016 11:32 PM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 11:30 AM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>>> VOP have integrated a hardware counter which indicate the exact display
>>> line that vop is scanning. And if we're interested in a specific line,
>>> we can set the line number to vop line_flag register, and then vop would
>>> generate a line_flag interrupt for it.
>>>
>>> For example eDP PSR function is interested in the vertical blanking
>>> period, then driver could set the line number to zero.
>>>
>>> This patch have exported a symbol that allow other driver to listen the
>>> line flag event with given timeout limit:
>>> -  rockchip_drm_wait_line_flag()
>>>
>>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
>>> - Add 'line_flag_num_0' for RK3288/RK3036
>>> - Remove the notify for waiting line_flag event (Daniel)
>>>
>>> Changes in v2:
>>> - Introduce in v2, split VOP line flag changes out
>>>
>>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>>>   4 files changed, 113 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> index ea39329..239b830 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>>>                                     struct device *dev);
>>>   void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>>>                                      struct device *dev);
>>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>>> +                               unsigned int mstimeout);
>>> +
>>>   #endif /* _ROCKCHIP_DRM_DRV_H_ */
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> index c8a62a8..cd3cac5 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> @@ -121,6 +121,8 @@ struct vop {
>>>          /* protected by dev->event_lock */
>>>          struct drm_pending_vblank_event *event;
>>>
>>> +       struct completion line_flag_completion;
>>> +
>>>          const struct vop_data *data;
>>>
>>>          uint32_t *regsbak;
>>> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>>   }
>>>
>>> +/*
>>> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
>>> + *     the "FRAME_SYNC" interrupt.
>>> + * (2) the active data region of each frame ends at dsp_vact_end
>>> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
>>> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
>>> + *
>>> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
>>> + * Interrupts
>>> + * LINE_FLAG -------------------------------+
>>> + * FRAME_SYNC ----+                         |
>>> + *                |                         |
>>> + *                v                         v
>>> + *                | Vsync | Vbp |  Vactive  | Vfp |
>>> + *                        ^     ^           ^     ^
>>> + *                        |     |           |     |
>>> + *                        |     |           |     |
>>> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
>>> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
>>> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
>>> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
>>> + */
>>> +
>>> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
>>> +{
>>> +       unsigned long flags;
>>> +
>>> +       if (WARN_ON(!vop->is_enabled))
>>> +               return;
>>> +
>>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>>> +
>>> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
>>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
>>> +       vop_cfg_done(vop);
>>> +
>>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +}
>>> +
>>> +static void vop_line_flag_irq_disable(struct vop *vop)
>>> +{
>>> +       unsigned long flags;
>>> +
>>> +       if (WARN_ON(!vop->is_enabled))
>>> +               return;
>>> +
>>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>>> +
>>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
>>> +
>>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +}
>>> +
>>>   static void vop_enable(struct drm_crtc *crtc)
>>>   {
>>>          struct vop *vop = to_vop(crtc);
>>> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>>>                  ret = IRQ_HANDLED;
>>>          }
>>>
>>> +       if (active_irqs & LINE_FLAG_INTR) {
>>> +               if (!completion_done(&vop->line_flag_completion))
>>> +                       complete(&vop->line_flag_completion);
>> I think there's potential to miss flags here if the timing is just
>> right because the completion_done and complete are not atomic wrt the
>> wait_line_flag function. I think you need some locking around the
>> completion operations to ensure they're properly sequenced.
>>
>> FWIW, if you're reinitializing the completion every time in
>> drm_wait_line_flag, you should be able to just call complete (or
>> complete_all) without checking if there are any waiters.
>>
> I should add, if you just call complete/complete_all() here, you
> shouldn't need to add locking (assuming only one waiter).

Yes, only one waiter.

>>> +               active_irqs &= ~LINE_FLAG_INTR;
>>> +               ret = IRQ_HANDLED;
>>> +       }
>>> +
>>>          if (active_irqs & FS_INTR) {
>>>                  drm_crtc_handle_vblank(crtc);
>>>                  vop_handle_vblank(vop);
>>> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>>>
>>>          init_completion(&vop->dsp_hold_completion);
>>>          init_completion(&vop->wait_update_complete);
>>> +       init_completion(&vop->line_flag_completion);
>>>          crtc->port = port;
>>>          rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>>>
>>> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>>>          }
>>>   }
>>>
>>> +/**
>>> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
>>> + * @crtc: CRTC to enable line flag
>>> + * @line_num: interested line number
>>> + * @mstimeout: millisecond for timeout
>>> + *
>>> + * Driver would hold here until the interested line flag interrupt have
>>> + * happened or timeout to wait.
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>>> +                               unsigned int mstimeout)
>>> +{
>>> +       struct vop *vop = to_vop(crtc);
>>> +       unsigned long jiffies_left;
>>> +
>>> +       if (!crtc || !vop->is_enabled)
>>> +               return -ENODEV;
>>> +
>>> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
>>> +               return -EINVAL;
>>> +
>>> +       reinit_completion(&vop->line_flag_completion);
>>> +       vop_line_flag_irq_enable(vop, line_num);
>>> +
>> This will only work for one waiter per vop. Is it worth enforcing this
>> explicitly to avoid weird behavior when there are more than one?

Yes, you're right, need to avoid the weird behavior here.

>>
>>> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
>>> +                                                  msecs_to_jiffies(mstimeout));
>>> +       vop_line_flag_irq_disable(vop);
>>> +
>>> +       if (jiffies_left == 0) {
>>> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
>>> +               return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
>>> +
>>>   static int vop_bind(struct device *dev, struct device *master, void *data)
>>>   {
>>>          struct platform_device *pdev = to_platform_device(dev);
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> index ff4f52e..34fcd03 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> @@ -61,6 +61,9 @@ struct vop_ctrl {
>>>          struct vop_reg hpost_st_end;
>>>          struct vop_reg vpost_st_end;
>>>
>>> +       struct vop_reg line_flag_num_0;
>>> +       struct vop_reg line_flag_num_1;
>>> +
>> nit: you could make this an array:
>>
>> struct vop_reg line_flag_num[2];

Done,

>>
>>>          struct vop_reg cfg_done;
>>>   };
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> index 6f42e56..e9211c9 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>>>          .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>>>          .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>>>          .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>>>          .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>>>          .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>>          .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>>          .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>>>          .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>>          .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>>          .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>>          .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
>>> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>>>          .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> --
>>> 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] 35+ messages in thread

* Re: [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function
  2016-07-01 20:05   ` Sean Paul
@ 2016-07-08  2:32       ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-08  2:32 UTC (permalink / raw)
  To: Sean Paul
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

Sean,

On 07/02/2016 04:05 AM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>> Alway enable the PSR function for Rockchip analogix_dp driver. If panel
>> don't support PSR, then the core analogix_dp would ignore this setting.
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> ---
>> Changes in v3:
>> - split the common psr logic into a seperate driver, make this to a
>>    simple sub-psr device driver.
>>
>> Changes in v2:
>> - remove vblank notify out (Daniel)
>> - create a psr_active() callback in vop data struct.
>>
>>   drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 52 +++++++++++++++++++++++++
>>   1 file changed, 52 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> index e81e19a..80a60a6 100644
>> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> @@ -32,6 +32,7 @@
>>   #include <drm/bridge/analogix_dp.h>
>>
>>   #include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>
>>   #define RK3288_GRF_SOC_CON6            0x25c
>> @@ -68,11 +69,53 @@ struct rockchip_dp_device {
>>          struct regmap            *grf;
>>          struct reset_control     *rst;
>>
>> +       struct delayed_work      psr_work;
>> +       unsigned int             psr_state;
>> +
>>          const struct rockchip_dp_chip_data *data;
>>
>>          struct analogix_dp_plat_data plat_data;
>>   };
>>
>> +static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
> Again, this function doesn't fail, but its return type is int.
> Fortunately you don't check the return in rockchip_drm_psr.c, so this
> also seems like a good void candidate.

Okay, done.

>> +{
>> +       struct rockchip_dp_device *dp = to_dp(encoder);
>> +
>> +       dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
>> +
>> +       if (enabled)
>> +               dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
>> +       else
>> +               dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
>> +
>> +       schedule_delayed_work(&dp->psr_work, msecs_to_jiffies(10));
> Pull 10 out into a #define

Done

>> +
>> +       return 0;
>> +}
>> +
>> +static void analogix_dp_psr_work(struct work_struct *work)
>> +{
>> +       struct rockchip_dp_device *dp =
>> +                               container_of(work, typeof(*dp), psr_work.work);
>> +       struct drm_crtc *crtc = dp->encoder.crtc;
>> +       int psr_state = dp->psr_state;
>> +       int vact_end;
>> +       int ret;
>> +
>> +       if (!crtc)
>> +               return;
>> +
>> +       vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
>> +
>> +       ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, 100);
>
> Pull 100 out into a #define
>

Done.

>> +       if (ret == 0) {
> if (ret) {
>      dev_err(... "line flag interrupt did not arrive");
>      return;
> }

Done.

Thanks
- Yakir

>> +               if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
>> +                       analogix_dp_active_psr(dp->dev);
>> +               else
>> +                       analogix_dp_inactive_psr(dp->dev);
>> +       }
>> +}
>> +
>>   static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
>>   {
>>          reset_control_assert(dp->rst);
>> @@ -340,12 +383,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
>>          dp->plat_data.power_off = rockchip_dp_powerdown;
>>          dp->plat_data.get_modes = rockchip_dp_get_modes;
>>
>> +       dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
>> +       INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
>> +
>> +       rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
>> +
>>          return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
>>   }
>>
>>   static void rockchip_dp_unbind(struct device *dev, struct device *master,
>>                                 void *data)
>>   {
>> +       struct rockchip_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +       rockchip_drm_psr_unregister(&dp->encoder);
>> +
>>          return analogix_dp_unbind(dev, master, data);
>>   }
>>
>> --
>> 1.9.1
>>
>>
>
>

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

* Re: [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function
@ 2016-07-08  2:32       ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-08  2:32 UTC (permalink / raw)
  To: Sean Paul
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip, Mark Yao,
	Daniel Vetter, Emil Velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Jingoo Han,
	Stéphane Marchesin, Thierry Reding,
	Linux Kernel Mailing List

Sean,

On 07/02/2016 04:05 AM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>> Alway enable the PSR function for Rockchip analogix_dp driver. If panel
>> don't support PSR, then the core analogix_dp would ignore this setting.
>>
>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>> ---
>> Changes in v3:
>> - split the common psr logic into a seperate driver, make this to a
>>    simple sub-psr device driver.
>>
>> Changes in v2:
>> - remove vblank notify out (Daniel)
>> - create a psr_active() callback in vop data struct.
>>
>>   drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 52 +++++++++++++++++++++++++
>>   1 file changed, 52 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> index e81e19a..80a60a6 100644
>> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> @@ -32,6 +32,7 @@
>>   #include <drm/bridge/analogix_dp.h>
>>
>>   #include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>
>>   #define RK3288_GRF_SOC_CON6            0x25c
>> @@ -68,11 +69,53 @@ struct rockchip_dp_device {
>>          struct regmap            *grf;
>>          struct reset_control     *rst;
>>
>> +       struct delayed_work      psr_work;
>> +       unsigned int             psr_state;
>> +
>>          const struct rockchip_dp_chip_data *data;
>>
>>          struct analogix_dp_plat_data plat_data;
>>   };
>>
>> +static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
> Again, this function doesn't fail, but its return type is int.
> Fortunately you don't check the return in rockchip_drm_psr.c, so this
> also seems like a good void candidate.

Okay, done.

>> +{
>> +       struct rockchip_dp_device *dp = to_dp(encoder);
>> +
>> +       dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
>> +
>> +       if (enabled)
>> +               dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
>> +       else
>> +               dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
>> +
>> +       schedule_delayed_work(&dp->psr_work, msecs_to_jiffies(10));
> Pull 10 out into a #define

Done

>> +
>> +       return 0;
>> +}
>> +
>> +static void analogix_dp_psr_work(struct work_struct *work)
>> +{
>> +       struct rockchip_dp_device *dp =
>> +                               container_of(work, typeof(*dp), psr_work.work);
>> +       struct drm_crtc *crtc = dp->encoder.crtc;
>> +       int psr_state = dp->psr_state;
>> +       int vact_end;
>> +       int ret;
>> +
>> +       if (!crtc)
>> +               return;
>> +
>> +       vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
>> +
>> +       ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, 100);
>
> Pull 100 out into a #define
>

Done.

>> +       if (ret == 0) {
> if (ret) {
>      dev_err(... "line flag interrupt did not arrive");
>      return;
> }

Done.

Thanks
- Yakir

>> +               if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
>> +                       analogix_dp_active_psr(dp->dev);
>> +               else
>> +                       analogix_dp_inactive_psr(dp->dev);
>> +       }
>> +}
>> +
>>   static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
>>   {
>>          reset_control_assert(dp->rst);
>> @@ -340,12 +383,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
>>          dp->plat_data.power_off = rockchip_dp_powerdown;
>>          dp->plat_data.get_modes = rockchip_dp_get_modes;
>>
>> +       dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
>> +       INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
>> +
>> +       rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
>> +
>>          return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
>>   }
>>
>>   static void rockchip_dp_unbind(struct device *dev, struct device *master,
>>                                 void *data)
>>   {
>> +       struct rockchip_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +       rockchip_drm_psr_unregister(&dp->encoder);
>> +
>>          return analogix_dp_unbind(dev, master, data);
>>   }
>>
>> --
>> 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] 35+ messages in thread

* Re: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
  2016-07-08  2:26     ` Yakir Yang
@ 2016-07-08  2:39       ` Yakir Yang
  2016-07-12 15:29       ` Sean Paul
  1 sibling, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-08  2:39 UTC (permalink / raw)
  To: Sean Paul
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip



On 07/08/2016 10:26 AM, Yakir Yang wrote:
> Sean,
>
> Thanks for your review.
>
> On 07/02/2016 03:46 AM, Sean Paul wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>>> The full name of PSR is Panel Self Refresh, panel device could refresh
>>> itself with the hardware framebuffer in panel, this would make lots of
>>> sense to save the power consumption.
>>>
>>> This patch have exported two symbols for platform driver to implement
>>> the PSR function in hardware side:
>>> - analogix_dp_active_psr()
>>> - analogix_dp_inactive_psr()
>>>
>>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - split analogix_dp_enable_psr(), make it more clearly
>>>      analogix_dp_detect_sink_psr()
>>>      analogix_dp_enable_sink_psr()
>>> - remove some nosie register setting comments
>>>
>>> Changes in v2:
>>> - introduce in v2, splite the common Analogix DP changes out
>>>
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 
>>> ++++++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 
>>> ++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>>>   include/drm/bridge/analogix_dp.h                   |  3 +
>>>   5 files changed, 153 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c 
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> index 32715da..b557097 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct 
>>> analogix_dp_device *dp)
>>>          return 0;
>>>   }
>>>
>>> +int analogix_dp_active_psr(struct device *dev)
>>> +{
>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       if (!dp->psr_support)
>>> +               return -EINVAL;
>>> +
>>> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
>>> + EDP_VSC_PSR_CRC_VALUES_VALID);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
>>> +
>>> +int analogix_dp_inactive_psr(struct device *dev)
>>> +{
>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       if (!dp->psr_support)
>>> +               return -EINVAL;
>>> +
>>> +       analogix_dp_send_psr_spd(dp, 0);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
>>> +
>>> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
>>> +{
>>> +       unsigned char psr_version;
>>> +
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, 
>>> &psr_version);
>>> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
>>> +
>> This info message is likely to be spammy since it's printed everytime
>> the panel toggle on. Perhaps downgrade to debug level.
>
> Okay, done.
>
>>> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
>>> +}
>>> +
>>> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
>> Return type is int, but the function never fails and you don't check
>> the return value when calling it. Seems like this should be void.
>
> Done.
>
>>> +{
>>> +       unsigned char psr_en;
>>> +
>>> +       /* Disable psr function */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en &= ~DP_PSR_ENABLE;
>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       /* Main-Link transmitter remains active during PSR active 
>>> states */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
>> Why read psr_en if you're just going to overwrite it? Perhaps you 
>> meant |= here.
>>
>
> Yes, it's my mistaken, no need to read the DP_PSR_EN_CFG, just 
> configure it directly is enough.
>
>>> + analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       /* Enable psr function */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
>>> +                DP_PSR_CRC_VERIFICATION;
>> Again, no need to read if you're just overwriting.
>
> Yes, ditto
>
>>> + analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       analogix_dp_enable_psr_crc(dp);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>>   static unsigned char analogix_dp_calc_edid_check_sum(unsigned char 
>>> *edid_data)
>>>   {
>>>          int i;
>>> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct 
>>> analogix_dp_device *dp)
>>>
>>>          /* Enable video */
>>>          analogix_dp_start_video(dp);
>>> +
>>> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
>>> +       if (dp->psr_support)
>>> +               analogix_dp_enable_sink_psr(dp);
>>>   }
>>>
>>>   int analogix_dp_get_modes(struct drm_connector *connector)
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h 
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> index b456380..6ca5dde 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>>>          int                     hpd_gpio;
>>>          bool                    force_hpd;
>>>          unsigned char           edid[EDID_BLOCK_LENGTH * 2];
>>> +       bool                    psr_support;
>>>
>>>          struct analogix_dp_plat_data *plat_data;
>>>   };
>>> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct 
>>> analogix_dp_device *dp);
>>>   void analogix_dp_config_video_slave_mode(struct analogix_dp_device 
>>> *dp);
>>>   void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>>>   void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
>>> +
>>>   #endif /* _ANALOGIX_DP_CORE_H */
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c 
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> index 48030f0..e8372c7 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct 
>>> analogix_dp_device *dp)
>>>          reg |= SCRAMBLING_DISABLE;
>>>          writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>>>   }
>>> +
>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
>>> +{
>>> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
>>> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
>>> +
>>> +       usleep_range(10, 20);
>> Is this sleep arbitrary, or documented somewhere? Could you add a
>> comment explaining how this was arrived at?
>>
>
> Yes, this sleep is arbitrary, there is no document about the

Oh, implement reply. I double check the datesheet, oh, i make a mistaken 
here. The CRC_FLUSH wasn't a trigger, but a enable gate.

 >PSR Video CRC flush enable. The PSR video CRC value is initialized at 
every v-sync leading edge.

>>>
>>> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);

So I need to delete this register setting.

Thanks,
- Yakir

>>> +}
>>> +
>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
>>> +{
>>> +       unsigned int val;
>>> +
>>> +       /* don't send info frame */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val &= ~IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +
>>> +       /* configure single frame update mode */
>>> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
>>> +
>>> +       /* configure VSC HB0 ~ HB3 */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
>>> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
>>> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
>>> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
>>> +
>>> +       /* configure VSC HB0 ~ HB3 */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
>>> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
>>> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
>>> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
>>> +
>>> +       /* configure DB0 / DB1 values */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
>> Lots of hardcoded values here. I think this could be cleaned up.
>
> For "HB0~HB3", "PB0~PB3" and "DB1", I don't understand very well. 
> Those seems to be a kind of head number, I got those magic values from 
> our IC side. So I think those should be okay to keep the hardcode 
> values here :-D
>
> But for the "0x3" in "ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL", I would fix 
> it with suitable macro.
>
>>> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
>>> +
>>> +       /* set reuse spd inforframe */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>> +       val |= REUSE_SPD_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>> +
>>> +       /* mark info frame update */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val = (val | IF_UP) & ~IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +
>>> +       /* send info frame */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val |= IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +}
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h 
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> index cdcc6c5..a2698e4 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> @@ -22,6 +22,8 @@
>>>   #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>>>   #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>>>
>>> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
>>> +
>>>   #define ANALOGIX_DP_PLL_REG_1                  0xfc
>>>   #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>>>   #define ANALOGIX_DP_PLL_REG_3                  0x9e8
>>> @@ -30,6 +32,21 @@
>>>
>>>   #define ANALOGIX_DP_PD                         0x12c
>>>
>>> +#define ANALOGIX_DP_IF_TYPE                    0x244
>>> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
>>> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
>>> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
>>> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
>>> +#define ANALOGIX_DP_SPD_HB2                    0x300
>>> +#define ANALOGIX_DP_SPD_HB3                    0x304
>>> +#define ANALOGIX_DP_SPD_PB0                    0x308
>>> +#define ANALOGIX_DP_SPD_PB1                    0x30C
>>> +#define ANALOGIX_DP_SPD_PB2                    0x310
>>> +#define ANALOGIX_DP_SPD_PB3                    0x314
>>> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
>>> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
>>> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
>>> +
>>>   #define ANALOGIX_DP_LANE_MAP                   0x35C
>>>
>>>   #define ANALOGIX_DP_ANALOG_CTL_1               0x370
>>> @@ -103,6 +120,8 @@
>>>
>>>   #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>>>
>>> +#define ANALOGIX_DP_CRC_CON                    0x890
>>> +
>>>   /* ANALOGIX_DP_TX_SW_RESET */
>>>   #define RESET_DP_TX                            (0x1 << 0)
>>>
>>> @@ -151,6 +170,7 @@
>>>   #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>>>   #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>>>   #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
>>> +#define REUSE_SPD_EN                           (0x1 << 3)
>>>
>>>   /* ANALOGIX_DP_VIDEO_CTL_8 */
>>>   #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
>>> @@ -376,4 +396,12 @@
>>>   #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>>>   #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>>>
>>> +/* ANALOGIX_DP_PKT_SEND_CTL */
>>> +#define IF_UP                                  (0x1 << 4)
>>> +#define IF_EN                                  (0x1 << 0)
>>> +
>>> +/* ANALOGIX_DP_CRC_CON */
>>> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
>>> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
>>> +
>>>   #endif /* _ANALOGIX_DP_REG_H */
>>> diff --git a/include/drm/bridge/analogix_dp.h 
>>> b/include/drm/bridge/analogix_dp.h
>>> index 261b86d..183a336 100644
>>> --- a/include/drm/bridge/analogix_dp.h
>>> +++ b/include/drm/bridge/analogix_dp.h
>>> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>>>                           struct drm_connector *);
>>>   };
>>>
>>> +int analogix_dp_active_psr(struct device *dev);
>>> +int analogix_dp_inactive_psr(struct device *dev);
>> Why active/inactive instead of enable/disable, which is used 
>> everywhere else?
>
> Done
>
> Thanks,
> - Yakir
>
>>> +
>>>   int analogix_dp_resume(struct device *dev);
>>>   int analogix_dp_suspend(struct device *dev);
>>>
>>> -- 
>>> 1.9.1
>>>
>>>
>>
>>
>

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
  2016-07-01 18:00   ` Sean Paul
@ 2016-07-12 12:38       ` Daniel Vetter
  2016-07-12 12:38       ` Daniel Vetter
  1 sibling, 0 replies; 35+ messages in thread
From: Daniel Vetter @ 2016-07-12 12:38 UTC (permalink / raw)
  To: Sean Paul
  Cc: Yakir Yang, Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner,
	Daniel Vetter, Stéphane Marchesin, Tomasz Figa,
	Doug Anderson, Thierry Reding, Krzysztof Kozlowski,
	Javier Martinez Canillas, David Airlie, Emil Velikov,
	Linux Kernel Mailing List, dri-devel, linux-samsung-soc,
	linux-rockchip

On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> > The PSR driver have exported four symbols for specific device driver:
> > - rockchip_drm_psr_register()
> > - rockchip_drm_psr_unregister()
> > - rockchip_drm_psr_enable()
> > - rockchip_drm_psr_disable()
> > - rockchip_drm_psr_flush()
> >
> > Encoder driver should call the register/unregister interfaces to hook
> > itself into common PSR driver, encoder have implement the 'psr_set'
> > callback which use the set PSR state in hardware side.
> >
> > Crtc driver would call the enable/disable interfaces when vblank is
> > enable/disable, after that the common PSR driver would call the encoder
> > registered callback to set the PSR state.
> >
> 
> This feels overly complicated. It seems like you could cut out a bunch
> of code by just coding the psr functions into vop and
> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> would be if you plan on supporting psr in a different encoder or crtc
> in rockchip, or if you're planning on moving this into drm core.

Agreed on the layers of indirection. Also, you end up with 3 delayed
timers in total:
- defio timer from fbdev emulation
- timer in this abstraction
- delayed work in the psr backend driver

I'd cut out at least the middle one.

But since this seems to correctly use the ->dirty callback it gets my Ack
either way ;-)

Cheers, Daniel

> 
> Perhaps others will disagree with this sentiment and this is the right
> thing to do.
> 
> > Fb driver would call the flush interface in 'fb->dirty' callback, this
> > helper function would force all PSR enabled encoders to exit from PSR
> > for 3 seconds.
> >
> > Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> > ---
> > Changes in v3:
> > - split the psr flow into an common abstracted PSR driver
> > - implement the 'fb->dirty' callback function (Daniel)
> > - avoid to use notify to acqiure for vact event (Daniel)
> > - remove psr_active() callback which introduce in v2
> >
> > Changes in v2: None
> >
> >  drivers/gpu/drm/rockchip/Makefile           |   2 +-
> >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
> >  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
> >  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
> >  5 files changed, 249 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> >  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> >
> > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> > index 05d0713..9746365 100644
> > --- a/drivers/gpu/drm/rockchip/Makefile
> > +++ b/drivers/gpu/drm/rockchip/Makefile
> > @@ -3,7 +3,7 @@
> >  # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> >
> >  rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> > -               rockchip_drm_gem.o rockchip_drm_vop.o
> > +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
> >  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
> >
> >  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > index 20f12bc..0fec18f 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > @@ -21,6 +21,7 @@
> >
> >  #include "rockchip_drm_drv.h"
> >  #include "rockchip_drm_gem.h"
> > +#include "rockchip_drm_psr.h"
> >
> >  #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
> >
> > @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
> >                                      rockchip_fb->obj[0], handle);
> >  }
> >
> > +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
> > +                                struct drm_file *file,
> > +                                unsigned int flags, unsigned int color,
> > +                                struct drm_clip_rect *clips,
> > +                                unsigned int num_clips)
> > +{
> > +       rockchip_drm_psr_flush();
> > +       return 0;
> > +}
> > +
> >  static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
> >         .destroy        = rockchip_drm_fb_destroy,
> >         .create_handle  = rockchip_drm_fb_create_handle,
> > +       .dirty          = rockchip_drm_fb_dirty,
> >  };
> >
> >  static struct rockchip_drm_fb *
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > new file mode 100644
> > index 0000000..c044443
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > @@ -0,0 +1,200 @@
> > +#include <drm/drm_crtc_helper.h>
> > +
> > +#include "rockchip_drm_psr.h"
> > +
> > +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
> > +
> > +static LIST_HEAD(psr_list);
> > +static DEFINE_MUTEX(psr_list_mutex);
> 
> I'm not crazy about these globals. Perhaps you can initialize them
> with the rockchip driver and tuck them in a driver-level struct
> (rockchip_drm_private or something).
> 
> 
> > +
> > +enum psr_state {
> > +       PSR_FLUSH,
> > +       PSR_ENABLE,
> > +       PSR_DISABLE,
> > +};
> > +
> > +struct psr_drv {
> > +       struct list_head list;
> > +       enum psr_state state;
> > +       struct mutex state_mutex;
> > +
> > +       struct timer_list flush_timer;
> > +
> > +       struct drm_encoder *encoder;
> > +       int (*set)(struct drm_encoder *encoder, bool enable);
> > +};
> > +
> > +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->encoder->crtc == crtc) {
> > +                       mutex_unlock(&psr_list_mutex);
> > +                       return psr;
> > +               }
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +
> > +       return ERR_PTR(-ENODEV);
> > +}
> > +
> > +static void psr_enable(struct psr_drv *psr)
> > +{
> > +       if (psr->state == PSR_ENABLE)
> > +               return;
> 
> Should you be worried about races by accessing this outside of the state mutex?
> 
> > +
> > +       mutex_lock(&psr->state_mutex);
> > +       psr->state = PSR_ENABLE;
> > +       psr->set(psr->encoder, true);
> > +       mutex_unlock(&psr->state_mutex);
> > +}
> > +
> > +static void psr_disable(struct psr_drv *psr)
> > +{
> > +       if (psr->state == PSR_DISABLE)
> > +               return;
> > +
> > +       mutex_lock(&psr->state_mutex);
> > +       psr->state = PSR_DISABLE;
> > +       psr->set(psr->encoder, false);
> > +       mutex_unlock(&psr->state_mutex);
> > +}
> > +
> > +static void psr_flush_handler(unsigned long data)
> > +{
> > +       struct psr_drv *psr = (struct psr_drv *)data;
> > +
> > +       if (!psr || psr->state != PSR_FLUSH)
> > +               return;
> > +
> > +       psr_enable(psr);
> > +}
> > +
> > +/**
> > + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
> > + * @crtc: CRTC to obtain the PSR encoder
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> > +
> > +       if (IS_ERR(psr))
> > +               return PTR_ERR(psr);
> > +
> > +       psr_enable(psr);
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_enable);
> > +
> > +/**
> > + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
> > + * @crtc: CRTC to obtain the PSR encoder
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> > +
> > +       if (IS_ERR(psr))
> > +               return PTR_ERR(psr);
> > +
> > +       psr_disable(psr);
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_disable);
> > +
> > +/**
> > + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
> > + *
> > + * Disable the PSR function for all registered encoders, and then enable the
> > + * PSR function back after 5 second. If encoder PSR state have been changed
> 
> s/5 second/PSR_FLUSH_TIMEOUT/
> 
> > + * during flush time, then keep the state no change after flush timeout.
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +void rockchip_drm_psr_flush(void)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->state == PSR_DISABLE)
> > +                       continue;
> > +
> > +               mod_timer(&psr->flush_timer,
> > +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
> > +
> > +               psr_disable(psr);
> > +               psr->state = PSR_FLUSH;
> 
> This is set outside of state_mutex, is that intentional?
> 
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_flush);
> > +
> > +/**
> > + * rockchip_drm_psr_register - register encoder to psr driver
> > + * @encoder: encoder that obtain the PSR function
> > + * @psr_set: call back to set PSR state
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> > +                             int (*psr_set)(struct drm_encoder *, bool enable))
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       if (!encoder || !psr_set)
> > +               return -EINVAL;
> > +
> > +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
> > +       if (!psr)
> > +               return -ENOMEM;
> > +
> > +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
> > +
> > +       mutex_init(&psr->state_mutex);
> > +
> > +       psr->state = PSR_DISABLE;
> > +       psr->encoder = encoder;
> > +       psr->set = psr_set;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_add_tail(&psr->list, &psr_list);
> > +       mutex_unlock(&psr_list_mutex);
> > +
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_register);
> > +
> > +/**
> > + * rockchip_drm_psr_unregister - unregister encoder to psr driver
> > + * @encoder: encoder that obtain the PSR function
> > + * @psr_set: call back to set PSR state
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->encoder == encoder) {
> > +                       del_timer(&psr->flush_timer);
> > +                       list_del(&psr->list);
> > +                       kfree(psr);
> > +               }
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> > new file mode 100644
> > index 0000000..622f605
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> > @@ -0,0 +1,12 @@
> > +#ifndef __ROCKCHIP_DRM_PSR___
> > +#define __ROCKCHIP_DRM_PSR___
> > +
> > +void rockchip_drm_psr_flush(void);
> > +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
> > +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
> > +
> > +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> > +                           int (*psr_set)(struct drm_encoder *, bool enable));
> > +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
> > +
> > +#endif /* __ROCKCHIP_DRM_PSR__ */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > index cd3cac5..3c6dfc5 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > @@ -34,6 +34,7 @@
> >  #include "rockchip_drm_drv.h"
> >  #include "rockchip_drm_gem.h"
> >  #include "rockchip_drm_fb.h"
> > +#include "rockchip_drm_psr.h"
> >  #include "rockchip_drm_vop.h"
> >
> >  #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
> > @@ -121,6 +122,9 @@ struct vop {
> >         /* protected by dev->event_lock */
> >         struct drm_pending_vblank_event *event;
> >
> > +       bool psr_enabled;
> > +       struct delayed_work psr_work;
> > +
> >         struct completion line_flag_completion;
> >
> >         const struct vop_data *data;
> > @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
> >         .atomic_destroy_state = vop_atomic_plane_destroy_state,
> >  };
> >
> > +static void vop_psr_work(struct work_struct *work)
> > +{
> > +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
> > +
> > +       if (vop->psr_enabled)
> > +               rockchip_drm_psr_enable(&vop->crtc);
> > +       else
> > +               rockchip_drm_psr_disable(&vop->crtc);
> > +}
> > +
> >  static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
> >  {
> >         struct vop *vop = to_vop(crtc);
> > @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
> >
> >         spin_unlock_irqrestore(&vop->irq_lock, flags);
> >
> > +       vop->psr_enabled = false;
> > +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
> > +
> >         return 0;
> >  }
> >
> > @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
> >         VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
> >
> >         spin_unlock_irqrestore(&vop->irq_lock, flags);
> > +
> > +       vop->psr_enabled = true;
> > +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
> >  }
> >
> >  static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
> > @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
> >                 return ret;
> >
> >         pm_runtime_enable(&pdev->dev);
> > +
> > +       vop->psr_enabled = false;
> > +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
> > +
> >         return 0;
> >  }
> >
> > --
> > 1.9.1
> >
> >

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
@ 2016-07-12 12:38       ` Daniel Vetter
  0 siblings, 0 replies; 35+ messages in thread
From: Daniel Vetter @ 2016-07-12 12:38 UTC (permalink / raw)
  To: Sean Paul
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip, Mark Yao,
	Jingoo Han, Emil Velikov, Doug Anderson, dri-devel, Tomasz Figa,
	Javier Martinez Canillas, Daniel Vetter, Stéphane Marchesin,
	Thierry Reding, Linux Kernel Mailing List

On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> > The PSR driver have exported four symbols for specific device driver:
> > - rockchip_drm_psr_register()
> > - rockchip_drm_psr_unregister()
> > - rockchip_drm_psr_enable()
> > - rockchip_drm_psr_disable()
> > - rockchip_drm_psr_flush()
> >
> > Encoder driver should call the register/unregister interfaces to hook
> > itself into common PSR driver, encoder have implement the 'psr_set'
> > callback which use the set PSR state in hardware side.
> >
> > Crtc driver would call the enable/disable interfaces when vblank is
> > enable/disable, after that the common PSR driver would call the encoder
> > registered callback to set the PSR state.
> >
> 
> This feels overly complicated. It seems like you could cut out a bunch
> of code by just coding the psr functions into vop and
> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> would be if you plan on supporting psr in a different encoder or crtc
> in rockchip, or if you're planning on moving this into drm core.

Agreed on the layers of indirection. Also, you end up with 3 delayed
timers in total:
- defio timer from fbdev emulation
- timer in this abstraction
- delayed work in the psr backend driver

I'd cut out at least the middle one.

But since this seems to correctly use the ->dirty callback it gets my Ack
either way ;-)

Cheers, Daniel

> 
> Perhaps others will disagree with this sentiment and this is the right
> thing to do.
> 
> > Fb driver would call the flush interface in 'fb->dirty' callback, this
> > helper function would force all PSR enabled encoders to exit from PSR
> > for 3 seconds.
> >
> > Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> > ---
> > Changes in v3:
> > - split the psr flow into an common abstracted PSR driver
> > - implement the 'fb->dirty' callback function (Daniel)
> > - avoid to use notify to acqiure for vact event (Daniel)
> > - remove psr_active() callback which introduce in v2
> >
> > Changes in v2: None
> >
> >  drivers/gpu/drm/rockchip/Makefile           |   2 +-
> >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
> >  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
> >  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
> >  5 files changed, 249 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> >  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> >
> > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> > index 05d0713..9746365 100644
> > --- a/drivers/gpu/drm/rockchip/Makefile
> > +++ b/drivers/gpu/drm/rockchip/Makefile
> > @@ -3,7 +3,7 @@
> >  # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> >
> >  rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> > -               rockchip_drm_gem.o rockchip_drm_vop.o
> > +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
> >  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
> >
> >  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > index 20f12bc..0fec18f 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > @@ -21,6 +21,7 @@
> >
> >  #include "rockchip_drm_drv.h"
> >  #include "rockchip_drm_gem.h"
> > +#include "rockchip_drm_psr.h"
> >
> >  #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
> >
> > @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
> >                                      rockchip_fb->obj[0], handle);
> >  }
> >
> > +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
> > +                                struct drm_file *file,
> > +                                unsigned int flags, unsigned int color,
> > +                                struct drm_clip_rect *clips,
> > +                                unsigned int num_clips)
> > +{
> > +       rockchip_drm_psr_flush();
> > +       return 0;
> > +}
> > +
> >  static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
> >         .destroy        = rockchip_drm_fb_destroy,
> >         .create_handle  = rockchip_drm_fb_create_handle,
> > +       .dirty          = rockchip_drm_fb_dirty,
> >  };
> >
> >  static struct rockchip_drm_fb *
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > new file mode 100644
> > index 0000000..c044443
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > @@ -0,0 +1,200 @@
> > +#include <drm/drm_crtc_helper.h>
> > +
> > +#include "rockchip_drm_psr.h"
> > +
> > +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
> > +
> > +static LIST_HEAD(psr_list);
> > +static DEFINE_MUTEX(psr_list_mutex);
> 
> I'm not crazy about these globals. Perhaps you can initialize them
> with the rockchip driver and tuck them in a driver-level struct
> (rockchip_drm_private or something).
> 
> 
> > +
> > +enum psr_state {
> > +       PSR_FLUSH,
> > +       PSR_ENABLE,
> > +       PSR_DISABLE,
> > +};
> > +
> > +struct psr_drv {
> > +       struct list_head list;
> > +       enum psr_state state;
> > +       struct mutex state_mutex;
> > +
> > +       struct timer_list flush_timer;
> > +
> > +       struct drm_encoder *encoder;
> > +       int (*set)(struct drm_encoder *encoder, bool enable);
> > +};
> > +
> > +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->encoder->crtc == crtc) {
> > +                       mutex_unlock(&psr_list_mutex);
> > +                       return psr;
> > +               }
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +
> > +       return ERR_PTR(-ENODEV);
> > +}
> > +
> > +static void psr_enable(struct psr_drv *psr)
> > +{
> > +       if (psr->state == PSR_ENABLE)
> > +               return;
> 
> Should you be worried about races by accessing this outside of the state mutex?
> 
> > +
> > +       mutex_lock(&psr->state_mutex);
> > +       psr->state = PSR_ENABLE;
> > +       psr->set(psr->encoder, true);
> > +       mutex_unlock(&psr->state_mutex);
> > +}
> > +
> > +static void psr_disable(struct psr_drv *psr)
> > +{
> > +       if (psr->state == PSR_DISABLE)
> > +               return;
> > +
> > +       mutex_lock(&psr->state_mutex);
> > +       psr->state = PSR_DISABLE;
> > +       psr->set(psr->encoder, false);
> > +       mutex_unlock(&psr->state_mutex);
> > +}
> > +
> > +static void psr_flush_handler(unsigned long data)
> > +{
> > +       struct psr_drv *psr = (struct psr_drv *)data;
> > +
> > +       if (!psr || psr->state != PSR_FLUSH)
> > +               return;
> > +
> > +       psr_enable(psr);
> > +}
> > +
> > +/**
> > + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
> > + * @crtc: CRTC to obtain the PSR encoder
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> > +
> > +       if (IS_ERR(psr))
> > +               return PTR_ERR(psr);
> > +
> > +       psr_enable(psr);
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_enable);
> > +
> > +/**
> > + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
> > + * @crtc: CRTC to obtain the PSR encoder
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> > +
> > +       if (IS_ERR(psr))
> > +               return PTR_ERR(psr);
> > +
> > +       psr_disable(psr);
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_disable);
> > +
> > +/**
> > + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
> > + *
> > + * Disable the PSR function for all registered encoders, and then enable the
> > + * PSR function back after 5 second. If encoder PSR state have been changed
> 
> s/5 second/PSR_FLUSH_TIMEOUT/
> 
> > + * during flush time, then keep the state no change after flush timeout.
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +void rockchip_drm_psr_flush(void)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->state == PSR_DISABLE)
> > +                       continue;
> > +
> > +               mod_timer(&psr->flush_timer,
> > +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
> > +
> > +               psr_disable(psr);
> > +               psr->state = PSR_FLUSH;
> 
> This is set outside of state_mutex, is that intentional?
> 
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_flush);
> > +
> > +/**
> > + * rockchip_drm_psr_register - register encoder to psr driver
> > + * @encoder: encoder that obtain the PSR function
> > + * @psr_set: call back to set PSR state
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> > +                             int (*psr_set)(struct drm_encoder *, bool enable))
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       if (!encoder || !psr_set)
> > +               return -EINVAL;
> > +
> > +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
> > +       if (!psr)
> > +               return -ENOMEM;
> > +
> > +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
> > +
> > +       mutex_init(&psr->state_mutex);
> > +
> > +       psr->state = PSR_DISABLE;
> > +       psr->encoder = encoder;
> > +       psr->set = psr_set;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_add_tail(&psr->list, &psr_list);
> > +       mutex_unlock(&psr_list_mutex);
> > +
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_register);
> > +
> > +/**
> > + * rockchip_drm_psr_unregister - unregister encoder to psr driver
> > + * @encoder: encoder that obtain the PSR function
> > + * @psr_set: call back to set PSR state
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->encoder == encoder) {
> > +                       del_timer(&psr->flush_timer);
> > +                       list_del(&psr->list);
> > +                       kfree(psr);
> > +               }
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> > new file mode 100644
> > index 0000000..622f605
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> > @@ -0,0 +1,12 @@
> > +#ifndef __ROCKCHIP_DRM_PSR___
> > +#define __ROCKCHIP_DRM_PSR___
> > +
> > +void rockchip_drm_psr_flush(void);
> > +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
> > +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
> > +
> > +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> > +                           int (*psr_set)(struct drm_encoder *, bool enable));
> > +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
> > +
> > +#endif /* __ROCKCHIP_DRM_PSR__ */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > index cd3cac5..3c6dfc5 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > @@ -34,6 +34,7 @@
> >  #include "rockchip_drm_drv.h"
> >  #include "rockchip_drm_gem.h"
> >  #include "rockchip_drm_fb.h"
> > +#include "rockchip_drm_psr.h"
> >  #include "rockchip_drm_vop.h"
> >
> >  #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
> > @@ -121,6 +122,9 @@ struct vop {
> >         /* protected by dev->event_lock */
> >         struct drm_pending_vblank_event *event;
> >
> > +       bool psr_enabled;
> > +       struct delayed_work psr_work;
> > +
> >         struct completion line_flag_completion;
> >
> >         const struct vop_data *data;
> > @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
> >         .atomic_destroy_state = vop_atomic_plane_destroy_state,
> >  };
> >
> > +static void vop_psr_work(struct work_struct *work)
> > +{
> > +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
> > +
> > +       if (vop->psr_enabled)
> > +               rockchip_drm_psr_enable(&vop->crtc);
> > +       else
> > +               rockchip_drm_psr_disable(&vop->crtc);
> > +}
> > +
> >  static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
> >  {
> >         struct vop *vop = to_vop(crtc);
> > @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
> >
> >         spin_unlock_irqrestore(&vop->irq_lock, flags);
> >
> > +       vop->psr_enabled = false;
> > +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
> > +
> >         return 0;
> >  }
> >
> > @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
> >         VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
> >
> >         spin_unlock_irqrestore(&vop->irq_lock, flags);
> > +
> > +       vop->psr_enabled = true;
> > +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
> >  }
> >
> >  static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
> > @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
> >                 return ret;
> >
> >         pm_runtime_enable(&pdev->dev);
> > +
> > +       vop->psr_enabled = false;
> > +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
> > +
> >         return 0;
> >  }
> >
> > --
> > 1.9.1
> >
> >

-- 
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] 35+ messages in thread

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
  2016-07-12 12:38       ` Daniel Vetter
  (?)
@ 2016-07-12 15:10       ` Tomasz Figa
  2016-07-12 16:10           ` Daniel Vetter
  -1 siblings, 1 reply; 35+ messages in thread
From: Tomasz Figa @ 2016-07-12 15:10 UTC (permalink / raw)
  To: Sean Paul, Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	Daniel Vetter, linux-samsung-soc, open list:ARM/Rockchip SoC...
  Cc: Yakir Yang

On Tue, Jul 12, 2016 at 9:38 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>> > The PSR driver have exported four symbols for specific device driver:
>> > - rockchip_drm_psr_register()
>> > - rockchip_drm_psr_unregister()
>> > - rockchip_drm_psr_enable()
>> > - rockchip_drm_psr_disable()
>> > - rockchip_drm_psr_flush()
>> >
>> > Encoder driver should call the register/unregister interfaces to hook
>> > itself into common PSR driver, encoder have implement the 'psr_set'
>> > callback which use the set PSR state in hardware side.
>> >
>> > Crtc driver would call the enable/disable interfaces when vblank is
>> > enable/disable, after that the common PSR driver would call the encoder
>> > registered callback to set the PSR state.
>> >
>>
>> This feels overly complicated. It seems like you could cut out a bunch
>> of code by just coding the psr functions into vop and
>> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
>> would be if you plan on supporting psr in a different encoder or crtc
>> in rockchip, or if you're planning on moving this into drm core.
>
> Agreed on the layers of indirection. Also, you end up with 3 delayed
> timers in total:
> - defio timer from fbdev emulation
> - timer in this abstraction
> - delayed work in the psr backend driver

Maybe I'm missing something obvious (don't know how the PSR is
implemented in hardware or in other drivers), but why do we need all
these timers? Couldn't we just trigger a fake page flip on fb dirty
call?

Best regards,
Tomasz

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

* Re: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
  2016-07-08  2:26     ` Yakir Yang
  2016-07-08  2:39       ` Yakir Yang
@ 2016-07-12 15:29       ` Sean Paul
  2016-07-13  1:57         ` Yakir Yang
  1 sibling, 1 reply; 35+ messages in thread
From: Sean Paul @ 2016-07-12 15:29 UTC (permalink / raw)
  To: Yakir Yang
  Cc: Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner, Daniel Vetter,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

On Thu, Jul 7, 2016 at 7:26 PM, Yakir Yang <ykk@rock-chips.com> wrote:
> Sean,
>
> Thanks for your review.
>
>
> On 07/02/2016 03:46 AM, Sean Paul wrote:
>>
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>>>
>>> The full name of PSR is Panel Self Refresh, panel device could refresh
>>> itself with the hardware framebuffer in panel, this would make lots of
>>> sense to save the power consumption.
>>>
>>> This patch have exported two symbols for platform driver to implement
>>> the PSR function in hardware side:
>>> - analogix_dp_active_psr()
>>> - analogix_dp_inactive_psr()
>>>
>>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - split analogix_dp_enable_psr(), make it more clearly
>>>      analogix_dp_detect_sink_psr()
>>>      analogix_dp_enable_sink_psr()
>>> - remove some nosie register setting comments
>>>
>>> Changes in v2:
>>> - introduce in v2, splite the common Analogix DP changes out
>>>
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64
>>> ++++++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54
>>> ++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>>>   include/drm/bridge/analogix_dp.h                   |  3 +
>>>   5 files changed, 153 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> index 32715da..b557097 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct
>>> analogix_dp_device *dp)
>>>          return 0;
>>>   }
>>>
>>> +int analogix_dp_active_psr(struct device *dev)
>>> +{
>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       if (!dp->psr_support)
>>> +               return -EINVAL;
>>> +
>>> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
>>> +                                EDP_VSC_PSR_CRC_VALUES_VALID);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
>>> +
>>> +int analogix_dp_inactive_psr(struct device *dev)
>>> +{
>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       if (!dp->psr_support)
>>> +               return -EINVAL;
>>> +
>>> +       analogix_dp_send_psr_spd(dp, 0);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
>>> +
>>> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
>>> +{
>>> +       unsigned char psr_version;
>>> +
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT,
>>> &psr_version);
>>> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
>>> +
>>
>> This info message is likely to be spammy since it's printed everytime
>> the panel toggle on. Perhaps downgrade to debug level.
>
>
> Okay, done.
>
>>> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
>>> +}
>>> +
>>> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
>>
>> Return type is int, but the function never fails and you don't check
>> the return value when calling it. Seems like this should be void.
>
>
> Done.
>
>>> +{
>>> +       unsigned char psr_en;
>>> +
>>> +       /* Disable psr function */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en &= ~DP_PSR_ENABLE;
>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       /* Main-Link transmitter remains active during PSR active states
>>> */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
>>
>> Why read psr_en if you're just going to overwrite it? Perhaps you meant |=
>> here.
>>
>
> Yes, it's my mistaken, no need to read the DP_PSR_EN_CFG, just configure it
> directly is enough.
>
>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       /* Enable psr function */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
>>> +                DP_PSR_CRC_VERIFICATION;
>>
>> Again, no need to read if you're just overwriting.
>
>
> Yes, ditto
>
>
>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       analogix_dp_enable_psr_crc(dp);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>>   static unsigned char analogix_dp_calc_edid_check_sum(unsigned char
>>> *edid_data)
>>>   {
>>>          int i;
>>> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct
>>> analogix_dp_device *dp)
>>>
>>>          /* Enable video */
>>>          analogix_dp_start_video(dp);
>>> +
>>> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
>>> +       if (dp->psr_support)
>>> +               analogix_dp_enable_sink_psr(dp);
>>>   }
>>>
>>>   int analogix_dp_get_modes(struct drm_connector *connector)
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> index b456380..6ca5dde 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>>>          int                     hpd_gpio;
>>>          bool                    force_hpd;
>>>          unsigned char           edid[EDID_BLOCK_LENGTH * 2];
>>> +       bool                    psr_support;
>>>
>>>          struct analogix_dp_plat_data *plat_data;
>>>   };
>>> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct
>>> analogix_dp_device *dp);
>>>   void analogix_dp_config_video_slave_mode(struct analogix_dp_device
>>> *dp);
>>>   void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>>>   void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
>>> +
>>>   #endif /* _ANALOGIX_DP_CORE_H */
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> index 48030f0..e8372c7 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct
>>> analogix_dp_device *dp)
>>>          reg |= SCRAMBLING_DISABLE;
>>>          writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>>>   }
>>> +
>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
>>> +{
>>> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
>>> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
>>> +
>>> +       usleep_range(10, 20);
>>
>> Is this sleep arbitrary, or documented somewhere? Could you add a
>> comment explaining how this was arrived at?
>>
>
> Yes, this sleep is arbitrary, there is no document about the
>

Ok, so I assume we actually need the delay for things to work? If not,
delete the delay. If we need it, please add a comment saying the wait
is required and the value was chosen via trial and error.

>
>>> +
>>> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
>>> +}
>>> +
>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
>>> +{
>>> +       unsigned int val;
>>> +
>>> +       /* don't send info frame */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val &= ~IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +
>>> +       /* configure single frame update mode */
>>> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
>>> +
>>> +       /* configure VSC HB0 ~ HB3 */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
>>> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
>>> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
>>> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
>>> +
>>> +       /* configure VSC HB0 ~ HB3 */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
>>> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
>>> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
>>> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
>>> +
>>> +       /* configure DB0 / DB1 values */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
>>
>> Lots of hardcoded values here. I think this could be cleaned up.
>
>
> For "HB0~HB3", "PB0~PB3" and "DB1", I don't understand very well. Those
> seems to be a kind of head number, I got those magic values from our IC
> side. So I think those should be okay to keep the hardcode values here :-D
>

Hmm. Well, I'd probably pull them out into some HBX_MAGIC_VALUE
define, but I suppose it's fine to keep them as-is. Please at least
add a comment above explaining that they're magic values provided by
your IC team.

> But for the "0x3" in "ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL", I would fix it
> with suitable macro.
>
>
>>> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
>>> +
>>> +       /* set reuse spd inforframe */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>> +       val |= REUSE_SPD_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>> +
>>> +       /* mark info frame update */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val = (val | IF_UP) & ~IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +
>>> +       /* send info frame */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val |= IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +}
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> index cdcc6c5..a2698e4 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> @@ -22,6 +22,8 @@
>>>   #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>>>   #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>>>
>>> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
>>> +
>>>   #define ANALOGIX_DP_PLL_REG_1                  0xfc
>>>   #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>>>   #define ANALOGIX_DP_PLL_REG_3                  0x9e8
>>> @@ -30,6 +32,21 @@
>>>
>>>   #define ANALOGIX_DP_PD                         0x12c
>>>
>>> +#define ANALOGIX_DP_IF_TYPE                    0x244
>>> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
>>> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
>>> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
>>> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
>>> +#define ANALOGIX_DP_SPD_HB2                    0x300
>>> +#define ANALOGIX_DP_SPD_HB3                    0x304
>>> +#define ANALOGIX_DP_SPD_PB0                    0x308
>>> +#define ANALOGIX_DP_SPD_PB1                    0x30C
>>> +#define ANALOGIX_DP_SPD_PB2                    0x310
>>> +#define ANALOGIX_DP_SPD_PB3                    0x314
>>> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
>>> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
>>> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
>>> +
>>>   #define ANALOGIX_DP_LANE_MAP                   0x35C
>>>
>>>   #define ANALOGIX_DP_ANALOG_CTL_1               0x370
>>> @@ -103,6 +120,8 @@
>>>
>>>   #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>>>
>>> +#define ANALOGIX_DP_CRC_CON                    0x890
>>> +
>>>   /* ANALOGIX_DP_TX_SW_RESET */
>>>   #define RESET_DP_TX                            (0x1 << 0)
>>>
>>> @@ -151,6 +170,7 @@
>>>   #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>>>   #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>>>   #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
>>> +#define REUSE_SPD_EN                           (0x1 << 3)
>>>
>>>   /* ANALOGIX_DP_VIDEO_CTL_8 */
>>>   #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
>>> @@ -376,4 +396,12 @@
>>>   #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>>>   #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>>>
>>> +/* ANALOGIX_DP_PKT_SEND_CTL */
>>> +#define IF_UP                                  (0x1 << 4)
>>> +#define IF_EN                                  (0x1 << 0)
>>> +
>>> +/* ANALOGIX_DP_CRC_CON */
>>> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
>>> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
>>> +
>>>   #endif /* _ANALOGIX_DP_REG_H */
>>> diff --git a/include/drm/bridge/analogix_dp.h
>>> b/include/drm/bridge/analogix_dp.h
>>> index 261b86d..183a336 100644
>>> --- a/include/drm/bridge/analogix_dp.h
>>> +++ b/include/drm/bridge/analogix_dp.h
>>> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>>>                           struct drm_connector *);
>>>   };
>>>
>>> +int analogix_dp_active_psr(struct device *dev);
>>> +int analogix_dp_inactive_psr(struct device *dev);
>>
>> Why active/inactive instead of enable/disable, which is used everywhere
>> else?
>
>
> Done
>
> Thanks,
> - Yakir
>
>
>>> +
>>>   int analogix_dp_resume(struct device *dev);
>>>   int analogix_dp_suspend(struct device *dev);
>>>
>>> --
>>> 1.9.1
>>>
>>>
>>
>>
>
>

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
  2016-07-12 15:10       ` Tomasz Figa
@ 2016-07-12 16:10           ` Daniel Vetter
  0 siblings, 0 replies; 35+ messages in thread
From: Daniel Vetter @ 2016-07-12 16:10 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Sean Paul, Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner,
	Stéphane Marchesin, Doug Anderson, Thierry Reding,
	Krzysztof Kozlowski, Javier Martinez Canillas, David Airlie,
	Emil Velikov, Linux Kernel Mailing List, dri-devel,
	Daniel Vetter, linux-samsung-soc, open list:ARM/Rockchip SoC...

On Wed, Jul 13, 2016 at 12:10:13AM +0900, Tomasz Figa wrote:
> On Tue, Jul 12, 2016 at 9:38 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
> >> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> >> > The PSR driver have exported four symbols for specific device driver:
> >> > - rockchip_drm_psr_register()
> >> > - rockchip_drm_psr_unregister()
> >> > - rockchip_drm_psr_enable()
> >> > - rockchip_drm_psr_disable()
> >> > - rockchip_drm_psr_flush()
> >> >
> >> > Encoder driver should call the register/unregister interfaces to hook
> >> > itself into common PSR driver, encoder have implement the 'psr_set'
> >> > callback which use the set PSR state in hardware side.
> >> >
> >> > Crtc driver would call the enable/disable interfaces when vblank is
> >> > enable/disable, after that the common PSR driver would call the encoder
> >> > registered callback to set the PSR state.
> >> >
> >>
> >> This feels overly complicated. It seems like you could cut out a bunch
> >> of code by just coding the psr functions into vop and
> >> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> >> would be if you plan on supporting psr in a different encoder or crtc
> >> in rockchip, or if you're planning on moving this into drm core.
> >
> > Agreed on the layers of indirection. Also, you end up with 3 delayed
> > timers in total:
> > - defio timer from fbdev emulation
> > - timer in this abstraction
> > - delayed work in the psr backend driver
> 
> Maybe I'm missing something obvious (don't know how the PSR is
> implemented in hardware or in other drivers), but why do we need all
> these timers? Couldn't we just trigger a fake page flip on fb dirty
> call?

Page flips are ratelimited to vrefresh, and not all drivers support it.
The other issue is that dirty ioctl supports dirty rectangles, but page
flip is still a full-screen upload. We discussed adding a dirty rectangle
to the kms properties to fix this gap, but until that's done I don't think
it makes much sense to remap dirty to an (atomic) page flip. One small
technicality is that dirty works on framebuffers, but page flips
(especially atomic ones) work on crtcs. That makes the remapping a notch
more tricky.

But in principle, fully agreed. And once we have atomic dirty rectangles
we could type up a small helper that drivers can plug into the dirty
ioctl. Since new properties requires new userspace we could just track the
dirty rectangle internally to get this helper landed, if there's
interested for that. I'll certainly be happy to review patches.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
@ 2016-07-12 16:10           ` Daniel Vetter
  0 siblings, 0 replies; 35+ messages in thread
From: Daniel Vetter @ 2016-07-12 16:10 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Krzysztof Kozlowski, linux-samsung-soc,
	open list:ARM/Rockchip SoC...,
	Mark Yao, Jingoo Han, Emil Velikov, Doug Anderson, dri-devel,
	Linux Kernel Mailing List, Javier Martinez Canillas,
	Daniel Vetter, Stéphane Marchesin, Thierry Reding

On Wed, Jul 13, 2016 at 12:10:13AM +0900, Tomasz Figa wrote:
> On Tue, Jul 12, 2016 at 9:38 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
> >> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
> >> > The PSR driver have exported four symbols for specific device driver:
> >> > - rockchip_drm_psr_register()
> >> > - rockchip_drm_psr_unregister()
> >> > - rockchip_drm_psr_enable()
> >> > - rockchip_drm_psr_disable()
> >> > - rockchip_drm_psr_flush()
> >> >
> >> > Encoder driver should call the register/unregister interfaces to hook
> >> > itself into common PSR driver, encoder have implement the 'psr_set'
> >> > callback which use the set PSR state in hardware side.
> >> >
> >> > Crtc driver would call the enable/disable interfaces when vblank is
> >> > enable/disable, after that the common PSR driver would call the encoder
> >> > registered callback to set the PSR state.
> >> >
> >>
> >> This feels overly complicated. It seems like you could cut out a bunch
> >> of code by just coding the psr functions into vop and
> >> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> >> would be if you plan on supporting psr in a different encoder or crtc
> >> in rockchip, or if you're planning on moving this into drm core.
> >
> > Agreed on the layers of indirection. Also, you end up with 3 delayed
> > timers in total:
> > - defio timer from fbdev emulation
> > - timer in this abstraction
> > - delayed work in the psr backend driver
> 
> Maybe I'm missing something obvious (don't know how the PSR is
> implemented in hardware or in other drivers), but why do we need all
> these timers? Couldn't we just trigger a fake page flip on fb dirty
> call?

Page flips are ratelimited to vrefresh, and not all drivers support it.
The other issue is that dirty ioctl supports dirty rectangles, but page
flip is still a full-screen upload. We discussed adding a dirty rectangle
to the kms properties to fix this gap, but until that's done I don't think
it makes much sense to remap dirty to an (atomic) page flip. One small
technicality is that dirty works on framebuffers, but page flips
(especially atomic ones) work on crtcs. That makes the remapping a notch
more tricky.

But in principle, fully agreed. And once we have atomic dirty rectangles
we could type up a small helper that drivers can plug into the dirty
ioctl. Since new properties requires new userspace we could just track the
dirty rectangle internally to get this helper landed, if there's
interested for that. I'll certainly be happy to review patches.
-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] 35+ messages in thread

* Re: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
  2016-07-12 15:29       ` Sean Paul
@ 2016-07-13  1:57         ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-13  1:57 UTC (permalink / raw)
  To: Sean Paul
  Cc: Krzysztof Kozlowski, linux-samsung-soc, linux-rockchip, Mark Yao,
	Daniel Vetter, Emil Velikov, Doug Anderson, dri-devel,
	Tomasz Figa, Javier Martinez Canillas, Jingoo Han,
	Stéphane Marchesin, Thierry Reding,
	Linux Kernel Mailing List


[-- Attachment #1.1: Type: text/plain, Size: 14265 bytes --]

Sean,

On 07/12/2016 11:29 PM, Sean Paul wrote:
> On Thu, Jul 7, 2016 at 7:26 PM, Yakir Yang <ykk@rock-chips.com> wrote:
>> Sean,
>>
>> Thanks for your review.
>>
>>
>> On 07/02/2016 03:46 AM, Sean Paul wrote:
>>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>>>> The full name of PSR is Panel Self Refresh, panel device could refresh
>>>> itself with the hardware framebuffer in panel, this would make lots of
>>>> sense to save the power consumption.
>>>>
>>>> This patch have exported two symbols for platform driver to implement
>>>> the PSR function in hardware side:
>>>> - analogix_dp_active_psr()
>>>> - analogix_dp_inactive_psr()
>>>>
>>>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>>>> ---
>>>> Changes in v3:
>>>> - split analogix_dp_enable_psr(), make it more clearly
>>>>       analogix_dp_detect_sink_psr()
>>>>       analogix_dp_enable_sink_psr()
>>>> - remove some nosie register setting comments
>>>>
>>>> Changes in v2:
>>>> - introduce in v2, splite the common Analogix DP changes out
>>>>
>>>>    drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64
>>>> ++++++++++++++++++++++
>>>>    drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>>>>    drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54
>>>> ++++++++++++++++++
>>>>    drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>>>>    include/drm/bridge/analogix_dp.h                   |  3 +
>>>>    5 files changed, 153 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>>> index 32715da..b557097 100644
>>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>>> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct
>>>> analogix_dp_device *dp)
>>>>           return 0;
>>>>    }
>>>>
>>>> +int analogix_dp_active_psr(struct device *dev)
>>>> +{
>>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>>> +
>>>> +       if (!dp->psr_support)
>>>> +               return -EINVAL;
>>>> +
>>>> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
>>>> +                                EDP_VSC_PSR_CRC_VALUES_VALID);
>>>> +       return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
>>>> +
>>>> +int analogix_dp_inactive_psr(struct device *dev)
>>>> +{
>>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>>> +
>>>> +       if (!dp->psr_support)
>>>> +               return -EINVAL;
>>>> +
>>>> +       analogix_dp_send_psr_spd(dp, 0);
>>>> +       return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
>>>> +
>>>> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
>>>> +{
>>>> +       unsigned char psr_version;
>>>> +
>>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT,
>>>> &psr_version);
>>>> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
>>>> +
>>> This info message is likely to be spammy since it's printed everytime
>>> the panel toggle on. Perhaps downgrade to debug level.
>>
>> Okay, done.
>>
>>>> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
>>>> +}
>>>> +
>>>> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
>>> Return type is int, but the function never fails and you don't check
>>> the return value when calling it. Seems like this should be void.
>>
>> Done.
>>
>>>> +{
>>>> +       unsigned char psr_en;
>>>> +
>>>> +       /* Disable psr function */
>>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>>> +       psr_en &= ~DP_PSR_ENABLE;
>>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>>> +
>>>> +       /* Main-Link transmitter remains active during PSR active states
>>>> */
>>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>>> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
>>> Why read psr_en if you're just going to overwrite it? Perhaps you meant |=
>>> here.
>>>
>> Yes, it's my mistaken, no need to read the DP_PSR_EN_CFG, just configure it
>> directly is enough.
>>
>>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>>> +
>>>> +       /* Enable psr function */
>>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>>> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
>>>> +                DP_PSR_CRC_VERIFICATION;
>>> Again, no need to read if you're just overwriting.
>>
>> Yes, ditto
>>
>>
>>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>>> +
>>>> +       analogix_dp_enable_psr_crc(dp);
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>>    static unsigned char analogix_dp_calc_edid_check_sum(unsigned char
>>>> *edid_data)
>>>>    {
>>>>           int i;
>>>> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct
>>>> analogix_dp_device *dp)
>>>>
>>>>           /* Enable video */
>>>>           analogix_dp_start_video(dp);
>>>> +
>>>> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
>>>> +       if (dp->psr_support)
>>>> +               analogix_dp_enable_sink_psr(dp);
>>>>    }
>>>>
>>>>    int analogix_dp_get_modes(struct drm_connector *connector)
>>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>>> index b456380..6ca5dde 100644
>>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>>> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>>>>           int                     hpd_gpio;
>>>>           bool                    force_hpd;
>>>>           unsigned char           edid[EDID_BLOCK_LENGTH * 2];
>>>> +       bool                    psr_support;
>>>>
>>>>           struct analogix_dp_plat_data *plat_data;
>>>>    };
>>>> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct
>>>> analogix_dp_device *dp);
>>>>    void analogix_dp_config_video_slave_mode(struct analogix_dp_device
>>>> *dp);
>>>>    void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>>>>    void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
>>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
>>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
>>>> +
>>>>    #endif /* _ANALOGIX_DP_CORE_H */
>>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>>> index 48030f0..e8372c7 100644
>>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>>> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct
>>>> analogix_dp_device *dp)
>>>>           reg |= SCRAMBLING_DISABLE;
>>>>           writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>>>>    }
>>>> +
>>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
>>>> +{
>>>> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
>>>> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
>>>> +
>>>> +       usleep_range(10, 20);
>>> Is this sleep arbitrary, or documented somewhere? Could you add a
>>> comment explaining how this was arrived at?
>>>
>> Yes, this sleep is arbitrary, there is no document about the
>>
> Ok, so I assume we actually need the delay for things to work? If not,
> delete the delay. If we need it, please add a comment saying the wait
> is required and the value was chosen via trial and error.

I should delete this delay, cause i found that PSR_VID_CRC_FLUSH not a 
trigger bit, but a enable gate. Here is the TRM explain:

PSR_VID_CRC_FLUSH:
     PSR Video CRC flush enable. The PSR video CRC value is initialized 
at every v-sync leading edge.

So I just need to set this flag after enable PSR CRC function, no need 
to remove it :-D


>>>> +
>>>> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
>>>> +}
>>>> +
>>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
>>>> +{
>>>> +       unsigned int val;
>>>> +
>>>> +       /* don't send info frame */
>>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>>> +       val &= ~IF_EN;
>>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>>> +
>>>> +       /* configure single frame update mode */
>>>> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
>>>> +
>>>> +       /* configure VSC HB0 ~ HB3 */
>>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
>>>> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
>>>> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
>>>> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
>>>> +
>>>> +       /* configure VSC HB0 ~ HB3 */
>>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
>>>> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
>>>> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
>>>> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
>>>> +
>>>> +       /* configure DB0 / DB1 values */
>>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
>>> Lots of hardcoded values here. I think this could be cleaned up.
>>
>> For "HB0~HB3", "PB0~PB3" and "DB1", I don't understand very well. Those
>> seems to be a kind of head number, I got those magic values from our IC
>> side. So I think those should be okay to keep the hardcode values here :-D
>>
> Hmm. Well, I'd probably pull them out into some HBX_MAGIC_VALUE
> define, but I suppose it's fine to keep them as-is. Please at least
> add a comment above explaining that they're magic values provided by
> your IC team.

Done ;)

Thanks,
- Yakir

>> But for the "0x3" in "ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL", I would fix it
>> with suitable macro.
>>
>>
>>>> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
>>>> +
>>>> +       /* set reuse spd inforframe */
>>>> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>>> +       val |= REUSE_SPD_EN;
>>>> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>>> +
>>>> +       /* mark info frame update */
>>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>>> +       val = (val | IF_UP) & ~IF_EN;
>>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>>> +
>>>> +       /* send info frame */
>>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>>> +       val |= IF_EN;
>>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>>> +}
>>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>>> index cdcc6c5..a2698e4 100644
>>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>>> @@ -22,6 +22,8 @@
>>>>    #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>>>>    #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>>>>
>>>> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
>>>> +
>>>>    #define ANALOGIX_DP_PLL_REG_1                  0xfc
>>>>    #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>>>>    #define ANALOGIX_DP_PLL_REG_3                  0x9e8
>>>> @@ -30,6 +32,21 @@
>>>>
>>>>    #define ANALOGIX_DP_PD                         0x12c
>>>>
>>>> +#define ANALOGIX_DP_IF_TYPE                    0x244
>>>> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
>>>> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
>>>> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
>>>> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
>>>> +#define ANALOGIX_DP_SPD_HB2                    0x300
>>>> +#define ANALOGIX_DP_SPD_HB3                    0x304
>>>> +#define ANALOGIX_DP_SPD_PB0                    0x308
>>>> +#define ANALOGIX_DP_SPD_PB1                    0x30C
>>>> +#define ANALOGIX_DP_SPD_PB2                    0x310
>>>> +#define ANALOGIX_DP_SPD_PB3                    0x314
>>>> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
>>>> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
>>>> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
>>>> +
>>>>    #define ANALOGIX_DP_LANE_MAP                   0x35C
>>>>
>>>>    #define ANALOGIX_DP_ANALOG_CTL_1               0x370
>>>> @@ -103,6 +120,8 @@
>>>>
>>>>    #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>>>>
>>>> +#define ANALOGIX_DP_CRC_CON                    0x890
>>>> +
>>>>    /* ANALOGIX_DP_TX_SW_RESET */
>>>>    #define RESET_DP_TX                            (0x1 << 0)
>>>>
>>>> @@ -151,6 +170,7 @@
>>>>    #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>>>>    #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>>>>    #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
>>>> +#define REUSE_SPD_EN                           (0x1 << 3)
>>>>
>>>>    /* ANALOGIX_DP_VIDEO_CTL_8 */
>>>>    #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
>>>> @@ -376,4 +396,12 @@
>>>>    #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>>>>    #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>>>>
>>>> +/* ANALOGIX_DP_PKT_SEND_CTL */
>>>> +#define IF_UP                                  (0x1 << 4)
>>>> +#define IF_EN                                  (0x1 << 0)
>>>> +
>>>> +/* ANALOGIX_DP_CRC_CON */
>>>> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
>>>> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
>>>> +
>>>>    #endif /* _ANALOGIX_DP_REG_H */
>>>> diff --git a/include/drm/bridge/analogix_dp.h
>>>> b/include/drm/bridge/analogix_dp.h
>>>> index 261b86d..183a336 100644
>>>> --- a/include/drm/bridge/analogix_dp.h
>>>> +++ b/include/drm/bridge/analogix_dp.h
>>>> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>>>>                            struct drm_connector *);
>>>>    };
>>>>
>>>> +int analogix_dp_active_psr(struct device *dev);
>>>> +int analogix_dp_inactive_psr(struct device *dev);
>>> Why active/inactive instead of enable/disable, which is used everywhere
>>> else?
>>
>> Done
>>
>> Thanks,
>> - Yakir
>>
>>
>>>> +
>>>>    int analogix_dp_resume(struct device *dev);
>>>>    int analogix_dp_suspend(struct device *dev);
>>>>
>>>> --
>>>> 1.9.1
>>>>
>>>>
>>>
>>
>
>


[-- Attachment #1.2: Type: text/html, Size: 16433 bytes --]

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

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

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
  2016-07-12 12:38       ` Daniel Vetter
@ 2016-07-13  3:36         ` Yakir Yang
  -1 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-13  3:36 UTC (permalink / raw)
  To: Sean Paul, Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

Daniel,

On 07/12/2016 08:38 PM, Daniel Vetter wrote:
> On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>>> The PSR driver have exported four symbols for specific device driver:
>>> - rockchip_drm_psr_register()
>>> - rockchip_drm_psr_unregister()
>>> - rockchip_drm_psr_enable()
>>> - rockchip_drm_psr_disable()
>>> - rockchip_drm_psr_flush()
>>>
>>> Encoder driver should call the register/unregister interfaces to hook
>>> itself into common PSR driver, encoder have implement the 'psr_set'
>>> callback which use the set PSR state in hardware side.
>>>
>>> Crtc driver would call the enable/disable interfaces when vblank is
>>> enable/disable, after that the common PSR driver would call the encoder
>>> registered callback to set the PSR state.
>>>
>> This feels overly complicated. It seems like you could cut out a bunch
>> of code by just coding the psr functions into vop and
>> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
>> would be if you plan on supporting psr in a different encoder or crtc
>> in rockchip, or if you're planning on moving this into drm core.
> Agreed on the layers of indirection. Also, you end up with 3 delayed
> timers in total:
> - defio timer from fbdev emulation
> - timer in this abstraction
> - delayed work in the psr backend driver
>
> I'd cut out at least the middle one.
>
> But since this seems to correctly use the ->dirty callback it gets my Ack
> either way ;-)

Aha, thanks :-D

- Yakir

> Cheers, Daniel
>
>> Perhaps others will disagree with this sentiment and this is the right
>> thing to do.
>>
>>> Fb driver would call the flush interface in 'fb->dirty' callback, this
>>> helper function would force all PSR enabled encoders to exit from PSR
>>> for 3 seconds.
>>>
>>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - split the psr flow into an common abstracted PSR driver
>>> - implement the 'fb->dirty' callback function (Daniel)
>>> - avoid to use notify to acqiure for vact event (Daniel)
>>> - remove psr_active() callback which introduce in v2
>>>
>>> Changes in v2: None
>>>
>>>   drivers/gpu/drm/rockchip/Makefile           |   2 +-
>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
>>>   5 files changed, 249 insertions(+), 1 deletion(-)
>>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>>> index 05d0713..9746365 100644
>>> --- a/drivers/gpu/drm/rockchip/Makefile
>>> +++ b/drivers/gpu/drm/rockchip/Makefile
>>> @@ -3,7 +3,7 @@
>>>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>>>
>>>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>> -               rockchip_drm_gem.o rockchip_drm_vop.o
>>> +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>>
>>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> index 20f12bc..0fec18f 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> @@ -21,6 +21,7 @@
>>>
>>>   #include "rockchip_drm_drv.h"
>>>   #include "rockchip_drm_gem.h"
>>> +#include "rockchip_drm_psr.h"
>>>
>>>   #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>>>
>>> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>>>                                       rockchip_fb->obj[0], handle);
>>>   }
>>>
>>> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
>>> +                                struct drm_file *file,
>>> +                                unsigned int flags, unsigned int color,
>>> +                                struct drm_clip_rect *clips,
>>> +                                unsigned int num_clips)
>>> +{
>>> +       rockchip_drm_psr_flush();
>>> +       return 0;
>>> +}
>>> +
>>>   static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>>>          .destroy        = rockchip_drm_fb_destroy,
>>>          .create_handle  = rockchip_drm_fb_create_handle,
>>> +       .dirty          = rockchip_drm_fb_dirty,
>>>   };
>>>
>>>   static struct rockchip_drm_fb *
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>> new file mode 100644
>>> index 0000000..c044443
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>> @@ -0,0 +1,200 @@
>>> +#include <drm/drm_crtc_helper.h>
>>> +
>>> +#include "rockchip_drm_psr.h"
>>> +
>>> +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
>>> +
>>> +static LIST_HEAD(psr_list);
>>> +static DEFINE_MUTEX(psr_list_mutex);
>> I'm not crazy about these globals. Perhaps you can initialize them
>> with the rockchip driver and tuck them in a driver-level struct
>> (rockchip_drm_private or something).
>>
>>
>>> +
>>> +enum psr_state {
>>> +       PSR_FLUSH,
>>> +       PSR_ENABLE,
>>> +       PSR_DISABLE,
>>> +};
>>> +
>>> +struct psr_drv {
>>> +       struct list_head list;
>>> +       enum psr_state state;
>>> +       struct mutex state_mutex;
>>> +
>>> +       struct timer_list flush_timer;
>>> +
>>> +       struct drm_encoder *encoder;
>>> +       int (*set)(struct drm_encoder *encoder, bool enable);
>>> +};
>>> +
>>> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->encoder->crtc == crtc) {
>>> +                       mutex_unlock(&psr_list_mutex);
>>> +                       return psr;
>>> +               }
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +
>>> +       return ERR_PTR(-ENODEV);
>>> +}
>>> +
>>> +static void psr_enable(struct psr_drv *psr)
>>> +{
>>> +       if (psr->state == PSR_ENABLE)
>>> +               return;
>> Should you be worried about races by accessing this outside of the state mutex?
>>
>>> +
>>> +       mutex_lock(&psr->state_mutex);
>>> +       psr->state = PSR_ENABLE;
>>> +       psr->set(psr->encoder, true);
>>> +       mutex_unlock(&psr->state_mutex);
>>> +}
>>> +
>>> +static void psr_disable(struct psr_drv *psr)
>>> +{
>>> +       if (psr->state == PSR_DISABLE)
>>> +               return;
>>> +
>>> +       mutex_lock(&psr->state_mutex);
>>> +       psr->state = PSR_DISABLE;
>>> +       psr->set(psr->encoder, false);
>>> +       mutex_unlock(&psr->state_mutex);
>>> +}
>>> +
>>> +static void psr_flush_handler(unsigned long data)
>>> +{
>>> +       struct psr_drv *psr = (struct psr_drv *)data;
>>> +
>>> +       if (!psr || psr->state != PSR_FLUSH)
>>> +               return;
>>> +
>>> +       psr_enable(psr);
>>> +}
>>> +
>>> +/**
>>> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
>>> + * @crtc: CRTC to obtain the PSR encoder
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>>> +
>>> +       if (IS_ERR(psr))
>>> +               return PTR_ERR(psr);
>>> +
>>> +       psr_enable(psr);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
>>> + * @crtc: CRTC to obtain the PSR encoder
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>>> +
>>> +       if (IS_ERR(psr))
>>> +               return PTR_ERR(psr);
>>> +
>>> +       psr_disable(psr);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
>>> + *
>>> + * Disable the PSR function for all registered encoders, and then enable the
>>> + * PSR function back after 5 second. If encoder PSR state have been changed
>> s/5 second/PSR_FLUSH_TIMEOUT/
>>
>>> + * during flush time, then keep the state no change after flush timeout.
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +void rockchip_drm_psr_flush(void)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->state == PSR_DISABLE)
>>> +                       continue;
>>> +
>>> +               mod_timer(&psr->flush_timer,
>>> +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
>>> +
>>> +               psr_disable(psr);
>>> +               psr->state = PSR_FLUSH;
>> This is set outside of state_mutex, is that intentional?
>>
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_register - register encoder to psr driver
>>> + * @encoder: encoder that obtain the PSR function
>>> + * @psr_set: call back to set PSR state
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>> +                             int (*psr_set)(struct drm_encoder *, bool enable))
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       if (!encoder || !psr_set)
>>> +               return -EINVAL;
>>> +
>>> +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
>>> +       if (!psr)
>>> +               return -ENOMEM;
>>> +
>>> +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
>>> +
>>> +       mutex_init(&psr->state_mutex);
>>> +
>>> +       psr->state = PSR_DISABLE;
>>> +       psr->encoder = encoder;
>>> +       psr->set = psr_set;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_add_tail(&psr->list, &psr_list);
>>> +       mutex_unlock(&psr_list_mutex);
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_register);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
>>> + * @encoder: encoder that obtain the PSR function
>>> + * @psr_set: call back to set PSR state
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->encoder == encoder) {
>>> +                       del_timer(&psr->flush_timer);
>>> +                       list_del(&psr->list);
>>> +                       kfree(psr);
>>> +               }
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>> new file mode 100644
>>> index 0000000..622f605
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>> @@ -0,0 +1,12 @@
>>> +#ifndef __ROCKCHIP_DRM_PSR___
>>> +#define __ROCKCHIP_DRM_PSR___
>>> +
>>> +void rockchip_drm_psr_flush(void);
>>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
>>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
>>> +
>>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>> +                           int (*psr_set)(struct drm_encoder *, bool enable));
>>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
>>> +
>>> +#endif /* __ROCKCHIP_DRM_PSR__ */
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> index cd3cac5..3c6dfc5 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> @@ -34,6 +34,7 @@
>>>   #include "rockchip_drm_drv.h"
>>>   #include "rockchip_drm_gem.h"
>>>   #include "rockchip_drm_fb.h"
>>> +#include "rockchip_drm_psr.h"
>>>   #include "rockchip_drm_vop.h"
>>>
>>>   #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
>>> @@ -121,6 +122,9 @@ struct vop {
>>>          /* protected by dev->event_lock */
>>>          struct drm_pending_vblank_event *event;
>>>
>>> +       bool psr_enabled;
>>> +       struct delayed_work psr_work;
>>> +
>>>          struct completion line_flag_completion;
>>>
>>>          const struct vop_data *data;
>>> @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>>          .atomic_destroy_state = vop_atomic_plane_destroy_state,
>>>   };
>>>
>>> +static void vop_psr_work(struct work_struct *work)
>>> +{
>>> +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
>>> +
>>> +       if (vop->psr_enabled)
>>> +               rockchip_drm_psr_enable(&vop->crtc);
>>> +       else
>>> +               rockchip_drm_psr_disable(&vop->crtc);
>>> +}
>>> +
>>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>>   {
>>>          struct vop *vop = to_vop(crtc);
>>> @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>>
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>>
>>> +       vop->psr_enabled = false;
>>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>> +
>>>          return 0;
>>>   }
>>>
>>> @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>>>          VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>>>
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +
>>> +       vop->psr_enabled = true;
>>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>>   }
>>>
>>>   static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
>>> @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>>>                  return ret;
>>>
>>>          pm_runtime_enable(&pdev->dev);
>>> +
>>> +       vop->psr_enabled = false;
>>> +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>>> +
>>>          return 0;
>>>   }
>>>
>>> --
>>> 1.9.1
>>>
>>>

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

* Re: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
@ 2016-07-13  3:36         ` Yakir Yang
  0 siblings, 0 replies; 35+ messages in thread
From: Yakir Yang @ 2016-07-13  3:36 UTC (permalink / raw)
  To: Sean Paul, Mark Yao, Inki Dae, Jingoo Han, Heiko Stuebner,
	Stéphane Marchesin, Tomasz Figa, Doug Anderson,
	Thierry Reding, Krzysztof Kozlowski, Javier Martinez Canillas,
	David Airlie, Emil Velikov, Linux Kernel Mailing List, dri-devel,
	linux-samsung-soc, linux-rockchip

Daniel,

On 07/12/2016 08:38 PM, Daniel Vetter wrote:
> On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk@rock-chips.com> wrote:
>>> The PSR driver have exported four symbols for specific device driver:
>>> - rockchip_drm_psr_register()
>>> - rockchip_drm_psr_unregister()
>>> - rockchip_drm_psr_enable()
>>> - rockchip_drm_psr_disable()
>>> - rockchip_drm_psr_flush()
>>>
>>> Encoder driver should call the register/unregister interfaces to hook
>>> itself into common PSR driver, encoder have implement the 'psr_set'
>>> callback which use the set PSR state in hardware side.
>>>
>>> Crtc driver would call the enable/disable interfaces when vblank is
>>> enable/disable, after that the common PSR driver would call the encoder
>>> registered callback to set the PSR state.
>>>
>> This feels overly complicated. It seems like you could cut out a bunch
>> of code by just coding the psr functions into vop and
>> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
>> would be if you plan on supporting psr in a different encoder or crtc
>> in rockchip, or if you're planning on moving this into drm core.
> Agreed on the layers of indirection. Also, you end up with 3 delayed
> timers in total:
> - defio timer from fbdev emulation
> - timer in this abstraction
> - delayed work in the psr backend driver
>
> I'd cut out at least the middle one.
>
> But since this seems to correctly use the ->dirty callback it gets my Ack
> either way ;-)

Aha, thanks :-D

- Yakir

> Cheers, Daniel
>
>> Perhaps others will disagree with this sentiment and this is the right
>> thing to do.
>>
>>> Fb driver would call the flush interface in 'fb->dirty' callback, this
>>> helper function would force all PSR enabled encoders to exit from PSR
>>> for 3 seconds.
>>>
>>> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - split the psr flow into an common abstracted PSR driver
>>> - implement the 'fb->dirty' callback function (Daniel)
>>> - avoid to use notify to acqiure for vact event (Daniel)
>>> - remove psr_active() callback which introduce in v2
>>>
>>> Changes in v2: None
>>>
>>>   drivers/gpu/drm/rockchip/Makefile           |   2 +-
>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
>>>   5 files changed, 249 insertions(+), 1 deletion(-)
>>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>>> index 05d0713..9746365 100644
>>> --- a/drivers/gpu/drm/rockchip/Makefile
>>> +++ b/drivers/gpu/drm/rockchip/Makefile
>>> @@ -3,7 +3,7 @@
>>>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>>>
>>>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>> -               rockchip_drm_gem.o rockchip_drm_vop.o
>>> +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>>
>>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> index 20f12bc..0fec18f 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> @@ -21,6 +21,7 @@
>>>
>>>   #include "rockchip_drm_drv.h"
>>>   #include "rockchip_drm_gem.h"
>>> +#include "rockchip_drm_psr.h"
>>>
>>>   #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>>>
>>> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>>>                                       rockchip_fb->obj[0], handle);
>>>   }
>>>
>>> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
>>> +                                struct drm_file *file,
>>> +                                unsigned int flags, unsigned int color,
>>> +                                struct drm_clip_rect *clips,
>>> +                                unsigned int num_clips)
>>> +{
>>> +       rockchip_drm_psr_flush();
>>> +       return 0;
>>> +}
>>> +
>>>   static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>>>          .destroy        = rockchip_drm_fb_destroy,
>>>          .create_handle  = rockchip_drm_fb_create_handle,
>>> +       .dirty          = rockchip_drm_fb_dirty,
>>>   };
>>>
>>>   static struct rockchip_drm_fb *
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>> new file mode 100644
>>> index 0000000..c044443
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>> @@ -0,0 +1,200 @@
>>> +#include <drm/drm_crtc_helper.h>
>>> +
>>> +#include "rockchip_drm_psr.h"
>>> +
>>> +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
>>> +
>>> +static LIST_HEAD(psr_list);
>>> +static DEFINE_MUTEX(psr_list_mutex);
>> I'm not crazy about these globals. Perhaps you can initialize them
>> with the rockchip driver and tuck them in a driver-level struct
>> (rockchip_drm_private or something).
>>
>>
>>> +
>>> +enum psr_state {
>>> +       PSR_FLUSH,
>>> +       PSR_ENABLE,
>>> +       PSR_DISABLE,
>>> +};
>>> +
>>> +struct psr_drv {
>>> +       struct list_head list;
>>> +       enum psr_state state;
>>> +       struct mutex state_mutex;
>>> +
>>> +       struct timer_list flush_timer;
>>> +
>>> +       struct drm_encoder *encoder;
>>> +       int (*set)(struct drm_encoder *encoder, bool enable);
>>> +};
>>> +
>>> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->encoder->crtc == crtc) {
>>> +                       mutex_unlock(&psr_list_mutex);
>>> +                       return psr;
>>> +               }
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +
>>> +       return ERR_PTR(-ENODEV);
>>> +}
>>> +
>>> +static void psr_enable(struct psr_drv *psr)
>>> +{
>>> +       if (psr->state == PSR_ENABLE)
>>> +               return;
>> Should you be worried about races by accessing this outside of the state mutex?
>>
>>> +
>>> +       mutex_lock(&psr->state_mutex);
>>> +       psr->state = PSR_ENABLE;
>>> +       psr->set(psr->encoder, true);
>>> +       mutex_unlock(&psr->state_mutex);
>>> +}
>>> +
>>> +static void psr_disable(struct psr_drv *psr)
>>> +{
>>> +       if (psr->state == PSR_DISABLE)
>>> +               return;
>>> +
>>> +       mutex_lock(&psr->state_mutex);
>>> +       psr->state = PSR_DISABLE;
>>> +       psr->set(psr->encoder, false);
>>> +       mutex_unlock(&psr->state_mutex);
>>> +}
>>> +
>>> +static void psr_flush_handler(unsigned long data)
>>> +{
>>> +       struct psr_drv *psr = (struct psr_drv *)data;
>>> +
>>> +       if (!psr || psr->state != PSR_FLUSH)
>>> +               return;
>>> +
>>> +       psr_enable(psr);
>>> +}
>>> +
>>> +/**
>>> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
>>> + * @crtc: CRTC to obtain the PSR encoder
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>>> +
>>> +       if (IS_ERR(psr))
>>> +               return PTR_ERR(psr);
>>> +
>>> +       psr_enable(psr);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
>>> + * @crtc: CRTC to obtain the PSR encoder
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>>> +
>>> +       if (IS_ERR(psr))
>>> +               return PTR_ERR(psr);
>>> +
>>> +       psr_disable(psr);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
>>> + *
>>> + * Disable the PSR function for all registered encoders, and then enable the
>>> + * PSR function back after 5 second. If encoder PSR state have been changed
>> s/5 second/PSR_FLUSH_TIMEOUT/
>>
>>> + * during flush time, then keep the state no change after flush timeout.
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +void rockchip_drm_psr_flush(void)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->state == PSR_DISABLE)
>>> +                       continue;
>>> +
>>> +               mod_timer(&psr->flush_timer,
>>> +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
>>> +
>>> +               psr_disable(psr);
>>> +               psr->state = PSR_FLUSH;
>> This is set outside of state_mutex, is that intentional?
>>
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_register - register encoder to psr driver
>>> + * @encoder: encoder that obtain the PSR function
>>> + * @psr_set: call back to set PSR state
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>> +                             int (*psr_set)(struct drm_encoder *, bool enable))
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       if (!encoder || !psr_set)
>>> +               return -EINVAL;
>>> +
>>> +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
>>> +       if (!psr)
>>> +               return -ENOMEM;
>>> +
>>> +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
>>> +
>>> +       mutex_init(&psr->state_mutex);
>>> +
>>> +       psr->state = PSR_DISABLE;
>>> +       psr->encoder = encoder;
>>> +       psr->set = psr_set;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_add_tail(&psr->list, &psr_list);
>>> +       mutex_unlock(&psr_list_mutex);
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_register);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
>>> + * @encoder: encoder that obtain the PSR function
>>> + * @psr_set: call back to set PSR state
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->encoder == encoder) {
>>> +                       del_timer(&psr->flush_timer);
>>> +                       list_del(&psr->list);
>>> +                       kfree(psr);
>>> +               }
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>> new file mode 100644
>>> index 0000000..622f605
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>> @@ -0,0 +1,12 @@
>>> +#ifndef __ROCKCHIP_DRM_PSR___
>>> +#define __ROCKCHIP_DRM_PSR___
>>> +
>>> +void rockchip_drm_psr_flush(void);
>>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
>>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
>>> +
>>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>> +                           int (*psr_set)(struct drm_encoder *, bool enable));
>>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
>>> +
>>> +#endif /* __ROCKCHIP_DRM_PSR__ */
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> index cd3cac5..3c6dfc5 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> @@ -34,6 +34,7 @@
>>>   #include "rockchip_drm_drv.h"
>>>   #include "rockchip_drm_gem.h"
>>>   #include "rockchip_drm_fb.h"
>>> +#include "rockchip_drm_psr.h"
>>>   #include "rockchip_drm_vop.h"
>>>
>>>   #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
>>> @@ -121,6 +122,9 @@ struct vop {
>>>          /* protected by dev->event_lock */
>>>          struct drm_pending_vblank_event *event;
>>>
>>> +       bool psr_enabled;
>>> +       struct delayed_work psr_work;
>>> +
>>>          struct completion line_flag_completion;
>>>
>>>          const struct vop_data *data;
>>> @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>>          .atomic_destroy_state = vop_atomic_plane_destroy_state,
>>>   };
>>>
>>> +static void vop_psr_work(struct work_struct *work)
>>> +{
>>> +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
>>> +
>>> +       if (vop->psr_enabled)
>>> +               rockchip_drm_psr_enable(&vop->crtc);
>>> +       else
>>> +               rockchip_drm_psr_disable(&vop->crtc);
>>> +}
>>> +
>>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>>   {
>>>          struct vop *vop = to_vop(crtc);
>>> @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>>
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>>
>>> +       vop->psr_enabled = false;
>>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>> +
>>>          return 0;
>>>   }
>>>
>>> @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>>>          VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>>>
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +
>>> +       vop->psr_enabled = true;
>>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>>   }
>>>
>>>   static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
>>> @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>>>                  return ret;
>>>
>>>          pm_runtime_enable(&pdev->dev);
>>> +
>>> +       vop->psr_enabled = false;
>>> +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>>> +
>>>          return 0;
>>>   }
>>>
>>> --
>>> 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] 35+ messages in thread

end of thread, other threads:[~2016-07-13  3:37 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-01  9:18 [PATCH v3 0/4] Add PSR function support for Analogix/Rockchip DP Yakir Yang
2016-07-01  9:18 ` Yakir Yang
2016-07-01  9:19 ` [PATCH v3 1/4] drm/rockchip: vop: export line flag function Yakir Yang
2016-07-01  9:19   ` Yakir Yang
2016-07-01 15:30   ` Sean Paul
2016-07-01 15:30     ` Sean Paul
2016-07-01 15:32     ` Sean Paul
2016-07-01 15:32       ` Sean Paul
2016-07-08  2:30       ` Yakir Yang
2016-07-08  2:30         ` Yakir Yang
2016-07-01  9:19 ` [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver Yakir Yang
2016-07-01  9:19   ` Yakir Yang
2016-07-01 18:00   ` Sean Paul
2016-07-08  2:12     ` Yakir Yang
2016-07-08  2:12       ` Yakir Yang
2016-07-12 12:38     ` Daniel Vetter
2016-07-12 12:38       ` Daniel Vetter
2016-07-12 15:10       ` Tomasz Figa
2016-07-12 16:10         ` Daniel Vetter
2016-07-12 16:10           ` Daniel Vetter
2016-07-13  3:36       ` Yakir Yang
2016-07-13  3:36         ` Yakir Yang
2016-07-01  9:19 ` [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support Yakir Yang
2016-07-01  9:19   ` Yakir Yang
2016-07-01 19:46   ` Sean Paul
2016-07-01 19:46     ` Sean Paul
2016-07-08  2:26     ` Yakir Yang
2016-07-08  2:39       ` Yakir Yang
2016-07-12 15:29       ` Sean Paul
2016-07-13  1:57         ` Yakir Yang
2016-07-01  9:19 ` [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function Yakir Yang
2016-07-01  9:19   ` Yakir Yang
2016-07-01 20:05   ` Sean Paul
2016-07-08  2:32     ` Yakir Yang
2016-07-08  2:32       ` Yakir Yang

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.