提交 6107cc58 编写于 作者: L Linus Torvalds

Merge tag 'drm-fixes-for-v4.12-rc5' of git://people.freedesktop.org/~airlied/linux

Pull drm fixes from Dave Airlie:
 "Intel, nouveau, rockchip, vmwgfx, imx, meson, mediatek and core fixes.

  Bit more spread out fixes this time, fixes for 7 drivers + a couple of
  core fixes.

  i915 and vmwgfx are the main ones. The vmwgfx ones fix a bunch of
  regressions in their atomic rework, and a few fixes destined for
  stable. i915 has some 4.12 regressions and older things that need to
  be fixed in stable as well.

  nouveau also has some runtime pm fixes and a timer list handling fix,
  otherwise a couple of core and small driver regression fixes"

* tag 'drm-fixes-for-v4.12-rc5' of git://people.freedesktop.org/~airlied/linux: (37 commits)
  drm/i915: fix warning for unused variable
  drm/meson: Fix driver bind when only CVBS is available
  drm/i915: Fix 90/270 rotated coordinates for FBC
  drm/i915: Restore has_fbc=1 for ILK-M
  drm/i915: Workaround VLV/CHV DSI scanline counter hardware fail
  drm/i915: Fix logical inversion for gen4 quirking
  drm/i915: Guard against i915_ggtt_disable_guc() being invoked unconditionally
  drm/i915: Always recompute watermarks when distrust_bios_wm is set, v2.
  drm/i915: Prevent the system suspend complete optimization
  drm/i915/psr: disable psr2 for resolution greater than 32X20
  drm/i915: Hold a wakeref for probing the ring registers
  drm/i915: Short-circuit i915_gem_wait_for_idle() if already idle
  drm/i915: Disable decoupled MMIO
  drm/i915/guc: Remove stale comment for q_fail
  drm/vmwgfx: Bump driver minor and date
  drm/vmwgfx: Remove unused legacy cursor functions
  drm/vmwgfx: fix spelling mistake "exeeds" -> "exceeds"
  drm/vmwgfx: Fix large topology crash
  drm/vmwgfx: Make sure to update STDU when FB is updated
  drm/vmwgfx: Make sure backup_handle is always valid
  ...
