From: Ville Syrjälä ville.syrjala@linux.intel.com
Our TV encoder support isn't in the best shape. This series fixes it up quite a bit.
The most important part is fixing the issues resulting from the lack of frame counter on i965gm which causes nasty flip_done timeouts when we attempt to do anything with the TV encoder (including load detection).
Dust off those component cables people! ;)
Entire series is available here: git://github.com/vsyrjala/linux.git tvout_fixes
Ville Syrjälä (15): drm/vblank: Allow dynamic per-crtc max_vblank_count drm/i915: Don't try to use the hardware frame counter with i965gm TV output drm/i915/tv: Fix interlaced ysize calculation drm/i915/tv: Fix tv mode clocks drm/i915/tv: Store the TV oversampling factor in the TV mode drm/i915/tv: Use bools where appropriate drm/i915/tv: Nuke silly 0 inittialization of xpos/ypos drm/i915/tv: Deobfuscate preferred mode selection drm/i915/tv: Use drm_mode_set_name() to name TV modes drm/i915/tv: Make TV mode autoselection actually useable drm/i915/tv: Nuke reported_modes[] drm/i915/tv: Add 1080p30/50/60 TV modes drm/i915/tv: Generate better pipe timings for TV encoder drm/i915/tv: Fix >1024 modes on gen3 drm/i915/tv: Filter out >1024 wide modes that would need vertical scaling on gen3
drivers/gpu/drm/drm_vblank.c | 39 +- drivers/gpu/drm/i915/i915_irq.c | 10 +- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_display.c | 40 +- drivers/gpu/drm/i915/intel_tv.c | 695 +++++++++++++++++++++------ include/drm/drm_vblank.h | 8 + 6 files changed, 640 insertions(+), 153 deletions(-)
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_vblank.c | 39 ++++++++++++++++++++++++++++++++---- include/drm/drm_vblank.h | 8 ++++++++ 2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..c3abbdca8aba 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + return vblank->max_vblank_count ?: dev->max_vblank_count; +} + /* * "No hw counter" fallback implementation of .get_vblank_counter() hook, * if there is no useable hardware frame counter available. */ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) { - WARN_ON_ONCE(dev->max_vblank_count != 0); + WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0; }
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns; + u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
/* * Interrupts were disabled prior to this call, so deal with counter @@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
- if (dev->max_vblank_count != 0) { + if (max_vblank_count) { /* trust the hw counter when it's around */ - diff = (cur_vblank - vblank->last) & dev->max_vblank_count; + diff = (cur_vblank - vblank->last) & max_vblank_count; } else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
@@ -258,7 +266,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) { - WARN_ON_ONCE(cur_vblank != vblank->last); + WARN_ON_ONCE(max_vblank_count && + cur_vblank != vblank->last); return; }
@@ -1204,6 +1213,28 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_reset);
+/** + * drm_crtc_set_max_vblank_count - configure the hw max vblank counter value + * @crtc: CRTC in question + * @max_vblank_count: max hardware vblank counter value + * + * Update the maximum hardware vblank counter value for @crtc. Useful + * for hardware where the operation of the hardware vblank counter + * depends on the active display configuration. + * + * If used, must be called before drm_vblank_on(). + */ +void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc, + u32 max_vblank_count) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + vblank->max_vblank_count = max_vblank_count; +} +EXPORT_SYMBOL(drm_crtc_set_max_vblank_count); + /** * drm_crtc_vblank_on - enable vblank events on a CRTC * @crtc: CRTC in question diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 6ad9630d4f48..ecb2cf9913e2 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -128,6 +128,12 @@ struct drm_vblank_crtc { * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. */ u32 last; + /** + * @max_vblank_count: Maximum value of the hardware vblank counter. + * If non-zero this takes precedence over &drm_device.max_vblank_count + * for this crtc. Otherwise &drm_device.max_vblank_count is used. + */ + u32 max_vblank_count; /** * @inmodeset: Tracks whether the vblank is disabled due to a modeset. * For legacy driver bit 2 additionally tracks whether an additional @@ -206,4 +212,6 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode); wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc); +void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc, + u32 max_vblank_count); #endif
On Mon, Nov 12, 2018 at 06:59:45PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/drm_vblank.c | 39 ++++++++++++++++++++++++++++++++---- include/drm/drm_vblank.h | 8 ++++++++ 2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..c3abbdca8aba 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- return vblank->max_vblank_count ?: dev->max_vblank_count;
+}
/*
- "No hw counter" fallback implementation of .get_vblank_counter() hook,
- if there is no useable hardware frame counter available.
*/ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) {
- WARN_ON_ONCE(dev->max_vblank_count != 0);
- WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0;
}
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
/*
- Interrupts were disabled prior to this call, so deal with counter
@@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
- if (dev->max_vblank_count != 0) {
- if (max_vblank_count) { /* trust the hw counter when it's around */
diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
} else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));diff = (cur_vblank - vblank->last) & max_vblank_count;
@@ -258,7 +266,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) {
WARN_ON_ONCE(cur_vblank != vblank->last);
WARN_ON_ONCE(max_vblank_count &&
cur_vblank != vblank->last);
Unrelated bugfix for this warning? Should be a separate patch I think, or I'm missing something.
return;
}
@@ -1204,6 +1213,28 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_reset);
+/**
- drm_crtc_set_max_vblank_count - configure the hw max vblank counter value
- @crtc: CRTC in question
- @max_vblank_count: max hardware vblank counter value
- Update the maximum hardware vblank counter value for @crtc. Useful
- for hardware where the operation of the hardware vblank counter
- depends on the active display configuration.
- If used, must be called before drm_vblank_on().
I think we should check this at runtime with a WARN_ON. Plus make the comment here a bit clearer that this is indeed for runtime adjusting of the max_vblank_count, in cases where that depends upon the connected outputs.
- */
+void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
u32 max_vblank_count)
+{
- struct drm_device *dev = crtc->dev;
- unsigned int pipe = drm_crtc_index(crtc);
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- vblank->max_vblank_count = max_vblank_count;
+} +EXPORT_SYMBOL(drm_crtc_set_max_vblank_count);
/**
- drm_crtc_vblank_on - enable vblank events on a CRTC
- @crtc: CRTC in question
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 6ad9630d4f48..ecb2cf9913e2 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -128,6 +128,12 @@ struct drm_vblank_crtc { * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. */ u32 last;
- /**
* @max_vblank_count: Maximum value of the hardware vblank counter.
* If non-zero this takes precedence over &drm_device.max_vblank_count
* for this crtc. Otherwise &drm_device.max_vblank_count is used.
*/
I'd add "This should be set by calling drm_crtc_set_max_vblank_count()."
And please also add a note to the kerneldoc of drm_driver.max_vblank_count pointing at &drm_vblank_crtc.max_vblank_count for per-crtc limits.
Aside from the nits lgtm. I think I'll skip looking at the TV out stuff though ...
Cheers, Daniel
- u32 max_vblank_count; /**
- @inmodeset: Tracks whether the vblank is disabled due to a modeset.
- For legacy driver bit 2 additionally tracks whether an additional
@@ -206,4 +212,6 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode); wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc); +void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
u32 max_vblank_count);
#endif
2.18.1
On Wed, Nov 21, 2018 at 10:27:27AM +0100, Daniel Vetter wrote:
On Mon, Nov 12, 2018 at 06:59:45PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/drm_vblank.c | 39 ++++++++++++++++++++++++++++++++---- include/drm/drm_vblank.h | 8 ++++++++ 2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..c3abbdca8aba 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- return vblank->max_vblank_count ?: dev->max_vblank_count;
+}
/*
- "No hw counter" fallback implementation of .get_vblank_counter() hook,
- if there is no useable hardware frame counter available.
*/ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) {
- WARN_ON_ONCE(dev->max_vblank_count != 0);
- WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0;
}
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
/*
- Interrupts were disabled prior to this call, so deal with counter
@@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
- if (dev->max_vblank_count != 0) {
- if (max_vblank_count) { /* trust the hw counter when it's around */
diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
} else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));diff = (cur_vblank - vblank->last) & max_vblank_count;
@@ -258,7 +266,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) {
WARN_ON_ONCE(cur_vblank != vblank->last);
WARN_ON_ONCE(max_vblank_count &&
cur_vblank != vblank->last);
Unrelated bugfix for this warning? Should be a separate patch I think, or I'm missing something.
Ah, yeah this was due to a quirk of i965gm hardware. The hw counter does work until the exact point when we enable TV encoder. Thus we will get non-zero values up to that point, and since the TV encoder isn't yet throttling the pipe it presumably runs at the oversample clock so our timestamp based estimates can give us a diff==0 even though the pipe did indeed pass a vblank already. I forgot to note this in the commit message.
I think we can handle this three ways: 1. do what I do here and just let the mismatch slip through 2. force i915_get_vblank_counter() to return 0 always when the TV encoder is going to be used 3. don't call drm_crtc_set_max_vblank_count() before drm_vblank_on() and instead delay it until just before we enable the TV encoder
I think option 3 is overly complicated to consider seriously. So option 1 or option 2 is what I think we should do. For whatever reason I went with option 1 here, but maybe option 2 is better since it would be all contained within i915...
return;
}
@@ -1204,6 +1213,28 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_reset);
+/**
- drm_crtc_set_max_vblank_count - configure the hw max vblank counter value
- @crtc: CRTC in question
- @max_vblank_count: max hardware vblank counter value
- Update the maximum hardware vblank counter value for @crtc. Useful
- for hardware where the operation of the hardware vblank counter
- depends on the active display configuration.
- If used, must be called before drm_vblank_on().
I think we should check this at runtime with a WARN_ON. Plus make the comment here a bit clearer that this is indeed for runtime adjusting of the max_vblank_count, in cases where that depends upon the connected outputs.
Sure. I'll try to pimp up the docs a bit.
- */
+void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
u32 max_vblank_count)
+{
- struct drm_device *dev = crtc->dev;
- unsigned int pipe = drm_crtc_index(crtc);
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- vblank->max_vblank_count = max_vblank_count;
+} +EXPORT_SYMBOL(drm_crtc_set_max_vblank_count);
/**
- drm_crtc_vblank_on - enable vblank events on a CRTC
- @crtc: CRTC in question
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 6ad9630d4f48..ecb2cf9913e2 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -128,6 +128,12 @@ struct drm_vblank_crtc { * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. */ u32 last;
- /**
* @max_vblank_count: Maximum value of the hardware vblank counter.
* If non-zero this takes precedence over &drm_device.max_vblank_count
* for this crtc. Otherwise &drm_device.max_vblank_count is used.
*/
I'd add "This should be set by calling drm_crtc_set_max_vblank_count()."
And please also add a note to the kerneldoc of drm_driver.max_vblank_count pointing at &drm_vblank_crtc.max_vblank_count for per-crtc limits.
Ack.
Aside from the nits lgtm. I think I'll skip looking at the TV out stuff though ...
:)
On Wed, Nov 21, 2018 at 01:37:51PM +0200, Ville Syrjälä wrote:
On Wed, Nov 21, 2018 at 10:27:27AM +0100, Daniel Vetter wrote:
On Mon, Nov 12, 2018 at 06:59:45PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/drm_vblank.c | 39 ++++++++++++++++++++++++++++++++---- include/drm/drm_vblank.h | 8 ++++++++ 2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..c3abbdca8aba 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- return vblank->max_vblank_count ?: dev->max_vblank_count;
+}
/*
- "No hw counter" fallback implementation of .get_vblank_counter() hook,
- if there is no useable hardware frame counter available.
*/ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) {
- WARN_ON_ONCE(dev->max_vblank_count != 0);
- WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0;
}
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
/*
- Interrupts were disabled prior to this call, so deal with counter
@@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
- if (dev->max_vblank_count != 0) {
- if (max_vblank_count) { /* trust the hw counter when it's around */
diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
} else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));diff = (cur_vblank - vblank->last) & max_vblank_count;
@@ -258,7 +266,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) {
WARN_ON_ONCE(cur_vblank != vblank->last);
WARN_ON_ONCE(max_vblank_count &&
cur_vblank != vblank->last);
Unrelated bugfix for this warning? Should be a separate patch I think, or I'm missing something.
Ah, yeah this was due to a quirk of i965gm hardware. The hw counter does work until the exact point when we enable TV encoder. Thus we will get non-zero values up to that point, and since the TV encoder isn't yet throttling the pipe it presumably runs at the oversample clock so our timestamp based estimates can give us a diff==0 even though the pipe did indeed pass a vblank already. I forgot to note this in the commit message.
I think we can handle this three ways:
- do what I do here and just let the mismatch slip through
- force i915_get_vblank_counter() to return 0 always when the TV encoder is going to be used
- don't call drm_crtc_set_max_vblank_count() before drm_vblank_on() and instead delay it until just before we enable the TV encoder
I think option 3 is overly complicated to consider seriously. So option 1 or option 2 is what I think we should do. For whatever reason I went with option 1 here, but maybe option 2 is better since it would be all contained within i915...
Delay drm_crtc_vblank_on until the vblank is stable? That seems like the semantically clean solution to me, instead of hacking around in core code when drivers leak garbage out ...
-Daniel
return;
}
@@ -1204,6 +1213,28 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_reset);
+/**
- drm_crtc_set_max_vblank_count - configure the hw max vblank counter value
- @crtc: CRTC in question
- @max_vblank_count: max hardware vblank counter value
- Update the maximum hardware vblank counter value for @crtc. Useful
- for hardware where the operation of the hardware vblank counter
- depends on the active display configuration.
- If used, must be called before drm_vblank_on().
I think we should check this at runtime with a WARN_ON. Plus make the comment here a bit clearer that this is indeed for runtime adjusting of the max_vblank_count, in cases where that depends upon the connected outputs.
Sure. I'll try to pimp up the docs a bit.
- */
+void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
u32 max_vblank_count)
+{
- struct drm_device *dev = crtc->dev;
- unsigned int pipe = drm_crtc_index(crtc);
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- vblank->max_vblank_count = max_vblank_count;
+} +EXPORT_SYMBOL(drm_crtc_set_max_vblank_count);
/**
- drm_crtc_vblank_on - enable vblank events on a CRTC
- @crtc: CRTC in question
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 6ad9630d4f48..ecb2cf9913e2 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -128,6 +128,12 @@ struct drm_vblank_crtc { * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. */ u32 last;
- /**
* @max_vblank_count: Maximum value of the hardware vblank counter.
* If non-zero this takes precedence over &drm_device.max_vblank_count
* for this crtc. Otherwise &drm_device.max_vblank_count is used.
*/
I'd add "This should be set by calling drm_crtc_set_max_vblank_count()."
And please also add a note to the kerneldoc of drm_driver.max_vblank_count pointing at &drm_vblank_crtc.max_vblank_count for per-crtc limits.
Ack.
Aside from the nits lgtm. I think I'll skip looking at the TV out stuff though ...
:)
-- Ville Syrjälä Intel
On Wed, Nov 21, 2018 at 04:19:36PM +0100, Daniel Vetter wrote:
On Wed, Nov 21, 2018 at 01:37:51PM +0200, Ville Syrjälä wrote:
On Wed, Nov 21, 2018 at 10:27:27AM +0100, Daniel Vetter wrote:
On Mon, Nov 12, 2018 at 06:59:45PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/drm_vblank.c | 39 ++++++++++++++++++++++++++++++++---- include/drm/drm_vblank.h | 8 ++++++++ 2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..c3abbdca8aba 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- return vblank->max_vblank_count ?: dev->max_vblank_count;
+}
/*
- "No hw counter" fallback implementation of .get_vblank_counter() hook,
- if there is no useable hardware frame counter available.
*/ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) {
- WARN_ON_ONCE(dev->max_vblank_count != 0);
- WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0;
}
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
/*
- Interrupts were disabled prior to this call, so deal with counter
@@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
- if (dev->max_vblank_count != 0) {
- if (max_vblank_count) { /* trust the hw counter when it's around */
diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
} else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));diff = (cur_vblank - vblank->last) & max_vblank_count;
@@ -258,7 +266,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) {
WARN_ON_ONCE(cur_vblank != vblank->last);
WARN_ON_ONCE(max_vblank_count &&
cur_vblank != vblank->last);
Unrelated bugfix for this warning? Should be a separate patch I think, or I'm missing something.
Ah, yeah this was due to a quirk of i965gm hardware. The hw counter does work until the exact point when we enable TV encoder. Thus we will get non-zero values up to that point, and since the TV encoder isn't yet throttling the pipe it presumably runs at the oversample clock so our timestamp based estimates can give us a diff==0 even though the pipe did indeed pass a vblank already. I forgot to note this in the commit message.
I think we can handle this three ways:
- do what I do here and just let the mismatch slip through
- force i915_get_vblank_counter() to return 0 always when the TV encoder is going to be used
- don't call drm_crtc_set_max_vblank_count() before drm_vblank_on() and instead delay it until just before we enable the TV encoder
I think option 3 is overly complicated to consider seriously. So option 1 or option 2 is what I think we should do. For whatever reason I went with option 1 here, but maybe option 2 is better since it would be all contained within i915...
Delay drm_crtc_vblank_on until the vblank is stable? That seems like the semantically clean solution to me, instead of hacking around in core code when drivers leak garbage out ...
We need a vblank wait before turning on the TV encoder. Chicken vs. egg.
On Wed, Nov 21, 2018 at 5:16 PM Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Wed, Nov 21, 2018 at 04:19:36PM +0100, Daniel Vetter wrote:
On Wed, Nov 21, 2018 at 01:37:51PM +0200, Ville Syrjälä wrote:
On Wed, Nov 21, 2018 at 10:27:27AM +0100, Daniel Vetter wrote:
On Mon, Nov 12, 2018 at 06:59:45PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/drm_vblank.c | 39 ++++++++++++++++++++++++++++++++---- include/drm/drm_vblank.h | 8 ++++++++ 2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..c3abbdca8aba 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
return vblank->max_vblank_count ?: dev->max_vblank_count;
+}
/*
- "No hw counter" fallback implementation of .get_vblank_counter() hook,
- if there is no useable hardware frame counter available.
*/ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) {
WARN_ON_ONCE(dev->max_vblank_count != 0);
WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0;
}
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe); /* * Interrupts were disabled prior to this call, so deal with counter
@@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
if (dev->max_vblank_count != 0) {
if (max_vblank_count) { /* trust the hw counter when it's around */
diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
diff = (cur_vblank - vblank->last) & max_vblank_count; } else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
@@ -258,7 +266,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) {
WARN_ON_ONCE(cur_vblank != vblank->last);
WARN_ON_ONCE(max_vblank_count &&
cur_vblank != vblank->last);
Unrelated bugfix for this warning? Should be a separate patch I think, or I'm missing something.
Ah, yeah this was due to a quirk of i965gm hardware. The hw counter does work until the exact point when we enable TV encoder. Thus we will get non-zero values up to that point, and since the TV encoder isn't yet throttling the pipe it presumably runs at the oversample clock so our timestamp based estimates can give us a diff==0 even though the pipe did indeed pass a vblank already. I forgot to note this in the commit message.
I think we can handle this three ways:
- do what I do here and just let the mismatch slip through
- force i915_get_vblank_counter() to return 0 always when the TV encoder is going to be used
- don't call drm_crtc_set_max_vblank_count() before drm_vblank_on() and instead delay it until just before we enable the TV encoder
I think option 3 is overly complicated to consider seriously. So option 1 or option 2 is what I think we should do. For whatever reason I went with option 1 here, but maybe option 2 is better since it would be all contained within i915...
Delay drm_crtc_vblank_on until the vblank is stable? That seems like the semantically clean solution to me, instead of hacking around in core code when drivers leak garbage out ...
We need a vblank wait before turning on the TV encoder. Chicken vs. egg.
Gah.
Ok I think I think having the hack here makes sense then, but split out as a separate patch with separate justification. It's a bit a tricky thing that deserves to be higlighted (and easier way to found the explanation with git blame in case it's ever needed). -Daniel
On Wed, Nov 21, 2018 at 05:22:49PM +0100, Daniel Vetter wrote:
On Wed, Nov 21, 2018 at 5:16 PM Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Wed, Nov 21, 2018 at 04:19:36PM +0100, Daniel Vetter wrote:
On Wed, Nov 21, 2018 at 01:37:51PM +0200, Ville Syrjälä wrote:
On Wed, Nov 21, 2018 at 10:27:27AM +0100, Daniel Vetter wrote:
On Mon, Nov 12, 2018 at 06:59:45PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/drm_vblank.c | 39 ++++++++++++++++++++++++++++++++---- include/drm/drm_vblank.h | 8 ++++++++ 2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..c3abbdca8aba 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
return vblank->max_vblank_count ?: dev->max_vblank_count;
+}
/*
- "No hw counter" fallback implementation of .get_vblank_counter() hook,
- if there is no useable hardware frame counter available.
*/ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) {
WARN_ON_ONCE(dev->max_vblank_count != 0);
WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0;
}
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe); /* * Interrupts were disabled prior to this call, so deal with counter
@@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
if (dev->max_vblank_count != 0) {
if (max_vblank_count) { /* trust the hw counter when it's around */
diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
diff = (cur_vblank - vblank->last) & max_vblank_count; } else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
@@ -258,7 +266,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, pipe, vblank->count, diff, cur_vblank, vblank->last);
if (diff == 0) {
WARN_ON_ONCE(cur_vblank != vblank->last);
WARN_ON_ONCE(max_vblank_count &&
cur_vblank != vblank->last);
Unrelated bugfix for this warning? Should be a separate patch I think, or I'm missing something.
Ah, yeah this was due to a quirk of i965gm hardware. The hw counter does work until the exact point when we enable TV encoder. Thus we will get non-zero values up to that point, and since the TV encoder isn't yet throttling the pipe it presumably runs at the oversample clock so our timestamp based estimates can give us a diff==0 even though the pipe did indeed pass a vblank already. I forgot to note this in the commit message.
I think we can handle this three ways:
- do what I do here and just let the mismatch slip through
- force i915_get_vblank_counter() to return 0 always when the TV encoder is going to be used
- don't call drm_crtc_set_max_vblank_count() before drm_vblank_on() and instead delay it until just before we enable the TV encoder
I think option 3 is overly complicated to consider seriously. So option 1 or option 2 is what I think we should do. For whatever reason I went with option 1 here, but maybe option 2 is better since it would be all contained within i915...
Delay drm_crtc_vblank_on until the vblank is stable? That seems like the semantically clean solution to me, instead of hacking around in core code when drivers leak garbage out ...
We need a vblank wait before turning on the TV encoder. Chicken vs. egg.
Gah.
Ok I think I think having the hack here makes sense then, but split out as a separate patch with separate justification. It's a bit a tricky thing that deserves to be higlighted (and easier way to found the explanation with git blame in case it's ever needed).
Or we go with option 2. It doesn't seem too bad actually.
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 019cb685986c..26d86aedc7ac 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -822,11 +822,26 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe) { struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + const struct drm_display_mode *mode = vblank->hwmode; i915_reg_t high_frame, low_frame; u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal; - const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode; unsigned long irqflags;
+ /* + * On i965gm TV output the frame counter works up to + * the point when we enable the TV encoder. After that the + * frame counter ceases to work and reads zero. We need a + * vblank wait before enabling the TV encoder and so we + * have to enable vblank interrupts while the frame counter + * is still in a working state. However the core vblank code + * does not like us returning non-zero frame counter values + * when we've told it that we don't have a working frame + * counter. Thus we must stop non-zero values leaking out. + */ + if (!vblank->max_vblank_count) + return 0; + htotal = mode->crtc_htotal; hsync_start = mode->crtc_hsync_start; vbl_start = mode->crtc_vblank_start;
On Wed, Nov 21, 2018 at 06:46:57PM +0200, Ville Syrjälä wrote:
On Wed, Nov 21, 2018 at 05:22:49PM +0100, Daniel Vetter wrote:
On Wed, Nov 21, 2018 at 5:16 PM Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Wed, Nov 21, 2018 at 04:19:36PM +0100, Daniel Vetter wrote:
On Wed, Nov 21, 2018 at 01:37:51PM +0200, Ville Syrjälä wrote:
On Wed, Nov 21, 2018 at 10:27:27AM +0100, Daniel Vetter wrote:
On Mon, Nov 12, 2018 at 06:59:45PM +0200, Ville Syrjala wrote: > From: Ville Syrjälä ville.syrjala@linux.intel.com > > On i965gm we need to adjust max_vblank_count dynamically > depending on whether the TV encoder is used or not. To > that end add a per-crtc max_vblank_count that takes > precedence over its device wide counterpart. The driver > can now call drm_crtc_set_max_vblank_count() to configure > the per-crtc value before calling drm_vblank_on(). > > Also looks like there was some discussion about exynos needing > similar treatment. > > Cc: stable@vger.kernel.org > Cc: Inki Dae inki.dae@samsung.com > Cc: Daniel Vetter daniel@ffwll.ch > Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com > --- > drivers/gpu/drm/drm_vblank.c | 39 ++++++++++++++++++++++++++++++++---- > include/drm/drm_vblank.h | 8 ++++++++ > 2 files changed, 43 insertions(+), 4 deletions(-) > > diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c > index 98e091175921..c3abbdca8aba 100644 > --- a/drivers/gpu/drm/drm_vblank.c > +++ b/drivers/gpu/drm/drm_vblank.c > @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, > write_sequnlock(&vblank->seqlock); > } > > +static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) > +{ > + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; > + > + return vblank->max_vblank_count ?: dev->max_vblank_count; > +} > + > /* > * "No hw counter" fallback implementation of .get_vblank_counter() hook, > * if there is no useable hardware frame counter available. > */ > static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) > { > - WARN_ON_ONCE(dev->max_vblank_count != 0); > + WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); > return 0; > } > > @@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, > ktime_t t_vblank; > int count = DRM_TIMESTAMP_MAXRETRIES; > int framedur_ns = vblank->framedur_ns; > + u32 max_vblank_count = drm_max_vblank_count(dev, pipe); > > /* > * Interrupts were disabled prior to this call, so deal with counter > @@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, > rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); > } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0); > > - if (dev->max_vblank_count != 0) { > + if (max_vblank_count) { > /* trust the hw counter when it's around */ > - diff = (cur_vblank - vblank->last) & dev->max_vblank_count; > + diff = (cur_vblank - vblank->last) & max_vblank_count; > } else if (rc && framedur_ns) { > u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time)); > > @@ -258,7 +266,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, > pipe, vblank->count, diff, cur_vblank, vblank->last); > > if (diff == 0) { > - WARN_ON_ONCE(cur_vblank != vblank->last); > + WARN_ON_ONCE(max_vblank_count && > + cur_vblank != vblank->last);
Unrelated bugfix for this warning? Should be a separate patch I think, or I'm missing something.
Ah, yeah this was due to a quirk of i965gm hardware. The hw counter does work until the exact point when we enable TV encoder. Thus we will get non-zero values up to that point, and since the TV encoder isn't yet throttling the pipe it presumably runs at the oversample clock so our timestamp based estimates can give us a diff==0 even though the pipe did indeed pass a vblank already. I forgot to note this in the commit message.
I think we can handle this three ways:
- do what I do here and just let the mismatch slip through
- force i915_get_vblank_counter() to return 0 always when the TV encoder is going to be used
- don't call drm_crtc_set_max_vblank_count() before drm_vblank_on() and instead delay it until just before we enable the TV encoder
I think option 3 is overly complicated to consider seriously. So option 1 or option 2 is what I think we should do. For whatever reason I went with option 1 here, but maybe option 2 is better since it would be all contained within i915...
Delay drm_crtc_vblank_on until the vblank is stable? That seems like the semantically clean solution to me, instead of hacking around in core code when drivers leak garbage out ...
We need a vblank wait before turning on the TV encoder. Chicken vs. egg.
Gah.
Ok I think I think having the hack here makes sense then, but split out as a separate patch with separate justification. It's a bit a tricky thing that deserves to be higlighted (and easier way to found the explanation with git blame in case it's ever needed).
Or we go with option 2. It doesn't seem too bad actually.
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 019cb685986c..26d86aedc7ac 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -822,11 +822,26 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe) { struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
const struct drm_display_mode *mode = vblank->hwmode; i915_reg_t high_frame, low_frame; u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode; unsigned long irqflags;
/*
* On i965gm TV output the frame counter works up to
* the point when we enable the TV encoder. After that the
* frame counter ceases to work and reads zero. We need a
* vblank wait before enabling the TV encoder and so we
* have to enable vblank interrupts while the frame counter
* is still in a working state. However the core vblank code
* does not like us returning non-zero frame counter values
* when we've told it that we don't have a working frame
* counter. Thus we must stop non-zero values leaking out.
*/
if (!vblank->max_vblank_count)
return 0;
Hm yeah, looks reasonable actually. And less hacks in the shared code. -Daniel
htotal = mode->crtc_htotal; hsync_start = mode->crtc_hsync_start; vbl_start = mode->crtc_vblank_start;
-- Ville Syrjälä Intel
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
v2: Drop the extra max_vblank_count!=0 check for the WARN(last!=current), will take care of it in i915 code (Daniel) WARN_ON(!inmodeset) (Daniel) WARN_ON(dev->max_vblank_count) Pimp up the docs (Daniel)
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_vblank.c | 45 +++++++++++++++++++++++++++++++++--- include/drm/drm_device.h | 8 ++++++- include/drm/drm_vblank.h | 22 ++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..cde71ee95a8f 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + return vblank->max_vblank_count ?: dev->max_vblank_count; +} + /* * "No hw counter" fallback implementation of .get_vblank_counter() hook, * if there is no useable hardware frame counter available. */ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) { - WARN_ON_ONCE(dev->max_vblank_count != 0); + WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0; }
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns; + u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
/* * Interrupts were disabled prior to this call, so deal with counter @@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
- if (dev->max_vblank_count != 0) { + if (max_vblank_count) { /* trust the hw counter when it's around */ - diff = (cur_vblank - vblank->last) & dev->max_vblank_count; + diff = (cur_vblank - vblank->last) & max_vblank_count; } else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
@@ -1204,6 +1212,37 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_reset);
+/** + * drm_crtc_set_max_vblank_count - configure the hw max vblank counter value + * @crtc: CRTC in question + * @max_vblank_count: max hardware vblank counter value + * + * Update the maximum hardware vblank counter value for @crtc + * at runtime. Useful for hardware where the operation of the + * hardware vblank counter depends on the currently active + * display configuration. + * + * For example, if the hardware vblank counter does not work + * when a specific connector is active the maximum can be set + * to zero. And when that specific connector isn't active the + * maximum can again be set to the appropriate non-zero value. + * + * If used, must be called before drm_vblank_on(). + */ +void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc, + u32 max_vblank_count) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + WARN_ON(dev->max_vblank_count); + WARN_ON(!READ_ONCE(vblank->inmodeset)); + + vblank->max_vblank_count = max_vblank_count; +} +EXPORT_SYMBOL(drm_crtc_set_max_vblank_count); + /** * drm_crtc_vblank_on - enable vblank events on a CRTC * @crtc: CRTC in question diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 42411b3ea0c8..45e43ce9652f 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -184,7 +184,13 @@ struct drm_device { * races and imprecision over longer time periods, hence exposing a * hardware vblank counter is always recommended. * - * If non-zeor, &drm_crtc_funcs.get_vblank_counter must be set. + * This is the statically configured device wide maximum. The driver + * can instead choose to use a runtime configurable per-crtc value + * &drm_vblank_crtc.max_vblank_count, in which case @max_vblank_count + * must be left at zero. See drm_crtc_set_max_vblank_count() on how + * to use the per-crtc value. + * + * If non-zero, &drm_crtc_funcs.get_vblank_counter must be set. */ u32 max_vblank_count; /**< size of vblank counter register */
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 6ad9630d4f48..e528bb2f659d 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -128,6 +128,26 @@ struct drm_vblank_crtc { * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. */ u32 last; + /** + * @max_vblank_count: + * + * Maximum value of the vblank registers for this crtc. This value +1 + * will result in a wrap-around of the vblank register. It is used + * by the vblank core to handle wrap-arounds. + * + * If set to zero the vblank core will try to guess the elapsed vblanks + * between times when the vblank interrupt is disabled through + * high-precision timestamps. That approach is suffering from small + * races and imprecision over longer time periods, hence exposing a + * hardware vblank counter is always recommended. + * + * This is the runtime configurable per-crtc maximum set through + * drm_crtc_set_max_vblank_count(). If this is used the driver + * must leave the device wide &drm_device.max_vblank_count at zero. + * + * If non-zero, &drm_crtc_funcs.get_vblank_counter must be set. + */ + u32 max_vblank_count; /** * @inmodeset: Tracks whether the vblank is disabled due to a modeset. * For legacy driver bit 2 additionally tracks whether an additional @@ -206,4 +226,6 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode); wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc); +void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc, + u32 max_vblank_count); #endif
On Tue, Nov 27, 2018 at 08:20:04PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm we need to adjust max_vblank_count dynamically depending on whether the TV encoder is used or not. To that end add a per-crtc max_vblank_count that takes precedence over its device wide counterpart. The driver can now call drm_crtc_set_max_vblank_count() to configure the per-crtc value before calling drm_vblank_on().
Also looks like there was some discussion about exynos needing similar treatment.
v2: Drop the extra max_vblank_count!=0 check for the WARN(last!=current), will take care of it in i915 code (Daniel) WARN_ON(!inmodeset) (Daniel) WARN_ON(dev->max_vblank_count) Pimp up the docs (Daniel)
Yeah I'm happy with the pile of WARN_ON here. And docs look great too.
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
Cc: stable@vger.kernel.org Cc: Inki Dae inki.dae@samsung.com Cc: Daniel Vetter daniel@ffwll.ch Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/drm_vblank.c | 45 +++++++++++++++++++++++++++++++++--- include/drm/drm_device.h | 8 ++++++- include/drm/drm_vblank.h | 22 ++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 98e091175921..cde71ee95a8f 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -105,13 +105,20 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, write_sequnlock(&vblank->seqlock); }
+static u32 drm_max_vblank_count(struct drm_device *dev, unsigned int pipe) +{
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- return vblank->max_vblank_count ?: dev->max_vblank_count;
+}
/*
- "No hw counter" fallback implementation of .get_vblank_counter() hook,
- if there is no useable hardware frame counter available.
*/ static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) {
- WARN_ON_ONCE(dev->max_vblank_count != 0);
- WARN_ON_ONCE(drm_max_vblank_count(dev, pipe) != 0); return 0;
}
@@ -198,6 +205,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
/*
- Interrupts were disabled prior to this call, so deal with counter
@@ -216,9 +224,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
- if (dev->max_vblank_count != 0) {
- if (max_vblank_count) { /* trust the hw counter when it's around */
diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
} else if (rc && framedur_ns) { u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));diff = (cur_vblank - vblank->last) & max_vblank_count;
@@ -1204,6 +1212,37 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_reset);
+/**
- drm_crtc_set_max_vblank_count - configure the hw max vblank counter value
- @crtc: CRTC in question
- @max_vblank_count: max hardware vblank counter value
- Update the maximum hardware vblank counter value for @crtc
- at runtime. Useful for hardware where the operation of the
- hardware vblank counter depends on the currently active
- display configuration.
- For example, if the hardware vblank counter does not work
- when a specific connector is active the maximum can be set
- to zero. And when that specific connector isn't active the
- maximum can again be set to the appropriate non-zero value.
- If used, must be called before drm_vblank_on().
- */
+void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
u32 max_vblank_count)
+{
- struct drm_device *dev = crtc->dev;
- unsigned int pipe = drm_crtc_index(crtc);
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- WARN_ON(dev->max_vblank_count);
- WARN_ON(!READ_ONCE(vblank->inmodeset));
- vblank->max_vblank_count = max_vblank_count;
+} +EXPORT_SYMBOL(drm_crtc_set_max_vblank_count);
/**
- drm_crtc_vblank_on - enable vblank events on a CRTC
- @crtc: CRTC in question
diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 42411b3ea0c8..45e43ce9652f 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -184,7 +184,13 @@ struct drm_device { * races and imprecision over longer time periods, hence exposing a * hardware vblank counter is always recommended. *
* If non-zeor, &drm_crtc_funcs.get_vblank_counter must be set.
* This is the statically configured device wide maximum. The driver
* can instead choose to use a runtime configurable per-crtc value
* &drm_vblank_crtc.max_vblank_count, in which case @max_vblank_count
* must be left at zero. See drm_crtc_set_max_vblank_count() on how
* to use the per-crtc value.
*
*/ u32 max_vblank_count; /**< size of vblank counter register */* If non-zero, &drm_crtc_funcs.get_vblank_counter must be set.
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 6ad9630d4f48..e528bb2f659d 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -128,6 +128,26 @@ struct drm_vblank_crtc { * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. */ u32 last;
- /**
* @max_vblank_count:
*
* Maximum value of the vblank registers for this crtc. This value +1
* will result in a wrap-around of the vblank register. It is used
* by the vblank core to handle wrap-arounds.
*
* If set to zero the vblank core will try to guess the elapsed vblanks
* between times when the vblank interrupt is disabled through
* high-precision timestamps. That approach is suffering from small
* races and imprecision over longer time periods, hence exposing a
* hardware vblank counter is always recommended.
*
* This is the runtime configurable per-crtc maximum set through
* drm_crtc_set_max_vblank_count(). If this is used the driver
* must leave the device wide &drm_device.max_vblank_count at zero.
*
* If non-zero, &drm_crtc_funcs.get_vblank_counter must be set.
*/
- u32 max_vblank_count; /**
- @inmodeset: Tracks whether the vblank is disabled due to a modeset.
- For legacy driver bit 2 additionally tracks whether an additional
@@ -206,4 +226,6 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode); wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc); +void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
u32 max_vblank_count);
#endif
2.18.1
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm the hardware frame counter does not work when the TV encoder is active. So let's not try to consult the hardware frame counter in that case. Instead we'll fall back to the timestamp based guesstimation method used on gen2.
Note that the pipe timings generated by the TV encoder are also rather peculiar. Apparently the pipe wants to run at a much higher speed (related to the oversample clock somehow it seems) but during the vertical active period the TV encoder stalls the pipe every few lines to keep its speed in check. But once the vertical blanking period is reached the pipe gets to run at full speed. This means our vblank timestamp estimates are suspect. Fixing all that would require quite a bit more work. This simple fix at least avoids the nasty vblank timeouts that are happening currently.
Curiously the frame counter works just fine on i945gm and gm45. I don't really understand what kind of mishap occurred with the hardware design on i965gm. Sadly I wasn't able to find any chicken bits etc. that would fix the frame counter :(
Cc: stable@vger.kernel.org Fixes: 51e31d49c890 ("drm/i915: Use generic vblank wait") Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=93782 Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_irq.c | 10 ++++++- drivers/gpu/drm/i915/intel_display.c | 40 +++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d447d7d508f4..019cb685986c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -4843,8 +4843,16 @@ void intel_irq_init(struct drm_i915_private *dev_priv) dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */ dev->driver->get_vblank_counter = g4x_get_vblank_counter; } else { + /* + * On i965gm the hardware frame counter reads zero + * when the TV encoder is used. Hence we configure + * max_vblank_count dynamically for each crtc. + */ + if (IS_I965GM(dev_priv)) + dev->max_vblank_count = 0; + else + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ dev->driver->get_vblank_counter = i915_get_vblank_counter; - dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ }
/* diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 940577f8c041..2dccb3310ad2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5504,6 +5504,32 @@ static void intel_encoders_post_pll_disable(struct drm_crtc *crtc, } }
+static u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev); + u32 max_vblank_count = dev_priv->drm.max_vblank_count; + + /* + * On i965gm the hardware frame counter always + * reads zero when the TV encoder is used :( + * Otherwise we get the 24 bit frame counter. + */ + if (IS_I965GM(dev_priv) && + (crtc_state->output_types & BIT(INTEL_OUTPUT_TVOUT)) == 0) + max_vblank_count = 0xffffff; /* only 24 bits of frame count */ + + return max_vblank_count; +} + +static void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); + + drm_crtc_set_max_vblank_count(&crtc->base, + intel_crtc_max_vblank_count(crtc_state)); + drm_crtc_vblank_on(&crtc->base); +} + static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config, struct drm_atomic_state *old_state) { @@ -5577,7 +5603,7 @@ static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config, ironlake_pch_enable(old_intel_state, pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
@@ -5734,7 +5760,7 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, intel_ddi_set_vc_payload_alloc(pipe_config, true);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
@@ -6074,7 +6100,7 @@ static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config, intel_enable_pipe(pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state); } @@ -6133,7 +6159,7 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config, intel_enable_pipe(pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state); } @@ -15752,10 +15778,12 @@ intel_modeset_setup_hw_state(struct drm_device *dev, * waits, so we need vblank interrupts restored beforehand. */ for_each_intel_crtc(&dev_priv->drm, crtc) { + crtc_state = to_intel_crtc_state(crtc->base.state); + drm_crtc_vblank_reset(&crtc->base);
- if (crtc->base.state->active) - drm_crtc_vblank_on(&crtc->base); + if (crtc_state->base.active) + intel_crtc_vblank_on(crtc_state); }
intel_sanitize_plane_mapping(dev_priv);
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm the hardware frame counter does not work when the TV encoder is active. So let's not try to consult the hardware frame counter in that case. Instead we'll fall back to the timestamp based guesstimation method used on gen2.
Note that the pipe timings generated by the TV encoder are also rather peculiar. Apparently the pipe wants to run at a much higher speed (related to the oversample clock somehow it seems) but during the vertical active period the TV encoder stalls the pipe every few lines to keep its speed in check. But once the vertical blanking period is reached the pipe gets to run at full speed. This means our vblank timestamp estimates are suspect. Fixing all that would require quite a bit more work. This simple fix at least avoids the nasty vblank timeouts that are happening currently.
Curiously the frame counter works just fine on i945gm and gm45. I don't really understand what kind of mishap occurred with the hardware design on i965gm. Sadly I wasn't able to find any chicken bits etc. that would fix the frame counter :(
v2: Move the zero vs. non-zero hw counter value handling into i915_get_vblank_counter() (Daniel) Use the per-crtc maximum exclusively, leaving the per-device maximum at zero
Cc: stable@vger.kernel.org Cc: Daniel Vetter daniel@ffwll.ch Fixes: 51e31d49c890 ("drm/i915: Use generic vblank wait") Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=93782 Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_irq.c | 27 ++++++++++----- drivers/gpu/drm/i915/intel_display.c | 49 +++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d447d7d508f4..ab2d4eefef18 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -822,11 +822,26 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe) { struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + const struct drm_display_mode *mode = &vblank->hwmode; i915_reg_t high_frame, low_frame; u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal; - const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode; unsigned long irqflags;
+ /* + * On i965gm TV output the frame counter only works up to + * the point when we enable the TV encoder. After that the + * frame counter ceases to work and reads zero. We need a + * vblank wait before enabling the TV encoder and so we + * have to enable vblank interrupts while the frame counter + * is still in a working state. However the core vblank code + * does not like us returning non-zero frame counter values + * when we've told it that we don't have a working frame + * counter. Thus we must stop non-zero values leaking out. + */ + if (!vblank->max_vblank_count) + return 0; + htotal = mode->crtc_htotal; hsync_start = mode->crtc_hsync_start; vbl_start = mode->crtc_vblank_start; @@ -4836,16 +4851,10 @@ void intel_irq_init(struct drm_i915_private *dev_priv) if (INTEL_GEN(dev_priv) >= 8) rps->pm_intrmsk_mbz |= GEN8_PMINTR_DISABLE_REDIRECT_TO_GUC;
- if (IS_GEN2(dev_priv)) { - /* Gen2 doesn't have a hardware frame counter */ - dev->max_vblank_count = 0; - } else if (IS_G4X(dev_priv) || INTEL_GEN(dev_priv) >= 5) { - dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */ + if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) dev->driver->get_vblank_counter = g4x_get_vblank_counter; - } else { + else if (INTEL_GEN(dev_priv) >= 3) dev->driver->get_vblank_counter = i915_get_vblank_counter; - dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ - }
/* * Opt out of the vblank disable timer on everything except gen2. diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e9f4e22b2a4e..3eafea4e598f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1758,6 +1758,7 @@ static void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state) { struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->base.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct drm_vblank_crtc *vblank = &dev_priv->drm.vblank[drm_crtc_index(&crtc->base)]; enum transcoder cpu_transcoder = new_crtc_state->cpu_transcoder; enum pipe pipe = crtc->pipe; i915_reg_t reg; @@ -1806,7 +1807,7 @@ static void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state) * when it's derived from the timestamps. So let's wait for the * pipe to start properly before we call drm_crtc_vblank_on() */ - if (dev_priv->drm.max_vblank_count == 0) + if (vblank->max_vblank_count == 0) intel_wait_for_pipe_scanline_moving(crtc); }
@@ -5544,6 +5545,35 @@ static void intel_encoders_post_pll_disable(struct drm_crtc *crtc, } }
+static u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev); + + /* + * On i965gm the hardware frame counter reads + * zero when the TV encoder is enabled :( + */ + if (IS_I965GM(dev_priv) && + (crtc_state->output_types & BIT(INTEL_OUTPUT_TVOUT))) + return 0; + + if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) + return 0xffffffff; /* full 32 bit counter */ + else if (INTEL_GEN(dev_priv) >= 3) + return 0xffffff; /* only 24 bits of frame count */ + else + return 0; /* Gen2 doesn't have a hardware frame counter */ +} + +static void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); + + drm_crtc_set_max_vblank_count(&crtc->base, + intel_crtc_max_vblank_count(crtc_state)); + drm_crtc_vblank_on(&crtc->base); +} + static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config, struct drm_atomic_state *old_state) { @@ -5617,7 +5647,7 @@ static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config, ironlake_pch_enable(old_intel_state, pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
@@ -5774,7 +5804,7 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, intel_ddi_set_vc_payload_alloc(pipe_config, true);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
@@ -6114,7 +6144,7 @@ static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config, intel_enable_pipe(pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state); } @@ -6173,7 +6203,7 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config, intel_enable_pipe(pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state); } @@ -12653,8 +12683,9 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; + struct drm_vblank_crtc *vblank = &dev->vblank[drm_crtc_index(&crtc->base)];
- if (!dev->max_vblank_count) + if (!vblank->max_vblank_count) return (u32)drm_crtc_accurate_vblank_count(&crtc->base);
return dev->driver->get_vblank_counter(dev, crtc->pipe); @@ -15736,10 +15767,12 @@ intel_modeset_setup_hw_state(struct drm_device *dev, * waits, so we need vblank interrupts restored beforehand. */ for_each_intel_crtc(&dev_priv->drm, crtc) { + crtc_state = to_intel_crtc_state(crtc->base.state); + drm_crtc_vblank_reset(&crtc->base);
- if (crtc->base.state->active) - drm_crtc_vblank_on(&crtc->base); + if (crtc_state->base.active) + intel_crtc_vblank_on(crtc_state); }
intel_sanitize_plane_mapping(dev_priv);
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm the hardware frame counter does not work when the TV encoder is active. So let's not try to consult the hardware frame counter in that case. Instead we'll fall back to the timestamp based guesstimation method used on gen2.
Note that the pipe timings generated by the TV encoder are also rather peculiar. Apparently the pipe wants to run at a much higher speed (related to the oversample clock somehow it seems) but during the vertical active period the TV encoder stalls the pipe every few lines to keep its speed in check. But once the vertical blanking period is reached the pipe gets to run at full speed. This means our vblank timestamp estimates are suspect. Fixing all that would require quite a bit more work. This simple fix at least avoids the nasty vblank timeouts that are happening currently.
Curiously the frame counter works just fine on i945gm and gm45. I don't really understand what kind of mishap occurred with the hardware design on i965gm. Sadly I wasn't able to find any chicken bits etc. that would fix the frame counter :(
v2: Move the zero vs. non-zero hw counter value handling into i915_get_vblank_counter() (Daniel) Use the per-crtc maximum exclusively, leaving the per-device maximum at zero v3: max_vblank_count not populated yet in intel_enable_pipe() use intel_crtc_max_vblank_count() instead
Cc: stable@vger.kernel.org Cc: Daniel Vetter daniel@ffwll.ch Fixes: 51e31d49c890 ("drm/i915: Use generic vblank wait") Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=93782 Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
fix# _slub_broken.S --- drivers/gpu/drm/i915/i915_irq.c | 27 ++++++++++------ drivers/gpu/drm/i915/intel_display.c | 48 +++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d447d7d508f4..ab2d4eefef18 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -822,11 +822,26 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe) { struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + const struct drm_display_mode *mode = &vblank->hwmode; i915_reg_t high_frame, low_frame; u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal; - const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode; unsigned long irqflags;
+ /* + * On i965gm TV output the frame counter only works up to + * the point when we enable the TV encoder. After that the + * frame counter ceases to work and reads zero. We need a + * vblank wait before enabling the TV encoder and so we + * have to enable vblank interrupts while the frame counter + * is still in a working state. However the core vblank code + * does not like us returning non-zero frame counter values + * when we've told it that we don't have a working frame + * counter. Thus we must stop non-zero values leaking out. + */ + if (!vblank->max_vblank_count) + return 0; + htotal = mode->crtc_htotal; hsync_start = mode->crtc_hsync_start; vbl_start = mode->crtc_vblank_start; @@ -4836,16 +4851,10 @@ void intel_irq_init(struct drm_i915_private *dev_priv) if (INTEL_GEN(dev_priv) >= 8) rps->pm_intrmsk_mbz |= GEN8_PMINTR_DISABLE_REDIRECT_TO_GUC;
- if (IS_GEN2(dev_priv)) { - /* Gen2 doesn't have a hardware frame counter */ - dev->max_vblank_count = 0; - } else if (IS_G4X(dev_priv) || INTEL_GEN(dev_priv) >= 5) { - dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */ + if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) dev->driver->get_vblank_counter = g4x_get_vblank_counter; - } else { + else if (INTEL_GEN(dev_priv) >= 3) dev->driver->get_vblank_counter = i915_get_vblank_counter; - dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ - }
/* * Opt out of the vblank disable timer on everything except gen2. diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e9f4e22b2a4e..cb13eff78ad9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1754,6 +1754,35 @@ enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc) return crtc->pipe; }
+static u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev); + + /* + * On i965gm the hardware frame counter reads + * zero when the TV encoder is enabled :( + */ + if (IS_I965GM(dev_priv) && + (crtc_state->output_types & BIT(INTEL_OUTPUT_TVOUT))) + return 0; + + if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) + return 0xffffffff; /* full 32 bit counter */ + else if (INTEL_GEN(dev_priv) >= 3) + return 0xffffff; /* only 24 bits of frame count */ + else + return 0; /* Gen2 doesn't have a hardware frame counter */ +} + +static void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); + + drm_crtc_set_max_vblank_count(&crtc->base, + intel_crtc_max_vblank_count(crtc_state)); + drm_crtc_vblank_on(&crtc->base); +} + static void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state) { struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->base.crtc); @@ -1806,7 +1835,7 @@ static void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state) * when it's derived from the timestamps. So let's wait for the * pipe to start properly before we call drm_crtc_vblank_on() */ - if (dev_priv->drm.max_vblank_count == 0) + if (intel_crtc_max_vblank_count(new_crtc_state) == 0) intel_wait_for_pipe_scanline_moving(crtc); }
@@ -5617,7 +5646,7 @@ static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config, ironlake_pch_enable(old_intel_state, pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
@@ -5774,7 +5803,7 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, intel_ddi_set_vc_payload_alloc(pipe_config, true);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
@@ -6114,7 +6143,7 @@ static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config, intel_enable_pipe(pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state); } @@ -6173,7 +6202,7 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config, intel_enable_pipe(pipe_config);
assert_vblank_disabled(crtc); - drm_crtc_vblank_on(crtc); + intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state); } @@ -12653,8 +12682,9 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; + struct drm_vblank_crtc *vblank = &dev->vblank[drm_crtc_index(&crtc->base)];
- if (!dev->max_vblank_count) + if (!vblank->max_vblank_count) return (u32)drm_crtc_accurate_vblank_count(&crtc->base);
return dev->driver->get_vblank_counter(dev, crtc->pipe); @@ -15736,10 +15766,12 @@ intel_modeset_setup_hw_state(struct drm_device *dev, * waits, so we need vblank interrupts restored beforehand. */ for_each_intel_crtc(&dev_priv->drm, crtc) { + crtc_state = to_intel_crtc_state(crtc->base.state); + drm_crtc_vblank_reset(&crtc->base);
- if (crtc->base.state->active) - drm_crtc_vblank_on(&crtc->base); + if (crtc_state->base.active) + intel_crtc_vblank_on(crtc_state); }
intel_sanitize_plane_mapping(dev_priv);
On Tue, Nov 27, 2018 at 10:05:50PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On i965gm the hardware frame counter does not work when the TV encoder is active. So let's not try to consult the hardware frame counter in that case. Instead we'll fall back to the timestamp based guesstimation method used on gen2.
Note that the pipe timings generated by the TV encoder are also rather peculiar. Apparently the pipe wants to run at a much higher speed (related to the oversample clock somehow it seems) but during the vertical active period the TV encoder stalls the pipe every few lines to keep its speed in check. But once the vertical blanking period is reached the pipe gets to run at full speed. This means our vblank timestamp estimates are suspect. Fixing all that would require quite a bit more work. This simple fix at least avoids the nasty vblank timeouts that are happening currently.
Curiously the frame counter works just fine on i945gm and gm45. I don't really understand what kind of mishap occurred with the hardware design on i965gm. Sadly I wasn't able to find any chicken bits etc. that would fix the frame counter :(
v2: Move the zero vs. non-zero hw counter value handling into i915_get_vblank_counter() (Daniel) Use the per-crtc maximum exclusively, leaving the per-device maximum at zero v3: max_vblank_count not populated yet in intel_enable_pipe() use intel_crtc_max_vblank_count() instead
Cc: stable@vger.kernel.org Cc: Daniel Vetter daniel@ffwll.ch Fixes: 51e31d49c890 ("drm/i915: Use generic vblank wait") Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=93782 Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
fix# _slub_broken.S
^ some remnant
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/i915_irq.c | 27 ++++++++++------ drivers/gpu/drm/i915/intel_display.c | 48 +++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d447d7d508f4..ab2d4eefef18 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -822,11 +822,26 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe) { struct drm_i915_private *dev_priv = to_i915(dev);
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
- const struct drm_display_mode *mode = &vblank->hwmode; i915_reg_t high_frame, low_frame; u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
- const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode; unsigned long irqflags;
- /*
* On i965gm TV output the frame counter only works up to
* the point when we enable the TV encoder. After that the
* frame counter ceases to work and reads zero. We need a
* vblank wait before enabling the TV encoder and so we
* have to enable vblank interrupts while the frame counter
* is still in a working state. However the core vblank code
* does not like us returning non-zero frame counter values
* when we've told it that we don't have a working frame
* counter. Thus we must stop non-zero values leaking out.
*/
- if (!vblank->max_vblank_count)
return 0;
- htotal = mode->crtc_htotal; hsync_start = mode->crtc_hsync_start; vbl_start = mode->crtc_vblank_start;
@@ -4836,16 +4851,10 @@ void intel_irq_init(struct drm_i915_private *dev_priv) if (INTEL_GEN(dev_priv) >= 8) rps->pm_intrmsk_mbz |= GEN8_PMINTR_DISABLE_REDIRECT_TO_GUC;
- if (IS_GEN2(dev_priv)) {
/* Gen2 doesn't have a hardware frame counter */
dev->max_vblank_count = 0;
- } else if (IS_G4X(dev_priv) || INTEL_GEN(dev_priv) >= 5) {
dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
- if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) dev->driver->get_vblank_counter = g4x_get_vblank_counter;
- } else {
- else if (INTEL_GEN(dev_priv) >= 3) dev->driver->get_vblank_counter = i915_get_vblank_counter;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
}
/*
- Opt out of the vblank disable timer on everything except gen2.
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e9f4e22b2a4e..cb13eff78ad9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1754,6 +1754,35 @@ enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc) return crtc->pipe; }
+static u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state) +{
- struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
- /*
* On i965gm the hardware frame counter reads
* zero when the TV encoder is enabled :(
*/
- if (IS_I965GM(dev_priv) &&
(crtc_state->output_types & BIT(INTEL_OUTPUT_TVOUT)))
return 0;
- if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv))
return 0xffffffff; /* full 32 bit counter */
- else if (INTEL_GEN(dev_priv) >= 3)
return 0xffffff; /* only 24 bits of frame count */
- else
return 0; /* Gen2 doesn't have a hardware frame counter */
+}
+static void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state) +{
- struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
- drm_crtc_set_max_vblank_count(&crtc->base,
intel_crtc_max_vblank_count(crtc_state));
- drm_crtc_vblank_on(&crtc->base);
+}
static void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state) { struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->base.crtc); @@ -1806,7 +1835,7 @@ static void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state) * when it's derived from the timestamps. So let's wait for the * pipe to start properly before we call drm_crtc_vblank_on() */
- if (dev_priv->drm.max_vblank_count == 0)
- if (intel_crtc_max_vblank_count(new_crtc_state) == 0) intel_wait_for_pipe_scanline_moving(crtc);
}
@@ -5617,7 +5646,7 @@ static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config, ironlake_pch_enable(old_intel_state, pipe_config);
assert_vblank_disabled(crtc);
- drm_crtc_vblank_on(crtc);
intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
@@ -5774,7 +5803,7 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, intel_ddi_set_vc_payload_alloc(pipe_config, true);
assert_vblank_disabled(crtc);
- drm_crtc_vblank_on(crtc);
intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
@@ -6114,7 +6143,7 @@ static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config, intel_enable_pipe(pipe_config);
assert_vblank_disabled(crtc);
- drm_crtc_vblank_on(crtc);
intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
} @@ -6173,7 +6202,7 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config, intel_enable_pipe(pipe_config);
assert_vblank_disabled(crtc);
- drm_crtc_vblank_on(crtc);
intel_crtc_vblank_on(pipe_config);
intel_encoders_enable(crtc, pipe_config, old_state);
} @@ -12653,8 +12682,9 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev;
- struct drm_vblank_crtc *vblank = &dev->vblank[drm_crtc_index(&crtc->base)];
- if (!dev->max_vblank_count)
if (!vblank->max_vblank_count) return (u32)drm_crtc_accurate_vblank_count(&crtc->base);
return dev->driver->get_vblank_counter(dev, crtc->pipe);
@@ -15736,10 +15766,12 @@ intel_modeset_setup_hw_state(struct drm_device *dev, * waits, so we need vblank interrupts restored beforehand. */ for_each_intel_crtc(&dev_priv->drm, crtc) {
crtc_state = to_intel_crtc_state(crtc->base.state);
- drm_crtc_vblank_reset(&crtc->base);
if (crtc->base.state->active)
drm_crtc_vblank_on(&crtc->base);
if (crtc_state->base.active)
intel_crtc_vblank_on(crtc_state);
}
intel_sanitize_plane_mapping(dev_priv);
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Ville Syrjälä ville.syrjala@linux.intel.com
Fix the calculation of the vertical active period for interlaced TV modes.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 860f306a23ba..219a16d6dcc2 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1087,7 +1087,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, if (tv_mode->progressive) ysize = tv_mode->nbr_end + 1; else - ysize = 2*tv_mode->nbr_end + 1; + ysize = 2 * (tv_mode->nbr_end + 1);
xpos += conn_state->tv.margins.left; ypos += conn_state->tv.margins.top;
On Mon, Nov 12, 2018 at 06:59:47PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
Fix the calculation of the vertical active period for interlaced TV modes.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Matches the spec: Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 860f306a23ba..219a16d6dcc2 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1087,7 +1087,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, if (tv_mode->progressive) ysize = tv_mode->nbr_end + 1; else
ysize = 2*tv_mode->nbr_end + 1;
ysize = 2 * (tv_mode->nbr_end + 1);
xpos += conn_state->tv.margins.left; ypos += conn_state->tv.margins.top;
-- 2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
From: Ville Syrjälä ville.syrjala@linux.intel.com
The oversample clock is always supposed to be either 108 MHz or 148.5 MHz. Make it so.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 219a16d6dcc2..dea24ef88763 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -636,7 +636,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "480p", - .clock = 107520, + .clock = 108000, .refresh = 59940, .oversample = TV_OVERSAMPLE_4X, .component_only = 1, @@ -660,7 +660,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "576p", - .clock = 107520, + .clock = 108000, .refresh = 50000, .oversample = TV_OVERSAMPLE_4X, .component_only = 1, @@ -684,7 +684,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "720p@60Hz", - .clock = 148800, + .clock = 148500, .refresh = 60000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, @@ -708,7 +708,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "720p@50Hz", - .clock = 148800, + .clock = 148500, .refresh = 50000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, @@ -733,7 +733,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "1080i@50Hz", - .clock = 148800, + .clock = 148500, .refresh = 50000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, @@ -759,7 +759,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "1080i@60Hz", - .clock = 148800, + .clock = 148500, .refresh = 60000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, @@ -1114,7 +1114,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, static const struct drm_display_mode reported_modes[] = { { .name = "NTSC 480i", - .clock = 107520, + .clock = 108000, .hdisplay = 1280, .hsync_start = 1368, .hsync_end = 1496,
On Mon, Nov 12, 2018 at 06:59:48PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
The oversample clock is always supposed to be either 108 MHz or 148.5 MHz. Make it so.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Matches the spec: Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 219a16d6dcc2..dea24ef88763 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -636,7 +636,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "480p",
.clock = 107520,
.refresh = 59940, .oversample = TV_OVERSAMPLE_4X, .component_only = 1,.clock = 108000,
@@ -660,7 +660,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "576p",
.clock = 107520,
.refresh = 50000, .oversample = TV_OVERSAMPLE_4X, .component_only = 1,.clock = 108000,
@@ -684,7 +684,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "720p@60Hz",
.clock = 148800,
.refresh = 60000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1,.clock = 148500,
@@ -708,7 +708,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "720p@50Hz",
.clock = 148800,
.refresh = 50000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1,.clock = 148500,
@@ -733,7 +733,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "1080i@50Hz",
.clock = 148800,
.refresh = 50000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1,.clock = 148500,
@@ -759,7 +759,7 @@ static const struct tv_mode tv_modes[] = { }, { .name = "1080i@60Hz",
.clock = 148800,
.refresh = 60000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1,.clock = 148500,
@@ -1114,7 +1114,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, static const struct drm_display_mode reported_modes[] = { { .name = "NTSC 480i",
.clock = 107520,
.hdisplay = 1280, .hsync_start = 1368, .hsync_end = 1496,.clock = 108000,
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Ville Syrjälä ville.syrjala@linux.intel.com
Store the oversampling factor as a number in the TV modes. We shall want to arithmetic with this which is easier if it's a number we can use directly.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 42 ++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index dea24ef88763..96257b29d07c 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -307,7 +307,7 @@ struct tv_mode {
u32 clock; u16 refresh; /* in millihertz (for precision) */ - u32 oversample; + u8 oversample; u8 hsync_end; u16 hblank_start, hblank_end, htotal; bool progressive : 1, trilevel_sync : 1, component_only : 1; @@ -379,7 +379,7 @@ static const struct tv_mode tv_modes[] = { .name = "NTSC-M", .clock = 108000, .refresh = 59940, - .oversample = TV_OVERSAMPLE_8X, + .oversample = 8, .component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
@@ -422,7 +422,7 @@ static const struct tv_mode tv_modes[] = { .name = "NTSC-443", .clock = 108000, .refresh = 59940, - .oversample = TV_OVERSAMPLE_8X, + .oversample = 8, .component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ .hsync_end = 64, .hblank_end = 124, @@ -464,7 +464,7 @@ static const struct tv_mode tv_modes[] = { .name = "NTSC-J", .clock = 108000, .refresh = 59940, - .oversample = TV_OVERSAMPLE_8X, + .oversample = 8, .component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ @@ -507,7 +507,7 @@ static const struct tv_mode tv_modes[] = { .name = "PAL-M", .clock = 108000, .refresh = 59940, - .oversample = TV_OVERSAMPLE_8X, + .oversample = 8, .component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ @@ -551,7 +551,7 @@ static const struct tv_mode tv_modes[] = { .name = "PAL-N", .clock = 108000, .refresh = 50000, - .oversample = TV_OVERSAMPLE_8X, + .oversample = 8, .component_only = 0,
.hsync_end = 64, .hblank_end = 128, @@ -596,7 +596,7 @@ static const struct tv_mode tv_modes[] = { .name = "PAL", .clock = 108000, .refresh = 50000, - .oversample = TV_OVERSAMPLE_8X, + .oversample = 8, .component_only = 0,
.hsync_end = 64, .hblank_end = 142, @@ -638,7 +638,7 @@ static const struct tv_mode tv_modes[] = { .name = "480p", .clock = 108000, .refresh = 59940, - .oversample = TV_OVERSAMPLE_4X, + .oversample = 4, .component_only = 1,
.hsync_end = 64, .hblank_end = 122, @@ -662,7 +662,7 @@ static const struct tv_mode tv_modes[] = { .name = "576p", .clock = 108000, .refresh = 50000, - .oversample = TV_OVERSAMPLE_4X, + .oversample = 4, .component_only = 1,
.hsync_end = 64, .hblank_end = 139, @@ -686,7 +686,7 @@ static const struct tv_mode tv_modes[] = { .name = "720p@60Hz", .clock = 148500, .refresh = 60000, - .oversample = TV_OVERSAMPLE_2X, + .oversample = 2, .component_only = 1,
.hsync_end = 80, .hblank_end = 300, @@ -710,7 +710,7 @@ static const struct tv_mode tv_modes[] = { .name = "720p@50Hz", .clock = 148500, .refresh = 50000, - .oversample = TV_OVERSAMPLE_2X, + .oversample = 2, .component_only = 1,
.hsync_end = 80, .hblank_end = 300, @@ -735,7 +735,7 @@ static const struct tv_mode tv_modes[] = { .name = "1080i@50Hz", .clock = 148500, .refresh = 50000, - .oversample = TV_OVERSAMPLE_2X, + .oversample = 2, .component_only = 1,
.hsync_end = 88, .hblank_end = 235, @@ -761,7 +761,7 @@ static const struct tv_mode tv_modes[] = { .name = "1080i@60Hz", .clock = 148500, .refresh = 60000, - .oversample = TV_OVERSAMPLE_2X, + .oversample = 2, .component_only = 1,
.hsync_end = 88, .hblank_end = 235, @@ -1030,7 +1030,21 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, }
tv_ctl |= TV_ENC_PIPE_SEL(intel_crtc->pipe); - tv_ctl |= tv_mode->oversample; + + switch (tv_mode->oversample) { + case 8: + tv_ctl |= TV_OVERSAMPLE_8X; + break; + case 4: + tv_ctl |= TV_OVERSAMPLE_4X; + break; + case 2: + tv_ctl |= TV_OVERSAMPLE_2X; + break; + default: + tv_ctl |= TV_OVERSAMPLE_NONE; + break; + }
if (tv_mode->progressive) tv_ctl |= TV_PROGRESSIVE;
On Mon, Nov 12, 2018 at 06:59:49PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
Store the oversampling factor as a number in the TV modes. We shall want to arithmetic with this which is easier if it's a number we can use directly.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 42 ++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index dea24ef88763..96257b29d07c 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -307,7 +307,7 @@ struct tv_mode {
u32 clock; u16 refresh; /* in millihertz (for precision) */
- u32 oversample;
- u8 oversample; u8 hsync_end; u16 hblank_start, hblank_end, htotal; bool progressive : 1, trilevel_sync : 1, component_only : 1;
@@ -379,7 +379,7 @@ static const struct tv_mode tv_modes[] = { .name = "NTSC-M", .clock = 108000, .refresh = 59940,
.oversample = TV_OVERSAMPLE_8X,
.component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */.oversample = 8,
@@ -422,7 +422,7 @@ static const struct tv_mode tv_modes[] = { .name = "NTSC-443", .clock = 108000, .refresh = 59940,
.oversample = TV_OVERSAMPLE_8X,
.component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ .hsync_end = 64, .hblank_end = 124,.oversample = 8,
@@ -464,7 +464,7 @@ static const struct tv_mode tv_modes[] = { .name = "NTSC-J", .clock = 108000, .refresh = 59940,
.oversample = TV_OVERSAMPLE_8X,
.oversample = 8,
.component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
@@ -507,7 +507,7 @@ static const struct tv_mode tv_modes[] = { .name = "PAL-M", .clock = 108000, .refresh = 59940,
.oversample = TV_OVERSAMPLE_8X,
.oversample = 8,
.component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
@@ -551,7 +551,7 @@ static const struct tv_mode tv_modes[] = { .name = "PAL-N", .clock = 108000, .refresh = 50000,
.oversample = TV_OVERSAMPLE_8X,
.oversample = 8,
.component_only = 0,
.hsync_end = 64, .hblank_end = 128,
@@ -596,7 +596,7 @@ static const struct tv_mode tv_modes[] = { .name = "PAL", .clock = 108000, .refresh = 50000,
.oversample = TV_OVERSAMPLE_8X,
.oversample = 8,
.component_only = 0,
.hsync_end = 64, .hblank_end = 142,
@@ -638,7 +638,7 @@ static const struct tv_mode tv_modes[] = { .name = "480p", .clock = 108000, .refresh = 59940,
.oversample = TV_OVERSAMPLE_4X,
.oversample = 4,
.component_only = 1,
.hsync_end = 64, .hblank_end = 122,
@@ -662,7 +662,7 @@ static const struct tv_mode tv_modes[] = { .name = "576p", .clock = 108000, .refresh = 50000,
.oversample = TV_OVERSAMPLE_4X,
.oversample = 4,
.component_only = 1,
.hsync_end = 64, .hblank_end = 139,
@@ -686,7 +686,7 @@ static const struct tv_mode tv_modes[] = { .name = "720p@60Hz", .clock = 148500, .refresh = 60000,
.oversample = TV_OVERSAMPLE_2X,
.oversample = 2,
.component_only = 1,
.hsync_end = 80, .hblank_end = 300,
@@ -710,7 +710,7 @@ static const struct tv_mode tv_modes[] = { .name = "720p@50Hz", .clock = 148500, .refresh = 50000,
.oversample = TV_OVERSAMPLE_2X,
.oversample = 2,
.component_only = 1,
.hsync_end = 80, .hblank_end = 300,
@@ -735,7 +735,7 @@ static const struct tv_mode tv_modes[] = { .name = "1080i@50Hz", .clock = 148500, .refresh = 50000,
.oversample = TV_OVERSAMPLE_2X,
.oversample = 2,
.component_only = 1,
.hsync_end = 88, .hblank_end = 235,
@@ -761,7 +761,7 @@ static const struct tv_mode tv_modes[] = { .name = "1080i@60Hz", .clock = 148500, .refresh = 60000,
.oversample = TV_OVERSAMPLE_2X,
.oversample = 2,
.component_only = 1,
.hsync_end = 88, .hblank_end = 235,
@@ -1030,7 +1030,21 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, }
tv_ctl |= TV_ENC_PIPE_SEL(intel_crtc->pipe);
- tv_ctl |= tv_mode->oversample;
switch (tv_mode->oversample) {
case 8:
tv_ctl |= TV_OVERSAMPLE_8X;
break;
case 4:
tv_ctl |= TV_OVERSAMPLE_4X;
break;
case 2:
tv_ctl |= TV_OVERSAMPLE_2X;
break;
default:
tv_ctl |= TV_OVERSAMPLE_NONE;
break;
}
if (tv_mode->progressive) tv_ctl |= TV_PROGRESSIVE;
-- 2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
From: Ville Syrjälä ville.syrjala@linux.intel.com
'component_only' is a bool. Initialize it like a bool.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 96257b29d07c..72de154b8627 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -380,7 +380,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 8, - .component_only = 0, + .component_only = false, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
.hsync_end = 64, .hblank_end = 124, @@ -423,7 +423,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 8, - .component_only = 0, + .component_only = false, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ .hsync_end = 64, .hblank_end = 124, .hblank_start = 836, .htotal = 857, @@ -465,7 +465,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 8, - .component_only = 0, + .component_only = false,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ .hsync_end = 64, .hblank_end = 124, @@ -508,7 +508,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 8, - .component_only = 0, + .component_only = false,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ .hsync_end = 64, .hblank_end = 124, @@ -552,7 +552,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 50000, .oversample = 8, - .component_only = 0, + .component_only = false,
.hsync_end = 64, .hblank_end = 128, .hblank_start = 844, .htotal = 863, @@ -597,7 +597,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 50000, .oversample = 8, - .component_only = 0, + .component_only = false,
.hsync_end = 64, .hblank_end = 142, .hblank_start = 844, .htotal = 863, @@ -639,7 +639,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 4, - .component_only = 1, + .component_only = true,
.hsync_end = 64, .hblank_end = 122, .hblank_start = 842, .htotal = 857, @@ -663,7 +663,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 50000, .oversample = 4, - .component_only = 1, + .component_only = true,
.hsync_end = 64, .hblank_end = 139, .hblank_start = 859, .htotal = 863, @@ -687,7 +687,7 @@ static const struct tv_mode tv_modes[] = { .clock = 148500, .refresh = 60000, .oversample = 2, - .component_only = 1, + .component_only = true,
.hsync_end = 80, .hblank_end = 300, .hblank_start = 1580, .htotal = 1649, @@ -711,7 +711,7 @@ static const struct tv_mode tv_modes[] = { .clock = 148500, .refresh = 50000, .oversample = 2, - .component_only = 1, + .component_only = true,
.hsync_end = 80, .hblank_end = 300, .hblank_start = 1580, .htotal = 1979, @@ -736,7 +736,7 @@ static const struct tv_mode tv_modes[] = { .clock = 148500, .refresh = 50000, .oversample = 2, - .component_only = 1, + .component_only = true,
.hsync_end = 88, .hblank_end = 235, .hblank_start = 2155, .htotal = 2639, @@ -762,7 +762,7 @@ static const struct tv_mode tv_modes[] = { .clock = 148500, .refresh = 60000, .oversample = 2, - .component_only = 1, + .component_only = true,
.hsync_end = 88, .hblank_end = 235, .hblank_start = 2155, .htotal = 2199,
On Mon, Nov 12, 2018 at 06:59:50PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
'component_only' is a bool. Initialize it like a bool.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 96257b29d07c..72de154b8627 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -380,7 +380,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 8,
.component_only = 0,
.component_only = false,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
.hsync_end = 64, .hblank_end = 124,
@@ -423,7 +423,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 8,
.component_only = 0,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ .hsync_end = 64, .hblank_end = 124, .hblank_start = 836, .htotal = 857,.component_only = false,
@@ -465,7 +465,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 8,
.component_only = 0,
.component_only = false,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ .hsync_end = 64, .hblank_end = 124,
@@ -508,7 +508,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 8,
.component_only = 0,
.component_only = false,
/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ .hsync_end = 64, .hblank_end = 124,
@@ -552,7 +552,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 50000, .oversample = 8,
.component_only = 0,
.component_only = false,
.hsync_end = 64, .hblank_end = 128, .hblank_start = 844, .htotal = 863,
@@ -597,7 +597,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 50000, .oversample = 8,
.component_only = 0,
.component_only = false,
.hsync_end = 64, .hblank_end = 142, .hblank_start = 844, .htotal = 863,
@@ -639,7 +639,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 59940, .oversample = 4,
.component_only = 1,
.component_only = true,
.hsync_end = 64, .hblank_end = 122, .hblank_start = 842, .htotal = 857,
@@ -663,7 +663,7 @@ static const struct tv_mode tv_modes[] = { .clock = 108000, .refresh = 50000, .oversample = 4,
.component_only = 1,
.component_only = true,
.hsync_end = 64, .hblank_end = 139, .hblank_start = 859, .htotal = 863,
@@ -687,7 +687,7 @@ static const struct tv_mode tv_modes[] = { .clock = 148500, .refresh = 60000, .oversample = 2,
.component_only = 1,
.component_only = true,
.hsync_end = 80, .hblank_end = 300, .hblank_start = 1580, .htotal = 1649,
@@ -711,7 +711,7 @@ static const struct tv_mode tv_modes[] = { .clock = 148500, .refresh = 50000, .oversample = 2,
.component_only = 1,
.component_only = true,
.hsync_end = 80, .hblank_end = 300, .hblank_start = 1580, .htotal = 1979,
@@ -736,7 +736,7 @@ static const struct tv_mode tv_modes[] = { .clock = 148500, .refresh = 50000, .oversample = 2,
.component_only = 1,
.component_only = true,
.hsync_end = 88, .hblank_end = 235, .hblank_start = 2155, .htotal = 2639,
@@ -762,7 +762,7 @@ static const struct tv_mode tv_modes[] = { .clock = 148500, .refresh = 60000, .oversample = 2,
.component_only = 1,
.component_only = true,
.hsync_end = 88, .hblank_end = 235, .hblank_start = 2155, .htotal = 2199,
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Ville Syrjälä ville.syrjala@linux.intel.com
Just assign the margin values directly to xpos/ypos instead of first initializing to zero and then adding the values.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 72de154b8627..97a82878359f 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -994,7 +994,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, const struct video_levels *video_levels; const struct color_conversion *color_conversion; bool burst_ena; - int xpos = 0x0, ypos = 0x0; + int xpos, ypos; unsigned int xsize, ysize;
if (!tv_mode) @@ -1103,8 +1103,8 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, else ysize = 2 * (tv_mode->nbr_end + 1);
- xpos += conn_state->tv.margins.left; - ypos += conn_state->tv.margins.top; + xpos = conn_state->tv.margins.left; + ypos = conn_state->tv.margins.top; xsize -= (conn_state->tv.margins.left + conn_state->tv.margins.right); ysize -= (conn_state->tv.margins.top +
On Mon, Nov 12, 2018 at 06:59:51PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
Just assign the margin values directly to xpos/ypos instead of first initializing to zero and then adding the values.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 72de154b8627..97a82878359f 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -994,7 +994,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, const struct video_levels *video_levels; const struct color_conversion *color_conversion; bool burst_ena;
- int xpos = 0x0, ypos = 0x0;
int xpos, ypos; unsigned int xsize, ysize;
if (!tv_mode)
@@ -1103,8 +1103,8 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, else ysize = 2 * (tv_mode->nbr_end + 1);
- xpos += conn_state->tv.margins.left;
- ypos += conn_state->tv.margins.top;
- xpos = conn_state->tv.margins.left;
- ypos = conn_state->tv.margins.top; xsize -= (conn_state->tv.margins.left + conn_state->tv.margins.right); ysize -= (conn_state->tv.margins.top +
-- 2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
From: Ville Syrjälä ville.syrjala@linux.intel.com
Just assign the margin values directly to xpos/ypos instead of first initializing to zero and then adding the values.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 72de154b8627..97a82878359f 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -994,7 +994,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, const struct video_levels *video_levels; const struct color_conversion *color_conversion; bool burst_ena; - int xpos = 0x0, ypos = 0x0; + int xpos, ypos; unsigned int xsize, ysize;
if (!tv_mode) @@ -1103,8 +1103,8 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, else ysize = 2 * (tv_mode->nbr_end + 1);
- xpos += conn_state->tv.margins.left; - ypos += conn_state->tv.margins.top; + xpos = conn_state->tv.margins.left; + ypos = conn_state->tv.margins.top; xsize -= (conn_state->tv.margins.left + conn_state->tv.margins.right); ysize -= (conn_state->tv.margins.top +
From: Ville Syrjälä ville.syrjala@linux.intel.com
Rewrite the preferred mode selection to just check whether the TV modes is HD or SD. For SD TV modes we favor 480 line modes, for 720p we prefer 720 line modes, and for 1080i/p we prefer 1080 line modes.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 50 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 97a82878359f..54189080cfb1 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -860,6 +860,14 @@ intel_tv_mode_valid(struct drm_connector *connector, return MODE_CLOCK_RANGE; }
+static int +intel_tv_mode_vdisplay(const struct tv_mode *tv_mode) +{ + if (tv_mode->progressive) + return tv_mode->nbr_end + 1; + else + return 2 * (tv_mode->nbr_end + 1); +}
static void intel_tv_get_config(struct intel_encoder *encoder, @@ -1098,10 +1106,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, /* Filter ctl must be set before TV_WIN_SIZE */ I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE); xsize = tv_mode->hblank_start - tv_mode->hblank_end; - if (tv_mode->progressive) - ysize = tv_mode->nbr_end + 1; - else - ysize = 2 * (tv_mode->nbr_end + 1); + ysize = intel_tv_mode_vdisplay(tv_mode);
xpos = conn_state->tv.margins.left; ypos = conn_state->tv.margins.top; @@ -1320,22 +1325,28 @@ static const struct input_res { {"1920x1080", 1920, 1080}, };
-/* - * Chose preferred mode according to line number of TV format - */ +/* Choose preferred mode according to line number of TV format */ +static bool +intel_tv_is_preferred_mode(const struct drm_display_mode *mode, + const struct tv_mode *tv_mode) +{ + int vdisplay = intel_tv_mode_vdisplay(tv_mode); + + /* prefer 480 line modes for all SD TV modes */ + if (vdisplay <= 576) + vdisplay = 480; + + return vdisplay == mode->vdisplay; +} + static void -intel_tv_choose_preferred_modes(const struct tv_mode *tv_mode, - struct drm_display_mode *mode_ptr) +intel_tv_set_mode_type(struct drm_display_mode *mode, + const struct tv_mode *tv_mode) { - if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480) - mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; - else if (tv_mode->nbr_end > 480) { - if (tv_mode->progressive == true && tv_mode->nbr_end < 720) { - if (mode_ptr->vdisplay == 720) - mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; - } else if (mode_ptr->vdisplay == 1080) - mode_ptr->type |= DRM_MODE_TYPE_PREFERRED; - } + mode->type = DRM_MODE_TYPE_DRIVER; + + if (intel_tv_is_preferred_mode(mode, tv_mode)) + mode->type |= DRM_MODE_TYPE_PREFERRED; }
static int @@ -1383,8 +1394,7 @@ intel_tv_get_modes(struct drm_connector *connector) tmp = div_u64(tmp, 1000000); mode_ptr->clock = (int) tmp;
- mode_ptr->type = DRM_MODE_TYPE_DRIVER; - intel_tv_choose_preferred_modes(tv_mode, mode_ptr); + intel_tv_set_mode_type(mode_ptr, tv_mode); drm_mode_probed_add(connector, mode_ptr); count++; }
On Mon, Nov 12, 2018 at 06:59:53PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
Rewrite the preferred mode selection to just check whether the TV modes is HD or SD. For SD TV modes we favor 480 line modes, for 720p we prefer 720 line modes, and for 1080i/p we prefer 1080 line modes.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 50 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 97a82878359f..54189080cfb1 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -860,6 +860,14 @@ intel_tv_mode_valid(struct drm_connector *connector, return MODE_CLOCK_RANGE; }
+static int +intel_tv_mode_vdisplay(const struct tv_mode *tv_mode) +{
- if (tv_mode->progressive)
return tv_mode->nbr_end + 1;
- else
return 2 * (tv_mode->nbr_end + 1);
+}
static void intel_tv_get_config(struct intel_encoder *encoder, @@ -1098,10 +1106,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, /* Filter ctl must be set before TV_WIN_SIZE */ I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE); xsize = tv_mode->hblank_start - tv_mode->hblank_end;
- if (tv_mode->progressive)
ysize = tv_mode->nbr_end + 1;
- else
ysize = 2 * (tv_mode->nbr_end + 1);
ysize = intel_tv_mode_vdisplay(tv_mode);
xpos = conn_state->tv.margins.left; ypos = conn_state->tv.margins.top;
@@ -1320,22 +1325,28 @@ static const struct input_res { {"1920x1080", 1920, 1080}, };
-/*
- Chose preferred mode according to line number of TV format
- */
+/* Choose preferred mode according to line number of TV format */ +static bool +intel_tv_is_preferred_mode(const struct drm_display_mode *mode,
const struct tv_mode *tv_mode)
+{
- int vdisplay = intel_tv_mode_vdisplay(tv_mode);
- /* prefer 480 line modes for all SD TV modes */
- if (vdisplay <= 576)
vdisplay = 480;
- return vdisplay == mode->vdisplay;
+}
static void -intel_tv_choose_preferred_modes(const struct tv_mode *tv_mode,
struct drm_display_mode *mode_ptr)
+intel_tv_set_mode_type(struct drm_display_mode *mode,
const struct tv_mode *tv_mode)
{
- if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
- else if (tv_mode->nbr_end > 480) {
if (tv_mode->progressive == true && tv_mode->nbr_end < 720) {
if (mode_ptr->vdisplay == 720)
mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
} else if (mode_ptr->vdisplay == 1080)
mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
- }
- mode->type = DRM_MODE_TYPE_DRIVER;
- if (intel_tv_is_preferred_mode(mode, tv_mode))
mode->type |= DRM_MODE_TYPE_PREFERRED;
}
static int @@ -1383,8 +1394,7 @@ intel_tv_get_modes(struct drm_connector *connector) tmp = div_u64(tmp, 1000000); mode_ptr->clock = (int) tmp;
mode_ptr->type = DRM_MODE_TYPE_DRIVER;
intel_tv_choose_preferred_modes(tv_mode, mode_ptr);
drm_mode_probed_add(connector, mode_ptr); count++; }intel_tv_set_mode_type(mode_ptr, tv_mode);
-- 2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
From: Ville Syrjälä ville.syrjala@linux.intel.com
No point in storing the mode names in the array. drm_mode_set_name() will give us the same names without wasting space for these string constants.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 54189080cfb1..433f538261c1 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1313,16 +1313,15 @@ intel_tv_detect(struct drm_connector *connector, }
static const struct input_res { - const char *name; - int w, h; + u16 w, h; } input_res_table[] = { - {"640x480", 640, 480}, - {"800x600", 800, 600}, - {"1024x768", 1024, 768}, - {"1280x1024", 1280, 1024}, - {"848x480", 848, 480}, - {"1280x720", 1280, 720}, - {"1920x1080", 1920, 1080}, + { 640, 480 }, + { 800, 600 }, + { 1024, 768 }, + { 1280, 1024 }, + { 848, 480 }, + { 1280, 720 }, + { 1920, 1080 }, };
/* Choose preferred mode according to line number of TV format */ @@ -1373,7 +1372,6 @@ intel_tv_get_modes(struct drm_connector *connector) mode_ptr = drm_mode_create(connector->dev); if (!mode_ptr) continue; - strlcpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
mode_ptr->hdisplay = hactive_s; mode_ptr->hsync_start = hactive_s + 1; @@ -1395,6 +1393,9 @@ intel_tv_get_modes(struct drm_connector *connector) mode_ptr->clock = (int) tmp;
intel_tv_set_mode_type(mode_ptr, tv_mode); + + drm_mode_set_name(mode_ptr); + drm_mode_probed_add(connector, mode_ptr); count++; }
On Mon, Nov 12, 2018 at 06:59:54PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
No point in storing the mode names in the array. drm_mode_set_name() will give us the same names without wasting space for these string constants.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 54189080cfb1..433f538261c1 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1313,16 +1313,15 @@ intel_tv_detect(struct drm_connector *connector, }
static const struct input_res {
- const char *name;
- int w, h;
- u16 w, h;
} input_res_table[] = {
- {"640x480", 640, 480},
- {"800x600", 800, 600},
- {"1024x768", 1024, 768},
- {"1280x1024", 1280, 1024},
- {"848x480", 848, 480},
- {"1280x720", 1280, 720},
- {"1920x1080", 1920, 1080},
- { 640, 480 },
- { 800, 600 },
- { 1024, 768 },
- { 1280, 1024 },
- { 848, 480 },
- { 1280, 720 },
- { 1920, 1080 },
};
/* Choose preferred mode according to line number of TV format */ @@ -1373,7 +1372,6 @@ intel_tv_get_modes(struct drm_connector *connector) mode_ptr = drm_mode_create(connector->dev); if (!mode_ptr) continue;
strlcpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
mode_ptr->hdisplay = hactive_s; mode_ptr->hsync_start = hactive_s + 1;
@@ -1395,6 +1393,9 @@ intel_tv_get_modes(struct drm_connector *connector) mode_ptr->clock = (int) tmp;
intel_tv_set_mode_type(mode_ptr, tv_mode);
drm_mode_set_name(mode_ptr);
- drm_mode_probed_add(connector, mode_ptr); count++; }
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Ville Syrjälä ville.syrjala@linux.intel.com
The current code insists on picking a new TV mode when switching between component and non-component cables. That's super annoying. Let's just keep the current TV mode unless the new cable type actually disagrees with it.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 433f538261c1..32a8786fe0da 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1253,16 +1253,18 @@ static void intel_tv_find_better_format(struct drm_connector *connector) const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state); int i;
- if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) == - tv_mode->component_only) + /* Component supports everything so we can keep the current mode */ + if (intel_tv->type == DRM_MODE_CONNECTOR_Component) return;
+ /* If the current mode is fine don't change it */ + if (!tv_mode->component_only) + return;
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { - tv_mode = tv_modes + i; + tv_mode = &tv_modes[i];
- if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) == - tv_mode->component_only) + if (!tv_mode->component_only) break; }
On Mon, Nov 12, 2018 at 06:59:55PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
The current code insists on picking a new TV mode when switching between component and non-component cables. That's super annoying. Let's just keep the current TV mode unless the new cable type actually disagrees with it.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 433f538261c1..32a8786fe0da 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1253,16 +1253,18 @@ static void intel_tv_find_better_format(struct drm_connector *connector) const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state); int i;
- if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
tv_mode->component_only)
/* Component supports everything so we can keep the current mode */
if (intel_tv->type == DRM_MODE_CONNECTOR_Component) return;
/* If the current mode is fine don't change it */
if (!tv_mode->component_only)
return;
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
tv_mode = tv_modes + i;
tv_mode = &tv_modes[i];
if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
tv_mode->component_only)
}if (!tv_mode->component_only) break;
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Ville Syrjälä ville.syrjala@linux.intel.com
Remove the silly reported_modes[] array. I suppse once upon a time this actually had something to do with modes we reported to userspace. Now it is just the placeholder for the mode we use for load detection. We don't need it even for that, and instead we can just rely on the fallback mode in intel_get_load_detect_pipe().
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 32a8786fe0da..c39e9a5b43db 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1130,23 +1130,6 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, I915_WRITE(TV_CTL, tv_ctl); }
-static const struct drm_display_mode reported_modes[] = { - { - .name = "NTSC 480i", - .clock = 108000, - .hdisplay = 1280, - .hsync_start = 1368, - .hsync_end = 1496, - .htotal = 1712, - - .vdisplay = 1024, - .vsync_start = 1027, - .vsync_end = 1034, - .vtotal = 1104, - .type = DRM_MODE_TYPE_DRIVER, - }, -}; - static int intel_tv_detect_type(struct intel_tv *intel_tv, struct drm_connector *connector) @@ -1276,7 +1259,6 @@ intel_tv_detect(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, bool force) { - struct drm_display_mode mode; struct intel_tv *intel_tv = intel_attached_tv(connector); enum drm_connector_status status; int type; @@ -1285,13 +1267,11 @@ intel_tv_detect(struct drm_connector *connector, connector->base.id, connector->name, force);
- mode = reported_modes[0]; - if (force) { struct intel_load_detect_pipe tmp; int ret;
- ret = intel_get_load_detect_pipe(connector, &mode, &tmp, ctx); + ret = intel_get_load_detect_pipe(connector, NULL, &tmp, ctx); if (ret < 0) return ret;
On Mon, Nov 12, 2018 at 06:59:56PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
Remove the silly reported_modes[] array. I suppse once upon a time this actually had something to do with modes we reported to userspace. Now it is just the placeholder for the mode we use for load detection. We don't need it even for that, and instead we can just rely on the fallback mode in intel_get_load_detect_pipe().
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 32a8786fe0da..c39e9a5b43db 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1130,23 +1130,6 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, I915_WRITE(TV_CTL, tv_ctl); }
-static const struct drm_display_mode reported_modes[] = {
- {
.name = "NTSC 480i",
.clock = 108000,
.hdisplay = 1280,
.hsync_start = 1368,
.hsync_end = 1496,
.htotal = 1712,
.vdisplay = 1024,
.vsync_start = 1027,
.vsync_end = 1034,
.vtotal = 1104,
.type = DRM_MODE_TYPE_DRIVER,
- },
-};
static int intel_tv_detect_type(struct intel_tv *intel_tv, struct drm_connector *connector) @@ -1276,7 +1259,6 @@ intel_tv_detect(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, bool force) {
- struct drm_display_mode mode; struct intel_tv *intel_tv = intel_attached_tv(connector); enum drm_connector_status status; int type;
@@ -1285,13 +1267,11 @@ intel_tv_detect(struct drm_connector *connector, connector->base.id, connector->name, force);
mode = reported_modes[0];
if (force) { struct intel_load_detect_pipe tmp; int ret;
ret = intel_get_load_detect_pipe(connector, &mode, &tmp, ctx);
if (ret < 0) return ret;ret = intel_get_load_detect_pipe(connector, NULL, &tmp, ctx);
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Ville Syrjälä ville.syrjala@linux.intel.com
Add the missing 1080p TV modes. On gen4 all of them work just fine, whereas on gen3 only the 30Hz mode actually works correctly.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 90 +++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index c39e9a5b43db..216525dd144a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -783,6 +783,84 @@ static const struct tv_mode tv_modes[] = {
.filter_table = filter_table, }, + + { + .name = "1080p@30Hz", + .clock = 148500, + .refresh = 30000, + .oversample = 2, + .component_only = true, + + .hsync_end = 88, .hblank_end = 235, + .hblank_start = 2155, .htotal = 2199, + + .progressive = true, .trilevel_sync = true, + + .vsync_start_f1 = 8, .vsync_start_f2 = 8, + .vsync_len = 10, + + .veq_ena = false, .veq_start_f1 = 0, + .veq_start_f2 = 0, .veq_len = 0, + + .vi_end_f1 = 44, .vi_end_f2 = 44, + .nbr_end = 1079, + + .burst_ena = false, + + .filter_table = filter_table, + }, + + { + .name = "1080p@50Hz", + .clock = 148500, + .refresh = 50000, + .oversample = 1, + .component_only = true, + + .hsync_end = 88, .hblank_end = 235, + .hblank_start = 2155, .htotal = 2639, + + .progressive = true, .trilevel_sync = true, + + .vsync_start_f1 = 8, .vsync_start_f2 = 8, + .vsync_len = 10, + + .veq_ena = false, .veq_start_f1 = 0, + .veq_start_f2 = 0, .veq_len = 0, + + .vi_end_f1 = 44, .vi_end_f2 = 44, + .nbr_end = 1079, + + .burst_ena = false, + + .filter_table = filter_table, + }, + + { + .name = "1080p@60Hz", + .clock = 148500, + .refresh = 60000, + .oversample = 1, + .component_only = true, + + .hsync_end = 88, .hblank_end = 235, + .hblank_start = 2155, .htotal = 2199, + + .progressive = true, .trilevel_sync = true, + + .vsync_start_f1 = 8, .vsync_start_f2 = 8, + .vsync_len = 10, + + .veq_ena = false, .veq_start_f1 = 0, + .veq_start_f2 = 0, .veq_len = 0, + + .vi_end_f1 = 44, .vi_end_f2 = 44, + .nbr_end = 1079, + + .burst_ena = false, + + .filter_table = filter_table, + }, };
static struct intel_tv *enc_to_tv(struct intel_encoder *encoder) @@ -1538,11 +1616,15 @@ intel_tv_init(struct drm_i915_private *dev_priv) connector->doublescan_allowed = false;
/* Create TV properties then attach current values */ - for (i = 0; i < ARRAY_SIZE(tv_modes); i++) + for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { + /* 1080p50/1080p60 not supported on gen3 */ + if (IS_GEN3(dev_priv) && + tv_modes[i].oversample == 1) + break; + tv_format_names[i] = tv_modes[i].name; - drm_mode_create_tv_properties(dev, - ARRAY_SIZE(tv_modes), - tv_format_names); + } + drm_mode_create_tv_properties(dev, i, tv_format_names);
drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property, state->tv.mode);
On Mon, Nov 12, 2018 at 06:59:57PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
Add the missing 1080p TV modes. On gen4 all of them work just fine, whereas on gen3 only the 30Hz mode actually works correctly.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Matches the spec: Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 90 +++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index c39e9a5b43db..216525dd144a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -783,6 +783,84 @@ static const struct tv_mode tv_modes[] = {
.filter_table = filter_table,
},
- {
.name = "1080p@30Hz",
.clock = 148500,
.refresh = 30000,
.oversample = 2,
.component_only = true,
.hsync_end = 88, .hblank_end = 235,
.hblank_start = 2155, .htotal = 2199,
.progressive = true, .trilevel_sync = true,
.vsync_start_f1 = 8, .vsync_start_f2 = 8,
.vsync_len = 10,
.veq_ena = false, .veq_start_f1 = 0,
.veq_start_f2 = 0, .veq_len = 0,
.vi_end_f1 = 44, .vi_end_f2 = 44,
.nbr_end = 1079,
.burst_ena = false,
.filter_table = filter_table,
- },
- {
.name = "1080p@50Hz",
.clock = 148500,
.refresh = 50000,
.oversample = 1,
.component_only = true,
.hsync_end = 88, .hblank_end = 235,
.hblank_start = 2155, .htotal = 2639,
.progressive = true, .trilevel_sync = true,
.vsync_start_f1 = 8, .vsync_start_f2 = 8,
.vsync_len = 10,
.veq_ena = false, .veq_start_f1 = 0,
.veq_start_f2 = 0, .veq_len = 0,
.vi_end_f1 = 44, .vi_end_f2 = 44,
.nbr_end = 1079,
.burst_ena = false,
.filter_table = filter_table,
- },
- {
.name = "1080p@60Hz",
.clock = 148500,
.refresh = 60000,
.oversample = 1,
.component_only = true,
.hsync_end = 88, .hblank_end = 235,
.hblank_start = 2155, .htotal = 2199,
.progressive = true, .trilevel_sync = true,
.vsync_start_f1 = 8, .vsync_start_f2 = 8,
.vsync_len = 10,
.veq_ena = false, .veq_start_f1 = 0,
.veq_start_f2 = 0, .veq_len = 0,
.vi_end_f1 = 44, .vi_end_f2 = 44,
.nbr_end = 1079,
.burst_ena = false,
.filter_table = filter_table,
- },
};
static struct intel_tv *enc_to_tv(struct intel_encoder *encoder) @@ -1538,11 +1616,15 @@ intel_tv_init(struct drm_i915_private *dev_priv) connector->doublescan_allowed = false;
/* Create TV properties then attach current values */
- for (i = 0; i < ARRAY_SIZE(tv_modes); i++)
- for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
/* 1080p50/1080p60 not supported on gen3 */
if (IS_GEN3(dev_priv) &&
tv_modes[i].oversample == 1)
break;
- tv_format_names[i] = tv_modes[i].name;
- drm_mode_create_tv_properties(dev,
ARRAY_SIZE(tv_modes),
tv_format_names);
}
drm_mode_create_tv_properties(dev, i, tv_format_names);
drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property, state->tv.mode);
-- 2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
From: Ville Syrjälä ville.syrjala@linux.intel.com
To make vblank timestamps work better with the TV encoder let's scale the pipe timings such that the relationship between the TV active and TV blanking periods is mirrored in the corresponding pipe timings.
Note that in reality the pipe runs at a faster speed during the TV vblank, and correspondigly there are periods when the pipe is enitrely stopped. We pretend that this isn't the case and as such we incur some error in the vblank timestamps during the TV vblank. Further explanation of the issues in a big comment in the code.
This makes the vblank timestamps good enough to make i965gm (which doesn't have a working frame counter with the TV encoder) report correct frame numbers. Previously you could get all kinds of nonsense which resulted in eg. glxgears reporting that it's running at twice the actual framerate in most cases.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_tv.c | 322 +++++++++++++++++++++++++++----- 2 files changed, 278 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fe4b913e46ac..10813ae7bee7 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4848,6 +4848,7 @@ enum { # define TV_OVERSAMPLE_NONE (2 << 18) /* Selects 8x oversampling */ # define TV_OVERSAMPLE_8X (3 << 18) +# define TV_OVERSAMPLE_MASK (3 << 18) /* Selects progressive mode rather than interlaced */ # define TV_PROGRESSIVE (1 << 17) /* Sets the colorburst to PAL mode. Required for non-M PAL modes. */ diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 216525dd144a..75126fce655d 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -340,7 +340,6 @@ struct tv_mode { const struct video_levels *composite_levels, *svideo_levels; const struct color_conversion *composite_color, *svideo_color; const u32 *filter_table; - u16 max_srcw; };
@@ -729,7 +728,6 @@ static const struct tv_mode tv_modes[] = { .burst_ena = false,
.filter_table = filter_table, - .max_srcw = 800 }, { .name = "1080i@50Hz", @@ -947,13 +945,183 @@ intel_tv_mode_vdisplay(const struct tv_mode *tv_mode) return 2 * (tv_mode->nbr_end + 1); }
+static void +intel_tv_mode_to_mode(struct drm_display_mode *mode, + const struct tv_mode *tv_mode) +{ + mode->clock = tv_mode->clock / + (tv_mode->oversample >> !tv_mode->progressive); + + /* + * tv_mode horizontal timings: + * + * hsync_end + * | hblank_end + * | | hblank_start + * | | | htotal + * | _______ | + * ____/ ___ + * __/ \ + */ + mode->hdisplay = + tv_mode->hblank_start - tv_mode->hblank_end; + mode->hsync_start = mode->hdisplay + + tv_mode->htotal - tv_mode->hblank_start; + mode->hsync_end = mode->hsync_start + + tv_mode->hsync_end; + mode->htotal = tv_mode->htotal + 1; + + /* + * tv_mode vertical timings: + * + * vsync_start + * | vsync_end + * | | vi_end nbr_end + * | | | | + * | | _______ + * __ ____/ \ + * __/ + */ + mode->vdisplay = intel_tv_mode_vdisplay(tv_mode); + if (tv_mode->progressive) { + mode->vsync_start = mode->vdisplay + + tv_mode->vsync_start_f1 + 1; + mode->vsync_end = mode->vsync_start + + tv_mode->vsync_len; + mode->vtotal = mode->vdisplay + + tv_mode->vi_end_f1 + 1; + } else { + mode->vsync_start = mode->vdisplay + + tv_mode->vsync_start_f1 + 1 + + tv_mode->vsync_start_f2 + 1; + mode->vsync_end = mode->vsync_start + + 2 * tv_mode->vsync_len; + mode->vtotal = mode->vdisplay + + tv_mode->vi_end_f1 + 1 + + tv_mode->vi_end_f2 + 1; + } + + /* TV has it's own notion of sync and other mode flags, so clear them. */ + mode->flags = 0; + + mode->vrefresh = 0; + mode->vrefresh = drm_mode_vrefresh(mode); + + snprintf(mode->name, sizeof(mode->name), + "%dx%d%c (%s)", + mode->hdisplay, mode->vdisplay, + tv_mode->progressive ? 'p' : 'i', + tv_mode->name); +} + +static void intel_tv_scale_mode_horiz(struct drm_display_mode *mode, + int hdisplay, int left_margin, + int right_margin) +{ + int hsync_start = mode->hsync_start - mode->hdisplay + right_margin; + int hsync_end = mode->hsync_end - mode->hdisplay + right_margin; + int new_htotal = mode->htotal * hdisplay / + (mode->hdisplay - left_margin - right_margin); + + mode->clock = mode->clock * new_htotal / mode->htotal; + + mode->hdisplay = hdisplay; + mode->hsync_start = hdisplay + hsync_start * new_htotal / mode->htotal; + mode->hsync_end = hdisplay + hsync_end * new_htotal / mode->htotal; + mode->htotal = new_htotal; +} + +static void intel_tv_scale_mode_vert(struct drm_display_mode *mode, + int vdisplay, int top_margin, + int bottom_margin) +{ + int vsync_start = mode->vsync_start - mode->vdisplay + bottom_margin; + int vsync_end = mode->vsync_end - mode->vdisplay + bottom_margin; + int new_vtotal = mode->vtotal * vdisplay / + (mode->vdisplay - top_margin - bottom_margin); + + mode->clock = mode->clock * new_vtotal / mode->vtotal; + + mode->vdisplay = vdisplay; + mode->vsync_start = vdisplay + vsync_start * new_vtotal / mode->vtotal; + mode->vsync_end = vdisplay + vsync_end * new_vtotal / mode->vtotal; + mode->vtotal = new_vtotal; +} + static void intel_tv_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct drm_display_mode *adjusted_mode = + &pipe_config->base.adjusted_mode; + struct drm_display_mode mode = {}; + u32 tv_ctl, hctl1, hctl3, vctl1, vctl2, tmp; + struct tv_mode tv_mode = {}; + int hdisplay = adjusted_mode->crtc_hdisplay; + int vdisplay = adjusted_mode->crtc_vdisplay; + int xsize, ysize, xpos, ypos; + pipe_config->output_types |= BIT(INTEL_OUTPUT_TVOUT);
- pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock; + tv_ctl = I915_READ(TV_CTL); + hctl1 = I915_READ(TV_H_CTL_1); + hctl3 = I915_READ(TV_H_CTL_3); + vctl1 = I915_READ(TV_V_CTL_1); + vctl2 = I915_READ(TV_V_CTL_2); + + tv_mode.htotal = (hctl1 & TV_HTOTAL_MASK) >> TV_HTOTAL_SHIFT; + tv_mode.hsync_end = (hctl1 & TV_HSYNC_END_MASK) >> TV_HSYNC_END_SHIFT; + + tv_mode.hblank_start = (hctl3 & TV_HBLANK_START_MASK) >> TV_HBLANK_START_SHIFT; + tv_mode.hblank_end = (hctl3 & TV_HSYNC_END_MASK) >> TV_HBLANK_END_SHIFT; + + tv_mode.nbr_end = (vctl1 & TV_NBR_END_MASK) >> TV_NBR_END_SHIFT; + tv_mode.vi_end_f1 = (vctl1 & TV_VI_END_F1_MASK) >> TV_VI_END_F1_SHIFT; + tv_mode.vi_end_f2 = (vctl1 & TV_VI_END_F2_MASK) >> TV_VI_END_F2_SHIFT; + + tv_mode.vsync_len = (vctl2 & TV_VSYNC_LEN_MASK) >> TV_VSYNC_LEN_SHIFT; + tv_mode.vsync_start_f1 = (vctl2 & TV_VSYNC_START_F1_MASK) >> TV_VSYNC_START_F1_SHIFT; + tv_mode.vsync_start_f2 = (vctl2 & TV_VSYNC_START_F2_MASK) >> TV_VSYNC_START_F2_SHIFT; + + tv_mode.clock = pipe_config->port_clock; + + tv_mode.progressive = tv_ctl & TV_PROGRESSIVE; + + switch (tv_ctl & TV_OVERSAMPLE_MASK) { + case TV_OVERSAMPLE_8X: + tv_mode.oversample = 8; + break; + case TV_OVERSAMPLE_4X: + tv_mode.oversample = 4; + break; + case TV_OVERSAMPLE_2X: + tv_mode.oversample = 2; + break; + default: + tv_mode.oversample = 1; + break; + } + + tmp = I915_READ(TV_WIN_POS); + xpos = tmp >> 16; + ypos = tmp & 0xffff; + + tmp = I915_READ(TV_WIN_SIZE); + xsize = tmp >> 16; + ysize = tmp & 0xffff; + + intel_tv_mode_to_mode(&mode, &tv_mode); + + DRM_DEBUG_KMS("TV mode:\n"); + drm_mode_debug_printmodeline(&mode); + + intel_tv_scale_mode_horiz(&mode, hdisplay, + xpos, mode.hdisplay - xsize - xpos); + intel_tv_scale_mode_vert(&mode, vdisplay, + ypos, mode.vdisplay - ysize - ypos); + + adjusted_mode->crtc_clock = mode.clock; }
static bool @@ -964,6 +1132,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; + int hdisplay = adjusted_mode->crtc_hdisplay; + int vdisplay = adjusted_mode->crtc_vdisplay;
if (!tv_mode) return false; @@ -972,17 +1142,90 @@ intel_tv_compute_config(struct intel_encoder *encoder, return false;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; - adjusted_mode->crtc_clock = tv_mode->clock; + DRM_DEBUG_KMS("forcing bpc to 8 for TV\n"); pipe_config->pipe_bpp = 8*3;
- /* TV has it's own notion of sync and other mode flags, so clear them. */ - adjusted_mode->flags = 0; + pipe_config->port_clock = tv_mode->clock; + + intel_tv_mode_to_mode(adjusted_mode, tv_mode); + + DRM_DEBUG_KMS("TV mode:\n"); + drm_mode_debug_printmodeline(adjusted_mode);
/* - * FIXME: We don't check whether the input mode is actually what we want - * or whether userspace is doing something stupid. + * The pipe scanline counter behaviour looks as follows when + * using the TV encoder: + * + * time -> + * + * dsl=vtotal-1 | | + * || || + * ___| | ___| | + * / | / | + * / | / | + * dsl=0 ___/ |_____/ | + * | | | | | | + * ^ ^ ^ ^ ^ + * | | | | pipe vblank/first part of tv vblank + * | | | bottom margin + * | | active + * | top margin + * remainder of tv vblank + * + * When the TV encoder is used the pipe wants to run faster + * than expected rate. During the active portion the TV + * encoder stalls the pipe every few lines to keep it in + * check. When the TV encoder reaches the bottom margin the + * pipe simply stops. Once we reach the TV vblank the pipe is + * no longer stalled and it runs at the max rate (apparently + * oversample clock on gen3, cdclk on gen4). Once the pipe + * reaches the pipe vtotal the pipe stops for the remainder + * of the TV vblank/top margin. The pipe starts up again when + * the TV encoder exits the top margin. + * + * To avoid huge hassles for vblank timestamping we scale + * the pipe timings as if the pipe always runs at the average + * rate it maintains during the active period. This also + * gives us a reasonable guesstimate as to the pixel rate. + * Due to the variation in the actual pipe speed the scanline + * counter will give us slightly erroneous results during the + * TV vblank/margins. But since vtotal was selected such that + * it matches the average rate of the pipe during the active + * portion the error shouldn't cause any serious grief to + * vblank timestamps. + * + * For posterity here is the empirically derived formula + * that gives us the maximum length of the pipe vblank + * we can use without causing display corruption. Following + * this would allow us to have a ticking scanline counter + * everywhere except during the bottom margin (there the + * pipe always stops). Ie. this would eliminate the second + * flat portion of the above graph. However this would also + * complicate vblank timestamping as the pipe vtotal would + * no longer match the average rate the pipe runs at during + * the active portion. Hence following this formula seems + * more trouble that it's worth. + * + * if (IS_GEN4(dev_priv)) { + * num = cdclk * (tv_mode->oversample >> !tv_mode->progressive); + * den = tv_mode->clock; + * } else { + * num = tv_mode->oversample >> !tv_mode->progressive; + * den = 1; + * } + * max_pipe_vblank_len ~= + * (num * tv_htotal * (tv_vblank_len + top_margin)) / + * (den * pipe_htotal); */ + intel_tv_scale_mode_horiz(adjusted_mode, hdisplay, + conn_state->tv.margins.left, + conn_state->tv.margins.right); + intel_tv_scale_mode_vert(adjusted_mode, vdisplay, + conn_state->tv.margins.top, + conn_state->tv.margins.bottom); + drm_mode_set_crtcinfo(adjusted_mode, 0); + adjusted_mode->name[0] = '\0';
return true; } @@ -1411,52 +1654,41 @@ intel_tv_set_mode_type(struct drm_display_mode *mode, static int intel_tv_get_modes(struct drm_connector *connector) { - struct drm_display_mode *mode_ptr; const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state); - int j, count = 0; - u64 tmp; + int i, count = 0;
- for (j = 0; j < ARRAY_SIZE(input_res_table); - j++) { - const struct input_res *input = &input_res_table[j]; - unsigned int hactive_s = input->w; - unsigned int vactive_s = input->h; - - if (tv_mode->max_srcw && input->w > tv_mode->max_srcw) - continue; + for (i = 0; i < ARRAY_SIZE(input_res_table); i++) { + const struct input_res *input = &input_res_table[i]; + struct drm_display_mode *mode;
- if (input->w > 1024 && (!tv_mode->progressive - && !tv_mode->component_only)) + if (input->w > 1024 && + !tv_mode->progressive && + !tv_mode->component_only) continue;
- mode_ptr = drm_mode_create(connector->dev); - if (!mode_ptr) + mode = drm_mode_create(connector->dev); + if (!mode) continue;
- mode_ptr->hdisplay = hactive_s; - mode_ptr->hsync_start = hactive_s + 1; - mode_ptr->hsync_end = hactive_s + 64; - if (mode_ptr->hsync_end <= mode_ptr->hsync_start) - mode_ptr->hsync_end = mode_ptr->hsync_start + 1; - mode_ptr->htotal = hactive_s + 96; - - mode_ptr->vdisplay = vactive_s; - mode_ptr->vsync_start = vactive_s + 1; - mode_ptr->vsync_end = vactive_s + 32; - if (mode_ptr->vsync_end <= mode_ptr->vsync_start) - mode_ptr->vsync_end = mode_ptr->vsync_start + 1; - mode_ptr->vtotal = vactive_s + 33; - - tmp = mul_u32_u32(tv_mode->refresh, mode_ptr->vtotal); - tmp *= mode_ptr->htotal; - tmp = div_u64(tmp, 1000000); - mode_ptr->clock = (int) tmp; - - intel_tv_set_mode_type(mode_ptr, tv_mode); + /* + * We take the TV mode and scale it to look + * like it had the expected h/vdisplay. This + * provides the most information to userspace + * about the actual timings of the mode. We + * do ignore the margins though. + */ + intel_tv_mode_to_mode(mode, tv_mode); + if (count == 0) { + DRM_DEBUG_KMS("TV mode:\n"); + drm_mode_debug_printmodeline(mode); + } + intel_tv_scale_mode_horiz(mode, input->w, 0, 0); + intel_tv_scale_mode_vert(mode, input->h, 0, 0); + intel_tv_set_mode_type(mode, tv_mode);
- drm_mode_set_name(mode_ptr); + drm_mode_set_name(mode);
- drm_mode_probed_add(connector, mode_ptr); + drm_mode_probed_add(connector, mode); count++; }
On Mon, Nov 12, 2018 at 06:59:58PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
To make vblank timestamps work better with the TV encoder let's scale the pipe timings such that the relationship between the TV active and TV blanking periods is mirrored in the corresponding pipe timings.
Note that in reality the pipe runs at a faster speed during the TV vblank, and correspondigly there are periods when the pipe is enitrely stopped. We pretend that this isn't the case and as such we incur some error in the vblank timestamps during the TV vblank. Further explanation of the issues in a big comment in the code.
This makes the vblank timestamps good enough to make i965gm (which doesn't have a working frame counter with the TV encoder) report correct frame numbers. Previously you could get all kinds of nonsense which resulted in eg. glxgears reporting that it's running at twice the actual framerate in most cases.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_tv.c | 322 +++++++++++++++++++++++++++----- 2 files changed, 278 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fe4b913e46ac..10813ae7bee7 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4848,6 +4848,7 @@ enum { # define TV_OVERSAMPLE_NONE (2 << 18) /* Selects 8x oversampling */ # define TV_OVERSAMPLE_8X (3 << 18) +# define TV_OVERSAMPLE_MASK (3 << 18) /* Selects progressive mode rather than interlaced */ # define TV_PROGRESSIVE (1 << 17) /* Sets the colorburst to PAL mode. Required for non-M PAL modes. */ diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 216525dd144a..75126fce655d 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -340,7 +340,6 @@ struct tv_mode { const struct video_levels *composite_levels, *svideo_levels; const struct color_conversion *composite_color, *svideo_color; const u32 *filter_table;
- u16 max_srcw;
};
@@ -729,7 +728,6 @@ static const struct tv_mode tv_modes[] = { .burst_ena = false,
.filter_table = filter_table,
}, { .name = "1080i@50Hz",.max_srcw = 800
@@ -947,13 +945,183 @@ intel_tv_mode_vdisplay(const struct tv_mode *tv_mode) return 2 * (tv_mode->nbr_end + 1); }
+static void +intel_tv_mode_to_mode(struct drm_display_mode *mode,
const struct tv_mode *tv_mode)
+{
- mode->clock = tv_mode->clock /
(tv_mode->oversample >> !tv_mode->progressive);
- /*
* tv_mode horizontal timings:
*
* hsync_end
* | hblank_end
* | | hblank_start
* | | | htotal
* | _______ |
* ____/ \___
* \__/ \
*/
- mode->hdisplay =
tv_mode->hblank_start - tv_mode->hblank_end;
- mode->hsync_start = mode->hdisplay +
tv_mode->htotal - tv_mode->hblank_start;
- mode->hsync_end = mode->hsync_start +
tv_mode->hsync_end;
- mode->htotal = tv_mode->htotal + 1;
- /*
* tv_mode vertical timings:
*
* vsync_start
* | vsync_end
* | | vi_end nbr_end
* | | | |
* | | _______
* \__ ____/ \
* \__/
*/
- mode->vdisplay = intel_tv_mode_vdisplay(tv_mode);
- if (tv_mode->progressive) {
mode->vsync_start = mode->vdisplay +
tv_mode->vsync_start_f1 + 1;
mode->vsync_end = mode->vsync_start +
tv_mode->vsync_len;
mode->vtotal = mode->vdisplay +
tv_mode->vi_end_f1 + 1;
- } else {
mode->vsync_start = mode->vdisplay +
tv_mode->vsync_start_f1 + 1 +
tv_mode->vsync_start_f2 + 1;
mode->vsync_end = mode->vsync_start +
2 * tv_mode->vsync_len;
mode->vtotal = mode->vdisplay +
tv_mode->vi_end_f1 + 1 +
tv_mode->vi_end_f2 + 1;
- }
- /* TV has it's own notion of sync and other mode flags, so clear them. */
- mode->flags = 0;
- mode->vrefresh = 0;
Redundant line.
- mode->vrefresh = drm_mode_vrefresh(mode);
- snprintf(mode->name, sizeof(mode->name),
"%dx%d%c (%s)",
mode->hdisplay, mode->vdisplay,
tv_mode->progressive ? 'p' : 'i',
tv_mode->name);
+}
+static void intel_tv_scale_mode_horiz(struct drm_display_mode *mode,
int hdisplay, int left_margin,
int right_margin)
+{
- int hsync_start = mode->hsync_start - mode->hdisplay + right_margin;
- int hsync_end = mode->hsync_end - mode->hdisplay + right_margin;
- int new_htotal = mode->htotal * hdisplay /
(mode->hdisplay - left_margin - right_margin);
- mode->clock = mode->clock * new_htotal / mode->htotal;
- mode->hdisplay = hdisplay;
- mode->hsync_start = hdisplay + hsync_start * new_htotal / mode->htotal;
- mode->hsync_end = hdisplay + hsync_end * new_htotal / mode->htotal;
- mode->htotal = new_htotal;
+}
+static void intel_tv_scale_mode_vert(struct drm_display_mode *mode,
int vdisplay, int top_margin,
int bottom_margin)
+{
- int vsync_start = mode->vsync_start - mode->vdisplay + bottom_margin;
- int vsync_end = mode->vsync_end - mode->vdisplay + bottom_margin;
- int new_vtotal = mode->vtotal * vdisplay /
(mode->vdisplay - top_margin - bottom_margin);
- mode->clock = mode->clock * new_vtotal / mode->vtotal;
- mode->vdisplay = vdisplay;
- mode->vsync_start = vdisplay + vsync_start * new_vtotal / mode->vtotal;
- mode->vsync_end = vdisplay + vsync_end * new_vtotal / mode->vtotal;
- mode->vtotal = new_vtotal;
+}
static void intel_tv_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) {
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct drm_display_mode *adjusted_mode =
&pipe_config->base.adjusted_mode;
- struct drm_display_mode mode = {};
- u32 tv_ctl, hctl1, hctl3, vctl1, vctl2, tmp;
- struct tv_mode tv_mode = {};
- int hdisplay = adjusted_mode->crtc_hdisplay;
- int vdisplay = adjusted_mode->crtc_vdisplay;
- int xsize, ysize, xpos, ypos;
- pipe_config->output_types |= BIT(INTEL_OUTPUT_TVOUT);
- pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
- tv_ctl = I915_READ(TV_CTL);
- hctl1 = I915_READ(TV_H_CTL_1);
- hctl3 = I915_READ(TV_H_CTL_3);
- vctl1 = I915_READ(TV_V_CTL_1);
- vctl2 = I915_READ(TV_V_CTL_2);
- tv_mode.htotal = (hctl1 & TV_HTOTAL_MASK) >> TV_HTOTAL_SHIFT;
- tv_mode.hsync_end = (hctl1 & TV_HSYNC_END_MASK) >> TV_HSYNC_END_SHIFT;
- tv_mode.hblank_start = (hctl3 & TV_HBLANK_START_MASK) >> TV_HBLANK_START_SHIFT;
- tv_mode.hblank_end = (hctl3 & TV_HSYNC_END_MASK) >> TV_HBLANK_END_SHIFT;
- tv_mode.nbr_end = (vctl1 & TV_NBR_END_MASK) >> TV_NBR_END_SHIFT;
- tv_mode.vi_end_f1 = (vctl1 & TV_VI_END_F1_MASK) >> TV_VI_END_F1_SHIFT;
- tv_mode.vi_end_f2 = (vctl1 & TV_VI_END_F2_MASK) >> TV_VI_END_F2_SHIFT;
- tv_mode.vsync_len = (vctl2 & TV_VSYNC_LEN_MASK) >> TV_VSYNC_LEN_SHIFT;
- tv_mode.vsync_start_f1 = (vctl2 & TV_VSYNC_START_F1_MASK) >> TV_VSYNC_START_F1_SHIFT;
- tv_mode.vsync_start_f2 = (vctl2 & TV_VSYNC_START_F2_MASK) >> TV_VSYNC_START_F2_SHIFT;
- tv_mode.clock = pipe_config->port_clock;
- tv_mode.progressive = tv_ctl & TV_PROGRESSIVE;
- switch (tv_ctl & TV_OVERSAMPLE_MASK) {
- case TV_OVERSAMPLE_8X:
tv_mode.oversample = 8;
break;
- case TV_OVERSAMPLE_4X:
tv_mode.oversample = 4;
break;
- case TV_OVERSAMPLE_2X:
tv_mode.oversample = 2;
break;
- default:
tv_mode.oversample = 1;
break;
- }
- tmp = I915_READ(TV_WIN_POS);
- xpos = tmp >> 16;
- ypos = tmp & 0xffff;
- tmp = I915_READ(TV_WIN_SIZE);
- xsize = tmp >> 16;
- ysize = tmp & 0xffff;
- intel_tv_mode_to_mode(&mode, &tv_mode);
- DRM_DEBUG_KMS("TV mode:\n");
- drm_mode_debug_printmodeline(&mode);
- intel_tv_scale_mode_horiz(&mode, hdisplay,
xpos, mode.hdisplay - xsize - xpos);
- intel_tv_scale_mode_vert(&mode, vdisplay,
ypos, mode.vdisplay - ysize - ypos);
- adjusted_mode->crtc_clock = mode.clock;
}
static bool @@ -964,6 +1132,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
int hdisplay = adjusted_mode->crtc_hdisplay;
int vdisplay = adjusted_mode->crtc_vdisplay;
if (!tv_mode) return false;
@@ -972,17 +1142,90 @@ intel_tv_compute_config(struct intel_encoder *encoder, return false;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
- adjusted_mode->crtc_clock = tv_mode->clock;
- DRM_DEBUG_KMS("forcing bpc to 8 for TV\n"); pipe_config->pipe_bpp = 8*3;
- /* TV has it's own notion of sync and other mode flags, so clear them. */
- adjusted_mode->flags = 0;
pipe_config->port_clock = tv_mode->clock;
intel_tv_mode_to_mode(adjusted_mode, tv_mode);
DRM_DEBUG_KMS("TV mode:\n");
drm_mode_debug_printmodeline(adjusted_mode);
/*
* FIXME: We don't check whether the input mode is actually what we want
* or whether userspace is doing something stupid.
* The pipe scanline counter behaviour looks as follows when
* using the TV encoder:
*
* time ->
*
* dsl=vtotal-1 | |
* || ||
* ___| | ___| |
* / | / |
* / | / |
* dsl=0 ___/ |_____/ |
* | | | | | |
* ^ ^ ^ ^ ^
* | | | | pipe vblank/first part of tv vblank
* | | | bottom margin
* | | active
* | top margin
* remainder of tv vblank
*
* When the TV encoder is used the pipe wants to run faster
* than expected rate. During the active portion the TV
* encoder stalls the pipe every few lines to keep it in
* check. When the TV encoder reaches the bottom margin the
* pipe simply stops. Once we reach the TV vblank the pipe is
* no longer stalled and it runs at the max rate (apparently
* oversample clock on gen3, cdclk on gen4). Once the pipe
* reaches the pipe vtotal the pipe stops for the remainder
* of the TV vblank/top margin. The pipe starts up again when
* the TV encoder exits the top margin.
*
* To avoid huge hassles for vblank timestamping we scale
* the pipe timings as if the pipe always runs at the average
* rate it maintains during the active period. This also
* gives us a reasonable guesstimate as to the pixel rate.
* Due to the variation in the actual pipe speed the scanline
* counter will give us slightly erroneous results during the
* TV vblank/margins. But since vtotal was selected such that
* it matches the average rate of the pipe during the active
* portion the error shouldn't cause any serious grief to
* vblank timestamps.
Nice rev. enging and description. Was thinking for a while what is the max error the isssue you found adds wrt. the actual vblank timestamp, after your change. AFAIU it's the duration of the flats in the curve, that is max_err=+(bottom margin+remainder of tv vblank+top margin).
Reviewed-by: Imre Deak imre.deak@intel.com
*
* For posterity here is the empirically derived formula
* that gives us the maximum length of the pipe vblank
* we can use without causing display corruption. Following
* this would allow us to have a ticking scanline counter
* everywhere except during the bottom margin (there the
* pipe always stops). Ie. this would eliminate the second
* flat portion of the above graph. However this would also
* complicate vblank timestamping as the pipe vtotal would
* no longer match the average rate the pipe runs at during
* the active portion. Hence following this formula seems
* more trouble that it's worth.
*
* if (IS_GEN4(dev_priv)) {
* num = cdclk * (tv_mode->oversample >> !tv_mode->progressive);
* den = tv_mode->clock;
* } else {
* num = tv_mode->oversample >> !tv_mode->progressive;
* den = 1;
* }
* max_pipe_vblank_len ~=
* (num * tv_htotal * (tv_vblank_len + top_margin)) /
* (den * pipe_htotal);
*/
intel_tv_scale_mode_horiz(adjusted_mode, hdisplay,
conn_state->tv.margins.left,
conn_state->tv.margins.right);
intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
conn_state->tv.margins.top,
conn_state->tv.margins.bottom);
drm_mode_set_crtcinfo(adjusted_mode, 0);
adjusted_mode->name[0] = '\0';
return true;
} @@ -1411,52 +1654,41 @@ intel_tv_set_mode_type(struct drm_display_mode *mode, static int intel_tv_get_modes(struct drm_connector *connector) {
- struct drm_display_mode *mode_ptr; const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
- int j, count = 0;
- u64 tmp;
- int i, count = 0;
- for (j = 0; j < ARRAY_SIZE(input_res_table);
j++) {
const struct input_res *input = &input_res_table[j];
unsigned int hactive_s = input->w;
unsigned int vactive_s = input->h;
if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
continue;
- for (i = 0; i < ARRAY_SIZE(input_res_table); i++) {
const struct input_res *input = &input_res_table[i];
struct drm_display_mode *mode;
if (input->w > 1024 && (!tv_mode->progressive
&& !tv_mode->component_only))
if (input->w > 1024 &&
!tv_mode->progressive &&
!tv_mode->component_only) continue;
mode_ptr = drm_mode_create(connector->dev);
if (!mode_ptr)
mode = drm_mode_create(connector->dev);
if (!mode) continue;
mode_ptr->hdisplay = hactive_s;
mode_ptr->hsync_start = hactive_s + 1;
mode_ptr->hsync_end = hactive_s + 64;
if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
mode_ptr->htotal = hactive_s + 96;
mode_ptr->vdisplay = vactive_s;
mode_ptr->vsync_start = vactive_s + 1;
mode_ptr->vsync_end = vactive_s + 32;
if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
mode_ptr->vsync_end = mode_ptr->vsync_start + 1;
mode_ptr->vtotal = vactive_s + 33;
tmp = mul_u32_u32(tv_mode->refresh, mode_ptr->vtotal);
tmp *= mode_ptr->htotal;
tmp = div_u64(tmp, 1000000);
mode_ptr->clock = (int) tmp;
intel_tv_set_mode_type(mode_ptr, tv_mode);
/*
* We take the TV mode and scale it to look
* like it had the expected h/vdisplay. This
* provides the most information to userspace
* about the actual timings of the mode. We
* do ignore the margins though.
*/
intel_tv_mode_to_mode(mode, tv_mode);
if (count == 0) {
DRM_DEBUG_KMS("TV mode:\n");
drm_mode_debug_printmodeline(mode);
}
intel_tv_scale_mode_horiz(mode, input->w, 0, 0);
intel_tv_scale_mode_vert(mode, input->h, 0, 0);
intel_tv_set_mode_type(mode, tv_mode);
drm_mode_set_name(mode_ptr);
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode_ptr);
count++; }drm_mode_probed_add(connector, mode);
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
On Tue, Jan 22, 2019 at 07:22:24PM +0200, Imre Deak wrote:
On Mon, Nov 12, 2018 at 06:59:58PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
To make vblank timestamps work better with the TV encoder let's scale the pipe timings such that the relationship between the TV active and TV blanking periods is mirrored in the corresponding pipe timings.
Note that in reality the pipe runs at a faster speed during the TV vblank, and correspondigly there are periods when the pipe is enitrely stopped. We pretend that this isn't the case and as such we incur some error in the vblank timestamps during the TV vblank. Further explanation of the issues in a big comment in the code.
This makes the vblank timestamps good enough to make i965gm (which doesn't have a working frame counter with the TV encoder) report correct frame numbers. Previously you could get all kinds of nonsense which resulted in eg. glxgears reporting that it's running at twice the actual framerate in most cases.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_tv.c | 322 +++++++++++++++++++++++++++----- 2 files changed, 278 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fe4b913e46ac..10813ae7bee7 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4848,6 +4848,7 @@ enum { # define TV_OVERSAMPLE_NONE (2 << 18) /* Selects 8x oversampling */ # define TV_OVERSAMPLE_8X (3 << 18) +# define TV_OVERSAMPLE_MASK (3 << 18) /* Selects progressive mode rather than interlaced */ # define TV_PROGRESSIVE (1 << 17) /* Sets the colorburst to PAL mode. Required for non-M PAL modes. */ diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 216525dd144a..75126fce655d 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -340,7 +340,6 @@ struct tv_mode { const struct video_levels *composite_levels, *svideo_levels; const struct color_conversion *composite_color, *svideo_color; const u32 *filter_table;
- u16 max_srcw;
};
@@ -729,7 +728,6 @@ static const struct tv_mode tv_modes[] = { .burst_ena = false,
.filter_table = filter_table,
}, { .name = "1080i@50Hz",.max_srcw = 800
@@ -947,13 +945,183 @@ intel_tv_mode_vdisplay(const struct tv_mode *tv_mode) return 2 * (tv_mode->nbr_end + 1); }
+static void +intel_tv_mode_to_mode(struct drm_display_mode *mode,
const struct tv_mode *tv_mode)
+{
- mode->clock = tv_mode->clock /
(tv_mode->oversample >> !tv_mode->progressive);
- /*
* tv_mode horizontal timings:
*
* hsync_end
* | hblank_end
* | | hblank_start
* | | | htotal
* | _______ |
* ____/ \___
* \__/ \
*/
- mode->hdisplay =
tv_mode->hblank_start - tv_mode->hblank_end;
- mode->hsync_start = mode->hdisplay +
tv_mode->htotal - tv_mode->hblank_start;
- mode->hsync_end = mode->hsync_start +
tv_mode->hsync_end;
- mode->htotal = tv_mode->htotal + 1;
- /*
* tv_mode vertical timings:
*
* vsync_start
* | vsync_end
* | | vi_end nbr_end
* | | | |
* | | _______
* \__ ____/ \
* \__/
*/
- mode->vdisplay = intel_tv_mode_vdisplay(tv_mode);
- if (tv_mode->progressive) {
mode->vsync_start = mode->vdisplay +
tv_mode->vsync_start_f1 + 1;
mode->vsync_end = mode->vsync_start +
tv_mode->vsync_len;
mode->vtotal = mode->vdisplay +
tv_mode->vi_end_f1 + 1;
- } else {
mode->vsync_start = mode->vdisplay +
tv_mode->vsync_start_f1 + 1 +
tv_mode->vsync_start_f2 + 1;
mode->vsync_end = mode->vsync_start +
2 * tv_mode->vsync_len;
mode->vtotal = mode->vdisplay +
tv_mode->vi_end_f1 + 1 +
tv_mode->vi_end_f2 + 1;
- }
- /* TV has it's own notion of sync and other mode flags, so clear them. */
- mode->flags = 0;
- mode->vrefresh = 0;
Redundant line.
drm_mode_vrefresh() won't recompute the value if it's already set. So unless we clear it first this would effectively become 'mode->vrefresh = mode->vrefresh'.
The whole mode->vrefesh story is a bit sad, and someone should really rework how it all works.
- mode->vrefresh = drm_mode_vrefresh(mode);
- snprintf(mode->name, sizeof(mode->name),
"%dx%d%c (%s)",
mode->hdisplay, mode->vdisplay,
tv_mode->progressive ? 'p' : 'i',
tv_mode->name);
+}
+static void intel_tv_scale_mode_horiz(struct drm_display_mode *mode,
int hdisplay, int left_margin,
int right_margin)
+{
- int hsync_start = mode->hsync_start - mode->hdisplay + right_margin;
- int hsync_end = mode->hsync_end - mode->hdisplay + right_margin;
- int new_htotal = mode->htotal * hdisplay /
(mode->hdisplay - left_margin - right_margin);
- mode->clock = mode->clock * new_htotal / mode->htotal;
- mode->hdisplay = hdisplay;
- mode->hsync_start = hdisplay + hsync_start * new_htotal / mode->htotal;
- mode->hsync_end = hdisplay + hsync_end * new_htotal / mode->htotal;
- mode->htotal = new_htotal;
+}
+static void intel_tv_scale_mode_vert(struct drm_display_mode *mode,
int vdisplay, int top_margin,
int bottom_margin)
+{
- int vsync_start = mode->vsync_start - mode->vdisplay + bottom_margin;
- int vsync_end = mode->vsync_end - mode->vdisplay + bottom_margin;
- int new_vtotal = mode->vtotal * vdisplay /
(mode->vdisplay - top_margin - bottom_margin);
- mode->clock = mode->clock * new_vtotal / mode->vtotal;
- mode->vdisplay = vdisplay;
- mode->vsync_start = vdisplay + vsync_start * new_vtotal / mode->vtotal;
- mode->vsync_end = vdisplay + vsync_end * new_vtotal / mode->vtotal;
- mode->vtotal = new_vtotal;
+}
static void intel_tv_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) {
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct drm_display_mode *adjusted_mode =
&pipe_config->base.adjusted_mode;
- struct drm_display_mode mode = {};
- u32 tv_ctl, hctl1, hctl3, vctl1, vctl2, tmp;
- struct tv_mode tv_mode = {};
- int hdisplay = adjusted_mode->crtc_hdisplay;
- int vdisplay = adjusted_mode->crtc_vdisplay;
- int xsize, ysize, xpos, ypos;
- pipe_config->output_types |= BIT(INTEL_OUTPUT_TVOUT);
- pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
- tv_ctl = I915_READ(TV_CTL);
- hctl1 = I915_READ(TV_H_CTL_1);
- hctl3 = I915_READ(TV_H_CTL_3);
- vctl1 = I915_READ(TV_V_CTL_1);
- vctl2 = I915_READ(TV_V_CTL_2);
- tv_mode.htotal = (hctl1 & TV_HTOTAL_MASK) >> TV_HTOTAL_SHIFT;
- tv_mode.hsync_end = (hctl1 & TV_HSYNC_END_MASK) >> TV_HSYNC_END_SHIFT;
- tv_mode.hblank_start = (hctl3 & TV_HBLANK_START_MASK) >> TV_HBLANK_START_SHIFT;
- tv_mode.hblank_end = (hctl3 & TV_HSYNC_END_MASK) >> TV_HBLANK_END_SHIFT;
- tv_mode.nbr_end = (vctl1 & TV_NBR_END_MASK) >> TV_NBR_END_SHIFT;
- tv_mode.vi_end_f1 = (vctl1 & TV_VI_END_F1_MASK) >> TV_VI_END_F1_SHIFT;
- tv_mode.vi_end_f2 = (vctl1 & TV_VI_END_F2_MASK) >> TV_VI_END_F2_SHIFT;
- tv_mode.vsync_len = (vctl2 & TV_VSYNC_LEN_MASK) >> TV_VSYNC_LEN_SHIFT;
- tv_mode.vsync_start_f1 = (vctl2 & TV_VSYNC_START_F1_MASK) >> TV_VSYNC_START_F1_SHIFT;
- tv_mode.vsync_start_f2 = (vctl2 & TV_VSYNC_START_F2_MASK) >> TV_VSYNC_START_F2_SHIFT;
- tv_mode.clock = pipe_config->port_clock;
- tv_mode.progressive = tv_ctl & TV_PROGRESSIVE;
- switch (tv_ctl & TV_OVERSAMPLE_MASK) {
- case TV_OVERSAMPLE_8X:
tv_mode.oversample = 8;
break;
- case TV_OVERSAMPLE_4X:
tv_mode.oversample = 4;
break;
- case TV_OVERSAMPLE_2X:
tv_mode.oversample = 2;
break;
- default:
tv_mode.oversample = 1;
break;
- }
- tmp = I915_READ(TV_WIN_POS);
- xpos = tmp >> 16;
- ypos = tmp & 0xffff;
- tmp = I915_READ(TV_WIN_SIZE);
- xsize = tmp >> 16;
- ysize = tmp & 0xffff;
- intel_tv_mode_to_mode(&mode, &tv_mode);
- DRM_DEBUG_KMS("TV mode:\n");
- drm_mode_debug_printmodeline(&mode);
- intel_tv_scale_mode_horiz(&mode, hdisplay,
xpos, mode.hdisplay - xsize - xpos);
- intel_tv_scale_mode_vert(&mode, vdisplay,
ypos, mode.vdisplay - ysize - ypos);
- adjusted_mode->crtc_clock = mode.clock;
}
static bool @@ -964,6 +1132,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
int hdisplay = adjusted_mode->crtc_hdisplay;
int vdisplay = adjusted_mode->crtc_vdisplay;
if (!tv_mode) return false;
@@ -972,17 +1142,90 @@ intel_tv_compute_config(struct intel_encoder *encoder, return false;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
- adjusted_mode->crtc_clock = tv_mode->clock;
- DRM_DEBUG_KMS("forcing bpc to 8 for TV\n"); pipe_config->pipe_bpp = 8*3;
- /* TV has it's own notion of sync and other mode flags, so clear them. */
- adjusted_mode->flags = 0;
pipe_config->port_clock = tv_mode->clock;
intel_tv_mode_to_mode(adjusted_mode, tv_mode);
DRM_DEBUG_KMS("TV mode:\n");
drm_mode_debug_printmodeline(adjusted_mode);
/*
* FIXME: We don't check whether the input mode is actually what we want
* or whether userspace is doing something stupid.
* The pipe scanline counter behaviour looks as follows when
* using the TV encoder:
*
* time ->
*
* dsl=vtotal-1 | |
* || ||
* ___| | ___| |
* / | / |
* / | / |
* dsl=0 ___/ |_____/ |
* | | | | | |
* ^ ^ ^ ^ ^
* | | | | pipe vblank/first part of tv vblank
* | | | bottom margin
* | | active
* | top margin
* remainder of tv vblank
*
* When the TV encoder is used the pipe wants to run faster
* than expected rate. During the active portion the TV
* encoder stalls the pipe every few lines to keep it in
* check. When the TV encoder reaches the bottom margin the
* pipe simply stops. Once we reach the TV vblank the pipe is
* no longer stalled and it runs at the max rate (apparently
* oversample clock on gen3, cdclk on gen4). Once the pipe
* reaches the pipe vtotal the pipe stops for the remainder
* of the TV vblank/top margin. The pipe starts up again when
* the TV encoder exits the top margin.
*
* To avoid huge hassles for vblank timestamping we scale
* the pipe timings as if the pipe always runs at the average
* rate it maintains during the active period. This also
* gives us a reasonable guesstimate as to the pixel rate.
* Due to the variation in the actual pipe speed the scanline
* counter will give us slightly erroneous results during the
* TV vblank/margins. But since vtotal was selected such that
* it matches the average rate of the pipe during the active
* portion the error shouldn't cause any serious grief to
* vblank timestamps.
Nice rev. enging and description. Was thinking for a while what is the max error the isssue you found adds wrt. the actual vblank timestamp, after your change. AFAIU it's the duration of the flats in the curve, that is max_err=+(bottom margin+remainder of tv vblank+top margin).
Reviewed-by: Imre Deak imre.deak@intel.com
*
* For posterity here is the empirically derived formula
* that gives us the maximum length of the pipe vblank
* we can use without causing display corruption. Following
* this would allow us to have a ticking scanline counter
* everywhere except during the bottom margin (there the
* pipe always stops). Ie. this would eliminate the second
* flat portion of the above graph. However this would also
* complicate vblank timestamping as the pipe vtotal would
* no longer match the average rate the pipe runs at during
* the active portion. Hence following this formula seems
* more trouble that it's worth.
*
* if (IS_GEN4(dev_priv)) {
* num = cdclk * (tv_mode->oversample >> !tv_mode->progressive);
* den = tv_mode->clock;
* } else {
* num = tv_mode->oversample >> !tv_mode->progressive;
* den = 1;
* }
* max_pipe_vblank_len ~=
* (num * tv_htotal * (tv_vblank_len + top_margin)) /
* (den * pipe_htotal);
*/
intel_tv_scale_mode_horiz(adjusted_mode, hdisplay,
conn_state->tv.margins.left,
conn_state->tv.margins.right);
intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
conn_state->tv.margins.top,
conn_state->tv.margins.bottom);
drm_mode_set_crtcinfo(adjusted_mode, 0);
adjusted_mode->name[0] = '\0';
return true;
} @@ -1411,52 +1654,41 @@ intel_tv_set_mode_type(struct drm_display_mode *mode, static int intel_tv_get_modes(struct drm_connector *connector) {
- struct drm_display_mode *mode_ptr; const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
- int j, count = 0;
- u64 tmp;
- int i, count = 0;
- for (j = 0; j < ARRAY_SIZE(input_res_table);
j++) {
const struct input_res *input = &input_res_table[j];
unsigned int hactive_s = input->w;
unsigned int vactive_s = input->h;
if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
continue;
- for (i = 0; i < ARRAY_SIZE(input_res_table); i++) {
const struct input_res *input = &input_res_table[i];
struct drm_display_mode *mode;
if (input->w > 1024 && (!tv_mode->progressive
&& !tv_mode->component_only))
if (input->w > 1024 &&
!tv_mode->progressive &&
!tv_mode->component_only) continue;
mode_ptr = drm_mode_create(connector->dev);
if (!mode_ptr)
mode = drm_mode_create(connector->dev);
if (!mode) continue;
mode_ptr->hdisplay = hactive_s;
mode_ptr->hsync_start = hactive_s + 1;
mode_ptr->hsync_end = hactive_s + 64;
if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
mode_ptr->htotal = hactive_s + 96;
mode_ptr->vdisplay = vactive_s;
mode_ptr->vsync_start = vactive_s + 1;
mode_ptr->vsync_end = vactive_s + 32;
if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
mode_ptr->vsync_end = mode_ptr->vsync_start + 1;
mode_ptr->vtotal = vactive_s + 33;
tmp = mul_u32_u32(tv_mode->refresh, mode_ptr->vtotal);
tmp *= mode_ptr->htotal;
tmp = div_u64(tmp, 1000000);
mode_ptr->clock = (int) tmp;
intel_tv_set_mode_type(mode_ptr, tv_mode);
/*
* We take the TV mode and scale it to look
* like it had the expected h/vdisplay. This
* provides the most information to userspace
* about the actual timings of the mode. We
* do ignore the margins though.
*/
intel_tv_mode_to_mode(mode, tv_mode);
if (count == 0) {
DRM_DEBUG_KMS("TV mode:\n");
drm_mode_debug_printmodeline(mode);
}
intel_tv_scale_mode_horiz(mode, input->w, 0, 0);
intel_tv_scale_mode_vert(mode, input->h, 0, 0);
intel_tv_set_mode_type(mode, tv_mode);
drm_mode_set_name(mode_ptr);
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode_ptr);
count++; }drm_mode_probed_add(connector, mode);
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
On Tue, Jan 22, 2019 at 07:34:55PM +0200, Ville Syrjälä wrote:
On Tue, Jan 22, 2019 at 07:22:24PM +0200, Imre Deak wrote:
On Mon, Nov 12, 2018 at 06:59:58PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
To make vblank timestamps work better with the TV encoder let's scale the pipe timings such that the relationship between the TV active and TV blanking periods is mirrored in the corresponding pipe timings.
Note that in reality the pipe runs at a faster speed during the TV vblank, and correspondigly there are periods when the pipe is enitrely stopped. We pretend that this isn't the case and as such we incur some error in the vblank timestamps during the TV vblank. Further explanation of the issues in a big comment in the code.
This makes the vblank timestamps good enough to make i965gm (which doesn't have a working frame counter with the TV encoder) report correct frame numbers. Previously you could get all kinds of nonsense which resulted in eg. glxgears reporting that it's running at twice the actual framerate in most cases.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_tv.c | 322 +++++++++++++++++++++++++++----- 2 files changed, 278 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fe4b913e46ac..10813ae7bee7 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4848,6 +4848,7 @@ enum { # define TV_OVERSAMPLE_NONE (2 << 18) /* Selects 8x oversampling */ # define TV_OVERSAMPLE_8X (3 << 18) +# define TV_OVERSAMPLE_MASK (3 << 18) /* Selects progressive mode rather than interlaced */ # define TV_PROGRESSIVE (1 << 17) /* Sets the colorburst to PAL mode. Required for non-M PAL modes. */ diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 216525dd144a..75126fce655d 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -340,7 +340,6 @@ struct tv_mode { const struct video_levels *composite_levels, *svideo_levels; const struct color_conversion *composite_color, *svideo_color; const u32 *filter_table;
- u16 max_srcw;
};
@@ -729,7 +728,6 @@ static const struct tv_mode tv_modes[] = { .burst_ena = false,
.filter_table = filter_table,
}, { .name = "1080i@50Hz",.max_srcw = 800
@@ -947,13 +945,183 @@ intel_tv_mode_vdisplay(const struct tv_mode *tv_mode) return 2 * (tv_mode->nbr_end + 1); }
+static void +intel_tv_mode_to_mode(struct drm_display_mode *mode,
const struct tv_mode *tv_mode)
+{
- mode->clock = tv_mode->clock /
(tv_mode->oversample >> !tv_mode->progressive);
- /*
* tv_mode horizontal timings:
*
* hsync_end
* | hblank_end
* | | hblank_start
* | | | htotal
* | _______ |
* ____/ \___
* \__/ \
*/
- mode->hdisplay =
tv_mode->hblank_start - tv_mode->hblank_end;
- mode->hsync_start = mode->hdisplay +
tv_mode->htotal - tv_mode->hblank_start;
- mode->hsync_end = mode->hsync_start +
tv_mode->hsync_end;
- mode->htotal = tv_mode->htotal + 1;
- /*
* tv_mode vertical timings:
*
* vsync_start
* | vsync_end
* | | vi_end nbr_end
* | | | |
* | | _______
* \__ ____/ \
* \__/
*/
- mode->vdisplay = intel_tv_mode_vdisplay(tv_mode);
- if (tv_mode->progressive) {
mode->vsync_start = mode->vdisplay +
tv_mode->vsync_start_f1 + 1;
mode->vsync_end = mode->vsync_start +
tv_mode->vsync_len;
mode->vtotal = mode->vdisplay +
tv_mode->vi_end_f1 + 1;
- } else {
mode->vsync_start = mode->vdisplay +
tv_mode->vsync_start_f1 + 1 +
tv_mode->vsync_start_f2 + 1;
mode->vsync_end = mode->vsync_start +
2 * tv_mode->vsync_len;
mode->vtotal = mode->vdisplay +
tv_mode->vi_end_f1 + 1 +
tv_mode->vi_end_f2 + 1;
- }
- /* TV has it's own notion of sync and other mode flags, so clear them. */
- mode->flags = 0;
- mode->vrefresh = 0;
Redundant line.
drm_mode_vrefresh() won't recompute the value if it's already set. So unless we clear it first this would effectively become 'mode->vrefresh = mode->vrefresh'.
Oops, correct.
The whole mode->vrefesh story is a bit sad, and someone should really rework how it all works.
Yea, would be nice.
- mode->vrefresh = drm_mode_vrefresh(mode);
- snprintf(mode->name, sizeof(mode->name),
"%dx%d%c (%s)",
mode->hdisplay, mode->vdisplay,
tv_mode->progressive ? 'p' : 'i',
tv_mode->name);
+}
+static void intel_tv_scale_mode_horiz(struct drm_display_mode *mode,
int hdisplay, int left_margin,
int right_margin)
+{
- int hsync_start = mode->hsync_start - mode->hdisplay + right_margin;
- int hsync_end = mode->hsync_end - mode->hdisplay + right_margin;
- int new_htotal = mode->htotal * hdisplay /
(mode->hdisplay - left_margin - right_margin);
- mode->clock = mode->clock * new_htotal / mode->htotal;
- mode->hdisplay = hdisplay;
- mode->hsync_start = hdisplay + hsync_start * new_htotal / mode->htotal;
- mode->hsync_end = hdisplay + hsync_end * new_htotal / mode->htotal;
- mode->htotal = new_htotal;
+}
+static void intel_tv_scale_mode_vert(struct drm_display_mode *mode,
int vdisplay, int top_margin,
int bottom_margin)
+{
- int vsync_start = mode->vsync_start - mode->vdisplay + bottom_margin;
- int vsync_end = mode->vsync_end - mode->vdisplay + bottom_margin;
- int new_vtotal = mode->vtotal * vdisplay /
(mode->vdisplay - top_margin - bottom_margin);
- mode->clock = mode->clock * new_vtotal / mode->vtotal;
- mode->vdisplay = vdisplay;
- mode->vsync_start = vdisplay + vsync_start * new_vtotal / mode->vtotal;
- mode->vsync_end = vdisplay + vsync_end * new_vtotal / mode->vtotal;
- mode->vtotal = new_vtotal;
+}
static void intel_tv_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) {
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct drm_display_mode *adjusted_mode =
&pipe_config->base.adjusted_mode;
- struct drm_display_mode mode = {};
- u32 tv_ctl, hctl1, hctl3, vctl1, vctl2, tmp;
- struct tv_mode tv_mode = {};
- int hdisplay = adjusted_mode->crtc_hdisplay;
- int vdisplay = adjusted_mode->crtc_vdisplay;
- int xsize, ysize, xpos, ypos;
- pipe_config->output_types |= BIT(INTEL_OUTPUT_TVOUT);
- pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
- tv_ctl = I915_READ(TV_CTL);
- hctl1 = I915_READ(TV_H_CTL_1);
- hctl3 = I915_READ(TV_H_CTL_3);
- vctl1 = I915_READ(TV_V_CTL_1);
- vctl2 = I915_READ(TV_V_CTL_2);
- tv_mode.htotal = (hctl1 & TV_HTOTAL_MASK) >> TV_HTOTAL_SHIFT;
- tv_mode.hsync_end = (hctl1 & TV_HSYNC_END_MASK) >> TV_HSYNC_END_SHIFT;
- tv_mode.hblank_start = (hctl3 & TV_HBLANK_START_MASK) >> TV_HBLANK_START_SHIFT;
- tv_mode.hblank_end = (hctl3 & TV_HSYNC_END_MASK) >> TV_HBLANK_END_SHIFT;
- tv_mode.nbr_end = (vctl1 & TV_NBR_END_MASK) >> TV_NBR_END_SHIFT;
- tv_mode.vi_end_f1 = (vctl1 & TV_VI_END_F1_MASK) >> TV_VI_END_F1_SHIFT;
- tv_mode.vi_end_f2 = (vctl1 & TV_VI_END_F2_MASK) >> TV_VI_END_F2_SHIFT;
- tv_mode.vsync_len = (vctl2 & TV_VSYNC_LEN_MASK) >> TV_VSYNC_LEN_SHIFT;
- tv_mode.vsync_start_f1 = (vctl2 & TV_VSYNC_START_F1_MASK) >> TV_VSYNC_START_F1_SHIFT;
- tv_mode.vsync_start_f2 = (vctl2 & TV_VSYNC_START_F2_MASK) >> TV_VSYNC_START_F2_SHIFT;
- tv_mode.clock = pipe_config->port_clock;
- tv_mode.progressive = tv_ctl & TV_PROGRESSIVE;
- switch (tv_ctl & TV_OVERSAMPLE_MASK) {
- case TV_OVERSAMPLE_8X:
tv_mode.oversample = 8;
break;
- case TV_OVERSAMPLE_4X:
tv_mode.oversample = 4;
break;
- case TV_OVERSAMPLE_2X:
tv_mode.oversample = 2;
break;
- default:
tv_mode.oversample = 1;
break;
- }
- tmp = I915_READ(TV_WIN_POS);
- xpos = tmp >> 16;
- ypos = tmp & 0xffff;
- tmp = I915_READ(TV_WIN_SIZE);
- xsize = tmp >> 16;
- ysize = tmp & 0xffff;
- intel_tv_mode_to_mode(&mode, &tv_mode);
- DRM_DEBUG_KMS("TV mode:\n");
- drm_mode_debug_printmodeline(&mode);
- intel_tv_scale_mode_horiz(&mode, hdisplay,
xpos, mode.hdisplay - xsize - xpos);
- intel_tv_scale_mode_vert(&mode, vdisplay,
ypos, mode.vdisplay - ysize - ypos);
- adjusted_mode->crtc_clock = mode.clock;
}
static bool @@ -964,6 +1132,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
int hdisplay = adjusted_mode->crtc_hdisplay;
int vdisplay = adjusted_mode->crtc_vdisplay;
if (!tv_mode) return false;
@@ -972,17 +1142,90 @@ intel_tv_compute_config(struct intel_encoder *encoder, return false;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
- adjusted_mode->crtc_clock = tv_mode->clock;
- DRM_DEBUG_KMS("forcing bpc to 8 for TV\n"); pipe_config->pipe_bpp = 8*3;
- /* TV has it's own notion of sync and other mode flags, so clear them. */
- adjusted_mode->flags = 0;
pipe_config->port_clock = tv_mode->clock;
intel_tv_mode_to_mode(adjusted_mode, tv_mode);
DRM_DEBUG_KMS("TV mode:\n");
drm_mode_debug_printmodeline(adjusted_mode);
/*
* FIXME: We don't check whether the input mode is actually what we want
* or whether userspace is doing something stupid.
* The pipe scanline counter behaviour looks as follows when
* using the TV encoder:
*
* time ->
*
* dsl=vtotal-1 | |
* || ||
* ___| | ___| |
* / | / |
* / | / |
* dsl=0 ___/ |_____/ |
* | | | | | |
* ^ ^ ^ ^ ^
* | | | | pipe vblank/first part of tv vblank
* | | | bottom margin
* | | active
* | top margin
* remainder of tv vblank
*
* When the TV encoder is used the pipe wants to run faster
* than expected rate. During the active portion the TV
* encoder stalls the pipe every few lines to keep it in
* check. When the TV encoder reaches the bottom margin the
* pipe simply stops. Once we reach the TV vblank the pipe is
* no longer stalled and it runs at the max rate (apparently
* oversample clock on gen3, cdclk on gen4). Once the pipe
* reaches the pipe vtotal the pipe stops for the remainder
* of the TV vblank/top margin. The pipe starts up again when
* the TV encoder exits the top margin.
*
* To avoid huge hassles for vblank timestamping we scale
* the pipe timings as if the pipe always runs at the average
* rate it maintains during the active period. This also
* gives us a reasonable guesstimate as to the pixel rate.
* Due to the variation in the actual pipe speed the scanline
* counter will give us slightly erroneous results during the
* TV vblank/margins. But since vtotal was selected such that
* it matches the average rate of the pipe during the active
* portion the error shouldn't cause any serious grief to
* vblank timestamps.
Nice rev. enging and description. Was thinking for a while what is the max error the isssue you found adds wrt. the actual vblank timestamp, after your change. AFAIU it's the duration of the flats in the curve, that is max_err=+(bottom margin+remainder of tv vblank+top margin).
Reviewed-by: Imre Deak imre.deak@intel.com
*
* For posterity here is the empirically derived formula
* that gives us the maximum length of the pipe vblank
* we can use without causing display corruption. Following
* this would allow us to have a ticking scanline counter
* everywhere except during the bottom margin (there the
* pipe always stops). Ie. this would eliminate the second
* flat portion of the above graph. However this would also
* complicate vblank timestamping as the pipe vtotal would
* no longer match the average rate the pipe runs at during
* the active portion. Hence following this formula seems
* more trouble that it's worth.
*
* if (IS_GEN4(dev_priv)) {
* num = cdclk * (tv_mode->oversample >> !tv_mode->progressive);
* den = tv_mode->clock;
* } else {
* num = tv_mode->oversample >> !tv_mode->progressive;
* den = 1;
* }
* max_pipe_vblank_len ~=
* (num * tv_htotal * (tv_vblank_len + top_margin)) /
* (den * pipe_htotal);
*/
intel_tv_scale_mode_horiz(adjusted_mode, hdisplay,
conn_state->tv.margins.left,
conn_state->tv.margins.right);
intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
conn_state->tv.margins.top,
conn_state->tv.margins.bottom);
drm_mode_set_crtcinfo(adjusted_mode, 0);
adjusted_mode->name[0] = '\0';
return true;
} @@ -1411,52 +1654,41 @@ intel_tv_set_mode_type(struct drm_display_mode *mode, static int intel_tv_get_modes(struct drm_connector *connector) {
- struct drm_display_mode *mode_ptr; const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
- int j, count = 0;
- u64 tmp;
- int i, count = 0;
- for (j = 0; j < ARRAY_SIZE(input_res_table);
j++) {
const struct input_res *input = &input_res_table[j];
unsigned int hactive_s = input->w;
unsigned int vactive_s = input->h;
if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
continue;
- for (i = 0; i < ARRAY_SIZE(input_res_table); i++) {
const struct input_res *input = &input_res_table[i];
struct drm_display_mode *mode;
if (input->w > 1024 && (!tv_mode->progressive
&& !tv_mode->component_only))
if (input->w > 1024 &&
!tv_mode->progressive &&
!tv_mode->component_only) continue;
mode_ptr = drm_mode_create(connector->dev);
if (!mode_ptr)
mode = drm_mode_create(connector->dev);
if (!mode) continue;
mode_ptr->hdisplay = hactive_s;
mode_ptr->hsync_start = hactive_s + 1;
mode_ptr->hsync_end = hactive_s + 64;
if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
mode_ptr->htotal = hactive_s + 96;
mode_ptr->vdisplay = vactive_s;
mode_ptr->vsync_start = vactive_s + 1;
mode_ptr->vsync_end = vactive_s + 32;
if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
mode_ptr->vsync_end = mode_ptr->vsync_start + 1;
mode_ptr->vtotal = vactive_s + 33;
tmp = mul_u32_u32(tv_mode->refresh, mode_ptr->vtotal);
tmp *= mode_ptr->htotal;
tmp = div_u64(tmp, 1000000);
mode_ptr->clock = (int) tmp;
intel_tv_set_mode_type(mode_ptr, tv_mode);
/*
* We take the TV mode and scale it to look
* like it had the expected h/vdisplay. This
* provides the most information to userspace
* about the actual timings of the mode. We
* do ignore the margins though.
*/
intel_tv_mode_to_mode(mode, tv_mode);
if (count == 0) {
DRM_DEBUG_KMS("TV mode:\n");
drm_mode_debug_printmodeline(mode);
}
intel_tv_scale_mode_horiz(mode, input->w, 0, 0);
intel_tv_scale_mode_vert(mode, input->h, 0, 0);
intel_tv_set_mode_type(mode, tv_mode);
drm_mode_set_name(mode_ptr);
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode_ptr);
count++; }drm_mode_probed_add(connector, mode);
-- 2.18.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
-- Ville Syrjälä Intel
From: Ville Syrjälä ville.syrjala@linux.intel.com
On gen3 we must disable the TV encoder vertical filter for >1024 pixel wide sources. Once that's done all we can is try to center the image on the screen. Naturally the TV mode vertical resolution must be equal or larger than the user mode vertical resolution or else we'd have to cut off part of the user mode.
And while we may not be able to respect the user's choice of top and bottom borders exactly (or we'd have to reject he mode most likely), we can try to maintain the relative sizes of the top and bottom border with respect to each orher.
Additionally we must configure the pipe as interlaced if the TV mode is interlaced.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 100 +++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 75126fce655d..7099d837e31a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -861,6 +861,44 @@ static const struct tv_mode tv_modes[] = { }, };
+struct intel_tv_connector_state { + struct drm_connector_state base; + + /* + * May need to override the user margins for + * gen3 >1024 wide source vertical centering. + */ + struct { + u16 top, bottom; + } margins; + + bool bypass_vfilter; +}; + +#define to_intel_tv_connector_state(x) container_of(x, struct intel_tv_connector_state, base) + +/** + * intel_digital_connector_duplicate_state - duplicate connector state + * @connector: digital connector + * + * Allocates and returns a copy of the connector state (both common and + * digital connector specific) for the specified connector. + * + * Returns: The newly allocated connector state, or NULL on failure. + */ +struct drm_connector_state * +intel_tv_connector_duplicate_state(struct drm_connector *connector) +{ + struct intel_tv_connector_state *state; + + state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_connector_duplicate_state(connector, &state->base); + return &state->base; +} + static struct intel_tv *enc_to_tv(struct intel_encoder *encoder) { return container_of(encoder, struct intel_tv, base); @@ -1129,6 +1167,9 @@ intel_tv_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_tv_connector_state *tv_conn_state = + to_intel_tv_connector_state(conn_state); const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; @@ -1149,6 +1190,43 @@ intel_tv_compute_config(struct intel_encoder *encoder, pipe_config->port_clock = tv_mode->clock;
intel_tv_mode_to_mode(adjusted_mode, tv_mode); + drm_mode_set_crtcinfo(adjusted_mode, 0); + + if (IS_GEN3(dev_priv) && hdisplay > 1024) { + int extra, top, bottom; + + extra = adjusted_mode->crtc_vdisplay - vdisplay; + + if (extra < 0) { + DRM_DEBUG_KMS("No vertical scaling for >1024 pixel wide modes\n"); + return false; + } + + /* Need to turn off the vertical filter and center the image */ + + /* Attempt to maintain the relative sizes of the margins */ + top = conn_state->tv.margins.top; + bottom = conn_state->tv.margins.bottom; + + if (top + bottom) + top = extra * top / (top + bottom); + else + top = extra / 2; + bottom = extra - top; + + tv_conn_state->margins.top = top; + tv_conn_state->margins.bottom = bottom; + + tv_conn_state->bypass_vfilter = true; + + if (!tv_mode->progressive) + adjusted_mode->flags |= DRM_MODE_FLAG_INTERLACE; + } else { + tv_conn_state->margins.top = conn_state->tv.margins.top; + tv_conn_state->margins.bottom = conn_state->tv.margins.bottom; + + tv_conn_state->bypass_vfilter = false; + }
DRM_DEBUG_KMS("TV mode:\n"); drm_mode_debug_printmodeline(adjusted_mode); @@ -1222,8 +1300,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, conn_state->tv.margins.left, conn_state->tv.margins.right); intel_tv_scale_mode_vert(adjusted_mode, vdisplay, - conn_state->tv.margins.top, - conn_state->tv.margins.bottom); + tv_conn_state->margins.top, + tv_conn_state->margins.bottom); drm_mode_set_crtcinfo(adjusted_mode, 0); adjusted_mode->name[0] = '\0';
@@ -1316,8 +1394,10 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc); struct intel_tv *intel_tv = enc_to_tv(encoder); + const struct intel_tv_connector_state *tv_conn_state = + to_intel_tv_connector_state(conn_state); const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); - u32 tv_ctl; + u32 tv_ctl, tv_filter_ctl; u32 scctl1, scctl2, scctl3; int i, j; const struct video_levels *video_levels; @@ -1425,16 +1505,20 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, assert_pipe_disabled(dev_priv, intel_crtc->pipe);
/* Filter ctl must be set before TV_WIN_SIZE */ - I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE); + tv_filter_ctl = TV_AUTO_SCALE; + if (tv_conn_state->bypass_vfilter) + tv_filter_ctl |= TV_V_FILTER_BYPASS; + I915_WRITE(TV_FILTER_CTL_1, tv_filter_ctl); + xsize = tv_mode->hblank_start - tv_mode->hblank_end; ysize = intel_tv_mode_vdisplay(tv_mode);
xpos = conn_state->tv.margins.left; - ypos = conn_state->tv.margins.top; + ypos = tv_conn_state->margins.top; xsize -= (conn_state->tv.margins.left + conn_state->tv.margins.right); - ysize -= (conn_state->tv.margins.top + - conn_state->tv.margins.bottom); + ysize -= (tv_conn_state->margins.top + + tv_conn_state->margins.bottom); I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
@@ -1701,7 +1785,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { .destroy = intel_connector_destroy, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_duplicate_state = intel_tv_connector_duplicate_state, };
static int intel_tv_atomic_check(struct drm_connector *connector,
On Mon, Nov 12, 2018 at 06:59:59PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On gen3 we must disable the TV encoder vertical filter for >1024 pixel wide sources. Once that's done all we can is try to center the image on the screen. Naturally the TV mode vertical resolution must be equal or larger than the user mode vertical resolution or else we'd have to cut off part of the user mode.
And while we may not be able to respect the user's choice of top and bottom borders exactly (or we'd have to reject he mode most likely), we can try to maintain the relative sizes of the top and bottom border with respect to each orher.
Additionally we must configure the pipe as interlaced if the TV mode is interlaced.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/intel_tv.c | 100 +++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 75126fce655d..7099d837e31a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -861,6 +861,44 @@ static const struct tv_mode tv_modes[] = { }, };
+struct intel_tv_connector_state {
- struct drm_connector_state base;
- /*
* May need to override the user margins for
* gen3 >1024 wide source vertical centering.
*/
- struct {
u16 top, bottom;
- } margins;
- bool bypass_vfilter;
+};
+#define to_intel_tv_connector_state(x) container_of(x, struct intel_tv_connector_state, base)
+/**
- intel_digital_connector_duplicate_state - duplicate connector state
^intel_tv_connector_duplicate_state
- @connector: digital connector
^tv connector?
- Allocates and returns a copy of the connector state (both common and
- digital connector specific) for the specified connector.
- Returns: The newly allocated connector state, or NULL on failure.
- */
+struct drm_connector_state * +intel_tv_connector_duplicate_state(struct drm_connector *connector) +{
- struct intel_tv_connector_state *state;
- state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
- if (!state)
return NULL;
- __drm_atomic_helper_connector_duplicate_state(connector, &state->base);
- return &state->base;
+}
You didn't add the corresponding checks for the new intel_tv_connector_state fields to intel_tv_atomic_check(). I suppose that's ok since something resulting in a change in those will force a modeset anyway:
Reviewed-by: Imre Deak imre.deak@intel.com
static struct intel_tv *enc_to_tv(struct intel_encoder *encoder) { return container_of(encoder, struct intel_tv, base); @@ -1129,6 +1167,9 @@ intel_tv_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) {
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_tv_connector_state *tv_conn_state =
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;to_intel_tv_connector_state(conn_state);
@@ -1149,6 +1190,43 @@ intel_tv_compute_config(struct intel_encoder *encoder, pipe_config->port_clock = tv_mode->clock;
intel_tv_mode_to_mode(adjusted_mode, tv_mode);
drm_mode_set_crtcinfo(adjusted_mode, 0);
if (IS_GEN3(dev_priv) && hdisplay > 1024) {
int extra, top, bottom;
extra = adjusted_mode->crtc_vdisplay - vdisplay;
if (extra < 0) {
DRM_DEBUG_KMS("No vertical scaling for >1024 pixel wide modes\n");
return false;
}
/* Need to turn off the vertical filter and center the image */
/* Attempt to maintain the relative sizes of the margins */
top = conn_state->tv.margins.top;
bottom = conn_state->tv.margins.bottom;
if (top + bottom)
top = extra * top / (top + bottom);
else
top = extra / 2;
bottom = extra - top;
tv_conn_state->margins.top = top;
tv_conn_state->margins.bottom = bottom;
tv_conn_state->bypass_vfilter = true;
if (!tv_mode->progressive)
adjusted_mode->flags |= DRM_MODE_FLAG_INTERLACE;
} else {
tv_conn_state->margins.top = conn_state->tv.margins.top;
tv_conn_state->margins.bottom = conn_state->tv.margins.bottom;
tv_conn_state->bypass_vfilter = false;
}
DRM_DEBUG_KMS("TV mode:\n"); drm_mode_debug_printmodeline(adjusted_mode);
@@ -1222,8 +1300,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, conn_state->tv.margins.left, conn_state->tv.margins.right); intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
conn_state->tv.margins.top,
conn_state->tv.margins.bottom);
tv_conn_state->margins.top,
drm_mode_set_crtcinfo(adjusted_mode, 0); adjusted_mode->name[0] = '\0';tv_conn_state->margins.bottom);
@@ -1316,8 +1394,10 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc); struct intel_tv *intel_tv = enc_to_tv(encoder);
- const struct intel_tv_connector_state *tv_conn_state =
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);to_intel_tv_connector_state(conn_state);
- u32 tv_ctl;
- u32 tv_ctl, tv_filter_ctl; u32 scctl1, scctl2, scctl3; int i, j; const struct video_levels *video_levels;
@@ -1425,16 +1505,20 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, assert_pipe_disabled(dev_priv, intel_crtc->pipe);
/* Filter ctl must be set before TV_WIN_SIZE */
- I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
tv_filter_ctl = TV_AUTO_SCALE;
if (tv_conn_state->bypass_vfilter)
tv_filter_ctl |= TV_V_FILTER_BYPASS;
I915_WRITE(TV_FILTER_CTL_1, tv_filter_ctl);
xsize = tv_mode->hblank_start - tv_mode->hblank_end; ysize = intel_tv_mode_vdisplay(tv_mode);
xpos = conn_state->tv.margins.left;
- ypos = conn_state->tv.margins.top;
- ypos = tv_conn_state->margins.top; xsize -= (conn_state->tv.margins.left + conn_state->tv.margins.right);
- ysize -= (conn_state->tv.margins.top +
conn_state->tv.margins.bottom);
- ysize -= (tv_conn_state->margins.top +
I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);tv_conn_state->margins.bottom);
@@ -1701,7 +1785,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { .destroy = intel_connector_destroy, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_duplicate_state = intel_tv_connector_duplicate_state,
};
static int intel_tv_atomic_check(struct drm_connector *connector,
2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Wed, Jan 23, 2019 at 03:49:02PM +0200, Imre Deak wrote:
On Mon, Nov 12, 2018 at 06:59:59PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On gen3 we must disable the TV encoder vertical filter for >1024 pixel wide sources. Once that's done all we can is try to center the image on the screen. Naturally the TV mode vertical resolution must be equal or larger than the user mode vertical resolution or else we'd have to cut off part of the user mode.
And while we may not be able to respect the user's choice of top and bottom borders exactly (or we'd have to reject he mode most likely), we can try to maintain the relative sizes of the top and bottom border with respect to each orher.
Additionally we must configure the pipe as interlaced if the TV mode is interlaced.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/intel_tv.c | 100 +++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 75126fce655d..7099d837e31a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -861,6 +861,44 @@ static const struct tv_mode tv_modes[] = { }, };
+struct intel_tv_connector_state {
- struct drm_connector_state base;
- /*
* May need to override the user margins for
* gen3 >1024 wide source vertical centering.
*/
- struct {
u16 top, bottom;
- } margins;
- bool bypass_vfilter;
+};
+#define to_intel_tv_connector_state(x) container_of(x, struct intel_tv_connector_state, base)
+/**
- intel_digital_connector_duplicate_state - duplicate connector state
^intel_tv_connector_duplicate_state
- @connector: digital connector
^tv connector?
- Allocates and returns a copy of the connector state (both common and
- digital connector specific) for the specified connector.
- Returns: The newly allocated connector state, or NULL on failure.
- */
+struct drm_connector_state * +intel_tv_connector_duplicate_state(struct drm_connector *connector) +{
- struct intel_tv_connector_state *state;
- state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
- if (!state)
return NULL;
- __drm_atomic_helper_connector_duplicate_state(connector, &state->base);
- return &state->base;
+}
You didn't add the corresponding checks for the new intel_tv_connector_state fields to intel_tv_atomic_check(). I suppose that's ok since something resulting in a change in those will force a modeset anyway:
The new fields are not visible to the user so nothing external will change them. intel_tv_compute_config() (which is executed after .atomic_check()) will just recompute them based on other user visible state.
Reviewed-by: Imre Deak imre.deak@intel.com
static struct intel_tv *enc_to_tv(struct intel_encoder *encoder) { return container_of(encoder, struct intel_tv, base); @@ -1129,6 +1167,9 @@ intel_tv_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) {
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_tv_connector_state *tv_conn_state =
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;to_intel_tv_connector_state(conn_state);
@@ -1149,6 +1190,43 @@ intel_tv_compute_config(struct intel_encoder *encoder, pipe_config->port_clock = tv_mode->clock;
intel_tv_mode_to_mode(adjusted_mode, tv_mode);
drm_mode_set_crtcinfo(adjusted_mode, 0);
if (IS_GEN3(dev_priv) && hdisplay > 1024) {
int extra, top, bottom;
extra = adjusted_mode->crtc_vdisplay - vdisplay;
if (extra < 0) {
DRM_DEBUG_KMS("No vertical scaling for >1024 pixel wide modes\n");
return false;
}
/* Need to turn off the vertical filter and center the image */
/* Attempt to maintain the relative sizes of the margins */
top = conn_state->tv.margins.top;
bottom = conn_state->tv.margins.bottom;
if (top + bottom)
top = extra * top / (top + bottom);
else
top = extra / 2;
bottom = extra - top;
tv_conn_state->margins.top = top;
tv_conn_state->margins.bottom = bottom;
tv_conn_state->bypass_vfilter = true;
if (!tv_mode->progressive)
adjusted_mode->flags |= DRM_MODE_FLAG_INTERLACE;
} else {
tv_conn_state->margins.top = conn_state->tv.margins.top;
tv_conn_state->margins.bottom = conn_state->tv.margins.bottom;
tv_conn_state->bypass_vfilter = false;
}
DRM_DEBUG_KMS("TV mode:\n"); drm_mode_debug_printmodeline(adjusted_mode);
@@ -1222,8 +1300,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, conn_state->tv.margins.left, conn_state->tv.margins.right); intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
conn_state->tv.margins.top,
conn_state->tv.margins.bottom);
tv_conn_state->margins.top,
drm_mode_set_crtcinfo(adjusted_mode, 0); adjusted_mode->name[0] = '\0';tv_conn_state->margins.bottom);
@@ -1316,8 +1394,10 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc); struct intel_tv *intel_tv = enc_to_tv(encoder);
- const struct intel_tv_connector_state *tv_conn_state =
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);to_intel_tv_connector_state(conn_state);
- u32 tv_ctl;
- u32 tv_ctl, tv_filter_ctl; u32 scctl1, scctl2, scctl3; int i, j; const struct video_levels *video_levels;
@@ -1425,16 +1505,20 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, assert_pipe_disabled(dev_priv, intel_crtc->pipe);
/* Filter ctl must be set before TV_WIN_SIZE */
- I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
tv_filter_ctl = TV_AUTO_SCALE;
if (tv_conn_state->bypass_vfilter)
tv_filter_ctl |= TV_V_FILTER_BYPASS;
I915_WRITE(TV_FILTER_CTL_1, tv_filter_ctl);
xsize = tv_mode->hblank_start - tv_mode->hblank_end; ysize = intel_tv_mode_vdisplay(tv_mode);
xpos = conn_state->tv.margins.left;
- ypos = conn_state->tv.margins.top;
- ypos = tv_conn_state->margins.top; xsize -= (conn_state->tv.margins.left + conn_state->tv.margins.right);
- ysize -= (conn_state->tv.margins.top +
conn_state->tv.margins.bottom);
- ysize -= (tv_conn_state->margins.top +
I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);tv_conn_state->margins.bottom);
@@ -1701,7 +1785,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { .destroy = intel_connector_destroy, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_duplicate_state = intel_tv_connector_duplicate_state,
};
static int intel_tv_atomic_check(struct drm_connector *connector,
2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Wed, Jan 23, 2019 at 06:38:17PM +0200, Ville Syrjälä wrote:
On Wed, Jan 23, 2019 at 03:49:02PM +0200, Imre Deak wrote:
On Mon, Nov 12, 2018 at 06:59:59PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On gen3 we must disable the TV encoder vertical filter for >1024 pixel wide sources. Once that's done all we can is try to center the image on the screen. Naturally the TV mode vertical resolution must be equal or larger than the user mode vertical resolution or else we'd have to cut off part of the user mode.
And while we may not be able to respect the user's choice of top and bottom borders exactly (or we'd have to reject he mode most likely), we can try to maintain the relative sizes of the top and bottom border with respect to each orher.
Additionally we must configure the pipe as interlaced if the TV mode is interlaced.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/intel_tv.c | 100 +++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 75126fce655d..7099d837e31a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -861,6 +861,44 @@ static const struct tv_mode tv_modes[] = { }, };
+struct intel_tv_connector_state {
- struct drm_connector_state base;
- /*
* May need to override the user margins for
* gen3 >1024 wide source vertical centering.
*/
- struct {
u16 top, bottom;
- } margins;
- bool bypass_vfilter;
+};
+#define to_intel_tv_connector_state(x) container_of(x, struct intel_tv_connector_state, base)
+/**
- intel_digital_connector_duplicate_state - duplicate connector state
^intel_tv_connector_duplicate_state
- @connector: digital connector
^tv connector?
- Allocates and returns a copy of the connector state (both common and
- digital connector specific) for the specified connector.
- Returns: The newly allocated connector state, or NULL on failure.
- */
+struct drm_connector_state * +intel_tv_connector_duplicate_state(struct drm_connector *connector) +{
- struct intel_tv_connector_state *state;
- state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
- if (!state)
return NULL;
- __drm_atomic_helper_connector_duplicate_state(connector, &state->base);
- return &state->base;
+}
You didn't add the corresponding checks for the new intel_tv_connector_state fields to intel_tv_atomic_check(). I suppose that's ok since something resulting in a change in those will force a modeset anyway:
The new fields are not visible to the user so nothing external will change them. intel_tv_compute_config() (which is executed after .atomic_check()) will just recompute them based on other user visible state.
Ah, right, that explains and I missed the fact that it makes no sense to check any params that are computed :/
Reviewed-by: Imre Deak imre.deak@intel.com
static struct intel_tv *enc_to_tv(struct intel_encoder *encoder) { return container_of(encoder, struct intel_tv, base); @@ -1129,6 +1167,9 @@ intel_tv_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) {
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_tv_connector_state *tv_conn_state =
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;to_intel_tv_connector_state(conn_state);
@@ -1149,6 +1190,43 @@ intel_tv_compute_config(struct intel_encoder *encoder, pipe_config->port_clock = tv_mode->clock;
intel_tv_mode_to_mode(adjusted_mode, tv_mode);
drm_mode_set_crtcinfo(adjusted_mode, 0);
if (IS_GEN3(dev_priv) && hdisplay > 1024) {
int extra, top, bottom;
extra = adjusted_mode->crtc_vdisplay - vdisplay;
if (extra < 0) {
DRM_DEBUG_KMS("No vertical scaling for >1024 pixel wide modes\n");
return false;
}
/* Need to turn off the vertical filter and center the image */
/* Attempt to maintain the relative sizes of the margins */
top = conn_state->tv.margins.top;
bottom = conn_state->tv.margins.bottom;
if (top + bottom)
top = extra * top / (top + bottom);
else
top = extra / 2;
bottom = extra - top;
tv_conn_state->margins.top = top;
tv_conn_state->margins.bottom = bottom;
tv_conn_state->bypass_vfilter = true;
if (!tv_mode->progressive)
adjusted_mode->flags |= DRM_MODE_FLAG_INTERLACE;
} else {
tv_conn_state->margins.top = conn_state->tv.margins.top;
tv_conn_state->margins.bottom = conn_state->tv.margins.bottom;
tv_conn_state->bypass_vfilter = false;
}
DRM_DEBUG_KMS("TV mode:\n"); drm_mode_debug_printmodeline(adjusted_mode);
@@ -1222,8 +1300,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, conn_state->tv.margins.left, conn_state->tv.margins.right); intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
conn_state->tv.margins.top,
conn_state->tv.margins.bottom);
tv_conn_state->margins.top,
drm_mode_set_crtcinfo(adjusted_mode, 0); adjusted_mode->name[0] = '\0';tv_conn_state->margins.bottom);
@@ -1316,8 +1394,10 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc); struct intel_tv *intel_tv = enc_to_tv(encoder);
- const struct intel_tv_connector_state *tv_conn_state =
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);to_intel_tv_connector_state(conn_state);
- u32 tv_ctl;
- u32 tv_ctl, tv_filter_ctl; u32 scctl1, scctl2, scctl3; int i, j; const struct video_levels *video_levels;
@@ -1425,16 +1505,20 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, assert_pipe_disabled(dev_priv, intel_crtc->pipe);
/* Filter ctl must be set before TV_WIN_SIZE */
- I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
tv_filter_ctl = TV_AUTO_SCALE;
if (tv_conn_state->bypass_vfilter)
tv_filter_ctl |= TV_V_FILTER_BYPASS;
I915_WRITE(TV_FILTER_CTL_1, tv_filter_ctl);
xsize = tv_mode->hblank_start - tv_mode->hblank_end; ysize = intel_tv_mode_vdisplay(tv_mode);
xpos = conn_state->tv.margins.left;
- ypos = conn_state->tv.margins.top;
- ypos = tv_conn_state->margins.top; xsize -= (conn_state->tv.margins.left + conn_state->tv.margins.right);
- ysize -= (conn_state->tv.margins.top +
conn_state->tv.margins.bottom);
- ysize -= (tv_conn_state->margins.top +
I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);tv_conn_state->margins.bottom);
@@ -1701,7 +1785,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { .destroy = intel_connector_destroy, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_duplicate_state = intel_tv_connector_duplicate_state,
};
static int intel_tv_atomic_check(struct drm_connector *connector,
2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
-- Ville Syrjälä Intel
On Wed, Jan 23, 2019 at 03:49:02PM +0200, Imre Deak wrote:
On Mon, Nov 12, 2018 at 06:59:59PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
On gen3 we must disable the TV encoder vertical filter for >1024 pixel wide sources. Once that's done all we can is try to center the image on the screen. Naturally the TV mode vertical resolution must be equal or larger than the user mode vertical resolution or else we'd have to cut off part of the user mode.
And while we may not be able to respect the user's choice of top and bottom borders exactly (or we'd have to reject he mode most likely), we can try to maintain the relative sizes of the top and bottom border with respect to each orher.
Additionally we must configure the pipe as interlaced if the TV mode is interlaced.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/intel_tv.c | 100 +++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 75126fce655d..7099d837e31a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -861,6 +861,44 @@ static const struct tv_mode tv_modes[] = { }, };
+struct intel_tv_connector_state {
- struct drm_connector_state base;
- /*
* May need to override the user margins for
* gen3 >1024 wide source vertical centering.
*/
- struct {
u16 top, bottom;
- } margins;
- bool bypass_vfilter;
+};
+#define to_intel_tv_connector_state(x) container_of(x, struct intel_tv_connector_state, base)
+/**
- intel_digital_connector_duplicate_state - duplicate connector state
^intel_tv_connector_duplicate_state
- @connector: digital connector
^tv connector?
Copy paste fail. I just dropped the kerneldoc here and made the function static. Also fixed up the s/IS_GEN3(x)/IS_GEN(x,3)/ type of things and pushed the lot to dinq.
Thanks for the review.
- Allocates and returns a copy of the connector state (both common and
- digital connector specific) for the specified connector.
- Returns: The newly allocated connector state, or NULL on failure.
- */
+struct drm_connector_state * +intel_tv_connector_duplicate_state(struct drm_connector *connector) +{
- struct intel_tv_connector_state *state;
- state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
- if (!state)
return NULL;
- __drm_atomic_helper_connector_duplicate_state(connector, &state->base);
- return &state->base;
+}
You didn't add the corresponding checks for the new intel_tv_connector_state fields to intel_tv_atomic_check(). I suppose that's ok since something resulting in a change in those will force a modeset anyway:
Reviewed-by: Imre Deak imre.deak@intel.com
static struct intel_tv *enc_to_tv(struct intel_encoder *encoder) { return container_of(encoder, struct intel_tv, base); @@ -1129,6 +1167,9 @@ intel_tv_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) {
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_tv_connector_state *tv_conn_state =
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;to_intel_tv_connector_state(conn_state);
@@ -1149,6 +1190,43 @@ intel_tv_compute_config(struct intel_encoder *encoder, pipe_config->port_clock = tv_mode->clock;
intel_tv_mode_to_mode(adjusted_mode, tv_mode);
drm_mode_set_crtcinfo(adjusted_mode, 0);
if (IS_GEN3(dev_priv) && hdisplay > 1024) {
int extra, top, bottom;
extra = adjusted_mode->crtc_vdisplay - vdisplay;
if (extra < 0) {
DRM_DEBUG_KMS("No vertical scaling for >1024 pixel wide modes\n");
return false;
}
/* Need to turn off the vertical filter and center the image */
/* Attempt to maintain the relative sizes of the margins */
top = conn_state->tv.margins.top;
bottom = conn_state->tv.margins.bottom;
if (top + bottom)
top = extra * top / (top + bottom);
else
top = extra / 2;
bottom = extra - top;
tv_conn_state->margins.top = top;
tv_conn_state->margins.bottom = bottom;
tv_conn_state->bypass_vfilter = true;
if (!tv_mode->progressive)
adjusted_mode->flags |= DRM_MODE_FLAG_INTERLACE;
} else {
tv_conn_state->margins.top = conn_state->tv.margins.top;
tv_conn_state->margins.bottom = conn_state->tv.margins.bottom;
tv_conn_state->bypass_vfilter = false;
}
DRM_DEBUG_KMS("TV mode:\n"); drm_mode_debug_printmodeline(adjusted_mode);
@@ -1222,8 +1300,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, conn_state->tv.margins.left, conn_state->tv.margins.right); intel_tv_scale_mode_vert(adjusted_mode, vdisplay,
conn_state->tv.margins.top,
conn_state->tv.margins.bottom);
tv_conn_state->margins.top,
drm_mode_set_crtcinfo(adjusted_mode, 0); adjusted_mode->name[0] = '\0';tv_conn_state->margins.bottom);
@@ -1316,8 +1394,10 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc); struct intel_tv *intel_tv = enc_to_tv(encoder);
- const struct intel_tv_connector_state *tv_conn_state =
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);to_intel_tv_connector_state(conn_state);
- u32 tv_ctl;
- u32 tv_ctl, tv_filter_ctl; u32 scctl1, scctl2, scctl3; int i, j; const struct video_levels *video_levels;
@@ -1425,16 +1505,20 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder, assert_pipe_disabled(dev_priv, intel_crtc->pipe);
/* Filter ctl must be set before TV_WIN_SIZE */
- I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
tv_filter_ctl = TV_AUTO_SCALE;
if (tv_conn_state->bypass_vfilter)
tv_filter_ctl |= TV_V_FILTER_BYPASS;
I915_WRITE(TV_FILTER_CTL_1, tv_filter_ctl);
xsize = tv_mode->hblank_start - tv_mode->hblank_end; ysize = intel_tv_mode_vdisplay(tv_mode);
xpos = conn_state->tv.margins.left;
- ypos = conn_state->tv.margins.top;
- ypos = tv_conn_state->margins.top; xsize -= (conn_state->tv.margins.left + conn_state->tv.margins.right);
- ysize -= (conn_state->tv.margins.top +
conn_state->tv.margins.bottom);
- ysize -= (tv_conn_state->margins.top +
I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);tv_conn_state->margins.bottom);
@@ -1701,7 +1785,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { .destroy = intel_connector_destroy, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_duplicate_state = intel_tv_connector_duplicate_state,
};
static int intel_tv_atomic_check(struct drm_connector *connector,
2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
From: Ville Syrjälä ville.syrjala@linux.intel.com
Since gen3 can't handle >1024 wide sources with vertical scaling let's not advertize such modes in the mode list. Less tempetation to the user to try out things that won't work.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_tv.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 7099d837e31a..89c537839273 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1738,6 +1738,7 @@ intel_tv_set_mode_type(struct drm_display_mode *mode, static int intel_tv_get_modes(struct drm_connector *connector) { + struct drm_i915_private *dev_priv = to_i915(connector->dev); const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state); int i, count = 0;
@@ -1750,6 +1751,11 @@ intel_tv_get_modes(struct drm_connector *connector) !tv_mode->component_only) continue;
+ /* no vertical scaling with wide sources on gen3 */ + if (IS_GEN3(dev_priv) && input->w > 1024 && + input->h > intel_tv_mode_vdisplay(tv_mode)) + continue; + mode = drm_mode_create(connector->dev); if (!mode) continue;
On Mon, Nov 12, 2018 at 07:00:00PM +0200, Ville Syrjala wrote:
From: Ville Syrjälä ville.syrjala@linux.intel.com
Since gen3 can't handle >1024 wide sources with vertical scaling let's not advertize such modes in the mode list. Less tempetation to the user to try out things that won't work.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com
Reviewed-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/intel_tv.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 7099d837e31a..89c537839273 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1738,6 +1738,7 @@ intel_tv_set_mode_type(struct drm_display_mode *mode, static int intel_tv_get_modes(struct drm_connector *connector) {
- struct drm_i915_private *dev_priv = to_i915(connector->dev); const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state); int i, count = 0;
@@ -1750,6 +1751,11 @@ intel_tv_get_modes(struct drm_connector *connector) !tv_mode->component_only) continue;
/* no vertical scaling with wide sources on gen3 */
if (IS_GEN3(dev_priv) && input->w > 1024 &&
input->h > intel_tv_mode_vdisplay(tv_mode))
continue;
- mode = drm_mode_create(connector->dev); if (!mode) continue;
-- 2.18.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
dri-devel@lists.freedesktop.org