summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/intel_atomic.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-06-26 13:18:51 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-26 13:18:51 -0700
commit099bfbfc7fbbe22356c02f0caf709ac32e1126ea (patch)
treec2dfe2f9445255d866e9203cff9e9f865ef93513 /drivers/gpu/drm/i915/intel_atomic.c
parent22165fa79814e71e7a5974b3c37a5028ed16c8f9 (diff)
parentc5fd936e992dd2829167d2adc63e151675ca6898 (diff)
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie: "This is the main drm pull request for v4.2. I've one other new driver from freescale on my radar, it's been posted and reviewed, I'd just like to get someone to give it a last look, so maybe I'll send it or maybe I'll leave it. There is no major nouveau changes in here, Ben was working on something big, and we agreed it was a bit late, there wasn't anything else he considered urgent to merge. There might be another msm pull for some bits that are waiting on arm-soc, I'll see how we time it. This touches some "of" stuff, acks are in place except for the fixes to the build in various configs,t hat I just applied. Summary: New drivers: - virtio-gpu: KMS only pieces of driver for virtio-gpu in qemu. This is just the first part of this driver, enough to run unaccelerated userspace on. As qemu merges more we'll start adding the 3D features for the virgl 3d work. - amdgpu: a new driver from AMD to driver their newer GPUs. (VI+) It contains a new cleaner userspace API, and is a clean break from radeon moving forward, that AMD are going to concentrate on. It also contains a set of register headers auto generated from AMD internal database. core: - atomic modesetting API completed, enabled by default now. - Add support for mode_id blob to atomic ioctl to complete interface. - bunch of Displayport MST fixes - lots of misc fixes. panel: - new simple panels - fix some long-standing build issues with bridge drivers radeon: - VCE1 support - add a GPU reset counter for userspace - lots of fixes. amdkfd: - H/W debugger support module - static user-mode queues - support killing all the waves when a process terminates - use standard DECLARE_BITMAP i915: - Add Broxton support - S3, rotation support for Skylake - RPS booting tuning - CPT modeset sequence fixes - ns2501 dither support - enable cmd parser on haswell - cdclk handling fixes - gen8 dynamic pte allocation - lots of atomic conversion work exynos: - Add atomic modesetting support - Add iommu support - Consolidate drm driver initialization - and MIC, DECON and MIPI-DSI support for exynos5433 omapdrm: - atomic modesetting support (fixes lots of things in rewrite) tegra: - DP aux transaction fixes - iommu support fix msm: - adreno a306 support - various dsi bits - various 64-bit fixes - NV12MT support rcar-du: - atomic and misc fixes sti: - fix HDMI timing complaince tilcdc: - use drm component API to access tda998x driver - fix module unloading qxl: - stability fixes" * 'drm-next' of git://people.freedesktop.org/~airlied/linux: (872 commits) drm/nouveau: Pause between setting gpu to D3hot and cutting the power drm/dp/mst: close deadlock in connector destruction. drm: Always enable atomic API drm/vgem: Set unique to "vgem" of: fix a build error to of_graph_get_endpoint_by_regs function drm/dp/mst: take lock around looking up the branch device on hpd irq drm/dp/mst: make sure mst_primary mstb is valid in work function of: add EXPORT_SYMBOL for of_graph_get_endpoint_by_regs ARM: dts: rename the clock of MIPI DSI 'pll_clk' to 'sclk_mipi' drm/atomic: Don't set crtc_state->enable manually drm/exynos: dsi: do not set TE GPIO direction by input drm/exynos: dsi: add support for MIC driver as a bridge drm/exynos: dsi: add support for Exynos5433 drm/exynos: dsi: make use of array for clock access drm/exynos: dsi: make use of driver data for static values drm/exynos: dsi: add macros for register access drm/exynos: dsi: rename pll_clk to sclk_clk drm/exynos: mic: add MIC driver of: add helper for getting endpoint node of specific identifiers drm/exynos: add Exynos5433 decon driver ...
Diffstat (limited to 'drivers/gpu/drm/i915/intel_atomic.c')
-rw-r--r--drivers/gpu/drm/i915/intel_atomic.c184
1 files changed, 182 insertions, 2 deletions
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
index 3903b90fb64e..7ed8033aae60 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -48,6 +48,8 @@ int intel_atomic_check(struct drm_device *dev,
int ncrtcs = dev->mode_config.num_crtc;
int nconnectors = dev->mode_config.num_connector;
enum pipe nuclear_pipe = INVALID_PIPE;
+ struct intel_crtc *nuclear_crtc = NULL;
+ struct intel_crtc_state *crtc_state = NULL;
int ret;
int i;
bool not_nuclear = false;
@@ -76,8 +78,14 @@ int intel_atomic_check(struct drm_device *dev,
state->allow_modeset = false;
for (i = 0; i < ncrtcs; i++) {
struct intel_crtc *crtc = to_intel_crtc(state->crtcs[i]);
+ if (crtc)
+ memset(&crtc->atomic, 0, sizeof(crtc->atomic));
if (crtc && crtc->pipe != nuclear_pipe)
not_nuclear = true;
+ if (crtc && crtc->pipe == nuclear_pipe) {
+ nuclear_crtc = crtc;
+ crtc_state = to_intel_crtc_state(state->crtc_states[i]);
+ }
}
for (i = 0; i < nconnectors; i++)
if (state->connectors[i] != NULL)
@@ -92,6 +100,11 @@ int intel_atomic_check(struct drm_device *dev,
if (ret)
return ret;
+ /* FIXME: move to crtc atomic check function once it is ready */
+ ret = intel_atomic_setup_scalers(dev, nuclear_crtc, crtc_state);
+ if (ret)
+ return ret;
+
return ret;
}
@@ -155,6 +168,21 @@ int intel_atomic_commit(struct drm_device *dev,
swap(state->plane_states[i], plane->state);
plane->state->state = NULL;
}
+
+ /* swap crtc_scaler_state */
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct drm_crtc *crtc = state->crtcs[i];
+ if (!crtc) {
+ continue;
+ }
+
+ to_intel_crtc(crtc)->config->scaler_state =
+ to_intel_crtc_state(state->crtc_states[i])->scaler_state;
+
+ if (INTEL_INFO(dev)->gen >= 9)
+ skl_detach_scalers(to_intel_crtc(crtc));
+ }
+
drm_atomic_helper_commit_planes(dev, state);
drm_atomic_helper_wait_for_vblanks(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
@@ -222,8 +250,12 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc)
crtc_state = kmemdup(intel_crtc->config,
sizeof(*intel_crtc->config), GFP_KERNEL);
- if (crtc_state)
- crtc_state->base.crtc = crtc;
+ if (!crtc_state)
+ return NULL;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &crtc_state->base);
+
+ crtc_state->base.crtc = crtc;
return &crtc_state->base;
}
@@ -241,3 +273,151 @@ intel_crtc_destroy_state(struct drm_crtc *crtc,
{
drm_atomic_helper_crtc_destroy_state(crtc, state);
}
+
+/**
+ * intel_atomic_setup_scalers() - setup scalers for crtc per staged requests
+ * @dev: DRM device
+ * @crtc: intel crtc
+ * @crtc_state: incoming crtc_state to validate and setup scalers
+ *
+ * This function sets up scalers based on staged scaling requests for
+ * a @crtc and its planes. It is called from crtc level check path. If request
+ * is a supportable request, it attaches scalers to requested planes and crtc.
+ *
+ * This function takes into account the current scaler(s) in use by any planes
+ * not being part of this atomic state
+ *
+ * Returns:
+ * 0 - scalers were setup succesfully
+ * error code - otherwise
+ */
+int intel_atomic_setup_scalers(struct drm_device *dev,
+ struct intel_crtc *intel_crtc,
+ struct intel_crtc_state *crtc_state)
+{
+ struct drm_plane *plane = NULL;
+ struct intel_plane *intel_plane;
+ struct intel_plane_state *plane_state = NULL;
+ struct intel_crtc_scaler_state *scaler_state;
+ struct drm_atomic_state *drm_state;
+ int num_scalers_need;
+ int i, j;
+
+ if (INTEL_INFO(dev)->gen < 9 || !intel_crtc || !crtc_state)
+ return 0;
+
+ scaler_state = &crtc_state->scaler_state;
+ drm_state = crtc_state->base.state;
+
+ num_scalers_need = hweight32(scaler_state->scaler_users);
+ DRM_DEBUG_KMS("crtc_state = %p need = %d avail = %d scaler_users = 0x%x\n",
+ crtc_state, num_scalers_need, intel_crtc->num_scalers,
+ scaler_state->scaler_users);
+
+ /*
+ * High level flow:
+ * - staged scaler requests are already in scaler_state->scaler_users
+ * - check whether staged scaling requests can be supported
+ * - add planes using scalers that aren't in current transaction
+ * - assign scalers to requested users
+ * - as part of plane commit, scalers will be committed
+ * (i.e., either attached or detached) to respective planes in hw
+ * - as part of crtc_commit, scaler will be either attached or detached
+ * to crtc in hw
+ */
+
+ /* fail if required scalers > available scalers */
+ if (num_scalers_need > intel_crtc->num_scalers){
+ DRM_DEBUG_KMS("Too many scaling requests %d > %d\n",
+ num_scalers_need, intel_crtc->num_scalers);
+ return -EINVAL;
+ }
+
+ /* walkthrough scaler_users bits and start assigning scalers */
+ for (i = 0; i < sizeof(scaler_state->scaler_users) * 8; i++) {
+ int *scaler_id;
+
+ /* skip if scaler not required */
+ if (!(scaler_state->scaler_users & (1 << i)))
+ continue;
+
+ if (i == SKL_CRTC_INDEX) {
+ /* panel fitter case: assign as a crtc scaler */
+ scaler_id = &scaler_state->scaler_id;
+ } else {
+ if (!drm_state)
+ continue;
+
+ /* plane scaler case: assign as a plane scaler */
+ /* find the plane that set the bit as scaler_user */
+ plane = drm_state->planes[i];
+
+ /*
+ * to enable/disable hq mode, add planes that are using scaler
+ * into this transaction
+ */
+ if (!plane) {
+ struct drm_plane_state *state;
+ plane = drm_plane_from_index(dev, i);
+ state = drm_atomic_get_plane_state(drm_state, plane);
+ if (IS_ERR(state)) {
+ DRM_DEBUG_KMS("Failed to add [PLANE:%d] to drm_state\n",
+ plane->base.id);
+ return PTR_ERR(state);
+ }
+ }
+
+ intel_plane = to_intel_plane(plane);
+
+ /* plane on different crtc cannot be a scaler user of this crtc */
+ if (WARN_ON(intel_plane->pipe != intel_crtc->pipe)) {
+ continue;
+ }
+
+ plane_state = to_intel_plane_state(drm_state->plane_states[i]);
+ scaler_id = &plane_state->scaler_id;
+ }
+
+ if (*scaler_id < 0) {
+ /* find a free scaler */
+ for (j = 0; j < intel_crtc->num_scalers; j++) {
+ if (!scaler_state->scalers[j].in_use) {
+ scaler_state->scalers[j].in_use = 1;
+ *scaler_id = scaler_state->scalers[j].id;
+ DRM_DEBUG_KMS("Attached scaler id %u.%u to %s:%d\n",
+ intel_crtc->pipe,
+ i == SKL_CRTC_INDEX ? scaler_state->scaler_id :
+ plane_state->scaler_id,
+ i == SKL_CRTC_INDEX ? "CRTC" : "PLANE",
+ i == SKL_CRTC_INDEX ? intel_crtc->base.base.id :
+ plane->base.id);
+ break;
+ }
+ }
+ }
+
+ if (WARN_ON(*scaler_id < 0)) {
+ DRM_DEBUG_KMS("Cannot find scaler for %s:%d\n",
+ i == SKL_CRTC_INDEX ? "CRTC" : "PLANE",
+ i == SKL_CRTC_INDEX ? intel_crtc->base.base.id:plane->base.id);
+ continue;
+ }
+
+ /* set scaler mode */
+ if (num_scalers_need == 1 && intel_crtc->pipe != PIPE_C) {
+ /*
+ * when only 1 scaler is in use on either pipe A or B,
+ * scaler 0 operates in high quality (HQ) mode.
+ * In this case use scaler 0 to take advantage of HQ mode
+ */
+ *scaler_id = 0;
+ scaler_state->scalers[0].in_use = 1;
+ scaler_state->scalers[0].mode = PS_SCALER_MODE_HQ;
+ scaler_state->scalers[1].in_use = 0;
+ } else {
+ scaler_state->scalers[*scaler_id].mode = PS_SCALER_MODE_DYN;
+ }
+ }
+
+ return 0;
+}