]> git.baikalelectronics.ru Git - kernel.git/commitdiff
Merge tag 'drm-intel-next-2019-06-19' of git://anongit.freedesktop.org/drm/drm-intel...
authorDave Airlie <airlied@redhat.com>
Fri, 21 Jun 2019 03:59:49 +0000 (13:59 +1000)
committerDave Airlie <airlied@redhat.com>
Fri, 21 Jun 2019 04:00:10 +0000 (14:00 +1000)
Features:
- HDR support (Uma, Ville)
- Add I2C symlink under HDMI connector similar to DP (Oleg)
- Add ICL multi-segmented gamma support (Shashank, Uma)
- Update register whitelist support for new hardware (Robert, John)
- GuC firmware update with updated ABI interface (Michal, Oscar)
- Add support for new DMC header versions (Lucas)
- In-kernel blitter client for selftest use (Matthew)
- Add Mule Creec Canyon (MCC) PCH support to go with EHL (Matt)
- EHL platform feature updates (Matt)
- Use Command Transport Buffers with GuC on all gens (Daniele)
- New i915.force_probe module parameter to replace i915.alpha_support (Jani)

Refactoring:
- Better runtime PM code abstraction/encapsulation (Daniele)
- VBT parsing cleanup and improvements (Jani)
- Move display code to its own subdirectory (Jani)
- Header cleanup (Jani, Daniele)
- Prep work for subsclice mask expansion (Stuart)
- Use uncore mmio register accessors more, remove unused macro wrappers (Tvrtko)
- Remove unused atomic property get/set stubs (Maarten)
- GTT cleanups and improvements (Mika)
- Pass intel_ types instead of drm_ types in plenty of display code (Ville)
- Engine reset, hangcheck, fault code cleanups and improvements (Tvrtko)
- Consider AML variants simply as either KBL or CFL ULX (Ville)
- State checker cleanups and improvements (Ville)
- GEM code reorganization to more files under gem subdirectory (Chris)
- Reducing dependency on a coarse struct_mutex (Chris)

Fixes:
- Fix use of uninitialized/incorrect error pointers (Colin, Dan)
- Fix DSI fastboot on some VLV/CHV platforms (Hans)
- Fix DSI error path (Hans)
- Add ICL port A combo PHY HW state check (Imre)
- Fix ICL AUX-B HW not done issue (Imre)
- Fix perf whitelist on gen10+ (Lionel)
- Fix PSR exit by forcing manual exit on older gens (José)
- Match voltage ranges instead of exact values (Lucas)
- Fix SDVO HDMI audio, with cleanups (Ville)
- Fix plane state dumps (Ville)
- Fix driver cleanup code to support driver hot unbind (Janusz)
- Add checks for ICL memory bandwidth requirements (Ville)
- Fix toggling between no C8 planes vs. at least one C8 plane (Ville)
- Improved checks on PLL usage conditions, refactoring (Ville)
- Avoid clobbering M/N values in fastset fuzzy checks (Ville)
- Take a runtime pm wakeref for atomic commits (Chris)
- Do not allow runtime pm autosuspend to remove userspace GGTT mmaps too quickly (Chris)
- Avoid refcount_inc on known zero count to avoid debug flagging (Chris)

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/87v9x1lpdh.fsf@intel.com
1  2 
drivers/gpu/drm/i915/display/intel_atomic.c
drivers/gpu/drm/i915/display/intel_atomic.h
drivers/gpu/drm/i915/display/intel_dp_mst.c
drivers/gpu/drm/i915/display/intel_sdvo.c
drivers/gpu/drm/i915/display/intel_tv.c
drivers/gpu/drm/i915/gt/intel_workarounds.c
drivers/gpu/drm/i915/gvt/cmd_parser.c
drivers/gpu/drm/i915/gvt/scheduler.c
drivers/gpu/drm/i915/i915_reg.h

index 0000000000000000000000000000000000000000,6b985e895a976d82ad428c3460b681fb11357afc..90ca11a4ae887188a0ef2137a72ceb7c23bff6f6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,438 +1,440 @@@
 -                                       struct drm_connector_state *new_state)
+ /*
+  * Copyright © 2015 Intel Corporation
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a
+  * copy of this software and associated documentation files (the "Software"),
+  * to deal in the Software without restriction, including without limitation
+  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  * and/or sell copies of the Software, and to permit persons to whom the
+  * Software is furnished to do so, subject to the following conditions:
+  *
+  * The above copyright notice and this permission notice (including the next
+  * paragraph) shall be included in all copies or substantial portions of the
+  * Software.
+  *
+  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+  * DEALINGS IN THE SOFTWARE.
+  */
+ /**
+  * DOC: atomic modeset support
+  *
+  * The functions here implement the state management and hardware programming
+  * dispatch required by the atomic modeset infrastructure.
+  * See intel_atomic_plane.c for the plane-specific atomic functionality.
+  */
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
+ #include <drm/drm_fourcc.h>
+ #include <drm/drm_plane_helper.h>
+ #include "intel_atomic.h"
+ #include "intel_drv.h"
+ #include "intel_hdcp.h"
+ #include "intel_sprite.h"
+ /**
+  * intel_digital_connector_atomic_get_property - hook for connector->atomic_get_property.
+  * @connector: Connector to get the property for.
+  * @state: Connector state to retrieve the property from.
+  * @property: Property to retrieve.
+  * @val: Return value for the property.
+  *
+  * Returns the atomic property value for a digital connector.
+  */
+ int intel_digital_connector_atomic_get_property(struct drm_connector *connector,
+                                               const struct drm_connector_state *state,
+                                               struct drm_property *property,
+                                               u64 *val)
+ {
+       struct drm_device *dev = connector->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_digital_connector_state *intel_conn_state =
+               to_intel_digital_connector_state(state);
+       if (property == dev_priv->force_audio_property)
+               *val = intel_conn_state->force_audio;
+       else if (property == dev_priv->broadcast_rgb_property)
+               *val = intel_conn_state->broadcast_rgb;
+       else {
+               DRM_DEBUG_ATOMIC("Unknown property [PROP:%d:%s]\n",
+                                property->base.id, property->name);
+               return -EINVAL;
+       }
+       return 0;
+ }
+ /**
+  * intel_digital_connector_atomic_set_property - hook for connector->atomic_set_property.
+  * @connector: Connector to set the property for.
+  * @state: Connector state to set the property on.
+  * @property: Property to set.
+  * @val: New value for the property.
+  *
+  * Sets the atomic property value for a digital connector.
+  */
+ int intel_digital_connector_atomic_set_property(struct drm_connector *connector,
+                                               struct drm_connector_state *state,
+                                               struct drm_property *property,
+                                               u64 val)
+ {
+       struct drm_device *dev = connector->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_digital_connector_state *intel_conn_state =
+               to_intel_digital_connector_state(state);
+       if (property == dev_priv->force_audio_property) {
+               intel_conn_state->force_audio = val;
+               return 0;
+       }
+       if (property == dev_priv->broadcast_rgb_property) {
+               intel_conn_state->broadcast_rgb = val;
+               return 0;
+       }
+       DRM_DEBUG_ATOMIC("Unknown property [PROP:%d:%s]\n",
+                        property->base.id, property->name);
+       return -EINVAL;
+ }
+ static bool blob_equal(const struct drm_property_blob *a,
+                      const struct drm_property_blob *b)
+ {
+       if (a && b)
+               return a->length == b->length &&
+                       !memcmp(a->data, b->data, a->length);
+       return !a == !b;
+ }
+ int intel_digital_connector_atomic_check(struct drm_connector *conn,
 -              drm_atomic_get_old_connector_state(new_state->state, conn);
++                                       struct drm_atomic_state *state)
+ {
++      struct drm_connector_state *new_state =
++              drm_atomic_get_new_connector_state(state, conn);
+       struct intel_digital_connector_state *new_conn_state =
+               to_intel_digital_connector_state(new_state);
+       struct drm_connector_state *old_state =
 -      crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
++              drm_atomic_get_old_connector_state(state, conn);
+       struct intel_digital_connector_state *old_conn_state =
+               to_intel_digital_connector_state(old_state);
+       struct drm_crtc_state *crtc_state;
+       intel_hdcp_atomic_check(conn, old_state, new_state);
+       if (!new_state->crtc)
+               return 0;
++      crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
+       /*
+        * These properties are handled by fastset, and might not end
+        * up in a modeset.
+        */
+       if (new_conn_state->force_audio != old_conn_state->force_audio ||
+           new_conn_state->broadcast_rgb != old_conn_state->broadcast_rgb ||
+           new_conn_state->base.colorspace != old_conn_state->base.colorspace ||
+           new_conn_state->base.picture_aspect_ratio != old_conn_state->base.picture_aspect_ratio ||
+           new_conn_state->base.content_type != old_conn_state->base.content_type ||
+           new_conn_state->base.scaling_mode != old_conn_state->base.scaling_mode ||
+           !blob_equal(new_conn_state->base.hdr_output_metadata,
+                       old_conn_state->base.hdr_output_metadata))
+               crtc_state->mode_changed = true;
+       return 0;
+ }
+ /**
+  * 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_digital_connector_duplicate_state(struct drm_connector *connector)
+ {
+       struct intel_digital_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;
+ }
+ /**
+  * intel_crtc_duplicate_state - duplicate crtc state
+  * @crtc: drm crtc
+  *
+  * Allocates and returns a copy of the crtc state (both common and
+  * Intel-specific) for the specified crtc.
+  *
+  * Returns: The newly allocated crtc state, or NULL on failure.
+  */
+ struct drm_crtc_state *
+ intel_crtc_duplicate_state(struct drm_crtc *crtc)
+ {
+       struct intel_crtc_state *crtc_state;
+       crtc_state = kmemdup(crtc->state, sizeof(*crtc_state), GFP_KERNEL);
+       if (!crtc_state)
+               return NULL;
+       __drm_atomic_helper_crtc_duplicate_state(crtc, &crtc_state->base);
+       crtc_state->update_pipe = false;
+       crtc_state->disable_lp_wm = false;
+       crtc_state->disable_cxsr = false;
+       crtc_state->update_wm_pre = false;
+       crtc_state->update_wm_post = false;
+       crtc_state->fb_changed = false;
+       crtc_state->fifo_changed = false;
+       crtc_state->wm.need_postvbl_update = false;
+       crtc_state->fb_bits = 0;
+       crtc_state->update_planes = 0;
+       return &crtc_state->base;
+ }
+ /**
+  * intel_crtc_destroy_state - destroy crtc state
+  * @crtc: drm crtc
+  * @state: the state to destroy
+  *
+  * Destroys the crtc state (both common and Intel-specific) for the
+  * specified crtc.
+  */
+ void
+ intel_crtc_destroy_state(struct drm_crtc *crtc,
+                        struct drm_crtc_state *state)
+ {
+       drm_atomic_helper_crtc_destroy_state(crtc, state);
+ }
+ static void intel_atomic_setup_scaler(struct intel_crtc_scaler_state *scaler_state,
+                                     int num_scalers_need, struct intel_crtc *intel_crtc,
+                                     const char *name, int idx,
+                                     struct intel_plane_state *plane_state,
+                                     int *scaler_id)
+ {
+       struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev);
+       int j;
+       u32 mode;
+       if (*scaler_id < 0) {
+               /* find a free scaler */
+               for (j = 0; j < intel_crtc->num_scalers; j++) {
+                       if (scaler_state->scalers[j].in_use)
+                               continue;
+                       *scaler_id = j;
+                       scaler_state->scalers[*scaler_id].in_use = 1;
+                       break;
+               }
+       }
+       if (WARN(*scaler_id < 0, "Cannot find scaler for %s:%d\n", name, idx))
+               return;
+       /* set scaler mode */
+       if (plane_state && plane_state->base.fb &&
+           plane_state->base.fb->format->is_yuv &&
+           plane_state->base.fb->format->num_planes > 1) {
+               struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
+               if (IS_GEN(dev_priv, 9) &&
+                   !IS_GEMINILAKE(dev_priv)) {
+                       mode = SKL_PS_SCALER_MODE_NV12;
+               } else if (icl_is_hdr_plane(dev_priv, plane->id)) {
+                       /*
+                        * On gen11+'s HDR planes we only use the scaler for
+                        * scaling. They have a dedicated chroma upsampler, so
+                        * we don't need the scaler to upsample the UV plane.
+                        */
+                       mode = PS_SCALER_MODE_NORMAL;
+               } else {
+                       mode = PS_SCALER_MODE_PLANAR;
+                       if (plane_state->linked_plane)
+                               mode |= PS_PLANE_Y_SEL(plane_state->linked_plane->id);
+               }
+       } else if (INTEL_GEN(dev_priv) > 9 || IS_GEMINILAKE(dev_priv)) {
+               mode = PS_SCALER_MODE_NORMAL;
+       } else if (num_scalers_need == 1 && intel_crtc->num_scalers > 1) {
+               /*
+                * when only 1 scaler is in use on a pipe with 2 scalers
+                * scaler 0 operates in high quality (HQ) mode.
+                * In this case use scaler 0 to take advantage of HQ mode
+                */
+               scaler_state->scalers[*scaler_id].in_use = 0;
+               *scaler_id = 0;
+               scaler_state->scalers[0].in_use = 1;
+               mode = SKL_PS_SCALER_MODE_HQ;
+       } else {
+               mode = SKL_PS_SCALER_MODE_DYN;
+       }
+       DRM_DEBUG_KMS("Attached scaler id %u.%u to %s:%d\n",
+                     intel_crtc->pipe, *scaler_id, name, idx);
+       scaler_state->scalers[*scaler_id].mode = mode;
+ }
+ /**
+  * intel_atomic_setup_scalers() - setup scalers for crtc per staged requests
+  * @dev_priv: i915 device
+  * @intel_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_i915_private *dev_priv,
+                              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 =
+               &crtc_state->scaler_state;
+       struct drm_atomic_state *drm_state = crtc_state->base.state;
+       struct intel_atomic_state *intel_state = to_intel_atomic_state(drm_state);
+       int num_scalers_need;
+       int i;
+       num_scalers_need = hweight32(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;
+               const char *name;
+               int idx;
+               /* skip if scaler not required */
+               if (!(scaler_state->scaler_users & (1 << i)))
+                       continue;
+               if (i == SKL_CRTC_INDEX) {
+                       name = "CRTC";
+                       idx = intel_crtc->base.base.id;
+                       /* panel fitter case: assign as a crtc scaler */
+                       scaler_id = &scaler_state->scaler_id;
+               } else {
+                       name = "PLANE";
+                       /* plane scaler case: assign as a plane scaler */
+                       /* find the plane that set the bit as scaler_user */
+                       plane = drm_state->planes[i].ptr;
+                       /*
+                        * 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_priv->drm, 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);
+                               }
+                               /*
+                                * the plane is added after plane checks are run,
+                                * but since this plane is unchanged just do the
+                                * minimum required validation.
+                                */
+                               crtc_state->base.planes_changed = true;
+                       }
+                       intel_plane = to_intel_plane(plane);
+                       idx = plane->base.id;
+                       /* plane on different crtc cannot be a scaler user of this crtc */
+                       if (WARN_ON(intel_plane->pipe != intel_crtc->pipe))
+                               continue;
+                       plane_state = intel_atomic_get_new_plane_state(intel_state,
+                                                                      intel_plane);
+                       scaler_id = &plane_state->scaler_id;
+               }
+               intel_atomic_setup_scaler(scaler_state, num_scalers_need,
+                                         intel_crtc, name, idx,
+                                         plane_state, scaler_id);
+       }
+       return 0;
+ }
+ struct drm_atomic_state *
+ intel_atomic_state_alloc(struct drm_device *dev)
+ {
+       struct intel_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state || drm_atomic_state_init(dev, &state->base) < 0) {
+               kfree(state);
+               return NULL;
+       }
+       return &state->base;
+ }
+ void intel_atomic_state_clear(struct drm_atomic_state *s)
+ {
+       struct intel_atomic_state *state = to_intel_atomic_state(s);
+       drm_atomic_state_default_clear(&state->base);
+       state->dpll_set = state->modeset = false;
+ }
+ struct intel_crtc_state *
+ intel_atomic_get_crtc_state(struct drm_atomic_state *state,
+                           struct intel_crtc *crtc)
+ {
+       struct drm_crtc_state *crtc_state;
+       crtc_state = drm_atomic_get_crtc_state(state, &crtc->base);
+       if (IS_ERR(crtc_state))
+               return ERR_CAST(crtc_state);
+       return to_intel_crtc_state(crtc_state);
+ }
index 0000000000000000000000000000000000000000,1c8507da1a690ba7255622a88a4cdf7c9e53d621..58065d3161a34190b0b7602b69eb3643462789f9
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,49 +1,49 @@@
 -                                       struct drm_connector_state *new_state);
+ /* SPDX-License-Identifier: MIT */
+ /*
+  * Copyright © 2019 Intel Corporation
+  */
+ #ifndef __INTEL_ATOMIC_H__
+ #define __INTEL_ATOMIC_H__
+ #include <linux/types.h>
+ struct drm_atomic_state;
+ struct drm_connector;
+ struct drm_connector_state;
+ struct drm_crtc;
+ struct drm_crtc_state;
+ struct drm_device;
+ struct drm_i915_private;
+ struct drm_property;
+ struct intel_crtc;
+ struct intel_crtc_state;
+ int intel_digital_connector_atomic_get_property(struct drm_connector *connector,
+                                               const struct drm_connector_state *state,
+                                               struct drm_property *property,
+                                               u64 *val);
+ int intel_digital_connector_atomic_set_property(struct drm_connector *connector,
+                                               struct drm_connector_state *state,
+                                               struct drm_property *property,
+                                               u64 val);
+ int intel_digital_connector_atomic_check(struct drm_connector *conn,
++                                       struct drm_atomic_state *state);
+ struct drm_connector_state *
+ intel_digital_connector_duplicate_state(struct drm_connector *connector);
+ struct drm_crtc_state *intel_crtc_duplicate_state(struct drm_crtc *crtc);
+ void intel_crtc_destroy_state(struct drm_crtc *crtc,
+                              struct drm_crtc_state *state);
+ struct drm_atomic_state *intel_atomic_state_alloc(struct drm_device *dev);
+ void intel_atomic_state_clear(struct drm_atomic_state *state);
+ struct intel_crtc_state *
+ intel_atomic_get_crtc_state(struct drm_atomic_state *state,
+                           struct intel_crtc *crtc);
+ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv,
+                              struct intel_crtc *intel_crtc,
+                              struct intel_crtc_state *crtc_state);
+ #endif /* __INTEL_ATOMIC_H__ */
index 0000000000000000000000000000000000000000,0caf645fbbb841b38a63e05c73ea198e4ed70d22..60652ebbdf610a1cfda5228147265c77fe30f438
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,664 +1,665 @@@
 -                        struct drm_connector_state *new_conn_state)