...@@ -508,6 +508,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, ...@@ -508,6 +508,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
bool has_connectors = bool has_connectors =
!!new_crtc_state->connector_mask; !!new_crtc_state->connector_mask;
WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
if (!drm_mode_equal(&old_crtc_state->mode, &new_crtc_state->mode)) { if (!drm_mode_equal(&old_crtc_state->mode, &new_crtc_state->mode)) {
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode changed\n", DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode changed\n",
crtc->base.id, crtc->name); crtc->base.id, crtc->name);
...@@ -551,6 +553,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, ...@@ -551,6 +553,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) {
const struct drm_connector_helper_funcs *funcs = connector->helper_private; const struct drm_connector_helper_funcs *funcs = connector->helper_private;
WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
/* /*
* This only sets crtc->connectors_changed for routing changes, * This only sets crtc->connectors_changed for routing changes,
* drivers must set crtc->connectors_changed themselves when * drivers must set crtc->connectors_changed themselves when
...@@ -650,6 +654,8 @@ drm_atomic_helper_check_planes(struct drm_device *dev, ...@@ -650,6 +654,8 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
const struct drm_plane_helper_funcs *funcs; const struct drm_plane_helper_funcs *funcs;
WARN_ON(!drm_modeset_is_locked(&plane->mutex));
funcs = plane->helper_private; funcs = plane->helper_private;
drm_atomic_helper_plane_changed(state, old_plane_state, new_plane_state, plane); drm_atomic_helper_plane_changed(state, old_plane_state, new_plane_state, plane);
...@@ -2663,7 +2669,12 @@ int drm_atomic_helper_resume(struct drm_device *dev, ...@@ -2663,7 +2669,12 @@ int drm_atomic_helper_resume(struct drm_device *dev,
drm_modeset_acquire_init(&ctx, 0); drm_modeset_acquire_init(&ctx, 0);
while (1) { while (1) {
err = drm_modeset_lock_all_ctx(dev, &ctx);
if (err)
goto out;
err = drm_atomic_helper_commit_duplicated_state(state, &ctx); err = drm_atomic_helper_commit_duplicated_state(state, &ctx);
out:
if (err != -EDEADLK) if (err != -EDEADLK)
break; break;
......
...@@ -358,7 +358,12 @@ EXPORT_SYMBOL(drm_put_dev); ...@@ -358,7 +358,12 @@ EXPORT_SYMBOL(drm_put_dev);
void drm_unplug_dev(struct drm_device *dev) void drm_unplug_dev(struct drm_device *dev)
{ {
/* for a USB device */ /* for a USB device */
drm_dev_unregister(dev); if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_unregister_all(dev);
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
drm_minor_unregister(dev, DRM_MINOR_RENDER);
drm_minor_unregister(dev, DRM_MINOR_CONTROL);
mutex_lock(&drm_global_mutex); mutex_lock(&drm_global_mutex);
......
...@@ -760,7 +760,7 @@ static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) ...@@ -760,7 +760,7 @@ static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi)
* Get the endpoint node. In our case, dsi has one output port1 * Get the endpoint node. In our case, dsi has one output port1
* to which the external HDMI bridge is connected. * to which the external HDMI bridge is connected.
*/ */
ret = drm_of_find_panel_or_bridge(np, 0, 0, NULL, &dsi->bridge); ret = drm_of_find_panel_or_bridge(np, 1, 0, NULL, &dsi->bridge);
if (ret) if (ret)
return ret; return ret;
......
...@@ -1235,6 +1235,15 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1235,6 +1235,15 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_fini; goto out_fini;
pci_set_drvdata(pdev, &dev_priv->drm); pci_set_drvdata(pdev, &dev_priv->drm);
/*
* Disable the system suspend direct complete optimization, which can
* leave the device suspended skipping the driver's suspend handlers
* if the device was already runtime suspended. This is needed due to
* the difference in our runtime and system suspend sequence and
* becaue the HDA driver may require us to enable the audio power
* domain during system suspend.
*/
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME;
ret = i915_driver_init_early(dev_priv, ent); ret = i915_driver_init_early(dev_priv, ent);
if (ret < 0) if (ret < 0)
......
...@@ -2991,6 +2991,16 @@ static inline bool intel_scanout_needs_vtd_wa(struct drm_i915_private *dev_priv) ...@@ -2991,6 +2991,16 @@ static inline bool intel_scanout_needs_vtd_wa(struct drm_i915_private *dev_priv)
return false; return false;
} }
static inline bool
intel_ggtt_update_needs_vtd_wa(struct drm_i915_private *dev_priv)
{
#ifdef CONFIG_INTEL_IOMMU
if (IS_BROXTON(dev_priv) && intel_iommu_gfx_mapped)
return true;
#endif
return false;
}
int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv,
int enable_ppgtt); int enable_ppgtt);
......
...@@ -3298,6 +3298,10 @@ int i915_gem_wait_for_idle(struct drm_i915_private *i915, unsigned int flags) ...@@ -3298,6 +3298,10 @@ int i915_gem_wait_for_idle(struct drm_i915_private *i915, unsigned int flags)
{ {
int ret; int ret;
/* If the device is asleep, we have no requests outstanding */
if (!READ_ONCE(i915->gt.awake))
return 0;
if (flags & I915_WAIT_LOCKED) { if (flags & I915_WAIT_LOCKED) {
struct i915_gem_timeline *tl; struct i915_gem_timeline *tl;
......
...@@ -2191,6 +2191,101 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm, ...@@ -2191,6 +2191,101 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm,
gen8_set_pte(&gtt_base[i], scratch_pte); gen8_set_pte(&gtt_base[i], scratch_pte);
} }
static void bxt_vtd_ggtt_wa(struct i915_address_space *vm)
{
struct drm_i915_private *dev_priv = vm->i915;
/*
* Make sure the internal GAM fifo has been cleared of all GTT
* writes before exiting stop_machine(). This guarantees that
* any aperture accesses waiting to start in another process
* cannot back up behind the GTT writes causing a hang.
* The register can be any arbitrary GAM register.
*/
POSTING_READ(GFX_FLSH_CNTL_GEN6);
}
struct insert_page {
struct i915_address_space *vm;
dma_addr_t addr;
u64 offset;
enum i915_cache_level level;
};
static int bxt_vtd_ggtt_insert_page__cb(void *_arg)
{
struct insert_page *arg = _arg;
gen8_ggtt_insert_page(arg->vm, arg->addr, arg->offset, arg->level, 0);
bxt_vtd_ggtt_wa(arg->vm);
return 0;
}
static void bxt_vtd_ggtt_insert_page__BKL(struct i915_address_space *vm,
dma_addr_t addr,
u64 offset,
enum i915_cache_level level,
u32 unused)
{
struct insert_page arg = { vm, addr, offset, level };
stop_machine(bxt_vtd_ggtt_insert_page__cb, &arg, NULL);
}
struct insert_entries {
struct i915_address_space *vm;
struct sg_table *st;
u64 start;
enum i915_cache_level level;
};
static int bxt_vtd_ggtt_insert_entries__cb(void *_arg)
{
struct insert_entries *arg = _arg;
gen8_ggtt_insert_entries(arg->vm, arg->st, arg->start, arg->level, 0);
bxt_vtd_ggtt_wa(arg->vm);
return 0;
}
static void bxt_vtd_ggtt_insert_entries__BKL(struct i915_address_space *vm,
struct sg_table *st,
u64 start,
enum i915_cache_level level,
u32 unused)
{
struct insert_entries arg = { vm, st, start, level };
stop_machine(bxt_vtd_ggtt_insert_entries__cb, &arg, NULL);
}
struct clear_range {
struct i915_address_space *vm;
u64 start;
u64 length;
};
static int bxt_vtd_ggtt_clear_range__cb(void *_arg)
{
struct clear_range *arg = _arg;
gen8_ggtt_clear_range(arg->vm, arg->start, arg->length);
bxt_vtd_ggtt_wa(arg->vm);
return 0;
}
static void bxt_vtd_ggtt_clear_range__BKL(struct i915_address_space *vm,
u64 start,
u64 length)
{
struct clear_range arg = { vm, start, length };
stop_machine(bxt_vtd_ggtt_clear_range__cb, &arg, NULL);
}
static void gen6_ggtt_clear_range(struct i915_address_space *vm, static void gen6_ggtt_clear_range(struct i915_address_space *vm,
u64 start, u64 length) u64 start, u64 length)
{ {
...@@ -2785,6 +2880,14 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt) ...@@ -2785,6 +2880,14 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
ggtt->base.insert_entries = gen8_ggtt_insert_entries; ggtt->base.insert_entries = gen8_ggtt_insert_entries;
/* Serialize GTT updates with aperture access on BXT if VT-d is on. */
if (intel_ggtt_update_needs_vtd_wa(dev_priv)) {
ggtt->base.insert_entries = bxt_vtd_ggtt_insert_entries__BKL;
ggtt->base.insert_page = bxt_vtd_ggtt_insert_page__BKL;
if (ggtt->base.clear_range != nop_clear_range)
ggtt->base.clear_range = bxt_vtd_ggtt_clear_range__BKL;
}
ggtt->invalidate = gen6_ggtt_invalidate; ggtt->invalidate = gen6_ggtt_invalidate;
return ggtt_probe_common(ggtt, size); return ggtt_probe_common(ggtt, size);
...@@ -2997,7 +3100,8 @@ void i915_ggtt_enable_guc(struct drm_i915_private *i915) ...@@ -2997,7 +3100,8 @@ void i915_ggtt_enable_guc(struct drm_i915_private *i915)
void i915_ggtt_disable_guc(struct drm_i915_private *i915) void i915_ggtt_disable_guc(struct drm_i915_private *i915)
{ {
i915->ggtt.invalidate = gen6_ggtt_invalidate; if (i915->ggtt.invalidate == guc_ggtt_invalidate)
i915->ggtt.invalidate = gen6_ggtt_invalidate;
} }
void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv) void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv)
......
...@@ -278,7 +278,7 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj, ...@@ -278,7 +278,7 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
obj->mm.quirked = false; obj->mm.quirked = false;
} }
if (!i915_gem_object_is_tiled(obj)) { if (!i915_gem_object_is_tiled(obj)) {
GEM_BUG_ON(!obj->mm.quirked); GEM_BUG_ON(obj->mm.quirked);
__i915_gem_object_pin_pages(obj); __i915_gem_object_pin_pages(obj);
obj->mm.quirked = true; obj->mm.quirked = true;
} }
......
...@@ -208,7 +208,7 @@ static const struct intel_device_info intel_ironlake_d_info = { ...@@ -208,7 +208,7 @@ static const struct intel_device_info intel_ironlake_d_info = {
static const struct intel_device_info intel_ironlake_m_info = { static const struct intel_device_info intel_ironlake_m_info = {
GEN5_FEATURES, GEN5_FEATURES,
.platform = INTEL_IRONLAKE, .platform = INTEL_IRONLAKE,
.is_mobile = 1, .is_mobile = 1, .has_fbc = 1,
}; };
#define GEN6_FEATURES \ #define GEN6_FEATURES \
...@@ -390,7 +390,6 @@ static const struct intel_device_info intel_skylake_gt3_info = { ...@@ -390,7 +390,6 @@ static const struct intel_device_info intel_skylake_gt3_info = {
.has_hw_contexts = 1, \ .has_hw_contexts = 1, \
.has_logical_ring_contexts = 1, \ .has_logical_ring_contexts = 1, \
.has_guc = 1, \ .has_guc = 1, \
.has_decoupled_mmio = 1, \
.has_aliasing_ppgtt = 1, \ .has_aliasing_ppgtt = 1, \
.has_full_ppgtt = 1, \ .has_full_ppgtt = 1, \
.has_full_48bit_ppgtt = 1, \ .has_full_48bit_ppgtt = 1, \
......
...@@ -12203,6 +12203,15 @@ static void update_scanline_offset(struct intel_crtc *crtc) ...@@ -12203,6 +12203,15 @@ static void update_scanline_offset(struct intel_crtc *crtc)
* type. For DP ports it behaves like most other platforms, but on HDMI * type. For DP ports it behaves like most other platforms, but on HDMI
* there's an extra 1 line difference. So we need to add two instead of * there's an extra 1 line difference. So we need to add two instead of
* one to the value. * one to the value.
*
* On VLV/CHV DSI the scanline counter would appear to increment
* approx. 1/3 of a scanline before start of vblank. Unfortunately
* that means we can't tell whether we're in vblank or not while
* we're on that particular line. We must still set scanline_offset
* to 1 so that the vblank timestamps come out correct when we query
* the scanline counter from within the vblank interrupt handler.
* However if queried just before the start of vblank we'll get an
* answer that's slightly in the future.
*/ */
if (IS_GEN2(dev_priv)) { if (IS_GEN2(dev_priv)) {
const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
......
...@@ -1075,6 +1075,22 @@ int intel_ring_workarounds_emit(struct drm_i915_gem_request *req) ...@@ -1075,6 +1075,22 @@ int intel_ring_workarounds_emit(struct drm_i915_gem_request *req)
return 0; return 0;
} }
static bool ring_is_idle(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
bool idle = true;
intel_runtime_pm_get(dev_priv);
/* No bit for gen2, so assume the CS parser is idle */
if (INTEL_GEN(dev_priv) > 2 && !(I915_READ_MODE(engine) & MODE_IDLE))
idle = false;
intel_runtime_pm_put(dev_priv);
return idle;
}
/** /**
* intel_engine_is_idle() - Report if the engine has finished process all work * intel_engine_is_idle() - Report if the engine has finished process all work
* @engine: the intel_engine_cs * @engine: the intel_engine_cs
...@@ -1084,8 +1100,6 @@ int intel_ring_workarounds_emit(struct drm_i915_gem_request *req) ...@@ -1084,8 +1100,6 @@ int intel_ring_workarounds_emit(struct drm_i915_gem_request *req)
*/ */
bool intel_engine_is_idle(struct intel_engine_cs *engine) bool intel_engine_is_idle(struct intel_engine_cs *engine)
{ {
struct drm_i915_private *dev_priv = engine->i915;
/* Any inflight/incomplete requests? */ /* Any inflight/incomplete requests? */
if (!i915_seqno_passed(intel_engine_get_seqno(engine), if (!i915_seqno_passed(intel_engine_get_seqno(engine),
intel_engine_last_submit(engine))) intel_engine_last_submit(engine)))
...@@ -1100,7 +1114,7 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine) ...@@ -1100,7 +1114,7 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
return false; return false;
/* Ring stopped? */ /* Ring stopped? */
if (INTEL_GEN(dev_priv) > 2 && !(I915_READ_MODE(engine) & MODE_IDLE)) if (!ring_is_idle(engine))
return false; return false;
return true; return true;
......
...@@ -82,20 +82,10 @@ static unsigned int get_crtc_fence_y_offset(struct intel_crtc *crtc) ...@@ -82,20 +82,10 @@ static unsigned int get_crtc_fence_y_offset(struct intel_crtc *crtc)
static void intel_fbc_get_plane_source_size(struct intel_fbc_state_cache *cache, static void intel_fbc_get_plane_source_size(struct intel_fbc_state_cache *cache,
int *width, int *height) int *width, int *height)
{ {
int w, h;
if (drm_rotation_90_or_270(cache->plane.rotation)) {
w = cache->plane.src_h;
h = cache->plane.src_w;
} else {
w = cache->plane.src_w;
h = cache->plane.src_h;
}
if (width) if (width)
*width = w; *width = cache->plane.src_w;
if (height) if (height)
*height = h; *height = cache->plane.src_h;
} }
static int intel_fbc_calculate_cfb_size(struct drm_i915_private *dev_priv, static int intel_fbc_calculate_cfb_size(struct drm_i915_private *dev_priv,
...@@ -746,6 +736,11 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc, ...@@ -746,6 +736,11 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc,
cache->crtc.hsw_bdw_pixel_rate = crtc_state->pixel_rate; cache->crtc.hsw_bdw_pixel_rate = crtc_state->pixel_rate;
cache->plane.rotation = plane_state->base.rotation; cache->plane.rotation = plane_state->base.rotation;
/*
* Src coordinates are already rotated by 270 degrees for
* the 90/270 degree plane rotation cases (to match the
* GTT mapping), hence no need to account for rotation here.
*/
cache->plane.src_w = drm_rect_width(&plane_state->base.src) >> 16; cache->plane.src_w = drm_rect_width(&plane_state->base.src) >> 16;
cache->plane.src_h = drm_rect_height(&plane_state->base.src) >> 16; cache->plane.src_h = drm_rect_height(&plane_state->base.src) >> 16;
cache->plane.visible = plane_state->base.visible; cache->plane.visible = plane_state->base.visible;
......
...@@ -4335,10 +4335,18 @@ skl_compute_wm(struct drm_atomic_state *state) ...@@ -4335,10 +4335,18 @@ skl_compute_wm(struct drm_atomic_state *state)
struct drm_crtc_state *cstate; struct drm_crtc_state *cstate;
struct intel_atomic_state *intel_state = to_intel_atomic_state(state); struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
struct skl_wm_values *results = &intel_state->wm_results; struct skl_wm_values *results = &intel_state->wm_results;
struct drm_device *dev = state->dev;
struct skl_pipe_wm *pipe_wm; struct skl_pipe_wm *pipe_wm;
bool changed = false; bool changed = false;
int ret, i; int ret, i;
/*
* When we distrust bios wm we always need to recompute to set the
* expected DDB allocations for each CRTC.
*/
if (to_i915(dev)->wm.distrust_bios_wm)
changed = true;
/* /*
* If this transaction isn't actually touching any CRTC's, don't * If this transaction isn't actually touching any CRTC's, don't
* bother with watermark calculation. Note that if we pass this * bother with watermark calculation. Note that if we pass this
...@@ -4349,6 +4357,7 @@ skl_compute_wm(struct drm_atomic_state *state) ...@@ -4349,6 +4357,7 @@ skl_compute_wm(struct drm_atomic_state *state)
*/ */
for_each_new_crtc_in_state(state, crtc, cstate, i) for_each_new_crtc_in_state(state, crtc, cstate, i)
changed = true; changed = true;
if (!changed) if (!changed)
return 0; return 0;
......
...@@ -435,8 +435,9 @@ static bool intel_psr_match_conditions(struct intel_dp *intel_dp) ...@@ -435,8 +435,9 @@ static bool intel_psr_match_conditions(struct intel_dp *intel_dp)
} }
/* PSR2 is restricted to work with panel resolutions upto 3200x2000 */ /* PSR2 is restricted to work with panel resolutions upto 3200x2000 */
if (intel_crtc->config->pipe_src_w > 3200 || if (dev_priv->psr.psr2_support &&
intel_crtc->config->pipe_src_h > 2000) { (intel_crtc->config->pipe_src_w > 3200 ||
intel_crtc->config->pipe_src_h > 2000)) {
dev_priv->psr.psr2_support = false; dev_priv->psr.psr2_support = false;
return false; return false;
} }
......
...@@ -83,10 +83,13 @@ int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode, ...@@ -83,10 +83,13 @@ int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
*/ */
void intel_pipe_update_start(struct intel_crtc *crtc) void intel_pipe_update_start(struct intel_crtc *crtc)
{ {
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
long timeout = msecs_to_jiffies_timeout(1); long timeout = msecs_to_jiffies_timeout(1);
int scanline, min, max, vblank_start; int scanline, min, max, vblank_start;
wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base); wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base);
bool need_vlv_dsi_wa = (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) &&
intel_crtc_has_type(crtc->config, INTEL_OUTPUT_DSI);
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
vblank_start = adjusted_mode->crtc_vblank_start; vblank_start = adjusted_mode->crtc_vblank_start;
...@@ -139,6 +142,24 @@ void intel_pipe_update_start(struct intel_crtc *crtc) ...@@ -139,6 +142,24 @@ void intel_pipe_update_start(struct intel_crtc *crtc)
drm_crtc_vblank_put(&crtc->base); drm_crtc_vblank_put(&crtc->base);
/*
* On VLV/CHV DSI the scanline counter would appear to
* increment approx. 1/3 of a scanline before start of vblank.
* The registers still get latched at start of vblank however.
* This means we must not write any registers on the first
* line of vblank (since not the whole line is actually in
* vblank). And unfortunately we can't use the interrupt to
* wait here since it will fire too soon. We could use the
* frame start interrupt instead since it will fire after the
* critical scanline, but that would require more changes
* in the interrupt code. So for now we'll just do the nasty
* thing and poll for the bad scanline to pass us by.
*
* FIXME figure out if BXT+ DSI suffers from this as well
*/
while (need_vlv_dsi_wa && scanline == vblank_start)
scanline = intel_get_crtc_scanline(crtc);
crtc->debug.scanline_start = scanline; crtc->debug.scanline_start = scanline;
crtc->debug.start_vbl_time = ktime_get(); crtc->debug.start_vbl_time = ktime_get();
crtc->debug.start_vbl_count = intel_crtc_get_vblank_counter(crtc); crtc->debug.start_vbl_count = intel_crtc_get_vblank_counter(crtc);
......
...@@ -59,8 +59,6 @@ struct drm_i915_gem_request; ...@@ -59,8 +59,6 @@ struct drm_i915_gem_request;
* available in the work queue (note, the queue is shared, * available in the work queue (note, the queue is shared,
* not per-engine). It is OK for this to be nonzero, but * not per-engine). It is OK for this to be nonzero, but
* it should not be huge! * it should not be huge!
* q_fail: failed to enqueue a work item. This should never happen,
* because we check for space beforehand.
* b_fail: failed to ring the doorbell. This should never happen, unless * b_fail: failed to ring the doorbell. This should never happen, unless
* somehow the hardware misbehaves, or maybe if the GuC firmware * somehow the hardware misbehaves, or maybe if the GuC firmware
* crashes? We probably need to reset the GPU to recover. * crashes? We probably need to reset the GPU to recover.
......
...@@ -673,7 +673,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) ...@@ -673,7 +673,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
ret = drm_of_find_panel_or_bridge(child, ret = drm_of_find_panel_or_bridge(child,
imx_ldb->lvds_mux ? 4 : 2, 0, imx_ldb->lvds_mux ? 4 : 2, 0,
&channel->panel, &channel->bridge); &channel->panel, &channel->bridge);
if (ret) if (ret && ret != -ENODEV)
return ret; return ret;
/* panel ddc only if there is no bridge */ /* panel ddc only if there is no bridge */
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/component.h> #include <linux/component.h>
#include <linux/iopoll.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
...@@ -900,16 +901,12 @@ static int mtk_dsi_host_detach(struct mipi_dsi_host *host, ...@@ -900,16 +901,12 @@ static int mtk_dsi_host_detach(struct mipi_dsi_host *host,
static void mtk_dsi_wait_for_idle(struct mtk_dsi *dsi) static void mtk_dsi_wait_for_idle(struct mtk_dsi *dsi)
{ {
u32 timeout_ms = 500000; /* total 1s ~ 2s timeout */ int ret;
u32 val;
while (timeout_ms--) {
if (!(readl(dsi->regs + DSI_INTSTA) & DSI_BUSY))
break;
usleep_range(2, 4);
}
if (timeout_ms == 0) { ret = readl_poll_timeout(dsi->regs + DSI_INTSTA, val, !(val & DSI_BUSY),
4, 2000000);
if (ret) {
DRM_WARN("polling dsi wait not busy timeout!\n"); DRM_WARN("polling dsi wait not busy timeout!\n");
mtk_dsi_enable(dsi); mtk_dsi_enable(dsi);
......
...@@ -1062,7 +1062,7 @@ static int mtk_hdmi_setup_vendor_specific_infoframe(struct mtk_hdmi *hdmi, ...@@ -1062,7 +1062,7 @@ static int mtk_hdmi_setup_vendor_specific_infoframe(struct mtk_hdmi *hdmi,
} }
err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err) { if (err < 0) {
dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n", dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n",
err); err);
return err; return err;
......
...@@ -152,7 +152,7 @@ static struct regmap_config meson_regmap_config = { ...@@ -152,7 +152,7 @@ static struct regmap_config meson_regmap_config = {
.max_register = 0x1000, .max_register = 0x1000,
}; };
static int meson_drv_bind(struct device *dev) static int meson_drv_bind_master(struct device *dev, bool has_components)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct meson_drm *priv; struct meson_drm *priv;
...@@ -233,10 +233,12 @@ static int meson_drv_bind(struct device *dev) ...@@ -233,10 +233,12 @@ static int meson_drv_bind(struct device *dev)
if (ret) if (ret)
goto free_drm; goto free_drm;
ret = component_bind_all(drm->dev, drm); if (has_components) {
if (ret) { ret = component_bind_all(drm->dev, drm);
dev_err(drm->dev, "Couldn't bind all components\n"); if (ret) {
goto free_drm; dev_err(drm->dev, "Couldn't bind all components\n");
goto free_drm;
}
} }
ret = meson_plane_create(priv); ret = meson_plane_create(priv);
...@@ -276,6 +278,11 @@ static int meson_drv_bind(struct device *dev) ...@@ -276,6 +278,11 @@ static int meson_drv_bind(struct device *dev)
return ret; return ret;
} }
static int meson_drv_bind(struct device *dev)
{
return meson_drv_bind_master(dev, true);
}
static void meson_drv_unbind(struct device *dev) static void meson_drv_unbind(struct device *dev)
{ {
struct drm_device *drm = dev_get_drvdata(dev); struct drm_device *drm = dev_get_drvdata(dev);
...@@ -357,6 +364,9 @@ static int meson_drv_probe(struct platform_device *pdev) ...@@ -357,6 +364,9 @@ static int meson_drv_probe(struct platform_device *pdev)
count += meson_probe_remote(pdev, &match, np, remote); count += meson_probe_remote(pdev, &match, np, remote);
} }
if (count && !match)
return meson_drv_bind_master(&pdev->dev, false);
/* If some endpoints were found, initialize the nodes */ /* If some endpoints were found, initialize the nodes */
if (count) { if (count) {
dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count); dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
struct nvkm_alarm { struct nvkm_alarm {
struct list_head head; struct list_head head;
struct list_head exec;
u64 timestamp; u64 timestamp;
void (*func)(struct nvkm_alarm *); void (*func)(struct nvkm_alarm *);
}; };
......
...@@ -80,7 +80,7 @@ int nouveau_modeset = -1; ...@@ -80,7 +80,7 @@ int nouveau_modeset = -1;
module_param_named(modeset, nouveau_modeset, int, 0400); module_param_named(modeset, nouveau_modeset, int, 0400);
MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)"); MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)");
int nouveau_runtime_pm = -1; static int nouveau_runtime_pm = -1;
module_param_named(runpm, nouveau_runtime_pm, int, 0400); module_param_named(runpm, nouveau_runtime_pm, int, 0400);
static struct drm_driver driver_stub; static struct drm_driver driver_stub;
...@@ -495,7 +495,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -495,7 +495,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
nouveau_fbcon_init(dev); nouveau_fbcon_init(dev);
nouveau_led_init(dev); nouveau_led_init(dev);
if (nouveau_runtime_pm != 0) { if (nouveau_pmops_runtime()) {
pm_runtime_use_autosuspend(dev->dev); pm_runtime_use_autosuspend(dev->dev);
pm_runtime_set_autosuspend_delay(dev->dev, 5000); pm_runtime_set_autosuspend_delay(dev->dev, 5000);
pm_runtime_set_active(dev->dev); pm_runtime_set_active(dev->dev);
...@@ -527,7 +527,7 @@ nouveau_drm_unload(struct drm_device *dev) ...@@ -527,7 +527,7 @@ nouveau_drm_unload(struct drm_device *dev)
{ {
struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_drm *drm = nouveau_drm(dev);
if (nouveau_runtime_pm != 0) { if (nouveau_pmops_runtime()) {
pm_runtime_get_sync(dev->dev); pm_runtime_get_sync(dev->dev);
pm_runtime_forbid(dev->dev); pm_runtime_forbid(dev->dev);
} }
...@@ -726,6 +726,14 @@ nouveau_pmops_thaw(struct device *dev) ...@@ -726,6 +726,14 @@ nouveau_pmops_thaw(struct device *dev)
return nouveau_do_resume(drm_dev, false); return nouveau_do_resume(drm_dev, false);
} }
bool
nouveau_pmops_runtime()
{
if (nouveau_runtime_pm == -1)
return nouveau_is_optimus() || nouveau_is_v1_dsm();
return nouveau_runtime_pm == 1;
}
static int static int
nouveau_pmops_runtime_suspend(struct device *dev) nouveau_pmops_runtime_suspend(struct device *dev)
{ {
...@@ -733,14 +741,7 @@ nouveau_pmops_runtime_suspend(struct device *dev) ...@@ -733,14 +741,7 @@ nouveau_pmops_runtime_suspend(struct device *dev)
struct drm_device *drm_dev = pci_get_drvdata(pdev); struct drm_device *drm_dev = pci_get_drvdata(pdev);
int ret; int ret;
if (nouveau_runtime_pm == 0) { if (!nouveau_pmops_runtime()) {
pm_runtime_forbid(dev);
return -EBUSY;
}
/* are we optimus enabled? */
if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) {
DRM_DEBUG_DRIVER("failing to power off - not optimus\n");
pm_runtime_forbid(dev); pm_runtime_forbid(dev);
return -EBUSY; return -EBUSY;
} }
...@@ -765,8 +766,10 @@ nouveau_pmops_runtime_resume(struct device *dev) ...@@ -765,8 +766,10 @@ nouveau_pmops_runtime_resume(struct device *dev)
struct nvif_device *device = &nouveau_drm(drm_dev)->client.device; struct nvif_device *device = &nouveau_drm(drm_dev)->client.device;
int ret; int ret;
if (nouveau_runtime_pm == 0) if (!nouveau_pmops_runtime()) {
return -EINVAL; pm_runtime_forbid(dev);
return -EBUSY;
}
pci_set_power_state(pdev, PCI_D0); pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev); pci_restore_state(pdev);
...@@ -796,14 +799,7 @@ nouveau_pmops_runtime_idle(struct device *dev) ...@@ -796,14 +799,7 @@ nouveau_pmops_runtime_idle(struct device *dev)
struct nouveau_drm *drm = nouveau_drm(drm_dev); struct nouveau_drm *drm = nouveau_drm(drm_dev);
struct drm_crtc *crtc; struct drm_crtc *crtc;
if (nouveau_runtime_pm == 0) { if (!nouveau_pmops_runtime()) {
pm_runtime_forbid(dev);
return -EBUSY;
}
/* are we optimus enabled? */
if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) {
DRM_DEBUG_DRIVER("failing to power off - not optimus\n");
pm_runtime_forbid(dev); pm_runtime_forbid(dev);
return -EBUSY; return -EBUSY;
} }
......
...@@ -108,8 +108,6 @@ nouveau_cli(struct drm_file *fpriv) ...@@ -108,8 +108,6 @@ nouveau_cli(struct drm_file *fpriv)
#include <nvif/object.h> #include <nvif/object.h>
#include <nvif/device.h> #include <nvif/device.h>
extern int nouveau_runtime_pm;
struct nouveau_drm { struct nouveau_drm {
struct nouveau_cli client; struct nouveau_cli client;
struct drm_device *dev; struct drm_device *dev;
...@@ -195,6 +193,7 @@ nouveau_drm(struct drm_device *dev) ...@@ -195,6 +193,7 @@ nouveau_drm(struct drm_device *dev)
int nouveau_pmops_suspend(struct device *); int nouveau_pmops_suspend(struct device *);
int nouveau_pmops_resume(struct device *); int nouveau_pmops_resume(struct device *);
bool nouveau_pmops_runtime(void);
#include <nvkm/core/tegra.h> #include <nvkm/core/tegra.h>
......
...@@ -87,7 +87,7 @@ void ...@@ -87,7 +87,7 @@ void
nouveau_vga_init(struct nouveau_drm *drm) nouveau_vga_init(struct nouveau_drm *drm)
{ {
struct drm_device *dev = drm->dev; struct drm_device *dev = drm->dev;
bool runtime = false; bool runtime = nouveau_pmops_runtime();
/* only relevant for PCI devices */ /* only relevant for PCI devices */
if (!dev->pdev) if (!dev->pdev)
...@@ -99,10 +99,6 @@ nouveau_vga_init(struct nouveau_drm *drm) ...@@ -99,10 +99,6 @@ nouveau_vga_init(struct nouveau_drm *drm)
if (pci_is_thunderbolt_attached(dev->pdev)) if (pci_is_thunderbolt_attached(dev->pdev))
return; return;
if (nouveau_runtime_pm == 1)
runtime = true;
if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm()))
runtime = true;
vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime); vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime);
if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
...@@ -113,18 +109,13 @@ void ...@@ -113,18 +109,13 @@ void
nouveau_vga_fini(struct nouveau_drm *drm) nouveau_vga_fini(struct nouveau_drm *drm)
{ {
struct drm_device *dev = drm->dev; struct drm_device *dev = drm->dev;
bool runtime = false; bool runtime = nouveau_pmops_runtime();
vga_client_register(dev->pdev, NULL, NULL, NULL); vga_client_register(dev->pdev, NULL, NULL, NULL);
if (pci_is_thunderbolt_attached(dev->pdev)) if (pci_is_thunderbolt_attached(dev->pdev))
return; return;
if (nouveau_runtime_pm == 1)
runtime = true;
if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm()))
runtime = true;
vga_switcheroo_unregister_client(dev->pdev); vga_switcheroo_unregister_client(dev->pdev);
if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
vga_switcheroo_fini_domain_pm_ops(drm->dev->dev); vga_switcheroo_fini_domain_pm_ops(drm->dev->dev);
......
...@@ -2107,7 +2107,8 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) ...@@ -2107,7 +2107,8 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
asyc->set.dither = true; asyc->set.dither = true;
} }
} else { } else {
asyc->set.mask = ~0; if (asyc)
asyc->set.mask = ~0;
asyh->set.mask = ~0; asyh->set.mask = ~0;
} }
......
...@@ -50,7 +50,8 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr) ...@@ -50,7 +50,8 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
/* Move to completed list. We'll drop the lock before /* Move to completed list. We'll drop the lock before
* executing the callback so it can reschedule itself. * executing the callback so it can reschedule itself.
*/ */
list_move_tail(&alarm->head, &exec); list_del_init(&alarm->head);
list_add(&alarm->exec, &exec);
} }
/* Shut down interrupt if no more pending alarms. */ /* Shut down interrupt if no more pending alarms. */
...@@ -59,8 +60,8 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr) ...@@ -59,8 +60,8 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
spin_unlock_irqrestore(&tmr->lock, flags); spin_unlock_irqrestore(&tmr->lock, flags);
/* Execute completed callbacks. */ /* Execute completed callbacks. */
list_for_each_entry_safe(alarm, atemp, &exec, head) { list_for_each_entry_safe(alarm, atemp, &exec, exec) {
list_del_init(&alarm->head); list_del(&alarm->exec);
alarm->func(alarm); alarm->func(alarm);
} }
} }
......
...@@ -245,8 +245,6 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder, ...@@ -245,8 +245,6 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct rockchip_dp_device *dp = to_dp(encoder);
int ret;
/* /*
* The hardware IC designed that VOP must output the RGB10 video * The hardware IC designed that VOP must output the RGB10 video
...@@ -258,16 +256,6 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder, ...@@ -258,16 +256,6 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
s->output_mode = ROCKCHIP_OUT_MODE_AAAA; s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
s->output_type = DRM_MODE_CONNECTOR_eDP; s->output_type = DRM_MODE_CONNECTOR_eDP;
if (dp->data->chip_type == RK3399_EDP) {
/*
* For RK3399, VOP Lit must code the out mode to RGB888,
* VOP Big must code the out mode to RGB10.
*/
ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node,
encoder);
if (ret > 0)
s->output_mode = ROCKCHIP_OUT_MODE_P888;
}
return 0; return 0;
} }
......
...@@ -615,7 +615,6 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) ...@@ -615,7 +615,6 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
{ {
struct cdn_dp_device *dp = encoder_to_dp(encoder); struct cdn_dp_device *dp = encoder_to_dp(encoder);
int ret, val; int ret, val;
struct rockchip_crtc_state *state;
ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder); ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
if (ret < 0) { if (ret < 0) {
...@@ -625,14 +624,10 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) ...@@ -625,14 +624,10 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
DRM_DEV_DEBUG_KMS(dp->dev, "vop %s output to cdn-dp\n", DRM_DEV_DEBUG_KMS(dp->dev, "vop %s output to cdn-dp\n",
(ret) ? "LIT" : "BIG"); (ret) ? "LIT" : "BIG");
state = to_rockchip_crtc_state(encoder->crtc->state); if (ret)
if (ret) {
val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16); val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
state->output_mode = ROCKCHIP_OUT_MODE_P888; else
} else {
val = DP_SEL_VOP_LIT << 16; val = DP_SEL_VOP_LIT << 16;
state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
}
ret = cdn_dp_grf_write(dp, GRF_SOC_CON9, val); ret = cdn_dp_grf_write(dp, GRF_SOC_CON9, val);
if (ret) if (ret)
......
...@@ -875,6 +875,7 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, ...@@ -875,6 +875,7 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
static void vop_crtc_enable(struct drm_crtc *crtc) static void vop_crtc_enable(struct drm_crtc *crtc)
{ {
struct vop *vop = to_vop(crtc); struct vop *vop = to_vop(crtc);
const struct vop_data *vop_data = vop->data;
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start; u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
...@@ -967,6 +968,13 @@ static void vop_crtc_enable(struct drm_crtc *crtc) ...@@ -967,6 +968,13 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n", DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n",
s->output_type); s->output_type);
} }
/*
* if vop is not support RGB10 output, need force RGB10 to RGB888.
*/
if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
!(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
s->output_mode = ROCKCHIP_OUT_MODE_P888;
VOP_CTRL_SET(vop, out_mode, s->output_mode); VOP_CTRL_SET(vop, out_mode, s->output_mode);
VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len); VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
......
...@@ -142,6 +142,9 @@ struct vop_data { ...@@ -142,6 +142,9 @@ struct vop_data {
const struct vop_intr *intr; const struct vop_intr *intr;
const struct vop_win_data *win; const struct vop_win_data *win;
unsigned int win_size; unsigned int win_size;
#define VOP_FEATURE_OUTPUT_RGB10 BIT(0)
u64 feature;
}; };
/* interrupt define */ /* interrupt define */
......
...@@ -275,6 +275,7 @@ static const struct vop_intr rk3288_vop_intr = { ...@@ -275,6 +275,7 @@ static const struct vop_intr rk3288_vop_intr = {
static const struct vop_data rk3288_vop = { static const struct vop_data rk3288_vop = {
.init_table = rk3288_init_reg_table, .init_table = rk3288_init_reg_table,
.table_size = ARRAY_SIZE(rk3288_init_reg_table), .table_size = ARRAY_SIZE(rk3288_init_reg_table),
.feature = VOP_FEATURE_OUTPUT_RGB10,
.intr = &rk3288_vop_intr, .intr = &rk3288_vop_intr,
.ctrl = &rk3288_ctrl_data, .ctrl = &rk3288_ctrl_data,
.win = rk3288_vop_win_data, .win = rk3288_vop_win_data,
...@@ -343,6 +344,7 @@ static const struct vop_reg_data rk3399_init_reg_table[] = { ...@@ -343,6 +344,7 @@ static const struct vop_reg_data rk3399_init_reg_table[] = {
static const struct vop_data rk3399_vop_big = { static const struct vop_data rk3399_vop_big = {
.init_table = rk3399_init_reg_table, .init_table = rk3399_init_reg_table,
.table_size = ARRAY_SIZE(rk3399_init_reg_table), .table_size = ARRAY_SIZE(rk3399_init_reg_table),
.feature = VOP_FEATURE_OUTPUT_RGB10,
.intr = &rk3399_vop_intr, .intr = &rk3399_vop_intr,
.ctrl = &rk3399_ctrl_data, .ctrl = &rk3399_ctrl_data,
/* /*
......
...@@ -41,9 +41,9 @@ ...@@ -41,9 +41,9 @@
#include <drm/ttm/ttm_module.h> #include <drm/ttm/ttm_module.h>
#include "vmwgfx_fence.h" #include "vmwgfx_fence.h"
#define VMWGFX_DRIVER_DATE "20170221" #define VMWGFX_DRIVER_DATE "20170607"
#define VMWGFX_DRIVER_MAJOR 2 #define VMWGFX_DRIVER_MAJOR 2
#define VMWGFX_DRIVER_MINOR 12 #define VMWGFX_DRIVER_MINOR 13
#define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_DRIVER_PATCHLEVEL 0
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024) #define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
......
...@@ -368,6 +368,8 @@ static void *vmw_local_fifo_reserve(struct vmw_private *dev_priv, ...@@ -368,6 +368,8 @@ static void *vmw_local_fifo_reserve(struct vmw_private *dev_priv,
return fifo_state->static_buffer; return fifo_state->static_buffer;
else { else {
fifo_state->dynamic_buffer = vmalloc(bytes); fifo_state->dynamic_buffer = vmalloc(bytes);
if (!fifo_state->dynamic_buffer)
goto out_err;
return fifo_state->dynamic_buffer; return fifo_state->dynamic_buffer;
} }
} }
......
...@@ -274,108 +274,6 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) ...@@ -274,108 +274,6 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
} }
/**
* vmw_du_cursor_plane_update() - Update cursor image and location
*
* @plane: plane object to update
* @crtc: owning CRTC of @plane
* @fb: framebuffer to flip onto plane
* @crtc_x: x offset of plane on crtc
* @crtc_y: y offset of plane on crtc
* @crtc_w: width of plane rectangle on crtc
* @crtc_h: height of plane rectangle on crtc
* @src_x: Not used
* @src_y: Not used
* @src_w: Not used
* @src_h: Not used
*
*
* RETURNS:
* Zero on success, error code on failure
*/
int vmw_du_cursor_plane_update(struct drm_plane *plane,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w,
unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
struct vmw_surface *surface = NULL;
struct vmw_dma_buffer *dmabuf = NULL;
s32 hotspot_x, hotspot_y;
int ret;
hotspot_x = du->hotspot_x + fb->hot_x;
hotspot_y = du->hotspot_y + fb->hot_y;
/* A lot of the code assumes this */
if (crtc_w != 64 || crtc_h != 64) {
ret = -EINVAL;
goto out;
}
if (vmw_framebuffer_to_vfb(fb)->dmabuf)
dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer;
else
surface = vmw_framebuffer_to_vfbs(fb)->surface;
if (surface && !surface->snooper.image) {
DRM_ERROR("surface not suitable for cursor\n");
ret = -EINVAL;
goto out;
}
/* setup new image */
ret = 0;
if (surface) {
/* vmw_user_surface_lookup takes one reference */
du->cursor_surface = surface;
du->cursor_age = du->cursor_surface->snooper.age;
ret = vmw_cursor_update_image(dev_priv, surface->snooper.image,
64, 64, hotspot_x, hotspot_y);
} else if (dmabuf) {
/* vmw_user_surface_lookup takes one reference */
du->cursor_dmabuf = dmabuf;
ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, crtc_w, crtc_h,
hotspot_x, hotspot_y);
} else {
vmw_cursor_update_position(dev_priv, false, 0, 0);
goto out;
}
if (!ret) {
du->cursor_x = crtc_x + du->set_gui_x;
du->cursor_y = crtc_y + du->set_gui_y;
vmw_cursor_update_position(dev_priv, true,
du->cursor_x + hotspot_x,
du->cursor_y + hotspot_y);
}
out:
return ret;
}
int vmw_du_cursor_plane_disable(struct drm_plane *plane)
{
if (plane->fb) {
drm_framebuffer_unreference(plane->fb);
plane->fb = NULL;
}
return -EINVAL;
}
void vmw_du_cursor_plane_destroy(struct drm_plane *plane) void vmw_du_cursor_plane_destroy(struct drm_plane *plane)
{ {
vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0); vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0);
...@@ -472,18 +370,6 @@ vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, ...@@ -472,18 +370,6 @@ vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
} }
void
vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
drm_atomic_set_fb_for_plane(plane->state, NULL);
vmw_cursor_update_position(dev_priv, false, 0, 0);
}
void void
vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state) struct drm_plane_state *old_state)
...@@ -1498,6 +1384,7 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv, ...@@ -1498,6 +1384,7 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv,
*/ */
if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) &&
dmabuf && only_2d && dmabuf && only_2d &&
mode_cmd->width > 64 && /* Don't create a proxy for cursor */
dev_priv->active_display_unit == vmw_du_screen_target) { dev_priv->active_display_unit == vmw_du_screen_target) {
ret = vmw_create_dmabuf_proxy(dev_priv->dev, mode_cmd, ret = vmw_create_dmabuf_proxy(dev_priv->dev, mode_cmd,
dmabuf, &surface); dmabuf, &surface);
......
...@@ -256,10 +256,6 @@ int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, ...@@ -256,10 +256,6 @@ int vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
u16 *r, u16 *g, u16 *b, u16 *r, u16 *g, u16 *b,
uint32_t size, uint32_t size,
struct drm_modeset_acquire_ctx *ctx); struct drm_modeset_acquire_ctx *ctx);
int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
uint32_t handle, uint32_t width, uint32_t height,
int32_t hot_x, int32_t hot_y);
int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
int vmw_du_connector_set_property(struct drm_connector *connector, int vmw_du_connector_set_property(struct drm_connector *connector,
struct drm_property *property, struct drm_property *property,
uint64_t val); uint64_t val);
...@@ -339,15 +335,6 @@ void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv, ...@@ -339,15 +335,6 @@ void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv,
/* Universal Plane Helpers */ /* Universal Plane Helpers */
void vmw_du_primary_plane_destroy(struct drm_plane *plane); void vmw_du_primary_plane_destroy(struct drm_plane *plane);
void vmw_du_cursor_plane_destroy(struct drm_plane *plane); void vmw_du_cursor_plane_destroy(struct drm_plane *plane);
int vmw_du_cursor_plane_disable(struct drm_plane *plane);
int vmw_du_cursor_plane_update(struct drm_plane *plane,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w,
unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h);
/* Atomic Helpers */ /* Atomic Helpers */
int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
...@@ -356,8 +343,6 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, ...@@ -356,8 +343,6 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state); struct drm_plane_state *state);
void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state); struct drm_plane_state *old_state);
void vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state);
int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state); struct drm_plane_state *new_state);
void vmw_du_plane_cleanup_fb(struct drm_plane *plane, void vmw_du_plane_cleanup_fb(struct drm_plane *plane,
......
...@@ -56,6 +56,8 @@ enum stdu_content_type { ...@@ -56,6 +56,8 @@ enum stdu_content_type {
* @right: Right side of bounding box. * @right: Right side of bounding box.
* @top: Top side of bounding box. * @top: Top side of bounding box.
* @bottom: Bottom side of bounding box. * @bottom: Bottom side of bounding box.
* @fb_left: Left side of the framebuffer/content bounding box
* @fb_top: Top of the framebuffer/content bounding box
* @buf: DMA buffer when DMA-ing between buffer and screen targets. * @buf: DMA buffer when DMA-ing between buffer and screen targets.
* @sid: Surface ID when copying between surface and screen targets. * @sid: Surface ID when copying between surface and screen targets.
*/ */
...@@ -63,6 +65,7 @@ struct vmw_stdu_dirty { ...@@ -63,6 +65,7 @@ struct vmw_stdu_dirty {
struct vmw_kms_dirty base; struct vmw_kms_dirty base;
SVGA3dTransferType transfer; SVGA3dTransferType transfer;
s32 left, right, top, bottom; s32 left, right, top, bottom;
s32 fb_left, fb_top;
u32 pitch; u32 pitch;
union { union {
struct vmw_dma_buffer *buf; struct vmw_dma_buffer *buf;
...@@ -647,7 +650,7 @@ static void vmw_stdu_dmabuf_fifo_commit(struct vmw_kms_dirty *dirty) ...@@ -647,7 +650,7 @@ static void vmw_stdu_dmabuf_fifo_commit(struct vmw_kms_dirty *dirty)
* *
* @dirty: The closure structure. * @dirty: The closure structure.
* *
* This function calculates the bounding box for all the incoming clips * This function calculates the bounding box for all the incoming clips.
*/ */
static void vmw_stdu_dmabuf_cpu_clip(struct vmw_kms_dirty *dirty) static void vmw_stdu_dmabuf_cpu_clip(struct vmw_kms_dirty *dirty)
{ {
...@@ -656,11 +659,19 @@ static void vmw_stdu_dmabuf_cpu_clip(struct vmw_kms_dirty *dirty) ...@@ -656,11 +659,19 @@ static void vmw_stdu_dmabuf_cpu_clip(struct vmw_kms_dirty *dirty)
dirty->num_hits = 1; dirty->num_hits = 1;
/* Calculate bounding box */ /* Calculate destination bounding box */
ddirty->left = min_t(s32, ddirty->left, dirty->unit_x1); ddirty->left = min_t(s32, ddirty->left, dirty->unit_x1);
ddirty->top = min_t(s32, ddirty->top, dirty->unit_y1); ddirty->top = min_t(s32, ddirty->top, dirty->unit_y1);
ddirty->right = max_t(s32, ddirty->right, dirty->unit_x2); ddirty->right = max_t(s32, ddirty->right, dirty->unit_x2);
ddirty->bottom = max_t(s32, ddirty->bottom, dirty->unit_y2); ddirty->bottom = max_t(s32, ddirty->bottom, dirty->unit_y2);
/*
* Calculate content bounding box. We only need the top-left
* coordinate because width and height will be the same as the
* destination bounding box above
*/
ddirty->fb_left = min_t(s32, ddirty->fb_left, dirty->fb_x);
ddirty->fb_top = min_t(s32, ddirty->fb_top, dirty->fb_y);
} }
...@@ -697,11 +708,11 @@ static void vmw_stdu_dmabuf_cpu_commit(struct vmw_kms_dirty *dirty) ...@@ -697,11 +708,11 @@ static void vmw_stdu_dmabuf_cpu_commit(struct vmw_kms_dirty *dirty)
/* Assume we are blitting from Host (display_srf) to Guest (dmabuf) */ /* Assume we are blitting from Host (display_srf) to Guest (dmabuf) */
src_pitch = stdu->display_srf->base_size.width * stdu->cpp; src_pitch = stdu->display_srf->base_size.width * stdu->cpp;
src = ttm_kmap_obj_virtual(&stdu->host_map, &not_used); src = ttm_kmap_obj_virtual(&stdu->host_map, &not_used);
src += dirty->unit_y1 * src_pitch + dirty->unit_x1 * stdu->cpp; src += ddirty->top * src_pitch + ddirty->left * stdu->cpp;
dst_pitch = ddirty->pitch; dst_pitch = ddirty->pitch;
dst = ttm_kmap_obj_virtual(&stdu->guest_map, &not_used); dst = ttm_kmap_obj_virtual(&stdu->guest_map, &not_used);
dst += dirty->fb_y * dst_pitch + dirty->fb_x * stdu->cpp; dst += ddirty->fb_top * dst_pitch + ddirty->fb_left * stdu->cpp;
/* Figure out the real direction */ /* Figure out the real direction */
...@@ -760,7 +771,7 @@ static void vmw_stdu_dmabuf_cpu_commit(struct vmw_kms_dirty *dirty) ...@@ -760,7 +771,7 @@ static void vmw_stdu_dmabuf_cpu_commit(struct vmw_kms_dirty *dirty)
} }
out_cleanup: out_cleanup:
ddirty->left = ddirty->top = S32_MAX; ddirty->left = ddirty->top = ddirty->fb_left = ddirty->fb_top = S32_MAX;
ddirty->right = ddirty->bottom = S32_MIN; ddirty->right = ddirty->bottom = S32_MIN;
} }
...@@ -812,6 +823,7 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv, ...@@ -812,6 +823,7 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv,
SVGA3D_READ_HOST_VRAM; SVGA3D_READ_HOST_VRAM;
ddirty.left = ddirty.top = S32_MAX; ddirty.left = ddirty.top = S32_MAX;
ddirty.right = ddirty.bottom = S32_MIN; ddirty.right = ddirty.bottom = S32_MIN;
ddirty.fb_left = ddirty.fb_top = S32_MAX;
ddirty.pitch = vfb->base.pitches[0]; ddirty.pitch = vfb->base.pitches[0];
ddirty.buf = buf; ddirty.buf = buf;
ddirty.base.fifo_commit = vmw_stdu_dmabuf_fifo_commit; ddirty.base.fifo_commit = vmw_stdu_dmabuf_fifo_commit;
...@@ -1355,6 +1367,11 @@ vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane, ...@@ -1355,6 +1367,11 @@ vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,
DRM_ERROR("Failed to bind surface to STDU.\n"); DRM_ERROR("Failed to bind surface to STDU.\n");
else else
crtc->primary->fb = plane->state->fb; crtc->primary->fb = plane->state->fb;
ret = vmw_stdu_update_st(dev_priv, stdu);
if (ret)
DRM_ERROR("Failed to update STDU.\n");
} }
......
...@@ -1274,11 +1274,14 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1274,11 +1274,14 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret; int ret;
uint32_t size; uint32_t size;
uint32_t backup_handle; uint32_t backup_handle = 0;
if (req->multisample_count != 0) if (req->multisample_count != 0)
return -EINVAL; return -EINVAL;
if (req->mip_levels > DRM_VMW_MAX_MIP_LEVELS)
return -EINVAL;
if (unlikely(vmw_user_surface_size == 0)) if (unlikely(vmw_user_surface_size == 0))
vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
128; 128;
...@@ -1314,12 +1317,16 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1314,12 +1317,16 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
ret = vmw_user_dmabuf_lookup(tfile, req->buffer_handle, ret = vmw_user_dmabuf_lookup(tfile, req->buffer_handle,
&res->backup, &res->backup,
&user_srf->backup_base); &user_srf->backup_base);
if (ret == 0 && res->backup->base.num_pages * PAGE_SIZE < if (ret == 0) {
res->backup_size) { if (res->backup->base.num_pages * PAGE_SIZE <
DRM_ERROR("Surface backup buffer is too small.\n"); res->backup_size) {
vmw_dmabuf_unreference(&res->backup); DRM_ERROR("Surface backup buffer is too small.\n");
ret = -EINVAL; vmw_dmabuf_unreference(&res->backup);
goto out_unlock; ret = -EINVAL;
goto out_unlock;
} else {
backup_handle = req->buffer_handle;
}
} }
} else if (req->drm_surface_flags & drm_vmw_surface_flag_create_buffer) } else if (req->drm_surface_flags & drm_vmw_surface_flag_create_buffer)
ret = vmw_user_dmabuf_alloc(dev_priv, tfile, ret = vmw_user_dmabuf_alloc(dev_priv, tfile,
...@@ -1491,7 +1498,7 @@ int vmw_surface_gb_priv_define(struct drm_device *dev, ...@@ -1491,7 +1498,7 @@ int vmw_surface_gb_priv_define(struct drm_device *dev,
dev_priv->stdu_max_height); dev_priv->stdu_max_height);
if (size.width > max_width || size.height > max_height) { if (size.width > max_width || size.height > max_height) {
DRM_ERROR("%ux%u\n, exeeds max surface size %ux%u", DRM_ERROR("%ux%u\n, exceeds max surface size %ux%u",
size.width, size.height, size.width, size.height,
max_width, max_height); max_width, max_height);
return -EINVAL; return -EINVAL;
......
...@@ -725,15 +725,16 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) ...@@ -725,15 +725,16 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
spin_lock_irqsave(&ipu->lock, flags); spin_lock_irqsave(&ipu->lock, flags);
val = ipu_cm_read(ipu, IPU_CONF); val = ipu_cm_read(ipu, IPU_CONF);
if (vdi) { if (vdi)
val |= IPU_CONF_IC_INPUT; val |= IPU_CONF_IC_INPUT;
} else { else
val &= ~IPU_CONF_IC_INPUT; val &= ~IPU_CONF_IC_INPUT;
if (csi_id == 1)
val |= IPU_CONF_CSI_SEL; if (csi_id == 1)
else val |= IPU_CONF_CSI_SEL;
val &= ~IPU_CONF_CSI_SEL; else
} val &= ~IPU_CONF_CSI_SEL;
ipu_cm_write(ipu, val, IPU_CONF); ipu_cm_write(ipu, val, IPU_CONF);
spin_unlock_irqrestore(&ipu->lock, flags); spin_unlock_irqrestore(&ipu->lock, flags);
......
...@@ -131,8 +131,6 @@ int ipu_pre_get(struct ipu_pre *pre) ...@@ -131,8 +131,6 @@ int ipu_pre_get(struct ipu_pre *pre)
if (pre->in_use) if (pre->in_use)
return -EBUSY; return -EBUSY;
clk_prepare_enable(pre->clk_axi);
/* first get the engine out of reset and remove clock gating */ /* first get the engine out of reset and remove clock gating */
writel(0, pre->regs + IPU_PRE_CTRL); writel(0, pre->regs + IPU_PRE_CTRL);
...@@ -149,12 +147,7 @@ int ipu_pre_get(struct ipu_pre *pre) ...@@ -149,12 +147,7 @@ int ipu_pre_get(struct ipu_pre *pre)
void ipu_pre_put(struct ipu_pre *pre) void ipu_pre_put(struct ipu_pre *pre)
{ {
u32 val; writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL);
val = IPU_PRE_CTRL_SFTRST | IPU_PRE_CTRL_CLKGATE;
writel(val, pre->regs + IPU_PRE_CTRL);
clk_disable_unprepare(pre->clk_axi);
pre->in_use = false; pre->in_use = false;
} }
...@@ -249,6 +242,8 @@ static int ipu_pre_probe(struct platform_device *pdev) ...@@ -249,6 +242,8 @@ static int ipu_pre_probe(struct platform_device *pdev)
if (!pre->buffer_virt) if (!pre->buffer_virt)
return -ENOMEM; return -ENOMEM;
clk_prepare_enable(pre->clk_axi);
pre->dev = dev; pre->dev = dev;
platform_set_drvdata(pdev, pre); platform_set_drvdata(pdev, pre);
mutex_lock(&ipu_pre_list_mutex); mutex_lock(&ipu_pre_list_mutex);
...@@ -268,6 +263,8 @@ static int ipu_pre_remove(struct platform_device *pdev) ...@@ -268,6 +263,8 @@ static int ipu_pre_remove(struct platform_device *pdev)
available_pres--; available_pres--;
mutex_unlock(&ipu_pre_list_mutex); mutex_unlock(&ipu_pre_list_mutex);
clk_disable_unprepare(pre->clk_axi);
if (pre->buffer_virt) if (pre->buffer_virt)
gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt,
IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册