+ /*
+  * Copyright © 2008 Intel Corporation
+  *             2014 Red Hat Inc.
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a
+  * copy of this software and associated documentation files (the "Software"),
+  * to deal in the Software without restriction, including without limitation
+  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  * and/or sell copies of the Software, and to permit persons to whom the
+  * Software is furnished to do so, subject to the following conditions:
+  *
+  * The above copyright notice and this permission notice (including the next
+  * paragraph) shall be included in all copies or substantial portions of the
+  * Software.
+  *
+  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+  * IN THE SOFTWARE.
+  *
+  */
+ #include <drm/drm_atomic_helper.h>
+ #include <drm/drm_edid.h>
+ #include <drm/drm_probe_helper.h>
+ #include "i915_drv.h"
+ #include "intel_atomic.h"
+ #include "intel_audio.h"
+ #include "intel_connector.h"
+ #include "intel_ddi.h"
+ #include "intel_dp.h"
+ #include "intel_dp_mst.h"
+ #include "intel_dpio_phy.h"
+ #include "intel_drv.h"
+ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
+                                           struct intel_crtc_state *crtc_state,
+                                           struct drm_connector_state *conn_state,
+                                           struct link_config_limits *limits)
+ {
+       struct drm_atomic_state *state = crtc_state->base.state;
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_dp *intel_dp = &intel_mst->primary->dp;
+       struct intel_connector *connector =
+               to_intel_connector(conn_state->connector);
+       const struct drm_display_mode *adjusted_mode =
+               &crtc_state->base.adjusted_mode;
+       void *port = connector->port;
+       bool constant_n = drm_dp_has_quirk(&intel_dp->desc,
+                                          DP_DPCD_QUIRK_CONSTANT_N);
+       int bpp, slots = -EINVAL;
+       crtc_state->lane_count = limits->max_lane_count;
+       crtc_state->port_clock = limits->max_clock;
+       for (bpp = limits->max_bpp; bpp >= limits->min_bpp; bpp -= 2 * 3) {
+               crtc_state->pipe_bpp = bpp;
+               crtc_state->pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock,
+                                                      crtc_state->pipe_bpp);
+               slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,
+                                                     port, crtc_state->pbn);
+               if (slots == -EDEADLK)
+                       return slots;
+               if (slots >= 0)
+                       break;
+       }
+       if (slots < 0) {
+               DRM_DEBUG_KMS("failed finding vcpi slots:%d\n", slots);
+               return slots;
+       }
+       intel_link_compute_m_n(crtc_state->pipe_bpp,
+                              crtc_state->lane_count,
+                              adjusted_mode->crtc_clock,
+                              crtc_state->port_clock,
+                              &crtc_state->dp_m_n,
+                              constant_n);
+       crtc_state->dp_m_n.tu = slots;
+       return 0;
+ }
+ static int intel_dp_mst_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_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_dp *intel_dp = &intel_mst->primary->dp;
+       struct intel_connector *connector =
+               to_intel_connector(conn_state->connector);
+       struct intel_digital_connector_state *intel_conn_state =
+               to_intel_digital_connector_state(conn_state);
+       const struct drm_display_mode *adjusted_mode =
+               &pipe_config->base.adjusted_mode;
+       void *port = connector->port;
+       struct link_config_limits limits;
+       int ret;
+       if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return -EINVAL;
+       pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
+       pipe_config->has_pch_encoder = false;
+       if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO)
+               pipe_config->has_audio =
+                       drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, port);
+       else
+               pipe_config->has_audio =
+                       intel_conn_state->force_audio == HDMI_AUDIO_ON;
+       /*
+        * for MST we always configure max link bw - the spec doesn't
+        * seem to suggest we should do otherwise.
+        */
+       limits.min_clock =
+       limits.max_clock = intel_dp_max_link_rate(intel_dp);
+       limits.min_lane_count =
+       limits.max_lane_count = intel_dp_max_lane_count(intel_dp);
+       limits.min_bpp = intel_dp_min_bpp(pipe_config);
+       limits.max_bpp = pipe_config->pipe_bpp;
+       intel_dp_adjust_compliance_config(intel_dp, pipe_config, &limits);
+       ret = intel_dp_mst_compute_link_config(encoder, pipe_config,
+                                              conn_state, &limits);
+       if (ret)
+               return ret;
+       pipe_config->limited_color_range =
+               intel_dp_limited_color_range(pipe_config, conn_state);
+       if (IS_GEN9_LP(dev_priv))
+               pipe_config->lane_lat_optim_mask =
+                       bxt_ddi_phy_calc_lane_lat_optim_mask(pipe_config->lane_count);
+       intel_ddi_compute_min_voltage_level(dev_priv, pipe_config);
+       return 0;
+ }
+ static int
+ intel_dp_mst_atomic_check(struct drm_connector *connector,
 -      struct drm_atomic_state *state = new_conn_state->state;
++                        struct drm_atomic_state *state)
+ {
 -      ret = intel_digital_connector_atomic_check(connector, new_conn_state);
++      struct drm_connector_state *new_conn_state =
++              drm_atomic_get_new_connector_state(state, connector);
+       struct drm_connector_state *old_conn_state =
+               drm_atomic_get_old_connector_state(state, connector);
+       struct intel_connector *intel_connector =
+               to_intel_connector(connector);
+       struct drm_crtc *new_crtc = new_conn_state->crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_dp_mst_topology_mgr *mgr;
+       int ret;
++      ret = intel_digital_connector_atomic_check(connector, state);
+       if (ret)
+               return ret;
+       if (!old_conn_state->crtc)
+               return 0;
+       /* We only want to free VCPI if this state disables the CRTC on this
+        * connector
+        */
+       if (new_crtc) {
+               crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
+               if (!crtc_state ||
+                   !drm_atomic_crtc_needs_modeset(crtc_state) ||
+                   crtc_state->enable)
+                       return 0;
+       }
+       mgr = &enc_to_mst(old_conn_state->best_encoder)->primary->dp.mst_mgr;
+       ret = drm_dp_atomic_release_vcpi_slots(state, mgr,
+                                              intel_connector->port);
+       return ret;
+ }
+ static void intel_mst_disable_dp(struct intel_encoder *encoder,
+                                const struct intel_crtc_state *old_crtc_state,
+                                const struct drm_connector_state *old_conn_state)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct intel_connector *connector =
+               to_intel_connector(old_conn_state->connector);
+       int ret;
+       DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
+       drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port);
+       ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+       if (ret) {
+               DRM_ERROR("failed to update payload %d\n", ret);
+       }
+       if (old_crtc_state->has_audio)
+               intel_audio_codec_disable(encoder,
+                                         old_crtc_state, old_conn_state);
+ }
+ static void intel_mst_post_disable_dp(struct intel_encoder *encoder,
+                                     const struct intel_crtc_state *old_crtc_state,
+                                     const struct drm_connector_state *old_conn_state)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct intel_connector *connector =
+               to_intel_connector(old_conn_state->connector);
+       intel_ddi_disable_pipe_clock(old_crtc_state);
+       /* this can fail */
+       drm_dp_check_act_status(&intel_dp->mst_mgr);
+       /* and this can also fail */
+       drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+       drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port);
+       /*
+        * Power down mst path before disabling the port, otherwise we end
+        * up getting interrupts from the sink upon detecting link loss.
+        */
+       drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port,
+                                    false);
+       intel_dp->active_mst_links--;
+       intel_mst->connector = NULL;
+       if (intel_dp->active_mst_links == 0) {
+               intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
+               intel_dig_port->base.post_disable(&intel_dig_port->base,
+                                                 old_crtc_state, NULL);
+       }
+       DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
+ }
+ static void intel_mst_pre_pll_enable_dp(struct intel_encoder *encoder,
+                                       const struct intel_crtc_state *pipe_config,
+                                       const struct drm_connector_state *conn_state)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       if (intel_dp->active_mst_links == 0)
+               intel_dig_port->base.pre_pll_enable(&intel_dig_port->base,
+                                                   pipe_config, NULL);
+ }
+ static void intel_mst_post_pll_disable_dp(struct intel_encoder *encoder,
+                                         const struct intel_crtc_state *old_crtc_state,
+                                         const struct drm_connector_state *old_conn_state)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       if (intel_dp->active_mst_links == 0)
+               intel_dig_port->base.post_pll_disable(&intel_dig_port->base,
+                                                     old_crtc_state,
+                                                     old_conn_state);
+ }
+ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder,
+                                   const struct intel_crtc_state *pipe_config,
+                                   const struct drm_connector_state *conn_state)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       enum port port = intel_dig_port->base.port;
+       struct intel_connector *connector =
+               to_intel_connector(conn_state->connector);
+       int ret;
+       u32 temp;
+       /* MST encoders are bound to a crtc, not to a connector,
+        * force the mapping here for get_hw_state.
+        */
+       connector->encoder = encoder;
+       intel_mst->connector = connector;
+       DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
+       if (intel_dp->active_mst_links == 0)
+               intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+       drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true);
+       if (intel_dp->active_mst_links == 0)
+               intel_dig_port->base.pre_enable(&intel_dig_port->base,
+                                               pipe_config, NULL);
+       ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
+                                      connector->port,
+                                      pipe_config->pbn,
+                                      pipe_config->dp_m_n.tu);
+       if (!ret)
+               DRM_ERROR("failed to allocate vcpi\n");
+       intel_dp->active_mst_links++;
+       temp = I915_READ(DP_TP_STATUS(port));
+       I915_WRITE(DP_TP_STATUS(port), temp);
+       ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+       intel_ddi_enable_pipe_clock(pipe_config);
+ }
+ static void intel_mst_enable_dp(struct intel_encoder *encoder,
+                               const struct intel_crtc_state *pipe_config,
+                               const struct drm_connector_state *conn_state)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       enum port port = intel_dig_port->base.port;
+       DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
+       if (intel_wait_for_register(&dev_priv->uncore,
+                                   DP_TP_STATUS(port),
+                                   DP_TP_STATUS_ACT_SENT,
+                                   DP_TP_STATUS_ACT_SENT,
+                                   1))
+               DRM_ERROR("Timed out waiting for ACT sent\n");
+       drm_dp_check_act_status(&intel_dp->mst_mgr);
+       drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+       if (pipe_config->has_audio)
+               intel_audio_codec_enable(encoder, pipe_config, conn_state);
+ }
+ static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
+                                     enum pipe *pipe)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       *pipe = intel_mst->pipe;
+       if (intel_mst->connector)
+               return true;
+       return false;
+ }
+ static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
+                                       struct intel_crtc_state *pipe_config)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       intel_ddi_get_config(&intel_dig_port->base, pipe_config);
+ }
+ static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
+ {
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct intel_dp *intel_dp = intel_connector->mst_port;
+       struct edid *edid;
+       int ret;
+       if (drm_connector_is_unregistered(connector))
+               return intel_connector_update_modes(connector, NULL);
+       edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
+       ret = intel_connector_update_modes(connector, edid);
+       kfree(edid);
+       return ret;
+ }
+ static enum drm_connector_status
+ intel_dp_mst_detect(struct drm_connector *connector, bool force)
+ {
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct intel_dp *intel_dp = intel_connector->mst_port;
+       if (drm_connector_is_unregistered(connector))
+               return connector_status_disconnected;
+       return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr,
+                                     intel_connector->port);
+ }
+ static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
+       .detect = intel_dp_mst_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .atomic_get_property = intel_digital_connector_atomic_get_property,
+       .atomic_set_property = intel_digital_connector_atomic_set_property,
+       .late_register = intel_connector_register,
+       .early_unregister = intel_connector_unregister,
+       .destroy = intel_connector_destroy,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+       .atomic_duplicate_state = intel_digital_connector_duplicate_state,
+ };
+ static int intel_dp_mst_get_modes(struct drm_connector *connector)
+ {
+       return intel_dp_mst_get_ddc_modes(connector);
+ }
+ static enum drm_mode_status
+ intel_dp_mst_mode_valid(struct drm_connector *connector,
+                       struct drm_display_mode *mode)
+ {
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct intel_dp *intel_dp = intel_connector->mst_port;
+       int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
+       int max_rate, mode_rate, max_lanes, max_link_clock;
+       if (drm_connector_is_unregistered(connector))
+               return MODE_ERROR;
+       if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+       max_link_clock = intel_dp_max_link_rate(intel_dp);
+       max_lanes = intel_dp_max_lane_count(intel_dp);
+       max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes);
+       mode_rate = intel_dp_link_required(mode->clock, 18);
+       /* TODO - validate mode against available PBN for link */
+       if (mode->clock < 10000)
+               return MODE_CLOCK_LOW;
+       if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+               return MODE_H_ILLEGAL;
+       if (mode_rate > max_rate || mode->clock > max_dotclk)
+               return MODE_CLOCK_HIGH;
+       return MODE_OK;
+ }
+ static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector,
+                                                        struct drm_connector_state *state)
+ {
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct intel_dp *intel_dp = intel_connector->mst_port;
+       struct intel_crtc *crtc = to_intel_crtc(state->crtc);
+       return &intel_dp->mst_encoders[crtc->pipe]->base.base;
+ }
+ static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
+       .get_modes = intel_dp_mst_get_modes,
+       .mode_valid = intel_dp_mst_mode_valid,
+       .atomic_best_encoder = intel_mst_atomic_best_encoder,
+       .atomic_check = intel_dp_mst_atomic_check,
+ };
+ static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
+ {
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
+       drm_encoder_cleanup(encoder);
+       kfree(intel_mst);
+ }
+ static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
+       .destroy = intel_dp_mst_encoder_destroy,
+ };
+ static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
+ {
+       if (connector->encoder && connector->base.state->crtc) {
+               enum pipe pipe;
+               if (!connector->encoder->get_hw_state(connector->encoder, &pipe))
+                       return false;
+               return true;
+       }
+       return false;
+ }
+ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *pathprop)
+ {
+       struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_connector *intel_connector;
+       struct drm_connector *connector;
+       enum pipe pipe;
+       int ret;
+       intel_connector = intel_connector_alloc();
+       if (!intel_connector)
+               return NULL;
+       intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
+       intel_connector->mst_port = intel_dp;
+       intel_connector->port = port;
+       drm_dp_mst_get_port_malloc(port);
+       connector = &intel_connector->base;
+       ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs,
+                                DRM_MODE_CONNECTOR_DisplayPort);
+       if (ret) {
+               intel_connector_free(intel_connector);
+               return NULL;
+       }
+       drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
+       for_each_pipe(dev_priv, pipe) {
+               struct drm_encoder *enc =
+                       &intel_dp->mst_encoders[pipe]->base.base;
+               ret = drm_connector_attach_encoder(&intel_connector->base, enc);
+               if (ret)
+                       goto err;
+       }
+       drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
+       drm_object_attach_property(&connector->base, dev->mode_config.tile_property, 0);
+       ret = drm_connector_set_path_property(connector, pathprop);
+       if (ret)
+               goto err;
+       intel_attach_force_audio_property(connector);
+       intel_attach_broadcast_rgb_property(connector);
+       drm_connector_attach_max_bpc_property(connector, 6, 12);
+       return connector;
+ err:
+       drm_connector_cleanup(connector);
+       return NULL;
+ }
+ static void intel_dp_register_mst_connector(struct drm_connector *connector)
+ {
+       struct drm_i915_private *dev_priv = to_i915(connector->dev);
+       if (dev_priv->fbdev)
+               drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper,
+                                               connector);
+       drm_connector_register(connector);
+ }
+ static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
+                                          struct drm_connector *connector)
+ {
+       struct drm_i915_private *dev_priv = to_i915(connector->dev);
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name);
+       drm_connector_unregister(connector);
+       if (dev_priv->fbdev)
+               drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper,
+                                                  connector);
+       drm_connector_put(connector);
+ }
+ static const struct drm_dp_mst_topology_cbs mst_cbs = {
+       .add_connector = intel_dp_add_mst_connector,
+       .register_connector = intel_dp_register_mst_connector,
+       .destroy_connector = intel_dp_destroy_mst_connector,
+ };
+ static struct intel_dp_mst_encoder *
+ intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe)
+ {
+       struct intel_dp_mst_encoder *intel_mst;
+       struct intel_encoder *intel_encoder;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
+       if (!intel_mst)
+               return NULL;
+       intel_mst->pipe = pipe;
+       intel_encoder = &intel_mst->base;
+       intel_mst->primary = intel_dig_port;
+       drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
+                        DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe));
+       intel_encoder->type = INTEL_OUTPUT_DP_MST;
+       intel_encoder->power_domain = intel_dig_port->base.power_domain;
+       intel_encoder->port = intel_dig_port->base.port;
+       intel_encoder->crtc_mask = 0x7;
+       intel_encoder->cloneable = 0;
+       intel_encoder->compute_config = intel_dp_mst_compute_config;
+       intel_encoder->disable = intel_mst_disable_dp;
+       intel_encoder->post_disable = intel_mst_post_disable_dp;
+       intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp;
+       intel_encoder->post_pll_disable = intel_mst_post_pll_disable_dp;
+       intel_encoder->pre_enable = intel_mst_pre_enable_dp;
+       intel_encoder->enable = intel_mst_enable_dp;
+       intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
+       intel_encoder->get_config = intel_dp_mst_enc_get_config;
+       return intel_mst;
+ }
+ static bool
+ intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port)
+ {
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev);
+       enum pipe pipe;
+       for_each_pipe(dev_priv, pipe)
+               intel_dp->mst_encoders[pipe] = intel_dp_create_fake_mst_encoder(intel_dig_port, pipe);
+       return true;
+ }
+ int
+ intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id)
+ {
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       int ret;
+       intel_dp->can_mst = true;
+       intel_dp->mst_mgr.cbs = &mst_cbs;
+       /* create encoders */
+       intel_dp_create_fake_mst_encoders(intel_dig_port);
+       ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev,
+                                          &intel_dp->aux, 16, 3, conn_base_id);
+       if (ret) {
+               intel_dp->can_mst = false;
+               return ret;
+       }
+       return 0;
+ }
+ void
+ intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
+ {
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       if (!intel_dp->can_mst)
+               return;
+       drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
+       /* encoders will get killed by normal cleanup */
+ }
index 0000000000000000000000000000000000000000,0860ae36bb875b1c5125a4a40ea58ea047401b1a..ceda03e5a3d4e0e50de7850fa155f13c7b464d34
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,3333 +1,3334 @@@
 -                                 struct drm_connector_state *new_conn_state)
+ /*
+  * Copyright 2006 Dave Airlie <airlied@linux.ie>
+  * Copyright © 2006-2007 Intel Corporation
+  *   Jesse Barnes <jesse.barnes@intel.com>
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a
+  * copy of this software and associated documentation files (the "Software"),
+  * to deal in the Software without restriction, including without limitation
+  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  * and/or sell copies of the Software, and to permit persons to whom the
+  * Software is furnished to do so, subject to the following conditions:
+  *
+  * The above copyright notice and this permission notice (including the next
+  * paragraph) shall be included in all copies or substantial portions of the
+  * Software.
+  *
+  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+  * DEALINGS IN THE SOFTWARE.
+  *
+  * Authors:
+  *    Eric Anholt <eric@anholt.net>
+  */
+ #include <linux/delay.h>
+ #include <linux/export.h>
+ #include <linux/i2c.h>
+ #include <linux/slab.h>
+ #include <drm/drm_atomic_helper.h>
+ #include <drm/drm_crtc.h>
+ #include <drm/drm_edid.h>
+ #include <drm/i915_drm.h>
+ #include "i915_drv.h"
+ #include "intel_atomic.h"
+ #include "intel_connector.h"
+ #include "intel_drv.h"
+ #include "intel_fifo_underrun.h"
+ #include "intel_gmbus.h"
+ #include "intel_hdmi.h"
+ #include "intel_hotplug.h"
+ #include "intel_panel.h"
+ #include "intel_sdvo.h"
+ #include "intel_sdvo_regs.h"
+ #define SDVO_TMDS_MASK (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)
+ #define SDVO_RGB_MASK  (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1)
+ #define SDVO_LVDS_MASK (SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1)
+ #define SDVO_TV_MASK   (SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_YPRPB0)
+ #define SDVO_OUTPUT_MASK (SDVO_TMDS_MASK | SDVO_RGB_MASK | SDVO_LVDS_MASK |\
+                       SDVO_TV_MASK)
+ #define IS_TV(c)      (c->output_flag & SDVO_TV_MASK)
+ #define IS_TMDS(c)    (c->output_flag & SDVO_TMDS_MASK)
+ #define IS_LVDS(c)    (c->output_flag & SDVO_LVDS_MASK)
+ #define IS_TV_OR_LVDS(c) (c->output_flag & (SDVO_TV_MASK | SDVO_LVDS_MASK))
+ #define IS_DIGITAL(c) (c->output_flag & (SDVO_TMDS_MASK | SDVO_LVDS_MASK))
+ static const char * const tv_format_names[] = {
+       "NTSC_M"   , "NTSC_J"  , "NTSC_443",
+       "PAL_B"    , "PAL_D"   , "PAL_G"   ,
+       "PAL_H"    , "PAL_I"   , "PAL_M"   ,
+       "PAL_N"    , "PAL_NC"  , "PAL_60"  ,
+       "SECAM_B"  , "SECAM_D" , "SECAM_G" ,
+       "SECAM_K"  , "SECAM_K1", "SECAM_L" ,
+       "SECAM_60"
+ };
+ #define TV_FORMAT_NUM  ARRAY_SIZE(tv_format_names)
+ struct intel_sdvo {
+       struct intel_encoder base;
+       struct i2c_adapter *i2c;
+       u8 slave_addr;
+       struct i2c_adapter ddc;
+       /* Register for the SDVO device: SDVOB or SDVOC */
+       i915_reg_t sdvo_reg;
+       /* Active outputs controlled by this SDVO output */
+       u16 controlled_output;
+       /*
+        * Capabilities of the SDVO device returned by
+        * intel_sdvo_get_capabilities()
+        */
+       struct intel_sdvo_caps caps;
+       /* Pixel clock limitations reported by the SDVO device, in kHz */
+       int pixel_clock_min, pixel_clock_max;
+       /*
+       * For multiple function SDVO device,
+       * this is for current attached outputs.
+       */
+       u16 attached_output;
+       /*
+        * Hotplug activation bits for this device
+        */
+       u16 hotplug_active;
+       enum port port;
+       bool has_hdmi_monitor;
+       bool has_hdmi_audio;
+       /* DDC bus used by this SDVO encoder */
+       u8 ddc_bus;
+       /*
+        * the sdvo flag gets lost in round trip: dtd->adjusted_mode->dtd
+        */
+       u8 dtd_sdvo_flags;
+ };
+ struct intel_sdvo_connector {
+       struct intel_connector base;
+       /* Mark the type of connector */
+       u16 output_flag;
+       /* This contains all current supported TV format */
+       u8 tv_format_supported[TV_FORMAT_NUM];
+       int   format_supported_num;
+       struct drm_property *tv_format;
+       /* add the property for the SDVO-TV */
+       struct drm_property *left;
+       struct drm_property *right;
+       struct drm_property *top;
+       struct drm_property *bottom;
+       struct drm_property *hpos;
+       struct drm_property *vpos;
+       struct drm_property *contrast;
+       struct drm_property *saturation;
+       struct drm_property *hue;
+       struct drm_property *sharpness;
+       struct drm_property *flicker_filter;
+       struct drm_property *flicker_filter_adaptive;
+       struct drm_property *flicker_filter_2d;
+       struct drm_property *tv_chroma_filter;
+       struct drm_property *tv_luma_filter;
+       struct drm_property *dot_crawl;
+       /* add the property for the SDVO-TV/LVDS */
+       struct drm_property *brightness;
+       /* this is to get the range of margin.*/
+       u32 max_hscan, max_vscan;
+       /**
+        * This is set if we treat the device as HDMI, instead of DVI.
+        */
+       bool is_hdmi;
+ };
+ struct intel_sdvo_connector_state {
+       /* base.base: tv.saturation/contrast/hue/brightness */
+       struct intel_digital_connector_state base;
+       struct {
+               unsigned overscan_h, overscan_v, hpos, vpos, sharpness;
+               unsigned flicker_filter, flicker_filter_2d, flicker_filter_adaptive;
+               unsigned chroma_filter, luma_filter, dot_crawl;
+       } tv;
+ };
+ static struct intel_sdvo *to_sdvo(struct intel_encoder *encoder)
+ {
+       return container_of(encoder, struct intel_sdvo, base);
+ }
+ static struct intel_sdvo *intel_attached_sdvo(struct drm_connector *connector)
+ {
+       return to_sdvo(intel_attached_encoder(connector));
+ }
+ static struct intel_sdvo_connector *
+ to_intel_sdvo_connector(struct drm_connector *connector)
+ {
+       return container_of(connector, struct intel_sdvo_connector, base.base);
+ }
+ #define to_intel_sdvo_connector_state(conn_state) \
+       container_of((conn_state), struct intel_sdvo_connector_state, base.base)
+ static bool
+ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, u16 flags);
+ static bool
+ intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
+                             struct intel_sdvo_connector *intel_sdvo_connector,
+                             int type);
+ static bool
+ intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
+                                  struct intel_sdvo_connector *intel_sdvo_connector);
+ /*
+  * Writes the SDVOB or SDVOC with the given value, but always writes both
+  * SDVOB and SDVOC to work around apparent hardware issues (according to
+  * comments in the BIOS).
+  */
+ static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val)
+ {
+       struct drm_device *dev = intel_sdvo->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       u32 bval = val, cval = val;
+       int i;
+       if (HAS_PCH_SPLIT(dev_priv)) {
+               I915_WRITE(intel_sdvo->sdvo_reg, val);
+               POSTING_READ(intel_sdvo->sdvo_reg);
+               /*
+                * HW workaround, need to write this twice for issue
+                * that may result in first write getting masked.
+                */
+               if (HAS_PCH_IBX(dev_priv)) {
+                       I915_WRITE(intel_sdvo->sdvo_reg, val);
+                       POSTING_READ(intel_sdvo->sdvo_reg);
+               }
+               return;
+       }
+       if (intel_sdvo->port == PORT_B)
+               cval = I915_READ(GEN3_SDVOC);
+       else
+               bval = I915_READ(GEN3_SDVOB);
+       /*
+        * Write the registers twice for luck. Sometimes,
+        * writing them only once doesn't appear to 'stick'.
+        * The BIOS does this too. Yay, magic
+        */
+       for (i = 0; i < 2; i++) {
+               I915_WRITE(GEN3_SDVOB, bval);
+               POSTING_READ(GEN3_SDVOB);
+               I915_WRITE(GEN3_SDVOC, cval);
+               POSTING_READ(GEN3_SDVOC);
+       }
+ }
+ static bool intel_sdvo_read_byte(struct intel_sdvo *intel_sdvo, u8 addr, u8 *ch)
+ {
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = intel_sdvo->slave_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = &addr,
+               },
+               {
+                       .addr = intel_sdvo->slave_addr,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = ch,
+               }
+       };
+       int ret;
+       if ((ret = i2c_transfer(intel_sdvo->i2c, msgs, 2)) == 2)
+               return true;
+       DRM_DEBUG_KMS("i2c transfer returned %d\n", ret);
+       return false;
+ }
+ #define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
+ /** Mapping of command numbers to names, for debug output */
+ static const struct _sdvo_cmd_name {
+       u8 cmd;
+       const char *name;
+ } __attribute__ ((packed)) sdvo_cmd_names[] = {
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS),
+       /* Add the op code for SDVO enhancements */
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HPOS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HPOS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HPOS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_VPOS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_VPOS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_VPOS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_2D),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_2D),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_2D),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SHARPNESS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SHARPNESS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SHARPNESS),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DOT_CRAWL),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DOT_CRAWL),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_CHROMA_FILTER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_CHROMA_FILTER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_CHROMA_FILTER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_LUMA_FILTER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_LUMA_FILTER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_LUMA_FILTER),
+       /* HDMI op code */
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA),
+       SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA),
+ };
+ #define SDVO_NAME(svdo) ((svdo)->port == PORT_B ? "SDVOB" : "SDVOC")
+ static void intel_sdvo_debug_write(struct intel_sdvo *intel_sdvo, u8 cmd,
+                                  const void *args, int args_len)
+ {
+       int i, pos = 0;
+ #define BUF_LEN 256
+       char buffer[BUF_LEN];
+ #define BUF_PRINT(args...) \
+       pos += snprintf(buffer + pos, max_t(int, BUF_LEN - pos, 0), args)
+       for (i = 0; i < args_len; i++) {
+               BUF_PRINT("%02X ", ((u8 *)args)[i]);
+       }
+       for (; i < 8; i++) {
+               BUF_PRINT("   ");
+       }
+       for (i = 0; i < ARRAY_SIZE(sdvo_cmd_names); i++) {
+               if (cmd == sdvo_cmd_names[i].cmd) {
+                       BUF_PRINT("(%s)", sdvo_cmd_names[i].name);
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(sdvo_cmd_names)) {
+               BUF_PRINT("(%02X)", cmd);
+       }
+       BUG_ON(pos >= BUF_LEN - 1);
+ #undef BUF_PRINT
+ #undef BUF_LEN
+       DRM_DEBUG_KMS("%s: W: %02X %s\n", SDVO_NAME(intel_sdvo), cmd, buffer);
+ }
+ static const char * const cmd_status_names[] = {
+       "Power on",
+       "Success",
+       "Not supported",
+       "Invalid arg",
+       "Pending",
+       "Target not specified",
+       "Scaling not supported"
+ };
+ static bool __intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
+                                  const void *args, int args_len,
+                                  bool unlocked)
+ {
+       u8 *buf, status;
+       struct i2c_msg *msgs;
+       int i, ret = true;
+       /* Would be simpler to allocate both in one go ? */
+       buf = kzalloc(args_len * 2 + 2, GFP_KERNEL);
+       if (!buf)
+               return false;
+       msgs = kcalloc(args_len + 3, sizeof(*msgs), GFP_KERNEL);
+       if (!msgs) {
+               kfree(buf);
+               return false;
+       }
+       intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len);
+       for (i = 0; i < args_len; i++) {
+               msgs[i].addr = intel_sdvo->slave_addr;
+               msgs[i].flags = 0;
+               msgs[i].len = 2;
+               msgs[i].buf = buf + 2 *i;
+               buf[2*i + 0] = SDVO_I2C_ARG_0 - i;
+               buf[2*i + 1] = ((u8*)args)[i];
+       }
+       msgs[i].addr = intel_sdvo->slave_addr;
+       msgs[i].flags = 0;
+       msgs[i].len = 2;
+       msgs[i].buf = buf + 2*i;
+       buf[2*i + 0] = SDVO_I2C_OPCODE;
+       buf[2*i + 1] = cmd;
+       /* the following two are to read the response */
+       status = SDVO_I2C_CMD_STATUS;
+       msgs[i+1].addr = intel_sdvo->slave_addr;
+       msgs[i+1].flags = 0;
+       msgs[i+1].len = 1;
+       msgs[i+1].buf = &status;
+       msgs[i+2].addr = intel_sdvo->slave_addr;
+       msgs[i+2].flags = I2C_M_RD;
+       msgs[i+2].len = 1;
+       msgs[i+2].buf = &status;
+       if (unlocked)
+               ret = i2c_transfer(intel_sdvo->i2c, msgs, i+3);
+       else
+               ret = __i2c_transfer(intel_sdvo->i2c, msgs, i+3);
+       if (ret < 0) {
+               DRM_DEBUG_KMS("I2c transfer returned %d\n", ret);
+               ret = false;
+               goto out;
+       }
+       if (ret != i+3) {
+               /* failure in I2C transfer */
+               DRM_DEBUG_KMS("I2c transfer returned %d/%d\n", ret, i+3);
+               ret = false;
+       }
+ out:
+       kfree(msgs);
+       kfree(buf);
+       return ret;
+ }
+ static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
+                                const void *args, int args_len)
+ {
+       return __intel_sdvo_write_cmd(intel_sdvo, cmd, args, args_len, true);
+ }
+ static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo,
+                                    void *response, int response_len)
+ {
+       u8 retry = 15; /* 5 quick checks, followed by 10 long checks */
+       u8 status;
+       int i, pos = 0;
+ #define BUF_LEN 256
+       char buffer[BUF_LEN];
+       buffer[0] = '\0';
+       /*
+        * The documentation states that all commands will be
+        * processed within 15µs, and that we need only poll
+        * the status byte a maximum of 3 times in order for the
+        * command to be complete.
+        *
+        * Check 5 times in case the hardware failed to read the docs.
+        *
+        * Also beware that the first response by many devices is to
+        * reply PENDING and stall for time. TVs are notorious for
+        * requiring longer than specified to complete their replies.
+        * Originally (in the DDX long ago), the delay was only ever 15ms
+        * with an additional delay of 30ms applied for TVs added later after
+        * many experiments. To accommodate both sets of delays, we do a
+        * sequence of slow checks if the device is falling behind and fails
+        * to reply within 5*15µs.
+        */
+       if (!intel_sdvo_read_byte(intel_sdvo,
+                                 SDVO_I2C_CMD_STATUS,
+                                 &status))
+               goto log_fail;
+       while ((status == SDVO_CMD_STATUS_PENDING ||
+               status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && --retry) {
+               if (retry < 10)
+                       msleep(15);
+               else
+                       udelay(15);
+               if (!intel_sdvo_read_byte(intel_sdvo,
+                                         SDVO_I2C_CMD_STATUS,
+                                         &status))
+                       goto log_fail;
+       }
+ #define BUF_PRINT(args...) \
+       pos += snprintf(buffer + pos, max_t(int, BUF_LEN - pos, 0), args)
+       if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
+               BUF_PRINT("(%s)", cmd_status_names[status]);
+       else
+               BUF_PRINT("(??? %d)", status);
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               goto log_fail;
+       /* Read the command response */
+       for (i = 0; i < response_len; i++) {
+               if (!intel_sdvo_read_byte(intel_sdvo,
+                                         SDVO_I2C_RETURN_0 + i,
+                                         &((u8 *)response)[i]))
+                       goto log_fail;
+               BUF_PRINT(" %02X", ((u8 *)response)[i]);
+       }
+       BUG_ON(pos >= BUF_LEN - 1);
+ #undef BUF_PRINT
+ #undef BUF_LEN
+       DRM_DEBUG_KMS("%s: R: %s\n", SDVO_NAME(intel_sdvo), buffer);
+       return true;
+ log_fail:
+       DRM_DEBUG_KMS("%s: R: ... failed %s\n",
+                     SDVO_NAME(intel_sdvo), buffer);
+       return false;
+ }
+ static int intel_sdvo_get_pixel_multiplier(const struct drm_display_mode *adjusted_mode)
+ {
+       if (adjusted_mode->crtc_clock >= 100000)
+               return 1;
+       else if (adjusted_mode->crtc_clock >= 50000)
+               return 2;
+       else
+               return 4;
+ }
+ static bool __intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo,
+                                               u8 ddc_bus)
+ {
+       /* This must be the immediately preceding write before the i2c xfer */
+       return __intel_sdvo_write_cmd(intel_sdvo,
+                                     SDVO_CMD_SET_CONTROL_BUS_SWITCH,
+                                     &ddc_bus, 1, false);
+ }
+ static bool intel_sdvo_set_value(struct intel_sdvo *intel_sdvo, u8 cmd, const void *data, int len)
+ {
+       if (!intel_sdvo_write_cmd(intel_sdvo, cmd, data, len))
+               return false;
+       return intel_sdvo_read_response(intel_sdvo, NULL, 0);
+ }
+ static bool
+ intel_sdvo_get_value(struct intel_sdvo *intel_sdvo, u8 cmd, void *value, int len)
+ {
+       if (!intel_sdvo_write_cmd(intel_sdvo, cmd, NULL, 0))
+               return false;
+       return intel_sdvo_read_response(intel_sdvo, value, len);
+ }
+ static bool intel_sdvo_set_target_input(struct intel_sdvo *intel_sdvo)
+ {
+       struct intel_sdvo_set_target_input_args targets = {0};
+       return intel_sdvo_set_value(intel_sdvo,
+                                   SDVO_CMD_SET_TARGET_INPUT,
+                                   &targets, sizeof(targets));
+ }
+ /*
+  * Return whether each input is trained.
+  *
+  * This function is making an assumption about the layout of the response,
+  * which should be checked against the docs.
+  */
+ static bool intel_sdvo_get_trained_inputs(struct intel_sdvo *intel_sdvo, bool *input_1, bool *input_2)
+ {
+       struct intel_sdvo_get_trained_inputs_response response;
+       BUILD_BUG_ON(sizeof(response) != 1);
+       if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_TRAINED_INPUTS,
+                                 &response, sizeof(response)))
+               return false;
+       *input_1 = response.input0_trained;
+       *input_2 = response.input1_trained;
+       return true;
+ }
+ static bool intel_sdvo_set_active_outputs(struct intel_sdvo *intel_sdvo,
+                                         u16 outputs)
+ {
+       return intel_sdvo_set_value(intel_sdvo,
+                                   SDVO_CMD_SET_ACTIVE_OUTPUTS,
+                                   &outputs, sizeof(outputs));
+ }
+ static bool intel_sdvo_get_active_outputs(struct intel_sdvo *intel_sdvo,
+                                         u16 *outputs)
+ {
+       return intel_sdvo_get_value(intel_sdvo,
+                                   SDVO_CMD_GET_ACTIVE_OUTPUTS,
+                                   outputs, sizeof(*outputs));
+ }
+ static bool intel_sdvo_set_encoder_power_state(struct intel_sdvo *intel_sdvo,
+                                              int mode)
+ {
+       u8 state = SDVO_ENCODER_STATE_ON;
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               state = SDVO_ENCODER_STATE_ON;
+               break;
+       case DRM_MODE_DPMS_STANDBY:
+               state = SDVO_ENCODER_STATE_STANDBY;
+               break;
+       case DRM_MODE_DPMS_SUSPEND:
+               state = SDVO_ENCODER_STATE_SUSPEND;
+               break;
+       case DRM_MODE_DPMS_OFF:
+               state = SDVO_ENCODER_STATE_OFF;
+               break;
+       }
+       return intel_sdvo_set_value(intel_sdvo,
+                                   SDVO_CMD_SET_ENCODER_POWER_STATE, &state, sizeof(state));
+ }
+ static bool intel_sdvo_get_input_pixel_clock_range(struct intel_sdvo *intel_sdvo,
+                                                  int *clock_min,
+                                                  int *clock_max)
+ {
+       struct intel_sdvo_pixel_clock_range clocks;
+       BUILD_BUG_ON(sizeof(clocks) != 4);
+       if (!intel_sdvo_get_value(intel_sdvo,
+                                 SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE,
+                                 &clocks, sizeof(clocks)))
+               return false;
+       /* Convert the values from units of 10 kHz to kHz. */
+       *clock_min = clocks.min * 10;
+       *clock_max = clocks.max * 10;
+       return true;
+ }
+ static bool intel_sdvo_set_target_output(struct intel_sdvo *intel_sdvo,
+                                        u16 outputs)
+ {
+       return intel_sdvo_set_value(intel_sdvo,
+                                   SDVO_CMD_SET_TARGET_OUTPUT,
+                                   &outputs, sizeof(outputs));
+ }
+ static bool intel_sdvo_set_timing(struct intel_sdvo *intel_sdvo, u8 cmd,
+                                 struct intel_sdvo_dtd *dtd)
+ {
+       return intel_sdvo_set_value(intel_sdvo, cmd, &dtd->part1, sizeof(dtd->part1)) &&
+               intel_sdvo_set_value(intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2));
+ }
+ static bool intel_sdvo_get_timing(struct intel_sdvo *intel_sdvo, u8 cmd,
+                                 struct intel_sdvo_dtd *dtd)
+ {
+       return intel_sdvo_get_value(intel_sdvo, cmd, &dtd->part1, sizeof(dtd->part1)) &&
+               intel_sdvo_get_value(intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2));
+ }
+ static bool intel_sdvo_set_input_timing(struct intel_sdvo *intel_sdvo,
+                                        struct intel_sdvo_dtd *dtd)
+ {
+       return intel_sdvo_set_timing(intel_sdvo,
+                                    SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd);
+ }
+ static bool intel_sdvo_set_output_timing(struct intel_sdvo *intel_sdvo,
+                                        struct intel_sdvo_dtd *dtd)
+ {
+       return intel_sdvo_set_timing(intel_sdvo,
+                                    SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
+ }
+ static bool intel_sdvo_get_input_timing(struct intel_sdvo *intel_sdvo,
+                                       struct intel_sdvo_dtd *dtd)
+ {
+       return intel_sdvo_get_timing(intel_sdvo,
+                                    SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd);
+ }
+ static bool
+ intel_sdvo_create_preferred_input_timing(struct intel_sdvo *intel_sdvo,
+                                        struct intel_sdvo_connector *intel_sdvo_connector,
+                                        u16 clock,
+                                        u16 width,
+                                        u16 height)
+ {
+       struct intel_sdvo_preferred_input_timing_args args;
+       memset(&args, 0, sizeof(args));
+       args.clock = clock;
+       args.width = width;
+       args.height = height;
+       args.interlace = 0;
+       if (IS_LVDS(intel_sdvo_connector)) {
+               const struct drm_display_mode *fixed_mode =
+                       intel_sdvo_connector->base.panel.fixed_mode;
+               if (fixed_mode->hdisplay != width ||
+                   fixed_mode->vdisplay != height)
+                       args.scaled = 1;
+       }
+       return intel_sdvo_set_value(intel_sdvo,
+                                   SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING,
+                                   &args, sizeof(args));
+ }
+ static bool intel_sdvo_get_preferred_input_timing(struct intel_sdvo *intel_sdvo,
+                                                 struct intel_sdvo_dtd *dtd)
+ {
+       BUILD_BUG_ON(sizeof(dtd->part1) != 8);
+       BUILD_BUG_ON(sizeof(dtd->part2) != 8);
+       return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1,
+                                   &dtd->part1, sizeof(dtd->part1)) &&
+               intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2,
+                                    &dtd->part2, sizeof(dtd->part2));
+ }
+ static bool intel_sdvo_set_clock_rate_mult(struct intel_sdvo *intel_sdvo, u8 val)
+ {
+       return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1);
+ }
+ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
+                                        const struct drm_display_mode *mode)
+ {
+       u16 width, height;
+       u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+       u16 h_sync_offset, v_sync_offset;
+       int mode_clock;
+       memset(dtd, 0, sizeof(*dtd));
+       width = mode->hdisplay;
+       height = mode->vdisplay;
+       /* do some mode translations */
+       h_blank_len = mode->htotal - mode->hdisplay;
+       h_sync_len = mode->hsync_end - mode->hsync_start;
+       v_blank_len = mode->vtotal - mode->vdisplay;
+       v_sync_len = mode->vsync_end - mode->vsync_start;
+       h_sync_offset = mode->hsync_start - mode->hdisplay;
+       v_sync_offset = mode->vsync_start - mode->vdisplay;
+       mode_clock = mode->clock;
+       mode_clock /= 10;
+       dtd->part1.clock = mode_clock;
+       dtd->part1.h_active = width & 0xff;
+       dtd->part1.h_blank = h_blank_len & 0xff;
+       dtd->part1.h_high = (((width >> 8) & 0xf) << 4) |
+               ((h_blank_len >> 8) & 0xf);
+       dtd->part1.v_active = height & 0xff;
+       dtd->part1.v_blank = v_blank_len & 0xff;
+       dtd->part1.v_high = (((height >> 8) & 0xf) << 4) |
+               ((v_blank_len >> 8) & 0xf);
+       dtd->part2.h_sync_off = h_sync_offset & 0xff;
+       dtd->part2.h_sync_width = h_sync_len & 0xff;
+       dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
+               (v_sync_len & 0xf);
+       dtd->part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
+               ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
+               ((v_sync_len & 0x30) >> 4);
+       dtd->part2.dtd_flags = 0x18;
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+               dtd->part2.dtd_flags |= DTD_FLAG_INTERLACE;
+       if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+               dtd->part2.dtd_flags |= DTD_FLAG_HSYNC_POSITIVE;
+       if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+               dtd->part2.dtd_flags |= DTD_FLAG_VSYNC_POSITIVE;
+       dtd->part2.v_sync_off_high = v_sync_offset & 0xc0;
+ }
+ static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode *pmode,
+                                        const struct intel_sdvo_dtd *dtd)
+ {
+       struct drm_display_mode mode = {};
+       mode.hdisplay = dtd->part1.h_active;
+       mode.hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8;
+       mode.hsync_start = mode.hdisplay + dtd->part2.h_sync_off;
+       mode.hsync_start += (dtd->part2.sync_off_width_high & 0xc0) << 2;
+       mode.hsync_end = mode.hsync_start + dtd->part2.h_sync_width;
+       mode.hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4;
+       mode.htotal = mode.hdisplay + dtd->part1.h_blank;
+       mode.htotal += (dtd->part1.h_high & 0xf) << 8;
+       mode.vdisplay = dtd->part1.v_active;
+       mode.vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8;
+       mode.vsync_start = mode.vdisplay;
+       mode.vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf;
+       mode.vsync_start += (dtd->part2.sync_off_width_high & 0x0c) << 2;
+       mode.vsync_start += dtd->part2.v_sync_off_high & 0xc0;
+       mode.vsync_end = mode.vsync_start +
+               (dtd->part2.v_sync_off_width & 0xf);
+       mode.vsync_end += (dtd->part2.sync_off_width_high & 0x3) << 4;
+       mode.vtotal = mode.vdisplay + dtd->part1.v_blank;
+       mode.vtotal += (dtd->part1.v_high & 0xf) << 8;
+       mode.clock = dtd->part1.clock * 10;
+       if (dtd->part2.dtd_flags & DTD_FLAG_INTERLACE)
+               mode.flags |= DRM_MODE_FLAG_INTERLACE;
+       if (dtd->part2.dtd_flags & DTD_FLAG_HSYNC_POSITIVE)
+               mode.flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               mode.flags |= DRM_MODE_FLAG_NHSYNC;
+       if (dtd->part2.dtd_flags & DTD_FLAG_VSYNC_POSITIVE)
+               mode.flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               mode.flags |= DRM_MODE_FLAG_NVSYNC;
+       drm_mode_set_crtcinfo(&mode, 0);
+       drm_mode_copy(pmode, &mode);
+ }
+ static bool intel_sdvo_check_supp_encode(struct intel_sdvo *intel_sdvo)
+ {
+       struct intel_sdvo_encode encode;
+       BUILD_BUG_ON(sizeof(encode) != 2);
+       return intel_sdvo_get_value(intel_sdvo,
+                                 SDVO_CMD_GET_SUPP_ENCODE,
+                                 &encode, sizeof(encode));
+ }
+ static bool intel_sdvo_set_encode(struct intel_sdvo *intel_sdvo,
+                                 u8 mode)
+ {
+       return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_ENCODE, &mode, 1);
+ }
+ static bool intel_sdvo_set_colorimetry(struct intel_sdvo *intel_sdvo,
+                                      u8 mode)
+ {
+       return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_COLORIMETRY, &mode, 1);
+ }
+ static bool intel_sdvo_set_audio_state(struct intel_sdvo *intel_sdvo,
+                                      u8 audio_state)
+ {
+       return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_AUDIO_STAT,
+                                   &audio_state, 1);
+ }
+ #if 0
+ static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo)
+ {
+       int i, j;
+       u8 set_buf_index[2];
+       u8 av_split;
+       u8 buf_size;
+       u8 buf[48];
+       u8 *pos;
+       intel_sdvo_get_value(encoder, SDVO_CMD_GET_HBUF_AV_SPLIT, &av_split, 1);
+       for (i = 0; i <= av_split; i++) {
+               set_buf_index[0] = i; set_buf_index[1] = 0;
+               intel_sdvo_write_cmd(encoder, SDVO_CMD_SET_HBUF_INDEX,
+                                    set_buf_index, 2);
+               intel_sdvo_write_cmd(encoder, SDVO_CMD_GET_HBUF_INFO, NULL, 0);
+               intel_sdvo_read_response(encoder, &buf_size, 1);
+               pos = buf;
+               for (j = 0; j <= buf_size; j += 8) {
+                       intel_sdvo_write_cmd(encoder, SDVO_CMD_GET_HBUF_DATA,
+                                            NULL, 0);
+                       intel_sdvo_read_response(encoder, pos, 8);
+                       pos += 8;
+               }
+       }
+ }
+ #endif
+ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo,
+                                      unsigned int if_index, u8 tx_rate,
+                                      const u8 *data, unsigned int length)
+ {
+       u8 set_buf_index[2] = { if_index, 0 };
+       u8 hbuf_size, tmp[8];
+       int i;
+       if (!intel_sdvo_set_value(intel_sdvo,
+                                 SDVO_CMD_SET_HBUF_INDEX,
+                                 set_buf_index, 2))
+               return false;
+       if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HBUF_INFO,
+                                 &hbuf_size, 1))
+               return false;
+       /* Buffer size is 0 based, hooray! */
+       hbuf_size++;
+       DRM_DEBUG_KMS("writing sdvo hbuf: %i, hbuf_size %i, hbuf_size: %i\n",
+                     if_index, length, hbuf_size);
+       if (hbuf_size < length)
+               return false;
+       for (i = 0; i < hbuf_size; i += 8) {
+               memset(tmp, 0, 8);
+               if (i < length)
+                       memcpy(tmp, data + i, min_t(unsigned, 8, length - i));
+               if (!intel_sdvo_set_value(intel_sdvo,
+                                         SDVO_CMD_SET_HBUF_DATA,
+                                         tmp, 8))
+                       return false;
+       }
+       return intel_sdvo_set_value(intel_sdvo,
+                                   SDVO_CMD_SET_HBUF_TXRATE,
+                                   &tx_rate, 1);
+ }
+ static ssize_t intel_sdvo_read_infoframe(struct intel_sdvo *intel_sdvo,
+                                        unsigned int if_index,
+                                        u8 *data, unsigned int length)
+ {
+       u8 set_buf_index[2] = { if_index, 0 };
+       u8 hbuf_size, tx_rate, av_split;
+       int i;
+       if (!intel_sdvo_get_value(intel_sdvo,
+                                 SDVO_CMD_GET_HBUF_AV_SPLIT,
+                                 &av_split, 1))
+               return -ENXIO;
+       if (av_split < if_index)
+               return 0;
+       if (!intel_sdvo_set_value(intel_sdvo,
+                                 SDVO_CMD_SET_HBUF_INDEX,
+                                 set_buf_index, 2))
+               return -ENXIO;
+       if (!intel_sdvo_get_value(intel_sdvo,
+                                 SDVO_CMD_GET_HBUF_TXRATE,
+                                 &tx_rate, 1))
+               return -ENXIO;
+       if (tx_rate == SDVO_HBUF_TX_DISABLED)
+               return 0;
+       if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HBUF_INFO,
+                                 &hbuf_size, 1))
+               return -ENXIO;
+       /* Buffer size is 0 based, hooray! */
+       hbuf_size++;
+       DRM_DEBUG_KMS("reading sdvo hbuf: %i, hbuf_size %i, hbuf_size: %i\n",
+                     if_index, length, hbuf_size);
+       hbuf_size = min_t(unsigned int, length, hbuf_size);
+       for (i = 0; i < hbuf_size; i += 8) {
+               if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_HBUF_DATA, NULL, 0))
+                       return -ENXIO;
+               if (!intel_sdvo_read_response(intel_sdvo, &data[i],
+                                             min_t(unsigned int, 8, hbuf_size - i)))
+                       return -ENXIO;
+       }
+       return hbuf_size;
+ }
+ static bool intel_sdvo_compute_avi_infoframe(struct intel_sdvo *intel_sdvo,
+                                            struct intel_crtc_state *crtc_state,
+                                            struct drm_connector_state *conn_state)
+ {
+       struct hdmi_avi_infoframe *frame = &crtc_state->infoframes.avi.avi;
+       const struct drm_display_mode *adjusted_mode =
+               &crtc_state->base.adjusted_mode;
+       int ret;
+       if (!crtc_state->has_hdmi_sink)
+               return true;
+       crtc_state->infoframes.enable |=
+               intel_hdmi_infoframe_enable(HDMI_INFOFRAME_TYPE_AVI);
+       ret = drm_hdmi_avi_infoframe_from_display_mode(frame,
+                                                      conn_state->connector,
+                                                      adjusted_mode);
+       if (ret)
+               return false;
+       drm_hdmi_avi_infoframe_quant_range(frame,
+                                          conn_state->connector,
+                                          adjusted_mode,
+                                          crtc_state->limited_color_range ?
+                                          HDMI_QUANTIZATION_RANGE_LIMITED :
+                                          HDMI_QUANTIZATION_RANGE_FULL);
+       ret = hdmi_avi_infoframe_check(frame);
+       if (WARN_ON(ret))
+               return false;
+       return true;
+ }
+ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo,
+                                        const struct intel_crtc_state *crtc_state)
+ {
+       u8 sdvo_data[HDMI_INFOFRAME_SIZE(AVI)];
+       const union hdmi_infoframe *frame = &crtc_state->infoframes.avi;
+       ssize_t len;
+       if ((crtc_state->infoframes.enable &
+            intel_hdmi_infoframe_enable(HDMI_INFOFRAME_TYPE_AVI)) == 0)
+               return true;
+       if (WARN_ON(frame->any.type != HDMI_INFOFRAME_TYPE_AVI))
+               return false;
+       len = hdmi_infoframe_pack_only(frame, sdvo_data, sizeof(sdvo_data));
+       if (WARN_ON(len < 0))
+               return false;
+       return intel_sdvo_write_infoframe(intel_sdvo, SDVO_HBUF_INDEX_AVI_IF,
+                                         SDVO_HBUF_TX_VSYNC,
+                                         sdvo_data, len);
+ }
+ static void intel_sdvo_get_avi_infoframe(struct intel_sdvo *intel_sdvo,
+                                        struct intel_crtc_state *crtc_state)
+ {
+       u8 sdvo_data[HDMI_INFOFRAME_SIZE(AVI)];
+       union hdmi_infoframe *frame = &crtc_state->infoframes.avi;
+       ssize_t len;
+       int ret;
+       if (!crtc_state->has_hdmi_sink)
+               return;
+       len = intel_sdvo_read_infoframe(intel_sdvo, SDVO_HBUF_INDEX_AVI_IF,
+                                       sdvo_data, sizeof(sdvo_data));
+       if (len < 0) {
+               DRM_DEBUG_KMS("failed to read AVI infoframe\n");
+               return;
+       } else if (len == 0) {
+               return;
+       }
+       crtc_state->infoframes.enable |=
+               intel_hdmi_infoframe_enable(HDMI_INFOFRAME_TYPE_AVI);
+       ret = hdmi_infoframe_unpack(frame, sdvo_data, len);
+       if (ret) {
+               DRM_DEBUG_KMS("Failed to unpack AVI infoframe\n");
+               return;
+       }
+       if (frame->any.type != HDMI_INFOFRAME_TYPE_AVI)
+               DRM_DEBUG_KMS("Found the wrong infoframe type 0x%x (expected 0x%02x)\n",
+                             frame->any.type, HDMI_INFOFRAME_TYPE_AVI);
+ }
+ static bool intel_sdvo_set_tv_format(struct intel_sdvo *intel_sdvo,
+                                    const struct drm_connector_state *conn_state)
+ {
+       struct intel_sdvo_tv_format format;
+       u32 format_map;
+       format_map = 1 << conn_state->tv.mode;
+       memset(&format, 0, sizeof(format));
+       memcpy(&format, &format_map, min(sizeof(format), sizeof(format_map)));
+       BUILD_BUG_ON(sizeof(format) != 6);
+       return intel_sdvo_set_value(intel_sdvo,
+                                   SDVO_CMD_SET_TV_FORMAT,
+                                   &format, sizeof(format));
+ }
+ static bool
+ intel_sdvo_set_output_timings_from_mode(struct intel_sdvo *intel_sdvo,
+                                       const struct drm_display_mode *mode)
+ {
+       struct intel_sdvo_dtd output_dtd;
+       if (!intel_sdvo_set_target_output(intel_sdvo,
+                                         intel_sdvo->attached_output))
+               return false;
+       intel_sdvo_get_dtd_from_mode(&output_dtd, mode);
+       if (!intel_sdvo_set_output_timing(intel_sdvo, &output_dtd))
+               return false;
+       return true;
+ }
+ /*
+  * Asks the sdvo controller for the preferred input mode given the output mode.
+  * Unfortunately we have to set up the full output mode to do that.
+  */
+ static bool
+ intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo,
+                                   struct intel_sdvo_connector *intel_sdvo_connector,
+                                   const struct drm_display_mode *mode,
+                                   struct drm_display_mode *adjusted_mode)
+ {
+       struct intel_sdvo_dtd input_dtd;
+       /* Reset the input timing to the screen. Assume always input 0. */
+       if (!intel_sdvo_set_target_input(intel_sdvo))
+               return false;
+       if (!intel_sdvo_create_preferred_input_timing(intel_sdvo,
+                                                     intel_sdvo_connector,
+                                                     mode->clock / 10,
+                                                     mode->hdisplay,
+                                                     mode->vdisplay))
+               return false;
+       if (!intel_sdvo_get_preferred_input_timing(intel_sdvo,
+                                                  &input_dtd))
+               return false;
+       intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
+       intel_sdvo->dtd_sdvo_flags = input_dtd.part2.sdvo_flags;
+       return true;
+ }
+ static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_state *pipe_config)
+ {
+       unsigned dotclock = pipe_config->port_clock;
+       struct dpll *clock = &pipe_config->dpll;
+       /*
+        * SDVO TV has fixed PLL values depend on its clock range,
+        * this mirrors vbios setting.
+        */
+       if (dotclock >= 100000 && dotclock < 140500) {
+               clock->p1 = 2;
+               clock->p2 = 10;
+               clock->n = 3;
+               clock->m1 = 16;
+               clock->m2 = 8;
+       } else if (dotclock >= 140500 && dotclock <= 200000) {
+               clock->p1 = 1;
+               clock->p2 = 10;
+               clock->n = 6;
+               clock->m1 = 12;
+               clock->m2 = 8;
+       } else {
+               WARN(1, "SDVO TV clock out of range: %i\n", dotclock);
+       }
+       pipe_config->clock_set = true;
+ }
+ static int intel_sdvo_compute_config(struct intel_encoder *encoder,
+                                    struct intel_crtc_state *pipe_config,
+                                    struct drm_connector_state *conn_state)
+ {
+       struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
+       struct intel_sdvo_connector_state *intel_sdvo_state =
+               to_intel_sdvo_connector_state(conn_state);
+       struct intel_sdvo_connector *intel_sdvo_connector =
+               to_intel_sdvo_connector(conn_state->connector);
+       struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
+       struct drm_display_mode *mode = &pipe_config->base.mode;
+       DRM_DEBUG_KMS("forcing bpc to 8 for SDVO\n");
+       pipe_config->pipe_bpp = 8*3;
+       pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
+       if (HAS_PCH_SPLIT(to_i915(encoder->base.dev)))
+               pipe_config->has_pch_encoder = true;
+       /*
+        * We need to construct preferred input timings based on our
+        * output timings.  To do that, we have to set the output
+        * timings, even though this isn't really the right place in
+        * the sequence to do it. Oh well.
+        */
+       if (IS_TV(intel_sdvo_connector)) {
+               if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo, mode))
+                       return -EINVAL;
+               (void) intel_sdvo_get_preferred_input_mode(intel_sdvo,
+                                                          intel_sdvo_connector,
+                                                          mode,
+                                                          adjusted_mode);
+               pipe_config->sdvo_tv_clock = true;
+       } else if (IS_LVDS(intel_sdvo_connector)) {
+               if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo,
+                                                            intel_sdvo_connector->base.panel.fixed_mode))
+                       return -EINVAL;
+               (void) intel_sdvo_get_preferred_input_mode(intel_sdvo,
+                                                          intel_sdvo_connector,
+                                                          mode,
+                                                          adjusted_mode);
+       }
+       if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return -EINVAL;
+       /*
+        * Make the CRTC code factor in the SDVO pixel multiplier.  The
+        * SDVO device will factor out the multiplier during mode_set.
+        */
+       pipe_config->pixel_multiplier =
+               intel_sdvo_get_pixel_multiplier(adjusted_mode);
+       if (intel_sdvo_state->base.force_audio != HDMI_AUDIO_OFF_DVI)
+               pipe_config->has_hdmi_sink = intel_sdvo->has_hdmi_monitor;
+       if (intel_sdvo_state->base.force_audio == HDMI_AUDIO_ON ||
+           (intel_sdvo_state->base.force_audio == HDMI_AUDIO_AUTO && intel_sdvo->has_hdmi_audio))
+               pipe_config->has_audio = true;
+       if (intel_sdvo_state->base.broadcast_rgb == INTEL_BROADCAST_RGB_AUTO) {
+               /*
+                * See CEA-861-E - 5.1 Default Encoding Parameters
+                *
+                * FIXME: This bit is only valid when using TMDS encoding and 8
+                * bit per color mode.
+                */
+               if (pipe_config->has_hdmi_sink &&
+                   drm_match_cea_mode(adjusted_mode) > 1)
+                       pipe_config->limited_color_range = true;
+       } else {
+               if (pipe_config->has_hdmi_sink &&
+                   intel_sdvo_state->base.broadcast_rgb == INTEL_BROADCAST_RGB_LIMITED)
+                       pipe_config->limited_color_range = true;
+       }
+       /* Clock computation needs to happen after pixel multiplier. */
+       if (IS_TV(intel_sdvo_connector))
+               i9xx_adjust_sdvo_tv_clock(pipe_config);
+       /* Set user selected PAR to incoming mode's member */
+       if (intel_sdvo_connector->is_hdmi)
+               adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
+       if (!intel_sdvo_compute_avi_infoframe(intel_sdvo,
+                                             pipe_config, conn_state)) {
+               DRM_DEBUG_KMS("bad AVI infoframe\n");
+               return -EINVAL;
+       }
+       return 0;
+ }
+ #define UPDATE_PROPERTY(input, NAME) \
+       do { \
+               val = input; \
+               intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_##NAME, &val, sizeof(val)); \
+       } while (0)
+ static void intel_sdvo_update_props(struct intel_sdvo *intel_sdvo,
+                                   const struct intel_sdvo_connector_state *sdvo_state)
+ {
+       const struct drm_connector_state *conn_state = &sdvo_state->base.base;
+       struct intel_sdvo_connector *intel_sdvo_conn =
+               to_intel_sdvo_connector(conn_state->connector);
+       u16 val;
+       if (intel_sdvo_conn->left)
+               UPDATE_PROPERTY(sdvo_state->tv.overscan_h, OVERSCAN_H);
+       if (intel_sdvo_conn->top)
+               UPDATE_PROPERTY(sdvo_state->tv.overscan_v, OVERSCAN_V);
+       if (intel_sdvo_conn->hpos)
+               UPDATE_PROPERTY(sdvo_state->tv.hpos, HPOS);
+       if (intel_sdvo_conn->vpos)
+               UPDATE_PROPERTY(sdvo_state->tv.vpos, VPOS);
+       if (intel_sdvo_conn->saturation)
+               UPDATE_PROPERTY(conn_state->tv.saturation, SATURATION);
+       if (intel_sdvo_conn->contrast)
+               UPDATE_PROPERTY(conn_state->tv.contrast, CONTRAST);
+       if (intel_sdvo_conn->hue)
+               UPDATE_PROPERTY(conn_state->tv.hue, HUE);
+       if (intel_sdvo_conn->brightness)
+               UPDATE_PROPERTY(conn_state->tv.brightness, BRIGHTNESS);
+       if (intel_sdvo_conn->sharpness)
+               UPDATE_PROPERTY(sdvo_state->tv.sharpness, SHARPNESS);
+       if (intel_sdvo_conn->flicker_filter)
+               UPDATE_PROPERTY(sdvo_state->tv.flicker_filter, FLICKER_FILTER);
+       if (intel_sdvo_conn->flicker_filter_2d)
+               UPDATE_PROPERTY(sdvo_state->tv.flicker_filter_2d, FLICKER_FILTER_2D);
+       if (intel_sdvo_conn->flicker_filter_adaptive)
+               UPDATE_PROPERTY(sdvo_state->tv.flicker_filter_adaptive, FLICKER_FILTER_ADAPTIVE);
+       if (intel_sdvo_conn->tv_chroma_filter)
+               UPDATE_PROPERTY(sdvo_state->tv.chroma_filter, TV_CHROMA_FILTER);
+       if (intel_sdvo_conn->tv_luma_filter)
+               UPDATE_PROPERTY(sdvo_state->tv.luma_filter, TV_LUMA_FILTER);
+       if (intel_sdvo_conn->dot_crawl)
+               UPDATE_PROPERTY(sdvo_state->tv.dot_crawl, DOT_CRAWL);
+ #undef UPDATE_PROPERTY
+ }
+ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder,
+                                 const struct intel_crtc_state *crtc_state,
+                                 const struct drm_connector_state *conn_state)
+ {
+       struct drm_i915_private *dev_priv = to_i915(intel_encoder->base.dev);
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       const struct drm_display_mode *adjusted_mode = &crtc_state->base.adjusted_mode;
+       const struct intel_sdvo_connector_state *sdvo_state =
+               to_intel_sdvo_connector_state(conn_state);
+       const struct intel_sdvo_connector *intel_sdvo_connector =
+               to_intel_sdvo_connector(conn_state->connector);
+       const struct drm_display_mode *mode = &crtc_state->base.mode;
+       struct intel_sdvo *intel_sdvo = to_sdvo(intel_encoder);
+       u32 sdvox;
+       struct intel_sdvo_in_out_map in_out;
+       struct intel_sdvo_dtd input_dtd, output_dtd;
+       int rate;
+       intel_sdvo_update_props(intel_sdvo, sdvo_state);
+       /*
+        * First, set the input mapping for the first input to our controlled
+        * output. This is only correct if we're a single-input device, in
+        * which case the first input is the output from the appropriate SDVO
+        * channel on the motherboard.  In a two-input device, the first input
+        * will be SDVOB and the second SDVOC.
+        */
+       in_out.in0 = intel_sdvo->attached_output;
+       in_out.in1 = 0;
+       intel_sdvo_set_value(intel_sdvo,
+                            SDVO_CMD_SET_IN_OUT_MAP,
+                            &in_out, sizeof(in_out));
+       /* Set the output timings to the screen */
+       if (!intel_sdvo_set_target_output(intel_sdvo,
+                                         intel_sdvo->attached_output))
+               return;
+       /* lvds has a special fixed output timing. */
+       if (IS_LVDS(intel_sdvo_connector))
+               intel_sdvo_get_dtd_from_mode(&output_dtd,
+                                            intel_sdvo_connector->base.panel.fixed_mode);
+       else
+               intel_sdvo_get_dtd_from_mode(&output_dtd, mode);
+       if (!intel_sdvo_set_output_timing(intel_sdvo, &output_dtd))
+               DRM_INFO("Setting output timings on %s failed\n",
+                        SDVO_NAME(intel_sdvo));
+       /* Set the input timing to the screen. Assume always input 0. */
+       if (!intel_sdvo_set_target_input(intel_sdvo))
+               return;
+       if (crtc_state->has_hdmi_sink) {
+               intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
+               intel_sdvo_set_colorimetry(intel_sdvo,
+                                          SDVO_COLORIMETRY_RGB256);
+               intel_sdvo_set_avi_infoframe(intel_sdvo, crtc_state);
+       } else
+               intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI);
+       if (IS_TV(intel_sdvo_connector) &&
+           !intel_sdvo_set_tv_format(intel_sdvo, conn_state))
+               return;
+       intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
+       if (IS_TV(intel_sdvo_connector) || IS_LVDS(intel_sdvo_connector))
+               input_dtd.part2.sdvo_flags = intel_sdvo->dtd_sdvo_flags;
+       if (!intel_sdvo_set_input_timing(intel_sdvo, &input_dtd))
+               DRM_INFO("Setting input timings on %s failed\n",
+                        SDVO_NAME(intel_sdvo));
+       switch (crtc_state->pixel_multiplier) {
+       default:
+               WARN(1, "unknown pixel multiplier specified\n");
+               /* fall through */
+       case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break;
+       case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break;
+       case 4: rate = SDVO_CLOCK_RATE_MULT_4X; break;
+       }
+       if (!intel_sdvo_set_clock_rate_mult(intel_sdvo, rate))
+               return;
+       /* Set the SDVO control regs. */
+       if (INTEL_GEN(dev_priv) >= 4) {
+               /* The real mode polarity is set by the SDVO commands, using
+                * struct intel_sdvo_dtd. */
+               sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH;
+               if (!HAS_PCH_SPLIT(dev_priv) && crtc_state->limited_color_range)
+                       sdvox |= HDMI_COLOR_RANGE_16_235;
+               if (INTEL_GEN(dev_priv) < 5)
+                       sdvox |= SDVO_BORDER_ENABLE;
+       } else {
+               sdvox = I915_READ(intel_sdvo->sdvo_reg);
+               if (intel_sdvo->port == PORT_B)
+                       sdvox &= SDVOB_PRESERVE_MASK;
+               else
+                       sdvox &= SDVOC_PRESERVE_MASK;
+               sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
+       }
+       if (HAS_PCH_CPT(dev_priv))
+               sdvox |= SDVO_PIPE_SEL_CPT(crtc->pipe);
+       else
+               sdvox |= SDVO_PIPE_SEL(crtc->pipe);
+       if (INTEL_GEN(dev_priv) >= 4) {
+               /* done in crtc_mode_set as the dpll_md reg must be written early */
+       } else if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) ||
+                  IS_G33(dev_priv) || IS_PINEVIEW(dev_priv)) {
+               /* done in crtc_mode_set as it lives inside the dpll register */
+       } else {
+               sdvox |= (crtc_state->pixel_multiplier - 1)
+                       << SDVO_PORT_MULTIPLY_SHIFT;
+       }
+       if (input_dtd.part2.sdvo_flags & SDVO_NEED_TO_STALL &&
+           INTEL_GEN(dev_priv) < 5)
+               sdvox |= SDVO_STALL_SELECT;
+       intel_sdvo_write_sdvox(intel_sdvo, sdvox);
+ }
+ static bool intel_sdvo_connector_get_hw_state(struct intel_connector *connector)
+ {
+       struct intel_sdvo_connector *intel_sdvo_connector =
+               to_intel_sdvo_connector(&connector->base);
+       struct intel_sdvo *intel_sdvo = intel_attached_sdvo(&connector->base);
+       u16 active_outputs = 0;
+       intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs);
+       return active_outputs & intel_sdvo_connector->output_flag;
+ }
+ bool intel_sdvo_port_enabled(struct drm_i915_private *dev_priv,
+                            i915_reg_t sdvo_reg, enum pipe *pipe)
+ {
+       u32 val;
+       val = I915_READ(sdvo_reg);
+       /* asserts want to know the pipe even if the port is disabled */
+       if (HAS_PCH_CPT(dev_priv))
+               *pipe = (val & SDVO_PIPE_SEL_MASK_CPT) >> SDVO_PIPE_SEL_SHIFT_CPT;
+       else if (IS_CHERRYVIEW(dev_priv))
+               *pipe = (val & SDVO_PIPE_SEL_MASK_CHV) >> SDVO_PIPE_SEL_SHIFT_CHV;
+       else
+               *pipe = (val & SDVO_PIPE_SEL_MASK) >> SDVO_PIPE_SEL_SHIFT;
+       return val & SDVO_ENABLE;
+ }
+ static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder,
+                                   enum pipe *pipe)
+ {
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
+       u16 active_outputs = 0;
+       bool ret;
+       intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs);
+       ret = intel_sdvo_port_enabled(dev_priv, intel_sdvo->sdvo_reg, pipe);
+       return ret || active_outputs;
+ }
+ static void intel_sdvo_get_config(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *pipe_config)
+ {
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
+       struct intel_sdvo_dtd dtd;
+       int encoder_pixel_multiplier = 0;
+       int dotclock;
+       u32 flags = 0, sdvox;
+       u8 val;
+       bool ret;
+       pipe_config->output_types |= BIT(INTEL_OUTPUT_SDVO);
+       sdvox = I915_READ(intel_sdvo->sdvo_reg);
+       ret = intel_sdvo_get_input_timing(intel_sdvo, &dtd);
+       if (!ret) {
+               /*
+                * Some sdvo encoders are not spec compliant and don't
+                * implement the mandatory get_timings function.
+                */
+               DRM_DEBUG_DRIVER("failed to retrieve SDVO DTD\n");
+               pipe_config->quirks |= PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS;
+       } else {
+               if (dtd.part2.dtd_flags & DTD_FLAG_HSYNC_POSITIVE)
+                       flags |= DRM_MODE_FLAG_PHSYNC;
+               else
+                       flags |= DRM_MODE_FLAG_NHSYNC;
+               if (dtd.part2.dtd_flags & DTD_FLAG_VSYNC_POSITIVE)
+                       flags |= DRM_MODE_FLAG_PVSYNC;
+               else
+                       flags |= DRM_MODE_FLAG_NVSYNC;
+       }
+       pipe_config->base.adjusted_mode.flags |= flags;
+       /*
+        * pixel multiplier readout is tricky: Only on i915g/gm it is stored in
+        * the sdvo port register, on all other platforms it is part of the dpll
+        * state. Since the general pipe state readout happens before the
+        * encoder->get_config we so already have a valid pixel multplier on all
+        * other platfroms.
+        */
+       if (IS_I915G(dev_priv) || IS_I915GM(dev_priv)) {
+               pipe_config->pixel_multiplier =
+                       ((sdvox & SDVO_PORT_MULTIPLY_MASK)
+                        >> SDVO_PORT_MULTIPLY_SHIFT) + 1;
+       }
+       dotclock = pipe_config->port_clock;
+       if (pipe_config->pixel_multiplier)
+               dotclock /= pipe_config->pixel_multiplier;
+       pipe_config->base.adjusted_mode.crtc_clock = dotclock;
+       /* Cross check the port pixel multiplier with the sdvo encoder state. */
+       if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_CLOCK_RATE_MULT,
+                                &val, 1)) {
+               switch (val) {
+               case SDVO_CLOCK_RATE_MULT_1X:
+                       encoder_pixel_multiplier = 1;
+                       break;
+               case SDVO_CLOCK_RATE_MULT_2X:
+                       encoder_pixel_multiplier = 2;
+                       break;
+               case SDVO_CLOCK_RATE_MULT_4X:
+                       encoder_pixel_multiplier = 4;
+                       break;
+               }
+       }
+       WARN(encoder_pixel_multiplier != pipe_config->pixel_multiplier,
+            "SDVO pixel multiplier mismatch, port: %i, encoder: %i\n",
+            pipe_config->pixel_multiplier, encoder_pixel_multiplier);
+       if (sdvox & HDMI_COLOR_RANGE_16_235)
+               pipe_config->limited_color_range = true;
+       if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_AUDIO_STAT,
+                                &val, 1)) {
+               u8 mask = SDVO_AUDIO_ELD_VALID | SDVO_AUDIO_PRESENCE_DETECT;
+               if ((val & mask) == mask)
+                       pipe_config->has_audio = true;
+       }
+       if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE,
+                                &val, 1)) {
+               if (val == SDVO_ENCODE_HDMI)
+                       pipe_config->has_hdmi_sink = true;
+       }
+       intel_sdvo_get_avi_infoframe(intel_sdvo, pipe_config);
+ }
+ static void intel_sdvo_disable_audio(struct intel_sdvo *intel_sdvo)
+ {
+       intel_sdvo_set_audio_state(intel_sdvo, 0);
+ }
+ static void intel_sdvo_enable_audio(struct intel_sdvo *intel_sdvo,
+                                   const struct intel_crtc_state *crtc_state,
+                                   const struct drm_connector_state *conn_state)
+ {
+       const struct drm_display_mode *adjusted_mode =
+               &crtc_state->base.adjusted_mode;
+       struct drm_connector *connector = conn_state->connector;
+       u8 *eld = connector->eld;
+       eld[6] = drm_av_sync_delay(connector, adjusted_mode) / 2;
+       intel_sdvo_set_audio_state(intel_sdvo, 0);
+       intel_sdvo_write_infoframe(intel_sdvo, SDVO_HBUF_INDEX_ELD,
+                                  SDVO_HBUF_TX_DISABLED,
+                                  eld, drm_eld_size(eld));
+       intel_sdvo_set_audio_state(intel_sdvo, SDVO_AUDIO_ELD_VALID |
+                                  SDVO_AUDIO_PRESENCE_DETECT);
+ }
+ static void intel_disable_sdvo(struct intel_encoder *encoder,
+                              const struct intel_crtc_state *old_crtc_state,
+                              const struct drm_connector_state *conn_state)
+ {
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
+       struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc);
+       u32 temp;
+       if (old_crtc_state->has_audio)
+               intel_sdvo_disable_audio(intel_sdvo);
+       intel_sdvo_set_active_outputs(intel_sdvo, 0);
+       if (0)
+               intel_sdvo_set_encoder_power_state(intel_sdvo,
+                                                  DRM_MODE_DPMS_OFF);
+       temp = I915_READ(intel_sdvo->sdvo_reg);
+       temp &= ~SDVO_ENABLE;
+       intel_sdvo_write_sdvox(intel_sdvo, temp);
+       /*
+        * HW workaround for IBX, we need to move the port
+        * to transcoder A after disabling it to allow the
+        * matching DP port to be enabled on transcoder A.
+        */
+       if (HAS_PCH_IBX(dev_priv) && crtc->pipe == PIPE_B) {
+               /*
+                * We get CPU/PCH FIFO underruns on the other pipe when
+                * doing the workaround. Sweep them under the rug.
+                */
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, PIPE_A, false);
+               intel_set_pch_fifo_underrun_reporting(dev_priv, PIPE_A, false);
+               temp &= ~SDVO_PIPE_SEL_MASK;
+               temp |= SDVO_ENABLE | SDVO_PIPE_SEL(PIPE_A);
+               intel_sdvo_write_sdvox(intel_sdvo, temp);
+               temp &= ~SDVO_ENABLE;
+               intel_sdvo_write_sdvox(intel_sdvo, temp);
+               intel_wait_for_vblank_if_active(dev_priv, PIPE_A);
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, PIPE_A, true);
+               intel_set_pch_fifo_underrun_reporting(dev_priv, PIPE_A, true);
+       }
+ }
+ static void pch_disable_sdvo(struct intel_encoder *encoder,
+                            const struct intel_crtc_state *old_crtc_state,
+                            const struct drm_connector_state *old_conn_state)
+ {
+ }
+ static void pch_post_disable_sdvo(struct intel_encoder *encoder,
+                                 const struct intel_crtc_state *old_crtc_state,
+                                 const struct drm_connector_state *old_conn_state)
+ {
+       intel_disable_sdvo(encoder, old_crtc_state, old_conn_state);
+ }
+ static void intel_enable_sdvo(struct intel_encoder *encoder,
+                             const struct intel_crtc_state *pipe_config,
+                             const struct drm_connector_state *conn_state)
+ {
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
+       struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc);
+       u32 temp;
+       bool input1, input2;
+       int i;
+       bool success;
+       temp = I915_READ(intel_sdvo->sdvo_reg);
+       temp |= SDVO_ENABLE;
+       intel_sdvo_write_sdvox(intel_sdvo, temp);
+       for (i = 0; i < 2; i++)
+               intel_wait_for_vblank(dev_priv, intel_crtc->pipe);
+       success = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
+       /*
+        * Warn if the device reported failure to sync.
+        *
+        * A lot of SDVO devices fail to notify of sync, but it's
+        * a given it the status is a success, we succeeded.
+        */
+       if (success && !input1) {
+               DRM_DEBUG_KMS("First %s output reported failure to "
+                               "sync\n", SDVO_NAME(intel_sdvo));
+       }
+       if (0)
+               intel_sdvo_set_encoder_power_state(intel_sdvo,
+                                                  DRM_MODE_DPMS_ON);
+       intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
+       if (pipe_config->has_audio)
+               intel_sdvo_enable_audio(intel_sdvo, pipe_config, conn_state);
+ }
+ static enum drm_mode_status
+ intel_sdvo_mode_valid(struct drm_connector *connector,
+                     struct drm_display_mode *mode)
+ {
+       struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
+       struct intel_sdvo_connector *intel_sdvo_connector =
+               to_intel_sdvo_connector(connector);
+       int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
+       if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+       if (intel_sdvo->pixel_clock_min > mode->clock)
+               return MODE_CLOCK_LOW;
+       if (intel_sdvo->pixel_clock_max < mode->clock)
+               return MODE_CLOCK_HIGH;
+       if (mode->clock > max_dotclk)
+               return MODE_CLOCK_HIGH;
+       if (IS_LVDS(intel_sdvo_connector)) {
+               const struct drm_display_mode *fixed_mode =
+                       intel_sdvo_connector->base.panel.fixed_mode;
+               if (mode->hdisplay > fixed_mode->hdisplay)
+                       return MODE_PANEL;
+               if (mode->vdisplay > fixed_mode->vdisplay)
+                       return MODE_PANEL;
+       }
+       return MODE_OK;
+ }
+ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct intel_sdvo_caps *caps)
+ {
+       BUILD_BUG_ON(sizeof(*caps) != 8);
+       if (!intel_sdvo_get_value(intel_sdvo,
+                                 SDVO_CMD_GET_DEVICE_CAPS,
+                                 caps, sizeof(*caps)))
+               return false;
+       DRM_DEBUG_KMS("SDVO capabilities:\n"
+                     "  vendor_id: %d\n"
+                     "  device_id: %d\n"
+                     "  device_rev_id: %d\n"
+                     "  sdvo_version_major: %d\n"
+                     "  sdvo_version_minor: %d\n"
+                     "  sdvo_inputs_mask: %d\n"
+                     "  smooth_scaling: %d\n"
+                     "  sharp_scaling: %d\n"
+                     "  up_scaling: %d\n"
+                     "  down_scaling: %d\n"
+                     "  stall_support: %d\n"
+                     "  output_flags: %d\n",
+                     caps->vendor_id,
+                     caps->device_id,
+                     caps->device_rev_id,
+                     caps->sdvo_version_major,
+                     caps->sdvo_version_minor,
+                     caps->sdvo_inputs_mask,
+                     caps->smooth_scaling,
+                     caps->sharp_scaling,
+                     caps->up_scaling,
+                     caps->down_scaling,
+                     caps->stall_support,
+                     caps->output_flags);
+       return true;
+ }
+ static u16 intel_sdvo_get_hotplug_support(struct intel_sdvo *intel_sdvo)
+ {
+       struct drm_i915_private *dev_priv = to_i915(intel_sdvo->base.base.dev);
+       u16 hotplug;
+       if (!I915_HAS_HOTPLUG(dev_priv))
+               return 0;
+       /*
+        * HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise
+        * on the line.
+        */
+       if (IS_I945G(dev_priv) || IS_I945GM(dev_priv))
+               return 0;
+       if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT,
+                                       &hotplug, sizeof(hotplug)))
+               return 0;
+       return hotplug;
+ }
+ static void intel_sdvo_enable_hotplug(struct intel_encoder *encoder)
+ {
+       struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
+       intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG,
+                            &intel_sdvo->hotplug_active, 2);
+ }
+ static bool intel_sdvo_hotplug(struct intel_encoder *encoder,
+                              struct intel_connector *connector)
+ {
+       intel_sdvo_enable_hotplug(encoder);
+       return intel_encoder_hotplug(encoder, connector);
+ }
+ static bool
+ intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo)
+ {
+       /* Is there more than one type of output? */
+       return hweight16(intel_sdvo->caps.output_flags) > 1;
+ }
+ static struct edid *
+ intel_sdvo_get_edid(struct drm_connector *connector)
+ {
+       struct intel_sdvo *sdvo = intel_attached_sdvo(connector);
+       return drm_get_edid(connector, &sdvo->ddc);
+ }
+ /* Mac mini hack -- use the same DDC as the analog connector */
+ static struct edid *
+ intel_sdvo_get_analog_edid(struct drm_connector *connector)
+ {
+       struct drm_i915_private *dev_priv = to_i915(connector->dev);
+       return drm_get_edid(connector,
+                           intel_gmbus_get_adapter(dev_priv,
+                                                   dev_priv->vbt.crt_ddc_pin));
+ }
+ static enum drm_connector_status
+ intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
+ {
+       struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
+       struct intel_sdvo_connector *intel_sdvo_connector =
+               to_intel_sdvo_connector(connector);
+       enum drm_connector_status status;
+       struct edid *edid;
+       edid = intel_sdvo_get_edid(connector);
+       if (edid == NULL && intel_sdvo_multifunc_encoder(intel_sdvo)) {
+               u8 ddc, saved_ddc = intel_sdvo->ddc_bus;
+               /*
+                * Don't use the 1 as the argument of DDC bus switch to get
+                * the EDID. It is used for SDVO SPD ROM.
+                */
+               for (ddc = intel_sdvo->ddc_bus >> 1; ddc > 1; ddc >>= 1) {
+                       intel_sdvo->ddc_bus = ddc;
+                       edid = intel_sdvo_get_edid(connector);
+                       if (edid)
+                               break;
+               }
+               /*
+                * If we found the EDID on the other bus,
+                * assume that is the correct DDC bus.
+                */
+               if (edid == NULL)
+                       intel_sdvo->ddc_bus = saved_ddc;
+       }
+       /*
+        * When there is no edid and no monitor is connected with VGA
+        * port, try to use the CRT ddc to read the EDID for DVI-connector.
+        */
+       if (edid == NULL)
+               edid = intel_sdvo_get_analog_edid(connector);
+       status = connector_status_unknown;
+       if (edid != NULL) {
+               /* DDC bus is shared, match EDID to connector type */
+               if (edid->input & DRM_EDID_INPUT_DIGITAL) {
+                       status = connector_status_connected;
+                       if (intel_sdvo_connector->is_hdmi) {
+                               intel_sdvo->has_hdmi_monitor = drm_detect_hdmi_monitor(edid);
+                               intel_sdvo->has_hdmi_audio = drm_detect_monitor_audio(edid);
+                       }
+               } else
+                       status = connector_status_disconnected;
+               kfree(edid);
+       }
+       return status;
+ }
+ static bool
+ intel_sdvo_connector_matches_edid(struct intel_sdvo_connector *sdvo,
+                                 struct edid *edid)
+ {
+       bool monitor_is_digital = !!(edid->input & DRM_EDID_INPUT_DIGITAL);
+       bool connector_is_digital = !!IS_DIGITAL(sdvo);
+       DRM_DEBUG_KMS("connector_is_digital? %d, monitor_is_digital? %d\n",
+                     connector_is_digital, monitor_is_digital);
+       return connector_is_digital == monitor_is_digital;
+ }
+ static enum drm_connector_status
+ intel_sdvo_detect(struct drm_connector *connector, bool force)
+ {
+       u16 response;
+       struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
+       struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
+       enum drm_connector_status ret;
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                     connector->base.id, connector->name);
+       if (!intel_sdvo_get_value(intel_sdvo,
+                                 SDVO_CMD_GET_ATTACHED_DISPLAYS,
+                                 &response, 2))
+               return connector_status_unknown;
+       DRM_DEBUG_KMS("SDVO response %d %d [%x]\n",
+                     response & 0xff, response >> 8,
+                     intel_sdvo_connector->output_flag);
+       if (response == 0)
+               return connector_status_disconnected;
+       intel_sdvo->attached_output = response;
+       intel_sdvo->has_hdmi_monitor = false;
+       intel_sdvo->has_hdmi_audio = false;
+       if ((intel_sdvo_connector->output_flag & response) == 0)
+               ret = connector_status_disconnected;
+       else if (IS_TMDS(intel_sdvo_connector))
+               ret = intel_sdvo_tmds_sink_detect(connector);
+       else {
+               struct edid *edid;
+               /* if we have an edid check it matches the connection */
+               edid = intel_sdvo_get_edid(connector);
+               if (edid == NULL)
+                       edid = intel_sdvo_get_analog_edid(connector);
+               if (edid != NULL) {
+                       if (intel_sdvo_connector_matches_edid(intel_sdvo_connector,
+                                                             edid))
+                               ret = connector_status_connected;
+                       else
+                               ret = connector_status_disconnected;
+                       kfree(edid);
+               } else
+                       ret = connector_status_connected;
+       }
+       return ret;
+ }
+ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
+ {
+       struct edid *edid;
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                     connector->base.id, connector->name);
+       /* set the bus switch and get the modes */
+       edid = intel_sdvo_get_edid(connector);
+       /*
+        * Mac mini hack.  On this device, the DVI-I connector shares one DDC
+        * link between analog and digital outputs. So, if the regular SDVO
+        * DDC fails, check to see if the analog output is disconnected, in
+        * which case we'll look there for the digital DDC data.
+        */
+       if (edid == NULL)
+               edid = intel_sdvo_get_analog_edid(connector);
+       if (edid != NULL) {
+               if (intel_sdvo_connector_matches_edid(to_intel_sdvo_connector(connector),
+                                                     edid)) {
+                       drm_connector_update_edid_property(connector, edid);
+                       drm_add_edid_modes(connector, edid);
+               }
+               kfree(edid);
+       }
+ }
+ /*
+  * Set of SDVO TV modes.
+  * Note!  This is in reply order (see loop in get_tv_modes).
+  * XXX: all 60Hz refresh?
+  */
+ static const struct drm_display_mode sdvo_tv_modes[] = {
+       { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815, 320, 321, 384,
+                  416, 0, 200, 201, 232, 233, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 6814, 320, 321, 384,
+                  416, 0, 240, 241, 272, 273, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 9910, 400, 401, 464,
+                  496, 0, 300, 301, 332, 333, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 16913, 640, 641, 704,
+                  736, 0, 350, 351, 382, 383, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121, 640, 641, 704,
+                  736, 0, 400, 401, 432, 433, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 22654, 640, 641, 704,
+                  736, 0, 480, 481, 512, 513, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("704x480", DRM_MODE_TYPE_DRIVER, 24624, 704, 705, 768,
+                  800, 0, 480, 481, 512, 513, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("704x576", DRM_MODE_TYPE_DRIVER, 29232, 704, 705, 768,
+                  800, 0, 576, 577, 608, 609, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("720x350", DRM_MODE_TYPE_DRIVER, 18751, 720, 721, 784,
+                  816, 0, 350, 351, 382, 383, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 21199, 720, 721, 784,
+                  816, 0, 400, 401, 432, 433, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25116, 720, 721, 784,
+                  816, 0, 480, 481, 512, 513, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("720x540", DRM_MODE_TYPE_DRIVER, 28054, 720, 721, 784,
+                  816, 0, 540, 541, 572, 573, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 29816, 720, 721, 784,
+                  816, 0, 576, 577, 608, 609, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("768x576", DRM_MODE_TYPE_DRIVER, 31570, 768, 769, 832,
+                  864, 0, 576, 577, 608, 609, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 34030, 800, 801, 864,
+                  896, 0, 600, 601, 632, 633, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 36581, 832, 833, 896,
+                  928, 0, 624, 625, 656, 657, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("920x766", DRM_MODE_TYPE_DRIVER, 48707, 920, 921, 984,
+                  1016, 0, 766, 767, 798, 799, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 53827, 1024, 1025, 1088,
+                  1120, 0, 768, 769, 800, 801, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+       { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 87265, 1280, 1281, 1344,
+                  1376, 0, 1024, 1025, 1056, 1057, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ };
+ static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
+ {
+       struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
+       const struct drm_connector_state *conn_state = connector->state;
+       struct intel_sdvo_sdtv_resolution_request tv_res;
+       u32 reply = 0, format_map = 0;
+       int i;
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                     connector->base.id, connector->name);
+       /*
+        * Read the list of supported input resolutions for the selected TV
+        * format.
+        */
+       format_map = 1 << conn_state->tv.mode;
+       memcpy(&tv_res, &format_map,
+              min(sizeof(format_map), sizeof(struct intel_sdvo_sdtv_resolution_request)));
+       if (!intel_sdvo_set_target_output(intel_sdvo, intel_sdvo->attached_output))
+               return;
+       BUILD_BUG_ON(sizeof(tv_res) != 3);
+       if (!intel_sdvo_write_cmd(intel_sdvo,
+                                 SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT,
+                                 &tv_res, sizeof(tv_res)))
+               return;
+       if (!intel_sdvo_read_response(intel_sdvo, &reply, 3))
+               return;
+       for (i = 0; i < ARRAY_SIZE(sdvo_tv_modes); i++)
+               if (reply & (1 << i)) {
+                       struct drm_display_mode *nmode;
+                       nmode = drm_mode_duplicate(connector->dev,
+                                                  &sdvo_tv_modes[i]);
+                       if (nmode)
+                               drm_mode_probed_add(connector, nmode);
+               }
+ }
+ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
+ {
+       struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
+       struct drm_i915_private *dev_priv = to_i915(connector->dev);
+       struct drm_display_mode *newmode;
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                     connector->base.id, connector->name);
+       /*
+        * Fetch modes from VBT. For SDVO prefer the VBT mode since some
+        * SDVO->LVDS transcoders can't cope with the EDID mode.
+        */
+       if (dev_priv->vbt.sdvo_lvds_vbt_mode != NULL) {
+               newmode = drm_mode_duplicate(connector->dev,
+                                            dev_priv->vbt.sdvo_lvds_vbt_mode);
+               if (newmode != NULL) {
+                       /* Guarantee the mode is preferred */
+                       newmode->type = (DRM_MODE_TYPE_PREFERRED |
+                                        DRM_MODE_TYPE_DRIVER);
+                       drm_mode_probed_add(connector, newmode);
+               }
+       }
+       /*
+        * Attempt to get the mode list from DDC.
+        * Assume that the preferred modes are
+        * arranged in priority order.
+        */
+       intel_ddc_get_modes(connector, &intel_sdvo->ddc);
+ }
+ static int intel_sdvo_get_modes(struct drm_connector *connector)
+ {
+       struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
+       if (IS_TV(intel_sdvo_connector))
+               intel_sdvo_get_tv_modes(connector);
+       else if (IS_LVDS(intel_sdvo_connector))
+               intel_sdvo_get_lvds_modes(connector);
+       else
+               intel_sdvo_get_ddc_modes(connector);
+       return !list_empty(&connector->probed_modes);
+ }
+ static int
+ intel_sdvo_connector_atomic_get_property(struct drm_connector *connector,
+                                        const struct drm_connector_state *state,
+                                        struct drm_property *property,
+                                        u64 *val)
+ {
+       struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
+       const struct intel_sdvo_connector_state *sdvo_state = to_intel_sdvo_connector_state((void *)state);
+       if (property == intel_sdvo_connector->tv_format) {
+               int i;
+               for (i = 0; i < intel_sdvo_connector->format_supported_num; i++)
+                       if (state->tv.mode == intel_sdvo_connector->tv_format_supported[i]) {
+                               *val = i;
+                               return 0;
+                       }
+               WARN_ON(1);
+               *val = 0;
+       } else if (property == intel_sdvo_connector->top ||
+                  property == intel_sdvo_connector->bottom)
+               *val = intel_sdvo_connector->max_vscan - sdvo_state->tv.overscan_v;
+       else if (property == intel_sdvo_connector->left ||
+                property == intel_sdvo_connector->right)
+               *val = intel_sdvo_connector->max_hscan - sdvo_state->tv.overscan_h;
+       else if (property == intel_sdvo_connector->hpos)
+               *val = sdvo_state->tv.hpos;
+       else if (property == intel_sdvo_connector->vpos)
+               *val = sdvo_state->tv.vpos;
+       else if (property == intel_sdvo_connector->saturation)
+               *val = state->tv.saturation;
+       else if (property == intel_sdvo_connector->contrast)
+               *val = state->tv.contrast;
+       else if (property == intel_sdvo_connector->hue)
+               *val = state->tv.hue;
+       else if (property == intel_sdvo_connector->brightness)
+               *val = state->tv.brightness;
+       else if (property == intel_sdvo_connector->sharpness)
+               *val = sdvo_state->tv.sharpness;
+       else if (property == intel_sdvo_connector->flicker_filter)
+               *val = sdvo_state->tv.flicker_filter;
+       else if (property == intel_sdvo_connector->flicker_filter_2d)
+               *val = sdvo_state->tv.flicker_filter_2d;
+       else if (property == intel_sdvo_connector->flicker_filter_adaptive)
+               *val = sdvo_state->tv.flicker_filter_adaptive;
+       else if (property == intel_sdvo_connector->tv_chroma_filter)
+               *val = sdvo_state->tv.chroma_filter;
+       else if (property == intel_sdvo_connector->tv_luma_filter)
+               *val = sdvo_state->tv.luma_filter;
+       else if (property == intel_sdvo_connector->dot_crawl)
+               *val = sdvo_state->tv.dot_crawl;
+       else
+               return intel_digital_connector_atomic_get_property(connector, state, property, val);
+       return 0;
+ }
+ static int
+ intel_sdvo_connector_atomic_set_property(struct drm_connector *connector,
+                                        struct drm_connector_state *state,
+                                        struct drm_property *property,
+                                        u64 val)
+ {
+       struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
+       struct intel_sdvo_connector_state *sdvo_state = to_intel_sdvo_connector_state(state);
+       if (property == intel_sdvo_connector->tv_format) {
+               state->tv.mode = intel_sdvo_connector->tv_format_supported[val];
+               if (state->crtc) {
+                       struct drm_crtc_state *crtc_state =
+                               drm_atomic_get_new_crtc_state(state->state, state->crtc);
+                       crtc_state->connectors_changed = true;
+               }
+       } else if (property == intel_sdvo_connector->top ||
+                  property == intel_sdvo_connector->bottom)
+               /* Cannot set these independent from each other */
+               sdvo_state->tv.overscan_v = intel_sdvo_connector->max_vscan - val;
+       else if (property == intel_sdvo_connector->left ||
+                property == intel_sdvo_connector->right)
+               /* Cannot set these independent from each other */
+               sdvo_state->tv.overscan_h = intel_sdvo_connector->max_hscan - val;
+       else if (property == intel_sdvo_connector->hpos)
+               sdvo_state->tv.hpos = val;
+       else if (property == intel_sdvo_connector->vpos)
+               sdvo_state->tv.vpos = val;
+       else if (property == intel_sdvo_connector->saturation)
+               state->tv.saturation = val;
+       else if (property == intel_sdvo_connector->contrast)
+               state->tv.contrast = val;
+       else if (property == intel_sdvo_connector->hue)
+               state->tv.hue = val;
+       else if (property == intel_sdvo_connector->brightness)
+               state->tv.brightness = val;
+       else if (property == intel_sdvo_connector->sharpness)
+               sdvo_state->tv.sharpness = val;
+       else if (property == intel_sdvo_connector->flicker_filter)
+               sdvo_state->tv.flicker_filter = val;
+       else if (property == intel_sdvo_connector->flicker_filter_2d)
+               sdvo_state->tv.flicker_filter_2d = val;
+       else if (property == intel_sdvo_connector->flicker_filter_adaptive)
+               sdvo_state->tv.flicker_filter_adaptive = val;
+       else if (property == intel_sdvo_connector->tv_chroma_filter)
+               sdvo_state->tv.chroma_filter = val;
+       else if (property == intel_sdvo_connector->tv_luma_filter)
+               sdvo_state->tv.luma_filter = val;
+       else if (property == intel_sdvo_connector->dot_crawl)
+               sdvo_state->tv.dot_crawl = val;
+       else
+               return intel_digital_connector_atomic_set_property(connector, state, property, val);
+       return 0;
+ }
+ static int
+ intel_sdvo_connector_register(struct drm_connector *connector)
+ {
+       struct intel_sdvo *sdvo = intel_attached_sdvo(connector);
+       int ret;
+       ret = intel_connector_register(connector);
+       if (ret)
+               return ret;
+       return sysfs_create_link(&connector->kdev->kobj,
+                                &sdvo->ddc.dev.kobj,
+                                sdvo->ddc.dev.kobj.name);
+ }
+ static void
+ intel_sdvo_connector_unregister(struct drm_connector *connector)
+ {
+       struct intel_sdvo *sdvo = intel_attached_sdvo(connector);
+       sysfs_remove_link(&connector->kdev->kobj,
+                         sdvo->ddc.dev.kobj.name);
+       intel_connector_unregister(connector);
+ }
+ static struct drm_connector_state *
+ intel_sdvo_connector_duplicate_state(struct drm_connector *connector)
+ {
+       struct intel_sdvo_connector_state *state;
+       state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return NULL;
+       __drm_atomic_helper_connector_duplicate_state(connector, &state->base.base);
+       return &state->base.base;
+ }
+ static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
+       .detect = intel_sdvo_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .atomic_get_property = intel_sdvo_connector_atomic_get_property,
+       .atomic_set_property = intel_sdvo_connector_atomic_set_property,
+       .late_register = intel_sdvo_connector_register,
+       .early_unregister = intel_sdvo_connector_unregister,
+       .destroy = intel_connector_destroy,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+       .atomic_duplicate_state = intel_sdvo_connector_duplicate_state,
+ };
+ static int intel_sdvo_atomic_check(struct drm_connector *conn,
 -      struct drm_atomic_state *state = new_conn_state->state;
++                                 struct drm_atomic_state *state)
+ {
 -                      drm_atomic_get_new_crtc_state(new_conn_state->state,
++      struct drm_connector_state *new_conn_state =
++              drm_atomic_get_new_connector_state(state, conn);
+       struct drm_connector_state *old_conn_state =
+               drm_atomic_get_old_connector_state(state, conn);
+       struct intel_sdvo_connector_state *old_state =
+               to_intel_sdvo_connector_state(old_conn_state);
+       struct intel_sdvo_connector_state *new_state =
+               to_intel_sdvo_connector_state(new_conn_state);
+       if (new_conn_state->crtc &&
+           (memcmp(&old_state->tv, &new_state->tv, sizeof(old_state->tv)) ||
+            memcmp(&old_conn_state->tv, &new_conn_state->tv, sizeof(old_conn_state->tv)))) {
+               struct drm_crtc_state *crtc_state =
 -      return intel_digital_connector_atomic_check(conn, new_conn_state);
++                      drm_atomic_get_new_crtc_state(state,
+                                                     new_conn_state->crtc);
+               crtc_state->connectors_changed = true;
+       }
++      return intel_digital_connector_atomic_check(conn, state);
+ }
+ static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
+       .get_modes = intel_sdvo_get_modes,
+       .mode_valid = intel_sdvo_mode_valid,
+       .atomic_check = intel_sdvo_atomic_check,
+ };
+ static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
+ {
+       struct intel_sdvo *intel_sdvo = to_sdvo(to_intel_encoder(encoder));
+       i2c_del_adapter(&intel_sdvo->ddc);
+       intel_encoder_destroy(encoder);
+ }
+ static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
+       .destroy = intel_sdvo_enc_destroy,
+ };
+ static void
+ intel_sdvo_guess_ddc_bus(struct intel_sdvo *sdvo)
+ {
+       u16 mask = 0;
+       unsigned int num_bits;
+       /*
+        * Make a mask of outputs less than or equal to our own priority in the
+        * list.
+        */
+       switch (sdvo->controlled_output) {
+       case SDVO_OUTPUT_LVDS1:
+               mask |= SDVO_OUTPUT_LVDS1;
+               /* fall through */
+       case SDVO_OUTPUT_LVDS0:
+               mask |= SDVO_OUTPUT_LVDS0;
+               /* fall through */
+       case SDVO_OUTPUT_TMDS1:
+               mask |= SDVO_OUTPUT_TMDS1;
+               /* fall through */
+       case SDVO_OUTPUT_TMDS0:
+               mask |= SDVO_OUTPUT_TMDS0;
+               /* fall through */
+       case SDVO_OUTPUT_RGB1:
+               mask |= SDVO_OUTPUT_RGB1;
+               /* fall through */
+       case SDVO_OUTPUT_RGB0:
+               mask |= SDVO_OUTPUT_RGB0;
+               break;
+       }
+       /* Count bits to find what number we are in the priority list. */
+       mask &= sdvo->caps.output_flags;
+       num_bits = hweight16(mask);
+       /* If more than 3 outputs, default to DDC bus 3 for now. */
+       if (num_bits > 3)
+               num_bits = 3;
+       /* Corresponds to SDVO_CONTROL_BUS_DDCx */
+       sdvo->ddc_bus = 1 << num_bits;
+ }
+ /*
+  * Choose the appropriate DDC bus for control bus switch command for this
+  * SDVO output based on the controlled output.
+  *
+  * DDC bus number assignment is in a priority order of RGB outputs, then TMDS
+  * outputs, then LVDS outputs.
+  */
+ static void
+ intel_sdvo_select_ddc_bus(struct drm_i915_private *dev_priv,
+                         struct intel_sdvo *sdvo)
+ {
+       struct sdvo_device_mapping *mapping;
+       if (sdvo->port == PORT_B)
+               mapping = &dev_priv->vbt.sdvo_mappings[0];
+       else
+               mapping = &dev_priv->vbt.sdvo_mappings[1];
+       if (mapping->initialized)
+               sdvo->ddc_bus = 1 << ((mapping->ddc_pin & 0xf0) >> 4);
+       else
+               intel_sdvo_guess_ddc_bus(sdvo);
+ }
+ static void
+ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv,
+                         struct intel_sdvo *sdvo)
+ {
+       struct sdvo_device_mapping *mapping;
+       u8 pin;
+       if (sdvo->port == PORT_B)
+               mapping = &dev_priv->vbt.sdvo_mappings[0];
+       else
+               mapping = &dev_priv->vbt.sdvo_mappings[1];
+       if (mapping->initialized &&
+           intel_gmbus_is_valid_pin(dev_priv, mapping->i2c_pin))
+               pin = mapping->i2c_pin;
+       else
+               pin = GMBUS_PIN_DPB;
+       sdvo->i2c = intel_gmbus_get_adapter(dev_priv, pin);
+       /*
+        * With gmbus we should be able to drive sdvo i2c at 2MHz, but somehow
+        * our code totally fails once we start using gmbus. Hence fall back to
+        * bit banging for now.
+        */
+       intel_gmbus_force_bit(sdvo->i2c, true);
+ }
+ /* undo any changes intel_sdvo_select_i2c_bus() did to sdvo->i2c */
+ static void
+ intel_sdvo_unselect_i2c_bus(struct intel_sdvo *sdvo)
+ {
+       intel_gmbus_force_bit(sdvo->i2c, false);
+ }
+ static bool
+ intel_sdvo_is_hdmi_connector(struct intel_sdvo *intel_sdvo, int device)
+ {
+       return intel_sdvo_check_supp_encode(intel_sdvo);
+ }
+ static u8
+ intel_sdvo_get_slave_addr(struct drm_i915_private *dev_priv,
+                         struct intel_sdvo *sdvo)
+ {
+       struct sdvo_device_mapping *my_mapping, *other_mapping;
+       if (sdvo->port == PORT_B) {
+               my_mapping = &dev_priv->vbt.sdvo_mappings[0];
+               other_mapping = &dev_priv->vbt.sdvo_mappings[1];
+       } else {
+               my_mapping = &dev_priv->vbt.sdvo_mappings[1];
+               other_mapping = &dev_priv->vbt.sdvo_mappings[0];
+       }
+       /* If the BIOS described our SDVO device, take advantage of it. */
+       if (my_mapping->slave_addr)
+               return my_mapping->slave_addr;
+       /*
+        * If the BIOS only described a different SDVO device, use the
+        * address that it isn't using.
+        */
+       if (other_mapping->slave_addr) {
+               if (other_mapping->slave_addr == 0x70)
+                       return 0x72;
+               else
+                       return 0x70;
+       }
+       /*
+        * No SDVO device info is found for another DVO port,
+        * so use mapping assumption we had before BIOS parsing.
+        */
+       if (sdvo->port == PORT_B)
+               return 0x70;
+       else
+               return 0x72;
+ }
+ static int
+ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
+                         struct intel_sdvo *encoder)
+ {
+       struct drm_connector *drm_connector;
+       int ret;
+       drm_connector = &connector->base.base;
+       ret = drm_connector_init(encoder->base.base.dev,
+                          drm_connector,
+                          &intel_sdvo_connector_funcs,
+                          connector->base.base.connector_type);
+       if (ret < 0)
+               return ret;
+       drm_connector_helper_add(drm_connector,
+                                &intel_sdvo_connector_helper_funcs);
+       connector->base.base.interlace_allowed = 1;
+       connector->base.base.doublescan_allowed = 0;
+       connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
+       connector->base.get_hw_state = intel_sdvo_connector_get_hw_state;
+       intel_connector_attach_encoder(&connector->base, &encoder->base);
+       return 0;
+ }
+ static void
+ intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo,
+                              struct intel_sdvo_connector *connector)
+ {
+       struct drm_i915_private *dev_priv = to_i915(connector->base.base.dev);
+       intel_attach_force_audio_property(&connector->base.base);
+       if (INTEL_GEN(dev_priv) >= 4 && IS_MOBILE(dev_priv)) {
+               intel_attach_broadcast_rgb_property(&connector->base.base);
+       }
+       intel_attach_aspect_ratio_property(&connector->base.base);
+       connector->base.base.state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+ }
+ static struct intel_sdvo_connector *intel_sdvo_connector_alloc(void)
+ {
+       struct intel_sdvo_connector *sdvo_connector;
+       struct intel_sdvo_connector_state *conn_state;
+       sdvo_connector = kzalloc(sizeof(*sdvo_connector), GFP_KERNEL);
+       if (!sdvo_connector)
+               return NULL;
+       conn_state = kzalloc(sizeof(*conn_state), GFP_KERNEL);
+       if (!conn_state) {
+               kfree(sdvo_connector);
+               return NULL;
+       }
+       __drm_atomic_helper_connector_reset(&sdvo_connector->base.base,
+                                           &conn_state->base.base);
+       return sdvo_connector;
+ }
+ static bool
+ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
+ {
+       struct drm_encoder *encoder = &intel_sdvo->base.base;
+       struct drm_connector *connector;
+       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+       struct intel_connector *intel_connector;
+       struct intel_sdvo_connector *intel_sdvo_connector;
+       DRM_DEBUG_KMS("initialising DVI device %d\n", device);
+       intel_sdvo_connector = intel_sdvo_connector_alloc();
+       if (!intel_sdvo_connector)
+               return false;
+       if (device == 0) {
+               intel_sdvo->controlled_output |= SDVO_OUTPUT_TMDS0;
+               intel_sdvo_connector->output_flag = SDVO_OUTPUT_TMDS0;
+       } else if (device == 1) {
+               intel_sdvo->controlled_output |= SDVO_OUTPUT_TMDS1;
+               intel_sdvo_connector->output_flag = SDVO_OUTPUT_TMDS1;
+       }
+       intel_connector = &intel_sdvo_connector->base;
+       connector = &intel_connector->base;
+       if (intel_sdvo_get_hotplug_support(intel_sdvo) &
+               intel_sdvo_connector->output_flag) {
+               intel_sdvo->hotplug_active |= intel_sdvo_connector->output_flag;
+               /*
+                * Some SDVO devices have one-shot hotplug interrupts.
+                * Ensure that they get re-enabled when an interrupt happens.
+                */
+               intel_encoder->hotplug = intel_sdvo_hotplug;
+               intel_sdvo_enable_hotplug(intel_encoder);
+       } else {
+               intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+       }
+       encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
+       connector->connector_type = DRM_MODE_CONNECTOR_DVID;
+       if (intel_sdvo_is_hdmi_connector(intel_sdvo, device)) {
+               connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
+               intel_sdvo_connector->is_hdmi = true;
+       }
+       if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
+               kfree(intel_sdvo_connector);
+               return false;
+       }
+       if (intel_sdvo_connector->is_hdmi)
+               intel_sdvo_add_hdmi_properties(intel_sdvo, intel_sdvo_connector);
+       return true;
+ }
+ static bool
+ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
+ {
+       struct drm_encoder *encoder = &intel_sdvo->base.base;
+       struct drm_connector *connector;
+       struct intel_connector *intel_connector;
+       struct intel_sdvo_connector *intel_sdvo_connector;
+       DRM_DEBUG_KMS("initialising TV type %d\n", type);
+       intel_sdvo_connector = intel_sdvo_connector_alloc();
+       if (!intel_sdvo_connector)
+               return false;
+       intel_connector = &intel_sdvo_connector->base;
+       connector = &intel_connector->base;
+       encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
+       connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO;
+       intel_sdvo->controlled_output |= type;
+       intel_sdvo_connector->output_flag = type;
+       if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
+               kfree(intel_sdvo_connector);
+               return false;
+       }
+       if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type))
+               goto err;
+       if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
+               goto err;
+       return true;
+ err:
+       intel_connector_destroy(connector);
+       return false;
+ }
+ static bool
+ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
+ {
+       struct drm_encoder *encoder = &intel_sdvo->base.base;
+       struct drm_connector *connector;
+       struct intel_connector *intel_connector;
+       struct intel_sdvo_connector *intel_sdvo_connector;
+       DRM_DEBUG_KMS("initialising analog device %d\n", device);
+       intel_sdvo_connector = intel_sdvo_connector_alloc();
+       if (!intel_sdvo_connector)
+               return false;
+       intel_connector = &intel_sdvo_connector->base;
+       connector = &intel_connector->base;
+       intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+       encoder->encoder_type = DRM_MODE_ENCODER_DAC;
+       connector->connector_type = DRM_MODE_CONNECTOR_VGA;
+       if (device == 0) {
+               intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB0;
+               intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB0;
+       } else if (device == 1) {
+               intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB1;
+               intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1;
+       }
+       if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
+               kfree(intel_sdvo_connector);
+               return false;
+       }
+       return true;
+ }
+ static bool
+ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
+ {
+       struct drm_encoder *encoder = &intel_sdvo->base.base;
+       struct drm_connector *connector;
+       struct intel_connector *intel_connector;
+       struct intel_sdvo_connector *intel_sdvo_connector;
+       struct drm_display_mode *mode;
+       DRM_DEBUG_KMS("initialising LVDS device %d\n", device);
+       intel_sdvo_connector = intel_sdvo_connector_alloc();
+       if (!intel_sdvo_connector)
+               return false;
+       intel_connector = &intel_sdvo_connector->base;
+       connector = &intel_connector->base;
+       encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
+       connector->connector_type = DRM_MODE_CONNECTOR_LVDS;
+       if (device == 0) {
+               intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS0;
+               intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS0;
+       } else if (device == 1) {
+               intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS1;
+               intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1;
+       }
+       if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
+               kfree(intel_sdvo_connector);
+               return false;
+       }
+       if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
+               goto err;
+       intel_sdvo_get_lvds_modes(connector);
+       list_for_each_entry(mode, &connector->probed_modes, head) {
+               if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+                       struct drm_display_mode *fixed_mode =
+                               drm_mode_duplicate(connector->dev, mode);
+                       intel_panel_init(&intel_connector->panel,
+                                        fixed_mode, NULL);
+                       break;
+               }
+       }
+       if (!intel_connector->panel.fixed_mode)
+               goto err;
+       return true;
+ err:
+       intel_connector_destroy(connector);
+       return false;
+ }
+ static bool
+ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, u16 flags)
+ {
+       /* SDVO requires XXX1 function may not exist unless it has XXX0 function.*/
+       if (flags & SDVO_OUTPUT_TMDS0)
+               if (!intel_sdvo_dvi_init(intel_sdvo, 0))
+                       return false;
+       if ((flags & SDVO_TMDS_MASK) == SDVO_TMDS_MASK)
+               if (!intel_sdvo_dvi_init(intel_sdvo, 1))
+                       return false;
+       /* TV has no XXX1 function block */
+       if (flags & SDVO_OUTPUT_SVID0)
+               if (!intel_sdvo_tv_init(intel_sdvo, SDVO_OUTPUT_SVID0))
+                       return false;
+       if (flags & SDVO_OUTPUT_CVBS0)
+               if (!intel_sdvo_tv_init(intel_sdvo, SDVO_OUTPUT_CVBS0))
+                       return false;
+       if (flags & SDVO_OUTPUT_YPRPB0)
+               if (!intel_sdvo_tv_init(intel_sdvo, SDVO_OUTPUT_YPRPB0))
+                       return false;
+       if (flags & SDVO_OUTPUT_RGB0)
+               if (!intel_sdvo_analog_init(intel_sdvo, 0))
+                       return false;
+       if ((flags & SDVO_RGB_MASK) == SDVO_RGB_MASK)
+               if (!intel_sdvo_analog_init(intel_sdvo, 1))
+                       return false;
+       if (flags & SDVO_OUTPUT_LVDS0)
+               if (!intel_sdvo_lvds_init(intel_sdvo, 0))
+                       return false;
+       if ((flags & SDVO_LVDS_MASK) == SDVO_LVDS_MASK)
+               if (!intel_sdvo_lvds_init(intel_sdvo, 1))
+                       return false;
+       if ((flags & SDVO_OUTPUT_MASK) == 0) {
+               unsigned char bytes[2];
+               intel_sdvo->controlled_output = 0;
+               memcpy(bytes, &intel_sdvo->caps.output_flags, 2);
+               DRM_DEBUG_KMS("%s: Unknown SDVO output type (0x%02x%02x)\n",
+                             SDVO_NAME(intel_sdvo),
+                             bytes[0], bytes[1]);
+               return false;
+       }
+       intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+       return true;
+ }
+ static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo)
+ {
+       struct drm_device *dev = intel_sdvo->base.base.dev;
+       struct drm_connector *connector, *tmp;
+       list_for_each_entry_safe(connector, tmp,
+                                &dev->mode_config.connector_list, head) {
+               if (intel_attached_encoder(connector) == &intel_sdvo->base) {
+                       drm_connector_unregister(connector);
+                       intel_connector_destroy(connector);
+               }
+       }
+ }
+ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
+                                         struct intel_sdvo_connector *intel_sdvo_connector,
+                                         int type)
+ {
+       struct drm_device *dev = intel_sdvo->base.base.dev;
+       struct intel_sdvo_tv_format format;
+       u32 format_map, i;
+       if (!intel_sdvo_set_target_output(intel_sdvo, type))
+               return false;
+       BUILD_BUG_ON(sizeof(format) != 6);
+       if (!intel_sdvo_get_value(intel_sdvo,
+                                 SDVO_CMD_GET_SUPPORTED_TV_FORMATS,
+                                 &format, sizeof(format)))
+               return false;
+       memcpy(&format_map, &format, min(sizeof(format_map), sizeof(format)));
+       if (format_map == 0)
+               return false;
+       intel_sdvo_connector->format_supported_num = 0;
+       for (i = 0 ; i < TV_FORMAT_NUM; i++)
+               if (format_map & (1 << i))
+                       intel_sdvo_connector->tv_format_supported[intel_sdvo_connector->format_supported_num++] = i;
+       intel_sdvo_connector->tv_format =
+                       drm_property_create(dev, DRM_MODE_PROP_ENUM,
+                                           "mode", intel_sdvo_connector->format_supported_num);
+       if (!intel_sdvo_connector->tv_format)
+               return false;
+       for (i = 0; i < intel_sdvo_connector->format_supported_num; i++)
+               drm_property_add_enum(intel_sdvo_connector->tv_format, i,
+                                     tv_format_names[intel_sdvo_connector->tv_format_supported[i]]);
+       intel_sdvo_connector->base.base.state->tv.mode = intel_sdvo_connector->tv_format_supported[0];
+       drm_object_attach_property(&intel_sdvo_connector->base.base.base,
+                                  intel_sdvo_connector->tv_format, 0);
+       return true;
+ }
+ #define _ENHANCEMENT(state_assignment, name, NAME) do { \
+       if (enhancements.name) { \
+               if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_MAX_##NAME, &data_value, 4) || \
+                   !intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_##NAME, &response, 2)) \
+                       return false; \
+               intel_sdvo_connector->name = \
+                       drm_property_create_range(dev, 0, #name, 0, data_value[0]); \
+               if (!intel_sdvo_connector->name) return false; \
+               state_assignment = response; \
+               drm_object_attach_property(&connector->base, \
+                                          intel_sdvo_connector->name, 0); \
+               DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \
+                             data_value[0], data_value[1], response); \
+       } \
+ } while (0)
+ #define ENHANCEMENT(state, name, NAME) _ENHANCEMENT((state)->name, name, NAME)
+ static bool
+ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo,
+                                     struct intel_sdvo_connector *intel_sdvo_connector,
+                                     struct intel_sdvo_enhancements_reply enhancements)
+ {
+       struct drm_device *dev = intel_sdvo->base.base.dev;
+       struct drm_connector *connector = &intel_sdvo_connector->base.base;
+       struct drm_connector_state *conn_state = connector->state;
+       struct intel_sdvo_connector_state *sdvo_state =
+               to_intel_sdvo_connector_state(conn_state);
+       u16 response, data_value[2];
+       /* when horizontal overscan is supported, Add the left/right property */
+       if (enhancements.overscan_h) {
+               if (!intel_sdvo_get_value(intel_sdvo,
+                                         SDVO_CMD_GET_MAX_OVERSCAN_H,
+                                         &data_value, 4))
+                       return false;
+               if (!intel_sdvo_get_value(intel_sdvo,
+                                         SDVO_CMD_GET_OVERSCAN_H,
+                                         &response, 2))
+                       return false;
+               sdvo_state->tv.overscan_h = response;
+               intel_sdvo_connector->max_hscan = data_value[0];
+               intel_sdvo_connector->left =
+                       drm_property_create_range(dev, 0, "left_margin", 0, data_value[0]);
+               if (!intel_sdvo_connector->left)
+                       return false;
+               drm_object_attach_property(&connector->base,
+                                          intel_sdvo_connector->left, 0);
+               intel_sdvo_connector->right =
+                       drm_property_create_range(dev, 0, "right_margin", 0, data_value[0]);
+               if (!intel_sdvo_connector->right)
+                       return false;
+               drm_object_attach_property(&connector->base,
+                                             intel_sdvo_connector->right, 0);
+               DRM_DEBUG_KMS("h_overscan: max %d, "
+                             "default %d, current %d\n",
+                             data_value[0], data_value[1], response);
+       }
+       if (enhancements.overscan_v) {
+               if (!intel_sdvo_get_value(intel_sdvo,
+                                         SDVO_CMD_GET_MAX_OVERSCAN_V,
+                                         &data_value, 4))
+                       return false;
+               if (!intel_sdvo_get_value(intel_sdvo,
+                                         SDVO_CMD_GET_OVERSCAN_V,
+                                         &response, 2))
+                       return false;
+               sdvo_state->tv.overscan_v = response;
+               intel_sdvo_connector->max_vscan = data_value[0];
+               intel_sdvo_connector->top =
+                       drm_property_create_range(dev, 0,
+                                           "top_margin", 0, data_value[0]);
+               if (!intel_sdvo_connector->top)
+                       return false;
+               drm_object_attach_property(&connector->base,
+                                          intel_sdvo_connector->top, 0);
+               intel_sdvo_connector->bottom =
+                       drm_property_create_range(dev, 0,
+                                           "bottom_margin", 0, data_value[0]);
+               if (!intel_sdvo_connector->bottom)
+                       return false;
+               drm_object_attach_property(&connector->base,
+                                             intel_sdvo_connector->bottom, 0);
+               DRM_DEBUG_KMS("v_overscan: max %d, "
+                             "default %d, current %d\n",
+                             data_value[0], data_value[1], response);
+       }
+       ENHANCEMENT(&sdvo_state->tv, hpos, HPOS);
+       ENHANCEMENT(&sdvo_state->tv, vpos, VPOS);
+       ENHANCEMENT(&conn_state->tv, saturation, SATURATION);
+       ENHANCEMENT(&conn_state->tv, contrast, CONTRAST);
+       ENHANCEMENT(&conn_state->tv, hue, HUE);
+       ENHANCEMENT(&conn_state->tv, brightness, BRIGHTNESS);
+       ENHANCEMENT(&sdvo_state->tv, sharpness, SHARPNESS);
+       ENHANCEMENT(&sdvo_state->tv, flicker_filter, FLICKER_FILTER);
+       ENHANCEMENT(&sdvo_state->tv, flicker_filter_adaptive, FLICKER_FILTER_ADAPTIVE);
+       ENHANCEMENT(&sdvo_state->tv, flicker_filter_2d, FLICKER_FILTER_2D);
+       _ENHANCEMENT(sdvo_state->tv.chroma_filter, tv_chroma_filter, TV_CHROMA_FILTER);
+       _ENHANCEMENT(sdvo_state->tv.luma_filter, tv_luma_filter, TV_LUMA_FILTER);
+       if (enhancements.dot_crawl) {
+               if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_DOT_CRAWL, &response, 2))
+                       return false;
+               sdvo_state->tv.dot_crawl = response & 0x1;
+               intel_sdvo_connector->dot_crawl =
+                       drm_property_create_range(dev, 0, "dot_crawl", 0, 1);
+               if (!intel_sdvo_connector->dot_crawl)
+                       return false;
+               drm_object_attach_property(&connector->base,
+                                          intel_sdvo_connector->dot_crawl, 0);
+               DRM_DEBUG_KMS("dot crawl: current %d\n", response);
+       }
+       return true;
+ }
+ static bool
+ intel_sdvo_create_enhance_property_lvds(struct intel_sdvo *intel_sdvo,
+                                       struct intel_sdvo_connector *intel_sdvo_connector,
+                                       struct intel_sdvo_enhancements_reply enhancements)
+ {
+       struct drm_device *dev = intel_sdvo->base.base.dev;
+       struct drm_connector *connector = &intel_sdvo_connector->base.base;
+       u16 response, data_value[2];
+       ENHANCEMENT(&connector->state->tv, brightness, BRIGHTNESS);
+       return true;
+ }
+ #undef ENHANCEMENT
+ #undef _ENHANCEMENT
+ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
+                                              struct intel_sdvo_connector *intel_sdvo_connector)
+ {
+       union {
+               struct intel_sdvo_enhancements_reply reply;
+               u16 response;
+       } enhancements;
+       BUILD_BUG_ON(sizeof(enhancements) != 2);
+       if (!intel_sdvo_get_value(intel_sdvo,
+                                 SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS,
+                                 &enhancements, sizeof(enhancements)) ||
+           enhancements.response == 0) {
+               DRM_DEBUG_KMS("No enhancement is supported\n");
+               return true;
+       }
+       if (IS_TV(intel_sdvo_connector))
+               return intel_sdvo_create_enhance_property_tv(intel_sdvo, intel_sdvo_connector, enhancements.reply);
+       else if (IS_LVDS(intel_sdvo_connector))
+               return intel_sdvo_create_enhance_property_lvds(intel_sdvo, intel_sdvo_connector, enhancements.reply);
+       else
+               return true;
+ }
+ static int intel_sdvo_ddc_proxy_xfer(struct i2c_adapter *adapter,
+                                    struct i2c_msg *msgs,
+                                    int num)
+ {
+       struct intel_sdvo *sdvo = adapter->algo_data;
+       if (!__intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus))
+               return -EIO;
+       return sdvo->i2c->algo->master_xfer(sdvo->i2c, msgs, num);
+ }
+ static u32 intel_sdvo_ddc_proxy_func(struct i2c_adapter *adapter)
+ {
+       struct intel_sdvo *sdvo = adapter->algo_data;
+       return sdvo->i2c->algo->functionality(sdvo->i2c);
+ }
+ static const struct i2c_algorithm intel_sdvo_ddc_proxy = {
+       .master_xfer    = intel_sdvo_ddc_proxy_xfer,
+       .functionality  = intel_sdvo_ddc_proxy_func
+ };
+ static void proxy_lock_bus(struct i2c_adapter *adapter,
+                          unsigned int flags)
+ {
+       struct intel_sdvo *sdvo = adapter->algo_data;
+       sdvo->i2c->lock_ops->lock_bus(sdvo->i2c, flags);
+ }
+ static int proxy_trylock_bus(struct i2c_adapter *adapter,
+                            unsigned int flags)
+ {
+       struct intel_sdvo *sdvo = adapter->algo_data;
+       return sdvo->i2c->lock_ops->trylock_bus(sdvo->i2c, flags);
+ }
+ static void proxy_unlock_bus(struct i2c_adapter *adapter,
+                            unsigned int flags)
+ {
+       struct intel_sdvo *sdvo = adapter->algo_data;
+       sdvo->i2c->lock_ops->unlock_bus(sdvo->i2c, flags);
+ }
+ static const struct i2c_lock_operations proxy_lock_ops = {
+       .lock_bus =    proxy_lock_bus,
+       .trylock_bus = proxy_trylock_bus,
+       .unlock_bus =  proxy_unlock_bus,
+ };
+ static bool
+ intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo,
+                         struct drm_i915_private *dev_priv)
+ {
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       sdvo->ddc.owner = THIS_MODULE;
+       sdvo->ddc.class = I2C_CLASS_DDC;
+       snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy");
+       sdvo->ddc.dev.parent = &pdev->dev;
+       sdvo->ddc.algo_data = sdvo;
+       sdvo->ddc.algo = &intel_sdvo_ddc_proxy;
+       sdvo->ddc.lock_ops = &proxy_lock_ops;
+       return i2c_add_adapter(&sdvo->ddc) == 0;
+ }
+ static void assert_sdvo_port_valid(const struct drm_i915_private *dev_priv,
+                                  enum port port)
+ {
+       if (HAS_PCH_SPLIT(dev_priv))
+               WARN_ON(port != PORT_B);
+       else
+               WARN_ON(port != PORT_B && port != PORT_C);
+ }
+ bool intel_sdvo_init(struct drm_i915_private *dev_priv,
+                    i915_reg_t sdvo_reg, enum port port)
+ {
+       struct intel_encoder *intel_encoder;
+       struct intel_sdvo *intel_sdvo;
+       int i;
+       assert_sdvo_port_valid(dev_priv, port);
+       intel_sdvo = kzalloc(sizeof(*intel_sdvo), GFP_KERNEL);
+       if (!intel_sdvo)
+               return false;
+       intel_sdvo->sdvo_reg = sdvo_reg;
+       intel_sdvo->port = port;
+       intel_sdvo->slave_addr =
+               intel_sdvo_get_slave_addr(dev_priv, intel_sdvo) >> 1;
+       intel_sdvo_select_i2c_bus(dev_priv, intel_sdvo);
+       if (!intel_sdvo_init_ddc_proxy(intel_sdvo, dev_priv))
+               goto err_i2c_bus;
+       /* encoder type will be decided later */
+       intel_encoder = &intel_sdvo->base;
+       intel_encoder->type = INTEL_OUTPUT_SDVO;
+       intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
+       intel_encoder->port = port;
+       drm_encoder_init(&dev_priv->drm, &intel_encoder->base,
+                        &intel_sdvo_enc_funcs, 0,
+                        "SDVO %c", port_name(port));
+       /* Read the regs to test if we can talk to the device */
+       for (i = 0; i < 0x40; i++) {
+               u8 byte;
+               if (!intel_sdvo_read_byte(intel_sdvo, i, &byte)) {
+                       DRM_DEBUG_KMS("No SDVO device found on %s\n",
+                                     SDVO_NAME(intel_sdvo));
+                       goto err;
+               }
+       }
+       intel_encoder->compute_config = intel_sdvo_compute_config;
+       if (HAS_PCH_SPLIT(dev_priv)) {
+               intel_encoder->disable = pch_disable_sdvo;
+               intel_encoder->post_disable = pch_post_disable_sdvo;
+       } else {
+               intel_encoder->disable = intel_disable_sdvo;
+       }
+       intel_encoder->pre_enable = intel_sdvo_pre_enable;
+       intel_encoder->enable = intel_enable_sdvo;
+       intel_encoder->get_hw_state = intel_sdvo_get_hw_state;
+       intel_encoder->get_config = intel_sdvo_get_config;
+       /* In default case sdvo lvds is false */
+       if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
+               goto err;
+       if (intel_sdvo_output_setup(intel_sdvo,
+                                   intel_sdvo->caps.output_flags) != true) {
+               DRM_DEBUG_KMS("SDVO output failed to setup on %s\n",
+                             SDVO_NAME(intel_sdvo));
+               /* Output_setup can leave behind connectors! */
+               goto err_output;
+       }
+       /*
+        * Only enable the hotplug irq if we need it, to work around noisy
+        * hotplug lines.
+        */
+       if (intel_sdvo->hotplug_active) {
+               if (intel_sdvo->port == PORT_B)
+                       intel_encoder->hpd_pin = HPD_SDVO_B;
+               else
+                       intel_encoder->hpd_pin = HPD_SDVO_C;
+       }
+       /*
+        * Cloning SDVO with anything is often impossible, since the SDVO
+        * encoder can request a special input timing mode. And even if that's
+        * not the case we have evidence that cloning a plain unscaled mode with
+        * VGA doesn't really work. Furthermore the cloning flags are way too
+        * simplistic anyway to express such constraints, so just give up on
+        * cloning for SDVO encoders.
+        */
+       intel_sdvo->base.cloneable = 0;
+       intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo);
+       /* Set the input timing to the screen. Assume always input 0. */
+       if (!intel_sdvo_set_target_input(intel_sdvo))
+               goto err_output;
+       if (!intel_sdvo_get_input_pixel_clock_range(intel_sdvo,
+                                                   &intel_sdvo->pixel_clock_min,
+                                                   &intel_sdvo->pixel_clock_max))
+               goto err_output;
+       DRM_DEBUG_KMS("%s device VID/DID: %02X:%02X.%02X, "
+                       "clock range %dMHz - %dMHz, "
+                       "input 1: %c, input 2: %c, "
+                       "output 1: %c, output 2: %c\n",
+                       SDVO_NAME(intel_sdvo),
+                       intel_sdvo->caps.vendor_id, intel_sdvo->caps.device_id,
+                       intel_sdvo->caps.device_rev_id,
+                       intel_sdvo->pixel_clock_min / 1000,
+                       intel_sdvo->pixel_clock_max / 1000,
+                       (intel_sdvo->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+                       (intel_sdvo->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+                       /* check currently supported outputs */
+                       intel_sdvo->caps.output_flags &
+                       (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
+                       intel_sdvo->caps.output_flags &
+                       (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
+       return true;
+ err_output:
+       intel_sdvo_output_cleanup(intel_sdvo);
+ err:
+       drm_encoder_cleanup(&intel_encoder->base);
+       i2c_del_adapter(&intel_sdvo->ddc);
+ err_i2c_bus:
+       intel_sdvo_unselect_i2c_bus(intel_sdvo);
+       kfree(intel_sdvo);
+       return false;
+ }
index 0000000000000000000000000000000000000000,5dc594eafaf2f1c98d768d0d083c9b86e591d92a..0a95df6c6a57f52401facfbaf88623780963fd8b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1991 +1,1993 @@@
 -                               struct drm_connector_state *new_state)
+ /*
+  * Copyright © 2006-2008 Intel Corporation
+  *   Jesse Barnes <jesse.barnes@intel.com>
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a
+  * copy of this software and associated documentation files (the "Software"),
+  * to deal in the Software without restriction, including without limitation
+  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  * and/or sell copies of the Software, and to permit persons to whom the
+  * Software is furnished to do so, subject to the following conditions:
+  *
+  * The above copyright notice and this permission notice (including the next
+  * paragraph) shall be included in all copies or substantial portions of the
+  * Software.
+  *
+  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+  * DEALINGS IN THE SOFTWARE.
+  *
+  * Authors:
+  *    Eric Anholt <eric@anholt.net>
+  *
+  */
+ /** @file
+  * Integrated TV-out support for the 915GM and 945GM.
+  */
+ #include <drm/drm_atomic_helper.h>
+ #include <drm/drm_crtc.h>
+ #include <drm/drm_edid.h>
+ #include <drm/i915_drm.h>
+ #include "i915_drv.h"
+ #include "intel_connector.h"
+ #include "intel_drv.h"
+ #include "intel_hotplug.h"
+ #include "intel_tv.h"
+ enum tv_margin {
+       TV_MARGIN_LEFT, TV_MARGIN_TOP,
+       TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
+ };
+ struct intel_tv {
+       struct intel_encoder base;
+       int type;
+ };
+ struct video_levels {
+       u16 blank, black;
+       u8 burst;
+ };
+ struct color_conversion {
+       u16 ry, gy, by, ay;
+       u16 ru, gu, bu, au;
+       u16 rv, gv, bv, av;
+ };
+ static const u32 filter_table[] = {
+       0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
+       0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
+       0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
+       0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
+       0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
+       0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
+       0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
+       0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
+       0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
+       0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
+       0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
+       0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
+       0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
+       0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
+       0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
+       0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
+       0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
+       0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
+       0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
+       0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
+       0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
+       0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
+       0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
+       0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
+       0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
+       0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
+       0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
+       0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
+       0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
+       0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
+       0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
+       0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
+       0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
+       0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
+       0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
+       0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
+       0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
+       0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
+       0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
+       0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
+       0x28003100, 0x28002F00, 0x00003100, 0x36403000,
+       0x2D002CC0, 0x30003640, 0x2D0036C0,
+       0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
+       0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
+       0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
+       0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
+       0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
+       0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
+       0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
+       0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
+       0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
+       0x28003100, 0x28002F00, 0x00003100,
+ };
+ /*
+  * Color conversion values have 3 separate fixed point formats:
+  *
+  * 10 bit fields (ay, au)
+  *   1.9 fixed point (b.bbbbbbbbb)
+  * 11 bit fields (ry, by, ru, gu, gv)
+  *   exp.mantissa (ee.mmmmmmmmm)
+  *   ee = 00 = 10^-1 (0.mmmmmmmmm)
+  *   ee = 01 = 10^-2 (0.0mmmmmmmmm)
+  *   ee = 10 = 10^-3 (0.00mmmmmmmmm)
+  *   ee = 11 = 10^-4 (0.000mmmmmmmmm)
+  * 12 bit fields (gy, rv, bu)
+  *   exp.mantissa (eee.mmmmmmmmm)
+  *   eee = 000 = 10^-1 (0.mmmmmmmmm)
+  *   eee = 001 = 10^-2 (0.0mmmmmmmmm)
+  *   eee = 010 = 10^-3 (0.00mmmmmmmmm)
+  *   eee = 011 = 10^-4 (0.000mmmmmmmmm)
+  *   eee = 100 = reserved
+  *   eee = 101 = reserved
+  *   eee = 110 = reserved
+  *   eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
+  *
+  * Saturation and contrast are 8 bits, with their own representation:
+  * 8 bit field (saturation, contrast)
+  *   exp.mantissa (ee.mmmmmm)
+  *   ee = 00 = 10^-1 (0.mmmmmm)
+  *   ee = 01 = 10^0 (m.mmmmm)
+  *   ee = 10 = 10^1 (mm.mmmm)
+  *   ee = 11 = 10^2 (mmm.mmm)
+  *
+  * Simple conversion function:
+  *
+  * static u32
+  * float_to_csc_11(float f)
+  * {
+  *     u32 exp;
+  *     u32 mant;
+  *     u32 ret;
+  *
+  *     if (f < 0)
+  *         f = -f;
+  *
+  *     if (f >= 1) {
+  *         exp = 0x7;
+  *       mant = 1 << 8;
+  *     } else {
+  *         for (exp = 0; exp < 3 && f < 0.5; exp++)
+  *       f *= 2.0;
+  *         mant = (f * (1 << 9) + 0.5);
+  *         if (mant >= (1 << 9))
+  *             mant = (1 << 9) - 1;
+  *     }
+  *     ret = (exp << 9) | mant;
+  *     return ret;
+  * }
+  */
+ /*
+  * Behold, magic numbers!  If we plant them they might grow a big
+  * s-video cable to the sky... or something.
+  *
+  * Pre-converted to appropriate hex value.
+  */
+ /*
+  * PAL & NTSC values for composite & s-video connections
+  */
+ static const struct color_conversion ntsc_m_csc_composite = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+       .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
+       .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
+ };
+ static const struct video_levels ntsc_m_levels_composite = {
+       .blank = 225, .black = 267, .burst = 113,
+ };
+ static const struct color_conversion ntsc_m_csc_svideo = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
+       .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
+       .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
+ };
+ static const struct video_levels ntsc_m_levels_svideo = {
+       .blank = 266, .black = 316, .burst = 133,
+ };
+ static const struct color_conversion ntsc_j_csc_composite = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
+       .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0200,
+       .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0200,
+ };
+ static const struct video_levels ntsc_j_levels_composite = {
+       .blank = 225, .black = 225, .burst = 113,
+ };
+ static const struct color_conversion ntsc_j_csc_svideo = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
+       .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0200,
+       .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0200,
+ };
+ static const struct video_levels ntsc_j_levels_svideo = {
+       .blank = 266, .black = 266, .burst = 133,
+ };
+ static const struct color_conversion pal_csc_composite = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
+       .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0200,
+       .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0200,
+ };
+ static const struct video_levels pal_levels_composite = {
+       .blank = 237, .black = 237, .burst = 118,
+ };
+ static const struct color_conversion pal_csc_svideo = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
+       .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0200,
+       .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0200,
+ };
+ static const struct video_levels pal_levels_svideo = {
+       .blank = 280, .black = 280, .burst = 139,
+ };
+ static const struct color_conversion pal_m_csc_composite = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+       .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
+       .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
+ };
+ static const struct video_levels pal_m_levels_composite = {
+       .blank = 225, .black = 267, .burst = 113,
+ };
+ static const struct color_conversion pal_m_csc_svideo = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
+       .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
+       .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
+ };
+ static const struct video_levels pal_m_levels_svideo = {
+       .blank = 266, .black = 316, .burst = 133,
+ };
+ static const struct color_conversion pal_n_csc_composite = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+       .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0200,
+       .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0200,
+ };
+ static const struct video_levels pal_n_levels_composite = {
+       .blank = 225, .black = 267, .burst = 118,
+ };
+ static const struct color_conversion pal_n_csc_svideo = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0133,
+       .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0200,
+       .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0200,
+ };
+ static const struct video_levels pal_n_levels_svideo = {
+       .blank = 266, .black = 316, .burst = 139,
+ };
+ /*
+  * Component connections
+  */
+ static const struct color_conversion sdtv_csc_yprpb = {
+       .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
+       .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0200,
+       .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0200,
+ };
+ static const struct color_conversion hdtv_csc_yprpb = {
+       .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0145,
+       .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0200,
+       .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0200,
+ };
+ static const struct video_levels component_levels = {
+       .blank = 279, .black = 279, .burst = 0,
+ };
+ struct tv_mode {
+       const char *name;
+       u32 clock;
+       u16 refresh; /* in millihertz (for precision) */
+       u8 oversample;
+       u8 hsync_end;
+       u16 hblank_start, hblank_end, htotal;
+       bool progressive : 1, trilevel_sync : 1, component_only : 1;
+       u8 vsync_start_f1, vsync_start_f2, vsync_len;
+       bool veq_ena : 1;
+       u8 veq_start_f1, veq_start_f2, veq_len;
+       u8 vi_end_f1, vi_end_f2;
+       u16 nbr_end;
+       bool burst_ena : 1;
+       u8 hburst_start, hburst_len;
+       u8 vburst_start_f1;
+       u16 vburst_end_f1;
+       u8 vburst_start_f2;
+       u16 vburst_end_f2;
+       u8 vburst_start_f3;
+       u16 vburst_end_f3;
+       u8 vburst_start_f4;
+       u16 vburst_end_f4;
+       /*
+        * subcarrier programming
+        */
+       u16 dda2_size, dda3_size;
+       u8 dda1_inc;
+       u16 dda2_inc, dda3_inc;
+       u32 sc_reset;
+       bool pal_burst : 1;
+       /*
+        * blank/black levels
+        */
+       const struct video_levels *composite_levels, *svideo_levels;
+       const struct color_conversion *composite_color, *svideo_color;
+       const u32 *filter_table;
+ };
+ /*
+  * Sub carrier DDA
+  *
+  *  I think this works as follows:
+  *
+  *  subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
+  *
+  * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
+  *
+  * So,
+  *  dda1_ideal = subcarrier/pixel * 4096
+  *  dda1_inc = floor (dda1_ideal)
+  *  dda2 = dda1_ideal - dda1_inc
+  *
+  *  then pick a ratio for dda2 that gives the closest approximation. If
+  *  you can't get close enough, you can play with dda3 as well. This
+  *  seems likely to happen when dda2 is small as the jumps would be larger
+  *
+  * To invert this,
+  *
+  *  pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
+  *
+  * The constants below were all computed using a 107.520MHz clock
+  */
+ /*
+  * Register programming values for TV modes.
+  *
+  * These values account for -1s required.
+  */
+ static const struct tv_mode tv_modes[] = {
+       {
+               .name           = "NTSC-M",
+               .clock          = 108000,
+               .refresh        = 59940,
+               .oversample     = 8,
+               .component_only = false,
+               /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+               .hsync_end      = 64,               .hblank_end         = 124,
+               .hblank_start   = 836,              .htotal             = 857,
+               .progressive    = false,            .trilevel_sync = false,
+               .vsync_start_f1 = 6,                .vsync_start_f2     = 7,
+               .vsync_len      = 6,
+               .veq_ena        = true,             .veq_start_f1       = 0,
+               .veq_start_f2   = 1,                .veq_len            = 18,
+               .vi_end_f1      = 20,               .vi_end_f2          = 21,
+               .nbr_end        = 240,
+               .burst_ena      = true,
+               .hburst_start   = 72,               .hburst_len         = 34,
+               .vburst_start_f1 = 9,               .vburst_end_f1      = 240,
+               .vburst_start_f2 = 10,              .vburst_end_f2      = 240,
+               .vburst_start_f3 = 9,               .vburst_end_f3      = 240,
+               .vburst_start_f4 = 10,              .vburst_end_f4      = 240,
+               /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+               .dda1_inc       =    135,
+               .dda2_inc       =  20800,           .dda2_size          =  27456,
+               .dda3_inc       =      0,           .dda3_size          =      0,
+               .sc_reset       = TV_SC_RESET_EVERY_4,
+               .pal_burst      = false,
+               .composite_levels = &ntsc_m_levels_composite,
+               .composite_color = &ntsc_m_csc_composite,
+               .svideo_levels  = &ntsc_m_levels_svideo,
+               .svideo_color = &ntsc_m_csc_svideo,
+               .filter_table = filter_table,
+       },
+       {
+               .name           = "NTSC-443",
+               .clock          = 108000,
+               .refresh        = 59940,
+               .oversample     = 8,
+               .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,
+               .progressive    = false,            .trilevel_sync = false,
+               .vsync_start_f1 = 6,                .vsync_start_f2     = 7,
+               .vsync_len      = 6,
+               .veq_ena        = true,             .veq_start_f1       = 0,
+               .veq_start_f2   = 1,                .veq_len            = 18,
+               .vi_end_f1      = 20,               .vi_end_f2          = 21,
+               .nbr_end        = 240,
+               .burst_ena      = true,
+               .hburst_start   = 72,               .hburst_len         = 34,
+               .vburst_start_f1 = 9,               .vburst_end_f1      = 240,
+               .vburst_start_f2 = 10,              .vburst_end_f2      = 240,
+               .vburst_start_f3 = 9,               .vburst_end_f3      = 240,
+               .vburst_start_f4 = 10,              .vburst_end_f4      = 240,
+               /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+               .dda1_inc       =    168,
+               .dda2_inc       =   4093,       .dda2_size      =  27456,
+               .dda3_inc       =    310,       .dda3_size      =    525,
+               .sc_reset   = TV_SC_RESET_NEVER,
+               .pal_burst  = false,
+               .composite_levels = &ntsc_m_levels_composite,
+               .composite_color = &ntsc_m_csc_composite,
+               .svideo_levels  = &ntsc_m_levels_svideo,
+               .svideo_color = &ntsc_m_csc_svideo,
+               .filter_table = filter_table,
+       },
+       {
+               .name           = "NTSC-J",
+               .clock          = 108000,
+               .refresh        = 59940,
+               .oversample     = 8,
+               .component_only = false,
+               /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+               .hsync_end      = 64,               .hblank_end         = 124,
+               .hblank_start = 836,        .htotal             = 857,
+               .progressive    = false,    .trilevel_sync = false,
+               .vsync_start_f1 = 6,        .vsync_start_f2     = 7,
+               .vsync_len      = 6,
+               .veq_ena      = true,       .veq_start_f1       = 0,
+               .veq_start_f2 = 1,          .veq_len            = 18,
+               .vi_end_f1      = 20,               .vi_end_f2          = 21,
+               .nbr_end        = 240,
+               .burst_ena      = true,
+               .hburst_start   = 72,               .hburst_len         = 34,
+               .vburst_start_f1 = 9,               .vburst_end_f1      = 240,
+               .vburst_start_f2 = 10,              .vburst_end_f2      = 240,
+               .vburst_start_f3 = 9,               .vburst_end_f3      = 240,
+               .vburst_start_f4 = 10,              .vburst_end_f4      = 240,
+               /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+               .dda1_inc       =    135,
+               .dda2_inc       =  20800,           .dda2_size          =  27456,
+               .dda3_inc       =      0,           .dda3_size          =      0,
+               .sc_reset       = TV_SC_RESET_EVERY_4,
+               .pal_burst      = false,
+               .composite_levels = &ntsc_j_levels_composite,
+               .composite_color = &ntsc_j_csc_composite,
+               .svideo_levels  = &ntsc_j_levels_svideo,
+               .svideo_color = &ntsc_j_csc_svideo,
+               .filter_table = filter_table,
+       },
+       {
+               .name           = "PAL-M",
+               .clock          = 108000,
+               .refresh        = 59940,
+               .oversample     = 8,
+               .component_only = false,
+               /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+               .hsync_end      = 64,             .hblank_end           = 124,
+               .hblank_start = 836,      .htotal               = 857,
+               .progressive    = false,            .trilevel_sync = false,
+               .vsync_start_f1 = 6,                .vsync_start_f2     = 7,
+               .vsync_len      = 6,
+               .veq_ena        = true,             .veq_start_f1       = 0,
+               .veq_start_f2   = 1,                .veq_len            = 18,
+               .vi_end_f1      = 20,               .vi_end_f2          = 21,
+               .nbr_end        = 240,
+               .burst_ena      = true,
+               .hburst_start   = 72,               .hburst_len         = 34,
+               .vburst_start_f1 = 9,               .vburst_end_f1      = 240,
+               .vburst_start_f2 = 10,              .vburst_end_f2      = 240,
+               .vburst_start_f3 = 9,               .vburst_end_f3      = 240,
+               .vburst_start_f4 = 10,              .vburst_end_f4      = 240,
+               /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+               .dda1_inc       =    135,
+               .dda2_inc       =  16704,           .dda2_size          =  27456,
+               .dda3_inc       =      0,           .dda3_size          =      0,
+               .sc_reset       = TV_SC_RESET_EVERY_8,
+               .pal_burst  = true,
+               .composite_levels = &pal_m_levels_composite,
+               .composite_color = &pal_m_csc_composite,
+               .svideo_levels  = &pal_m_levels_svideo,
+               .svideo_color = &pal_m_csc_svideo,
+               .filter_table = filter_table,
+       },
+       {
+               /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
+               .name       = "PAL-N",
+               .clock          = 108000,
+               .refresh        = 50000,
+               .oversample     = 8,
+               .component_only = false,
+               .hsync_end      = 64,               .hblank_end         = 128,
+               .hblank_start = 844,        .htotal             = 863,
+               .progressive  = false,    .trilevel_sync = false,
+               .vsync_start_f1 = 6,       .vsync_start_f2      = 7,
+               .vsync_len      = 6,
+               .veq_ena        = true,             .veq_start_f1       = 0,
+               .veq_start_f2   = 1,                .veq_len            = 18,
+               .vi_end_f1      = 24,               .vi_end_f2          = 25,
+               .nbr_end        = 286,
+               .burst_ena      = true,
+               .hburst_start = 73,         .hburst_len         = 34,
+               .vburst_start_f1 = 8,       .vburst_end_f1      = 285,
+               .vburst_start_f2 = 8,       .vburst_end_f2      = 286,
+               .vburst_start_f3 = 9,       .vburst_end_f3      = 286,
+               .vburst_start_f4 = 9,       .vburst_end_f4      = 285,
+               /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+               .dda1_inc       =    135,
+               .dda2_inc       =  23578,       .dda2_size      =  27648,
+               .dda3_inc       =    134,       .dda3_size      =    625,
+               .sc_reset   = TV_SC_RESET_EVERY_8,
+               .pal_burst  = true,
+               .composite_levels = &pal_n_levels_composite,
+               .composite_color = &pal_n_csc_composite,
+               .svideo_levels  = &pal_n_levels_svideo,
+               .svideo_color = &pal_n_csc_svideo,
+               .filter_table = filter_table,
+       },
+       {
+               /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
+               .name       = "PAL",
+               .clock          = 108000,
+               .refresh        = 50000,
+               .oversample     = 8,
+               .component_only = false,
+               .hsync_end      = 64,               .hblank_end         = 142,
+               .hblank_start   = 844,      .htotal             = 863,
+               .progressive    = false,    .trilevel_sync = false,
+               .vsync_start_f1 = 5,        .vsync_start_f2     = 6,
+               .vsync_len      = 5,
+               .veq_ena        = true,     .veq_start_f1       = 0,
+               .veq_start_f2   = 1,        .veq_len            = 15,
+               .vi_end_f1      = 24,               .vi_end_f2          = 25,
+               .nbr_end        = 286,
+               .burst_ena      = true,
+               .hburst_start   = 73,               .hburst_len         = 32,
+               .vburst_start_f1 = 8,               .vburst_end_f1      = 285,
+               .vburst_start_f2 = 8,               .vburst_end_f2      = 286,
+               .vburst_start_f3 = 9,               .vburst_end_f3      = 286,
+               .vburst_start_f4 = 9,               .vburst_end_f4      = 285,
+               /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+               .dda1_inc       =    168,
+               .dda2_inc       =   4122,       .dda2_size      =  27648,
+               .dda3_inc       =     67,       .dda3_size      =    625,
+               .sc_reset   = TV_SC_RESET_EVERY_8,
+               .pal_burst  = true,
+               .composite_levels = &pal_levels_composite,
+               .composite_color = &pal_csc_composite,
+               .svideo_levels  = &pal_levels_svideo,
+               .svideo_color = &pal_csc_svideo,
+               .filter_table = filter_table,
+       },
+       {
+               .name       = "480p",
+               .clock          = 108000,
+               .refresh        = 59940,
+               .oversample     = 4,
+               .component_only = true,
+               .hsync_end      = 64,               .hblank_end         = 122,
+               .hblank_start   = 842,              .htotal             = 857,
+               .progressive    = true,             .trilevel_sync = false,
+               .vsync_start_f1 = 12,               .vsync_start_f2     = 12,
+               .vsync_len      = 12,
+               .veq_ena        = false,
+               .vi_end_f1      = 44,               .vi_end_f2          = 44,
+               .nbr_end        = 479,
+               .burst_ena      = false,
+               .filter_table = filter_table,
+       },
+       {
+               .name       = "576p",
+               .clock          = 108000,
+               .refresh        = 50000,
+               .oversample     = 4,
+               .component_only = true,
+               .hsync_end      = 64,               .hblank_end         = 139,
+               .hblank_start   = 859,              .htotal             = 863,
+               .progressive    = true,             .trilevel_sync = false,
+               .vsync_start_f1 = 10,               .vsync_start_f2     = 10,
+               .vsync_len      = 10,
+               .veq_ena        = false,
+               .vi_end_f1      = 48,               .vi_end_f2          = 48,
+               .nbr_end        = 575,
+               .burst_ena      = false,
+               .filter_table = filter_table,
+       },
+       {
+               .name       = "720p@60Hz",
+               .clock          = 148500,
+               .refresh        = 60000,
+               .oversample     = 2,
+               .component_only = true,
+               .hsync_end      = 80,               .hblank_end         = 300,
+               .hblank_start   = 1580,             .htotal             = 1649,
+               .progressive    = true,             .trilevel_sync = true,
+               .vsync_start_f1 = 10,               .vsync_start_f2     = 10,
+               .vsync_len      = 10,
+               .veq_ena        = false,
+               .vi_end_f1      = 29,               .vi_end_f2          = 29,
+               .nbr_end        = 719,
+               .burst_ena      = false,
+               .filter_table = filter_table,
+       },
+       {
+               .name       = "720p@50Hz",
+               .clock          = 148500,
+               .refresh        = 50000,
+               .oversample     = 2,
+               .component_only = true,
+               .hsync_end      = 80,               .hblank_end         = 300,
+               .hblank_start   = 1580,             .htotal             = 1979,
+               .progressive    = true,             .trilevel_sync = true,
+               .vsync_start_f1 = 10,               .vsync_start_f2     = 10,
+               .vsync_len      = 10,
+               .veq_ena        = false,
+               .vi_end_f1      = 29,               .vi_end_f2          = 29,
+               .nbr_end        = 719,
+               .burst_ena      = false,
+               .filter_table = filter_table,
+       },
+       {
+               .name       = "1080i@50Hz",
+               .clock          = 148500,
+               .refresh        = 50000,
+               .oversample     = 2,
+               .component_only = true,
+               .hsync_end      = 88,               .hblank_end         = 235,
+               .hblank_start   = 2155,             .htotal             = 2639,
+               .progressive    = false,          .trilevel_sync = true,
+               .vsync_start_f1 = 4,              .vsync_start_f2     = 5,
+               .vsync_len      = 10,
+               .veq_ena        = true,     .veq_start_f1       = 4,
+               .veq_start_f2   = 4,        .veq_len            = 10,
+               .vi_end_f1      = 21,           .vi_end_f2          = 22,
+               .nbr_end        = 539,
+               .burst_ena      = false,
+               .filter_table = filter_table,
+       },
+       {
+               .name       = "1080i@60Hz",
+               .clock          = 148500,
+               .refresh        = 60000,
+               .oversample     = 2,
+               .component_only = true,
+               .hsync_end      = 88,               .hblank_end         = 235,
+               .hblank_start   = 2155,             .htotal             = 2199,
+               .progressive    = false,            .trilevel_sync = true,
+               .vsync_start_f1 = 4,               .vsync_start_f2     = 5,
+               .vsync_len      = 10,
+               .veq_ena        = true,             .veq_start_f1       = 4,
+               .veq_start_f2   = 4,                .veq_len            = 10,
+               .vi_end_f1      = 21,               .vi_end_f2          = 22,
+               .nbr_end        = 539,
+               .burst_ena      = false,
+               .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,
+       },
+ };
+ 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)
+ static 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);
+ }
+ static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
+ {
+       return enc_to_tv(intel_attached_encoder(connector));
+ }
+ static bool
+ intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
+ {
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       u32 tmp = I915_READ(TV_CTL);
+       *pipe = (tmp & TV_ENC_PIPE_SEL_MASK) >> TV_ENC_PIPE_SEL_SHIFT;
+       return tmp & TV_ENC_ENABLE;
+ }
+ static void
+ intel_enable_tv(struct intel_encoder *encoder,
+               const struct intel_crtc_state *pipe_config,
+               const struct drm_connector_state *conn_state)
+ {
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       /* Prevents vblank waits from timing out in intel_tv_detect_type() */
+       intel_wait_for_vblank(dev_priv,
+                             to_intel_crtc(pipe_config->base.crtc)->pipe);
+       I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
+ }
+ static void
+ intel_disable_tv(struct intel_encoder *encoder,
+                const struct intel_crtc_state *old_crtc_state,
+                const struct drm_connector_state *old_conn_state)
+ {
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
+ }
+ static const struct tv_mode *intel_tv_mode_find(const struct drm_connector_state *conn_state)
+ {
+       int format = conn_state->tv.mode;
+       return &tv_modes[format];
+ }
+ static enum drm_mode_status
+ intel_tv_mode_valid(struct drm_connector *connector,
+                   struct drm_display_mode *mode)
+ {
+       const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
+       int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
+       if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+       if (mode->clock > max_dotclk)
+               return MODE_CLOCK_HIGH;
+       /* Ensure TV refresh is close to desired refresh */
+       if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000)
+                               < 1000)
+               return MODE_OK;
+       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_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);
+       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;
+       if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
+               adjusted_mode->crtc_clock /= 2;
+       /* pixel counter doesn't work on i965gm TV output */
+       if (IS_I965GM(dev_priv))
+               adjusted_mode->private_flags |=
+                       I915_MODE_FLAG_USE_SCANLINE_COUNTER;
+ }
+ static bool intel_tv_source_too_wide(struct drm_i915_private *dev_priv,
+                                    int hdisplay)
+ {
+       return IS_GEN(dev_priv, 3) && hdisplay > 1024;
+ }
+ static bool intel_tv_vert_scaling(const struct drm_display_mode *tv_mode,
+                                 const struct drm_connector_state *conn_state,
+                                 int vdisplay)
+ {
+       return tv_mode->crtc_vdisplay -
+               conn_state->tv.margins.top -
+               conn_state->tv.margins.bottom !=
+               vdisplay;
+ }
+ static int
+ 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;
+       int hdisplay = adjusted_mode->crtc_hdisplay;
+       int vdisplay = adjusted_mode->crtc_vdisplay;
+       if (!tv_mode)
+               return -EINVAL;
+       if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return -EINVAL;
+       pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
+       DRM_DEBUG_KMS("forcing bpc to 8 for TV\n");
+       pipe_config->pipe_bpp = 8*3;
+       pipe_config->port_clock = tv_mode->clock;
+       intel_tv_mode_to_mode(adjusted_mode, tv_mode);
+       drm_mode_set_crtcinfo(adjusted_mode, 0);
+       if (intel_tv_source_too_wide(dev_priv, hdisplay) ||
+           !intel_tv_vert_scaling(adjusted_mode, conn_state, vdisplay)) {
+               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 -EINVAL;
+               }
+               /* 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->clock /= 2;
+                       adjusted_mode->crtc_clock /= 2;
+                       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);
+       /*
+        * 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_GEN(dev_priv, 4)) {
+        *      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,
+                                tv_conn_state->margins.top,
+                                tv_conn_state->margins.bottom);
+       drm_mode_set_crtcinfo(adjusted_mode, 0);
+       adjusted_mode->name[0] = '\0';
+       /* pixel counter doesn't work on i965gm TV output */
+       if (IS_I965GM(dev_priv))
+               adjusted_mode->private_flags |=
+                       I915_MODE_FLAG_USE_SCANLINE_COUNTER;
+       return 0;
+ }
+ static void
+ set_tv_mode_timings(struct drm_i915_private *dev_priv,
+                   const struct tv_mode *tv_mode,
+                   bool burst_ena)
+ {
+       u32 hctl1, hctl2, hctl3;
+       u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
+       hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
+               (tv_mode->htotal << TV_HTOTAL_SHIFT);
+       hctl2 = (tv_mode->hburst_start << 16) |
+               (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
+       if (burst_ena)
+               hctl2 |= TV_BURST_ENA;
+       hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
+               (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
+       vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
+               (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
+               (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
+       vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
+               (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
+               (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
+       vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
+               (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
+               (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
+       if (tv_mode->veq_ena)
+               vctl3 |= TV_EQUAL_ENA;
+       vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
+               (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
+       vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
+               (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
+       vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
+               (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
+       vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
+               (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
+       I915_WRITE(TV_H_CTL_1, hctl1);
+       I915_WRITE(TV_H_CTL_2, hctl2);
+       I915_WRITE(TV_H_CTL_3, hctl3);
+       I915_WRITE(TV_V_CTL_1, vctl1);
+       I915_WRITE(TV_V_CTL_2, vctl2);
+       I915_WRITE(TV_V_CTL_3, vctl3);
+       I915_WRITE(TV_V_CTL_4, vctl4);
+       I915_WRITE(TV_V_CTL_5, vctl5);
+       I915_WRITE(TV_V_CTL_6, vctl6);
+       I915_WRITE(TV_V_CTL_7, vctl7);
+ }
+ static void set_color_conversion(struct drm_i915_private *dev_priv,
+                                const struct color_conversion *color_conversion)
+ {
+       if (!color_conversion)
+               return;
+       I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
+                  color_conversion->gy);
+       I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
+                  color_conversion->ay);
+       I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
+                  color_conversion->gu);
+       I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
+                  color_conversion->au);
+       I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
+                  color_conversion->gv);
+       I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
+                  color_conversion->av);
+ }
+ static void intel_tv_pre_enable(struct intel_encoder *encoder,
+                               const struct intel_crtc_state *pipe_config,
+                               const struct drm_connector_state *conn_state)
+ {
+       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, tv_filter_ctl;
+       u32 scctl1, scctl2, scctl3;
+       int i, j;
+       const struct video_levels *video_levels;
+       const struct color_conversion *color_conversion;
+       bool burst_ena;
+       int xpos, ypos;
+       unsigned int xsize, ysize;
+       if (!tv_mode)
+               return; /* can't happen (mode_prepare prevents this) */
+       tv_ctl = I915_READ(TV_CTL);
+       tv_ctl &= TV_CTL_SAVE;
+       switch (intel_tv->type) {
+       default:
+       case DRM_MODE_CONNECTOR_Unknown:
+       case DRM_MODE_CONNECTOR_Composite:
+               tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
+               video_levels = tv_mode->composite_levels;
+               color_conversion = tv_mode->composite_color;
+               burst_ena = tv_mode->burst_ena;
+               break;
+       case DRM_MODE_CONNECTOR_Component:
+               tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
+               video_levels = &component_levels;
+               if (tv_mode->burst_ena)
+                       color_conversion = &sdtv_csc_yprpb;
+               else
+                       color_conversion = &hdtv_csc_yprpb;
+               burst_ena = false;
+               break;
+       case DRM_MODE_CONNECTOR_SVIDEO:
+               tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
+               video_levels = tv_mode->svideo_levels;
+               color_conversion = tv_mode->svideo_color;
+               burst_ena = tv_mode->burst_ena;
+               break;
+       }
+       tv_ctl |= TV_ENC_PIPE_SEL(intel_crtc->pipe);
+       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;
+       if (tv_mode->trilevel_sync)
+               tv_ctl |= TV_TRILEVEL_SYNC;
+       if (tv_mode->pal_burst)
+               tv_ctl |= TV_PAL_BURST;
+       scctl1 = 0;
+       if (tv_mode->dda1_inc)
+               scctl1 |= TV_SC_DDA1_EN;
+       if (tv_mode->dda2_inc)
+               scctl1 |= TV_SC_DDA2_EN;
+       if (tv_mode->dda3_inc)
+               scctl1 |= TV_SC_DDA3_EN;
+       scctl1 |= tv_mode->sc_reset;
+       if (video_levels)
+               scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
+       scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
+       scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
+               tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
+       scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
+               tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
+       /* Enable two fixes for the chips that need them. */
+       if (IS_I915GM(dev_priv))
+               tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
+       set_tv_mode_timings(dev_priv, tv_mode, burst_ena);
+       I915_WRITE(TV_SC_CTL_1, scctl1);
+       I915_WRITE(TV_SC_CTL_2, scctl2);
+       I915_WRITE(TV_SC_CTL_3, scctl3);
+       set_color_conversion(dev_priv, color_conversion);
+       if (INTEL_GEN(dev_priv) >= 4)
+               I915_WRITE(TV_CLR_KNOBS, 0x00404000);
+       else
+               I915_WRITE(TV_CLR_KNOBS, 0x00606000);
+       if (video_levels)
+               I915_WRITE(TV_CLR_LEVEL,
+                          ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
+                           (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
+       assert_pipe_disabled(dev_priv, intel_crtc->pipe);
+       /* Filter ctl must be set before TV_WIN_SIZE */
+       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 = tv_conn_state->margins.top;
+       xsize -= (conn_state->tv.margins.left +
+                 conn_state->tv.margins.right);
+       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);
+       j = 0;
+       for (i = 0; i < 60; i++)
+               I915_WRITE(TV_H_LUMA(i), tv_mode->filter_table[j++]);
+       for (i = 0; i < 60; i++)
+               I915_WRITE(TV_H_CHROMA(i), tv_mode->filter_table[j++]);
+       for (i = 0; i < 43; i++)
+               I915_WRITE(TV_V_LUMA(i), tv_mode->filter_table[j++]);
+       for (i = 0; i < 43; i++)
+               I915_WRITE(TV_V_CHROMA(i), tv_mode->filter_table[j++]);
+       I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE);
+       I915_WRITE(TV_CTL, tv_ctl);
+ }
+ static int
+ intel_tv_detect_type(struct intel_tv *intel_tv,
+                     struct drm_connector *connector)
+ {
+       struct drm_crtc *crtc = connector->state->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_device *dev = connector->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       u32 tv_ctl, save_tv_ctl;
+       u32 tv_dac, save_tv_dac;
+       int type;
+       /* Disable TV interrupts around load detect or we'll recurse */
+       if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
+               spin_lock_irq(&dev_priv->irq_lock);
+               i915_disable_pipestat(dev_priv, 0,
+                                     PIPE_HOTPLUG_INTERRUPT_STATUS |
+                                     PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
+               spin_unlock_irq(&dev_priv->irq_lock);
+       }
+       save_tv_dac = tv_dac = I915_READ(TV_DAC);
+       save_tv_ctl = tv_ctl = I915_READ(TV_CTL);
+       /* Poll for TV detection */
+       tv_ctl &= ~(TV_ENC_ENABLE | TV_ENC_PIPE_SEL_MASK | TV_TEST_MODE_MASK);
+       tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
+       tv_ctl |= TV_ENC_PIPE_SEL(intel_crtc->pipe);
+       tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
+       tv_dac |= (TVDAC_STATE_CHG_EN |
+                  TVDAC_A_SENSE_CTL |
+                  TVDAC_B_SENSE_CTL |
+                  TVDAC_C_SENSE_CTL |
+                  DAC_CTL_OVERRIDE |
+                  DAC_A_0_7_V |
+                  DAC_B_0_7_V |
+                  DAC_C_0_7_V);
+       /*
+        * The TV sense state should be cleared to zero on cantiga platform. Otherwise
+        * the TV is misdetected. This is hardware requirement.
+        */
+       if (IS_GM45(dev_priv))
+               tv_dac &= ~(TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL |
+                           TVDAC_B_SENSE_CTL | TVDAC_C_SENSE_CTL);
+       I915_WRITE(TV_CTL, tv_ctl);
+       I915_WRITE(TV_DAC, tv_dac);
+       POSTING_READ(TV_DAC);
+       intel_wait_for_vblank(dev_priv, intel_crtc->pipe);
+       type = -1;
+       tv_dac = I915_READ(TV_DAC);
+       DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
+       /*
+        *  A B C
+        *  0 1 1 Composite
+        *  1 0 X svideo
+        *  0 0 0 Component
+        */
+       if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+               DRM_DEBUG_KMS("Detected Composite TV connection\n");
+               type = DRM_MODE_CONNECTOR_Composite;
+       } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
+               DRM_DEBUG_KMS("Detected S-Video TV connection\n");
+               type = DRM_MODE_CONNECTOR_SVIDEO;
+       } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+               DRM_DEBUG_KMS("Detected Component TV connection\n");
+               type = DRM_MODE_CONNECTOR_Component;
+       } else {
+               DRM_DEBUG_KMS("Unrecognised TV connection\n");
+               type = -1;
+       }
+       I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
+       I915_WRITE(TV_CTL, save_tv_ctl);
+       POSTING_READ(TV_CTL);
+       /* For unknown reasons the hw barfs if we don't do this vblank wait. */
+       intel_wait_for_vblank(dev_priv, intel_crtc->pipe);
+       /* Restore interrupt config */
+       if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
+               spin_lock_irq(&dev_priv->irq_lock);
+               i915_enable_pipestat(dev_priv, 0,
+                                    PIPE_HOTPLUG_INTERRUPT_STATUS |
+                                    PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
+               spin_unlock_irq(&dev_priv->irq_lock);
+       }
+       return type;
+ }
+ /*
+  * Here we set accurate tv format according to connector type
+  * i.e Component TV should not be assigned by NTSC or PAL
+  */
+ static void intel_tv_find_better_format(struct drm_connector *connector)
+ {
+       struct intel_tv *intel_tv = intel_attached_tv(connector);
+       const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
+       int i;
+       /* 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];
+               if (!tv_mode->component_only)
+                       break;
+       }
+       connector->state->tv.mode = i;
+ }
+ static int
+ intel_tv_detect(struct drm_connector *connector,
+               struct drm_modeset_acquire_ctx *ctx,
+               bool force)
+ {
+       struct intel_tv *intel_tv = intel_attached_tv(connector);
+       enum drm_connector_status status;
+       int type;
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
+                     connector->base.id, connector->name,
+                     force);
+       if (force) {
+               struct intel_load_detect_pipe tmp;
+               int ret;
+               ret = intel_get_load_detect_pipe(connector, NULL, &tmp, ctx);
+               if (ret < 0)
+                       return ret;
+               if (ret > 0) {
+                       type = intel_tv_detect_type(intel_tv, connector);
+                       intel_release_load_detect_pipe(connector, &tmp, ctx);
+                       status = type < 0 ?
+                               connector_status_disconnected :
+                               connector_status_connected;
+               } else
+                       status = connector_status_unknown;
+               if (status == connector_status_connected) {
+                       intel_tv->type = type;
+                       intel_tv_find_better_format(connector);
+               }
+               return status;
+       } else
+               return connector->status;
+ }
+ static const struct input_res {
+       u16 w, h;
+ } input_res_table[] = {
+       { 640, 480 },
+       { 800, 600 },
+       { 1024, 768 },
+       { 1280, 1024 },
+       { 848, 480 },
+       { 1280, 720 },
+       { 1920, 1080 },
+ };
+ /* 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_set_mode_type(struct drm_display_mode *mode,
+                      const struct tv_mode *tv_mode)
+ {
+       mode->type = DRM_MODE_TYPE_DRIVER;
+       if (intel_tv_is_preferred_mode(mode, tv_mode))
+               mode->type |= DRM_MODE_TYPE_PREFERRED;
+ }
+ 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;
+       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)
+                       continue;
+               /* no vertical scaling with wide sources on gen3 */
+               if (IS_GEN(dev_priv, 3) && input->w > 1024 &&
+                   input->h > intel_tv_mode_vdisplay(tv_mode))
+                       continue;
+               mode = drm_mode_create(connector->dev);
+               if (!mode)
+                       continue;
+               /*
+                * 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);
+               drm_mode_probed_add(connector, mode);
+               count++;
+       }
+       return count;
+ }
+ static const struct drm_connector_funcs intel_tv_connector_funcs = {
+       .late_register = intel_connector_register,
+       .early_unregister = intel_connector_unregister,
+       .destroy = intel_connector_destroy,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+       .atomic_duplicate_state = intel_tv_connector_duplicate_state,
+ };
+ static int intel_tv_atomic_check(struct drm_connector *connector,
 -      old_state = drm_atomic_get_old_connector_state(new_state->state, connector);
 -      new_crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
++                               struct drm_atomic_state *state)
+ {
++      struct drm_connector_state *new_state;
+       struct drm_crtc_state *new_crtc_state;
+       struct drm_connector_state *old_state;
++      new_state = drm_atomic_get_new_connector_state(state, connector);
+       if (!new_state->crtc)
+               return 0;
++      old_state = drm_atomic_get_old_connector_state(state, connector);
++      new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
+       if (old_state->tv.mode != new_state->tv.mode ||
+           old_state->tv.margins.left != new_state->tv.margins.left ||
+           old_state->tv.margins.right != new_state->tv.margins.right ||
+           old_state->tv.margins.top != new_state->tv.margins.top ||
+           old_state->tv.margins.bottom != new_state->tv.margins.bottom) {
+               /* Force a modeset. */
+               new_crtc_state->connectors_changed = true;
+       }
+       return 0;
+ }
+ static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
+       .detect_ctx = intel_tv_detect,
+       .mode_valid = intel_tv_mode_valid,
+       .get_modes = intel_tv_get_modes,
+       .atomic_check = intel_tv_atomic_check,
+ };
+ static const struct drm_encoder_funcs intel_tv_enc_funcs = {
+       .destroy = intel_encoder_destroy,
+ };
+ void
+ intel_tv_init(struct drm_i915_private *dev_priv)
+ {
+       struct drm_device *dev = &dev_priv->drm;
+       struct drm_connector *connector;
+       struct intel_tv *intel_tv;
+       struct intel_encoder *intel_encoder;
+       struct intel_connector *intel_connector;
+       u32 tv_dac_on, tv_dac_off, save_tv_dac;
+       const char *tv_format_names[ARRAY_SIZE(tv_modes)];
+       int i, initial_mode = 0;
+       struct drm_connector_state *state;
+       if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
+               return;
+       if (!intel_bios_is_tv_present(dev_priv)) {
+               DRM_DEBUG_KMS("Integrated TV is not present.\n");
+               return;
+       }
+       /*
+        * Sanity check the TV output by checking to see if the
+        * DAC register holds a value
+        */
+       save_tv_dac = I915_READ(TV_DAC);
+       I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
+       tv_dac_on = I915_READ(TV_DAC);
+       I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
+       tv_dac_off = I915_READ(TV_DAC);
+       I915_WRITE(TV_DAC, save_tv_dac);
+       /*
+        * If the register does not hold the state change enable
+        * bit, (either as a 0 or a 1), assume it doesn't really
+        * exist
+        */
+       if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
+           (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
+               return;
+       intel_tv = kzalloc(sizeof(*intel_tv), GFP_KERNEL);
+       if (!intel_tv) {
+               return;
+       }
+       intel_connector = intel_connector_alloc();
+       if (!intel_connector) {
+               kfree(intel_tv);
+               return;
+       }
+       intel_encoder = &intel_tv->base;
+       connector = &intel_connector->base;
+       state = connector->state;
+       /*
+        * The documentation, for the older chipsets at least, recommend
+        * using a polling method rather than hotplug detection for TVs.
+        * This is because in order to perform the hotplug detection, the PLLs
+        * for the TV must be kept alive increasing power drain and starving
+        * bandwidth from other encoders. Notably for instance, it causes
+        * pipe underruns on Crestline when this encoder is supposedly idle.
+        *
+        * More recent chipsets favour HDMI rather than integrated S-Video.
+        */
+       intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+       drm_connector_init(dev, connector, &intel_tv_connector_funcs,
+                          DRM_MODE_CONNECTOR_SVIDEO);
+       drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
+                        DRM_MODE_ENCODER_TVDAC, "TV");
+       intel_encoder->compute_config = intel_tv_compute_config;
+       intel_encoder->get_config = intel_tv_get_config;
+       intel_encoder->pre_enable = intel_tv_pre_enable;
+       intel_encoder->enable = intel_enable_tv;
+       intel_encoder->disable = intel_disable_tv;
+       intel_encoder->get_hw_state = intel_tv_get_hw_state;
+       intel_connector->get_hw_state = intel_connector_get_hw_state;
+       intel_connector_attach_encoder(intel_connector, intel_encoder);
+       intel_encoder->type = INTEL_OUTPUT_TVOUT;
+       intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
+       intel_encoder->port = PORT_NONE;
+       intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
+       intel_encoder->cloneable = 0;
+       intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
+       intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
+       /* BIOS margin values */
+       state->tv.margins.left = 54;
+       state->tv.margins.top = 36;
+       state->tv.margins.right = 46;
+       state->tv.margins.bottom = 37;
+       state->tv.mode = initial_mode;
+       drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
+       connector->interlace_allowed = false;
+       connector->doublescan_allowed = false;
+       /* Create TV properties then attach current values */
+       for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
+               /* 1080p50/1080p60 not supported on gen3 */
+               if (IS_GEN(dev_priv, 3) &&
+                   tv_modes[i].oversample == 1)
+                       break;
+               tv_format_names[i] = tv_modes[i].name;
+       }
+       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);
+       drm_object_attach_property(&connector->base,
+                                  dev->mode_config.tv_left_margin_property,
+                                  state->tv.margins.left);
+       drm_object_attach_property(&connector->base,
+                                  dev->mode_config.tv_top_margin_property,
+                                  state->tv.margins.top);
+       drm_object_attach_property(&connector->base,
+                                  dev->mode_config.tv_right_margin_property,
+                                  state->tv.margins.right);
+       drm_object_attach_property(&connector->base,
+                                  dev->mode_config.tv_bottom_margin_property,
+                                  state->tv.margins.bottom);
+ }
Simple merge
Simple merge
Simple merge