Merge tag 'drm/tegra/for-4.5-rc1' of git://anongit.freedesktop.org/tegra/linux into...
authorDave Airlie <airlied@redhat.com>
Wed, 16 Dec 2015 22:38:57 +0000 (08:38 +1000)
committerDave Airlie <airlied@redhat.com>
Wed, 16 Dec 2015 22:38:57 +0000 (08:38 +1000)
drm/tegra: Changes for v4.5-rc1

This adds support for the version of host1x found on Tegra210 SoCs. It
also makes use of the new atomic suspend/resume functionality to bring
this feature to Tegra.

Other than that it's mostly small fixes and cleanups, with some prep-
work for things that will hopefully get merged for the next release.

* tag 'drm/tegra/for-4.5-rc1' of git://anongit.freedesktop.org/tegra/linux:
  drm/tegra: Advertise DRIVER_ATOMIC
  drm/tegra: Use DRIVER level for IOMMU aperture message
  drm/tegra: checking for IS_ERR() instead of NULL
  drm/tegra: dc: Add missing of_node_put()
  drm/tegra: Implement subsystem-level suspend/resume
  drm/tegra: sor: Remove unnecessary conditional
  drm/tegra: sor: Operate on struct drm_dp_aux *
  drm/tegra: Use drm_gem_object_unreference_unlocked()
  drm/tegra: Don't take dev->struct_mutex in mmap offset ioctl
  drm/tegra: Use unlocked gem unreferencing
  drm/tegra: Use new multi-driver module helpers
  gpu: host1x: Add Tegra210 support
  gpu: host1x: Remove core driver on unregister
  gpu: host1x: Use platform_register/unregister_drivers()

245 files changed:
Documentation/DocBook/gpu.tmpl
Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
Documentation/devicetree/bindings/display/msm/dsi.txt
Documentation/devicetree/bindings/display/msm/mdp.txt
Documentation/devicetree/bindings/display/panel/boe,tv080wum-nl0.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/innolux,g121x1-l03.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/kyo,tcg121xglp.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/sharp,ls043t1le01.txt [new file with mode: 0644]
Documentation/devicetree/bindings/media/exynos5-gsc.txt
Documentation/devicetree/bindings/panel/panasonic,vvx10f034n00.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/qiaodian,qd43003c0-40.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
arch/arm/boot/dts/exynos5800-peach-pi.dts
drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
drivers/gpu/drm/armada/armada_crtc.c
drivers/gpu/drm/armada/armada_overlay.c
drivers/gpu/drm/ast/ast_mode.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
drivers/gpu/drm/bochs/bochs_kms.c
drivers/gpu/drm/bridge/Kconfig
drivers/gpu/drm/bridge/Makefile
drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c [new file with mode: 0644]
drivers/gpu/drm/bridge/dw-hdmi-audio.h [new file with mode: 0644]
drivers/gpu/drm/bridge/dw-hdmi.c [new file with mode: 0644]
drivers/gpu/drm/bridge/dw-hdmi.h [new file with mode: 0644]
drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c [deleted file]
drivers/gpu/drm/bridge/dw_hdmi-audio.h [deleted file]
drivers/gpu/drm/bridge/dw_hdmi.c [deleted file]
drivers/gpu/drm/bridge/dw_hdmi.h [deleted file]
drivers/gpu/drm/cirrus/cirrus_mode.c
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_bridge.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_gem_cma_helper.c
drivers/gpu/drm/drm_mipi_dsi.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_plane_helper.c
drivers/gpu/drm/drm_probe_helper.c
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/exynos5433_drm_decon.c
drivers/gpu/drm/exynos/exynos7_drm_decon.c
drivers/gpu/drm/exynos/exynos_dp_core.c
drivers/gpu/drm/exynos/exynos_dp_core.h
drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_dpi.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_dsi.c
drivers/gpu/drm/exynos/exynos_drm_fb.c
drivers/gpu/drm/exynos/exynos_drm_fb.h
drivers/gpu/drm/exynos/exynos_drm_fimd.c
drivers/gpu/drm/exynos/exynos_drm_gem.h
drivers/gpu/drm/exynos/exynos_drm_gsc.c
drivers/gpu/drm/exynos/exynos_drm_plane.c
drivers/gpu/drm/exynos/exynos_drm_plane.h
drivers/gpu/drm/exynos/exynos_drm_rotator.c
drivers/gpu/drm/exynos/exynos_drm_vidi.c
drivers/gpu/drm/exynos/exynos_hdmi.c
drivers/gpu/drm/exynos/exynos_mixer.c
drivers/gpu/drm/exynos/regs-gsc.h
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
drivers/gpu/drm/gma500/cdv_device.c
drivers/gpu/drm/gma500/cdv_intel_crt.c
drivers/gpu/drm/gma500/cdv_intel_display.c
drivers/gpu/drm/gma500/cdv_intel_dp.c
drivers/gpu/drm/gma500/cdv_intel_hdmi.c
drivers/gpu/drm/gma500/cdv_intel_lvds.c
drivers/gpu/drm/gma500/mdfld_device.c
drivers/gpu/drm/gma500/mdfld_dsi_dpi.c
drivers/gpu/drm/gma500/mdfld_dsi_output.c
drivers/gpu/drm/gma500/oaktrail_device.c
drivers/gpu/drm/gma500/oaktrail_hdmi.c
drivers/gpu/drm/gma500/oaktrail_lvds.c
drivers/gpu/drm/gma500/psb_device.c
drivers/gpu/drm/gma500/psb_drv.h
drivers/gpu/drm/gma500/psb_intel_display.c
drivers/gpu/drm/gma500/psb_intel_drv.h
drivers/gpu/drm/gma500/psb_intel_lvds.c
drivers/gpu/drm/gma500/psb_intel_sdvo.c
drivers/gpu/drm/i2c/tda998x_drv.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_context.c
drivers/gpu/drm/i915/i915_guc_submission.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_bios.c
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_dp_mst.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_dsi.c
drivers/gpu/drm/i915/intel_dvo.c
drivers/gpu/drm/i915/intel_fbc.c
drivers/gpu/drm/i915/intel_fifo_underrun.c
drivers/gpu/drm/i915/intel_guc.h
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_psr.c
drivers/gpu/drm/i915/intel_runtime_pm.c
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/imx/dw_hdmi-imx.c
drivers/gpu/drm/imx/imx-drm-core.c
drivers/gpu/drm/imx/imx-ldb.c
drivers/gpu/drm/imx/imx-tve.c
drivers/gpu/drm/imx/ipuv3-plane.c
drivers/gpu/drm/imx/parallel-display.c
drivers/gpu/drm/mgag200/mgag200_mode.c
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/msm/Makefile
drivers/gpu/drm/msm/adreno/adreno_device.c
drivers/gpu/drm/msm/dsi/dsi.h
drivers/gpu/drm/msm/dsi/dsi_cfg.c
drivers/gpu/drm/msm/dsi/dsi_cfg.h
drivers/gpu/drm/msm/dsi/dsi_host.c
drivers/gpu/drm/msm/dsi/dsi_manager.c
drivers/gpu/drm/msm/dsi/phy/dsi_phy.c
drivers/gpu/drm/msm/dsi/phy/dsi_phy.h
drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c [new file with mode: 0644]
drivers/gpu/drm/msm/dsi/pll/dsi_pll.c
drivers/gpu/drm/msm/dsi/pll/dsi_pll.h
drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm_8960.c [new file with mode: 0644]
drivers/gpu/drm/msm/hdmi/hdmi.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_dsi_encoder.c [new file with mode: 0644]
drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_drv.h
drivers/gpu/drm/msm/msm_fbdev.c
drivers/gpu/drm/nouveau/dispnv04/crtc.c
drivers/gpu/drm/nouveau/dispnv04/dac.c
drivers/gpu/drm/nouveau/dispnv04/dfp.c
drivers/gpu/drm/nouveau/dispnv04/disp.c
drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_crtc.h
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/omapdrm/omap_crtc.c
drivers/gpu/drm/omapdrm/omap_encoder.c
drivers/gpu/drm/omapdrm/omap_plane.c
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/Makefile
drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c [new file with mode: 0644]
drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c [new file with mode: 0644]
drivers/gpu/drm/panel/panel-simple.c
drivers/gpu/drm/qxl/qxl_display.c
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/radeon/radeon_dp_mst.c
drivers/gpu/drm/radeon/radeon_legacy_crtc.c
drivers/gpu/drm/radeon/radeon_legacy_encoders.c
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
drivers/gpu/drm/rcar-du/rcar_du_encoder.c
drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
drivers/gpu/drm/rcar-du/rcar_du_plane.c
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/shmobile/shmob_drm_crtc.c
drivers/gpu/drm/sti/sti_crtc.c
drivers/gpu/drm/sti/sti_cursor.c
drivers/gpu/drm/sti/sti_gdp.c
drivers/gpu/drm/sti/sti_hda.c
drivers/gpu/drm/sti/sti_hqvdp.c
drivers/gpu/drm/sti/sti_tvout.c
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/dsi.c
drivers/gpu/drm/tegra/hdmi.c
drivers/gpu/drm/tegra/rgb.c
drivers/gpu/drm/tegra/sor.c
drivers/gpu/drm/tilcdc/tilcdc_panel.c
drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
drivers/gpu/drm/udl/udl_encoder.c
drivers/gpu/drm/vc4/Makefile
drivers/gpu/drm/vc4/vc4_bo.c
drivers/gpu/drm/vc4/vc4_crtc.c
drivers/gpu/drm/vc4/vc4_debugfs.c
drivers/gpu/drm/vc4/vc4_drv.c
drivers/gpu/drm/vc4/vc4_drv.h
drivers/gpu/drm/vc4/vc4_gem.c [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_irq.c [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_kms.c
drivers/gpu/drm/vc4/vc4_packet.h [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_plane.c
drivers/gpu/drm/vc4/vc4_qpu_defines.h [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_regs.h
drivers/gpu/drm/vc4/vc4_render_cl.c [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_trace.h [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_trace_points.c [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_v3d.c [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_validate.c [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_validate_shaders.c [new file with mode: 0644]
drivers/gpu/drm/virtio/virtgpu_display.c
drivers/gpu/drm/virtio/virtgpu_plane.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_crtc_helper.h
include/drm/drm_fb_helper.h
include/drm/drm_mipi_dsi.h
include/drm/drm_modes.h
include/drm/drm_modeset_helper_vtables.h [new file with mode: 0644]
include/drm/drm_plane_helper.h
include/drm/i915_pciids.h
include/drm/ttm/ttm_bo_driver.h
include/uapi/drm/Kbuild
include/uapi/drm/msm_drm.h
include/uapi/drm/vc4_drm.h [new file with mode: 0644]

index 03f01e7..c66d641 100644 (file)
     <para>
       [Insert diagram of typical DRM stack here]
     </para>
+  <sect1>
+    <title>Style Guidelines</title>
+    <para>
+      For consistency this documentation uses American English. Abbreviations
+      are written as all-uppercase, for example: DRM, KMS, IOCTL, CRTC, and so
+      on. To aid in reading, documentations make full use of the markup
+      characters kerneldoc provides: @parameter for function parameters, @member
+      for structure members, &amp;structure to reference structures and
+      function() for functions. These all get automatically hyperlinked if
+      kerneldoc for the referenced objects exists. When referencing entries in
+      function vtables please use -&gt;vfunc(). Note that kerneldoc does
+      not support referencing struct members directly, so please add a reference
+      to the vtable struct somewhere in the same paragraph or at least section.
+    </para>
+    <para>
+      Except in special situations (to separate locked from unlocked variants)
+      locking requirements for functions aren't documented in the kerneldoc.
+      Instead locking should be check at runtime using e.g.
+      <code>WARN_ON(!mutex_is_locked(...));</code>. Since it's much easier to
+      ignore documentation than runtime noise this provides more value. And on
+      top of that runtime checks do need to be updated when the locking rules
+      change, increasing the chances that they're correct. Within the
+      documentation the locking rules should be explained in the relevant
+      structures: Either in the comment for the lock explaining what it
+      protects, or data fields need a note about which lock protects them, or
+      both.
+    </para>
+    <para>
+      Functions which have a non-<code>void</code> return value should have a
+      section called "Returns" explaining the expected return values in
+      different cases and their meanings. Currently there's no consensus whether
+      that section name should be all upper-case or not, and whether it should
+      end in a colon or not. Go with the file-local style. Other common section
+      names are "Notes" with information for dangerous or tricky corner cases,
+      and "FIXME" where the interface could be cleaned up.
+    </para>
+  </sect1>
   </chapter>
 
   <!-- Internals -->
@@ -946,6 +983,7 @@ int max_width, max_height;</synopsis>
     <sect2>
       <title>Atomic Mode Setting Function Reference</title>
 !Edrivers/gpu/drm/drm_atomic.c
+!Idrivers/gpu/drm/drm_atomic.c
     </sect2>
     <sect2>
       <title>Frame Buffer Creation</title>
@@ -1173,137 +1211,6 @@ int max_width, max_height;</synopsis>
           pointer to CRTC functions.
         </para>
       </sect3>
-      <sect3 id="drm-kms-crtcops">
-        <title>CRTC Operations</title>
-        <sect4>
-          <title>Set Configuration</title>
-          <synopsis>int (*set_config)(struct drm_mode_set *set);</synopsis>
-          <para>
-            Apply a new CRTC configuration to the device. The configuration
-            specifies a CRTC, a frame buffer to scan out from, a (x,y) position in
-            the frame buffer, a display mode and an array of connectors to drive
-            with the CRTC if possible.
-          </para>
-          <para>
-            If the frame buffer specified in the configuration is NULL, the driver
-            must detach all encoders connected to the CRTC and all connectors
-            attached to those encoders and disable them.
-          </para>
-          <para>
-            This operation is called with the mode config lock held.
-          </para>
-          <note><para>
-           Note that the drm core has no notion of restoring the mode setting
-           state after resume, since all resume handling is in the full
-           responsibility of the driver. The common mode setting helper library
-           though provides a helper which can be used for this:
-           <function>drm_helper_resume_force_mode</function>.
-          </para></note>
-        </sect4>
-        <sect4>
-          <title>Page Flipping</title>
-          <synopsis>int (*page_flip)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
-                   struct drm_pending_vblank_event *event);</synopsis>
-          <para>
-            Schedule a page flip to the given frame buffer for the CRTC. This
-            operation is called with the mode config mutex held.
-          </para>
-          <para>
-            Page flipping is a synchronization mechanism that replaces the frame
-            buffer being scanned out by the CRTC with a new frame buffer during
-            vertical blanking, avoiding tearing. When an application requests a page
-            flip the DRM core verifies that the new frame buffer is large enough to
-            be scanned out by  the CRTC in the currently configured mode and then
-            calls the CRTC <methodname>page_flip</methodname> operation with a
-            pointer to the new frame buffer.
-          </para>
-          <para>
-            The <methodname>page_flip</methodname> operation schedules a page flip.
-            Once any pending rendering targeting the new frame buffer has
-            completed, the CRTC will be reprogrammed to display that frame buffer
-            after the next vertical refresh. The operation must return immediately
-            without waiting for rendering or page flip to complete and must block
-            any new rendering to the frame buffer until the page flip completes.
-          </para>
-          <para>
-            If a page flip can be successfully scheduled the driver must set the
-            <code>drm_crtc-&gt;fb</code> field to the new framebuffer pointed to
-            by <code>fb</code>. This is important so that the reference counting
-            on framebuffers stays balanced.
-          </para>
-          <para>
-            If a page flip is already pending, the
-            <methodname>page_flip</methodname> operation must return
-            -<errorname>EBUSY</errorname>.
-          </para>
-          <para>
-            To synchronize page flip to vertical blanking the driver will likely
-            need to enable vertical blanking interrupts. It should call
-            <function>drm_vblank_get</function> for that purpose, and call
-            <function>drm_vblank_put</function> after the page flip completes.
-          </para>
-          <para>
-            If the application has requested to be notified when page flip completes
-            the <methodname>page_flip</methodname> operation will be called with a
-            non-NULL <parameter>event</parameter> argument pointing to a
-            <structname>drm_pending_vblank_event</structname> instance. Upon page
-            flip completion the driver must call <methodname>drm_send_vblank_event</methodname>
-            to fill in the event and send to wake up any waiting processes.
-            This can be performed with
-            <programlisting><![CDATA[
-            spin_lock_irqsave(&dev->event_lock, flags);
-            ...
-            drm_send_vblank_event(dev, pipe, event);
-            spin_unlock_irqrestore(&dev->event_lock, flags);
-            ]]></programlisting>
-          </para>
-          <note><para>
-            FIXME: Could drivers that don't need to wait for rendering to complete
-            just add the event to <literal>dev-&gt;vblank_event_list</literal> and
-            let the DRM core handle everything, as for "normal" vertical blanking
-            events?
-          </para></note>
-          <para>
-            While waiting for the page flip to complete, the
-            <literal>event-&gt;base.link</literal> list head can be used freely by
-            the driver to store the pending event in a driver-specific list.
-          </para>
-          <para>
-            If the file handle is closed before the event is signaled, drivers must
-            take care to destroy the event in their
-            <methodname>preclose</methodname> operation (and, if needed, call
-            <function>drm_vblank_put</function>).
-          </para>
-        </sect4>
-        <sect4>
-          <title>Miscellaneous</title>
-          <itemizedlist>
-            <listitem>
-              <synopsis>void (*set_property)(struct drm_crtc *crtc,
-                     struct drm_property *property, uint64_t value);</synopsis>
-              <para>
-                Set the value of the given CRTC property to
-                <parameter>value</parameter>. See <xref linkend="drm-kms-properties"/>
-                for more information about properties.
-              </para>
-            </listitem>
-            <listitem>
-              <synopsis>void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
-                        uint32_t start, uint32_t size);</synopsis>
-              <para>
-                Apply a gamma table to the device. The operation is optional.
-              </para>
-            </listitem>
-            <listitem>
-              <synopsis>void (*destroy)(struct drm_crtc *crtc);</synopsis>
-              <para>
-                Destroy the CRTC when not needed anymore. See
-                <xref linkend="drm-kms-init"/>.
-              </para>
-            </listitem>
-          </itemizedlist>
-        </sect4>
-      </sect3>
     </sect2>
     <sect2>
       <title>Planes (struct <structname>drm_plane</structname>)</title>
@@ -1320,7 +1227,7 @@ int max_width, max_height;</synopsis>
         <listitem>
         DRM_PLANE_TYPE_PRIMARY represents a "main" plane for a CRTC.  Primary
         planes are the planes operated upon by CRTC modesetting and flipping
-        operations described in <xref linkend="drm-kms-crtcops"/>.
+       operations described in the page_flip hook in <structname>drm_crtc_funcs</structname>.
         </listitem>
         <listitem>
         DRM_PLANE_TYPE_CURSOR represents a "cursor" plane for a CRTC.  Cursor
@@ -1357,52 +1264,6 @@ int max_width, max_height;</synopsis>
           primary plane with standard capabilities.
         </para>
       </sect3>
-      <sect3>
-        <title>Plane Operations</title>
-        <itemizedlist>
-          <listitem>
-            <synopsis>int (*update_plane)(struct drm_plane *plane, struct drm_crtc *crtc,
-                        struct drm_framebuffer *fb, int crtc_x, int crtc_y,
-                        unsigned int crtc_w, unsigned int crtc_h,
-                        uint32_t src_x, uint32_t src_y,
-                        uint32_t src_w, uint32_t src_h);</synopsis>
-            <para>
-              Enable and configure the plane to use the given CRTC and frame buffer.
-            </para>
-            <para>
-              The source rectangle in frame buffer memory coordinates is given by
-              the <parameter>src_x</parameter>, <parameter>src_y</parameter>,
-              <parameter>src_w</parameter> and <parameter>src_h</parameter>
-              parameters (as 16.16 fixed point values). Devices that don't support
-              subpixel plane coordinates can ignore the fractional part.
-            </para>
-            <para>
-              The destination rectangle in CRTC coordinates is given by the
-              <parameter>crtc_x</parameter>, <parameter>crtc_y</parameter>,
-              <parameter>crtc_w</parameter> and <parameter>crtc_h</parameter>
-              parameters (as integer values). Devices scale the source rectangle to
-              the destination rectangle. If scaling is not supported, and the source
-              rectangle size doesn't match the destination rectangle size, the
-              driver must return a -<errorname>EINVAL</errorname> error.
-            </para>
-          </listitem>
-          <listitem>
-            <synopsis>int (*disable_plane)(struct drm_plane *plane);</synopsis>
-            <para>
-              Disable the plane. The DRM core calls this method in response to a
-              DRM_IOCTL_MODE_SETPLANE ioctl call with the frame buffer ID set to 0.
-              Disabled planes must not be processed by the CRTC.
-            </para>
-          </listitem>
-          <listitem>
-            <synopsis>void (*destroy)(struct drm_plane *plane);</synopsis>
-            <para>
-              Destroy the plane when not needed anymore. See
-              <xref linkend="drm-kms-init"/>.
-            </para>
-          </listitem>
-        </itemizedlist>
-      </sect3>
     </sect2>
     <sect2>
       <title>Encoders (struct <structname>drm_encoder</structname>)</title>
@@ -1459,27 +1320,6 @@ int max_width, max_height;</synopsis>
           encoders they want to use to a CRTC.
         </para>
       </sect3>
-      <sect3>
-        <title>Encoder Operations</title>
-        <itemizedlist>
-          <listitem>
-            <synopsis>void (*destroy)(struct drm_encoder *encoder);</synopsis>
-            <para>
-              Called to destroy the encoder when not needed anymore. See
-              <xref linkend="drm-kms-init"/>.
-            </para>
-          </listitem>
-          <listitem>
-            <synopsis>void (*set_property)(struct drm_plane *plane,
-                     struct drm_property *property, uint64_t value);</synopsis>
-            <para>
-              Set the value of the given plane property to
-              <parameter>value</parameter>. See <xref linkend="drm-kms-properties"/>
-              for more information about properties.
-            </para>
-          </listitem>
-        </itemizedlist>
-      </sect3>
     </sect2>
     <sect2>
       <title>Connectors (struct <structname>drm_connector</structname>)</title>
@@ -1683,27 +1523,6 @@ int max_width, max_height;</synopsis>
             connector_status_unknown.
           </para>
         </sect4>
-        <sect4>
-          <title>Miscellaneous</title>
-          <itemizedlist>
-            <listitem>
-              <synopsis>void (*set_property)(struct drm_connector *connector,
-                     struct drm_property *property, uint64_t value);</synopsis>
-              <para>
-                Set the value of the given connector property to
-                <parameter>value</parameter>. See <xref linkend="drm-kms-properties"/>
-                for more information about properties.
-              </para>
-            </listitem>
-            <listitem>
-              <synopsis>void (*destroy)(struct drm_connector *connector);</synopsis>
-              <para>
-                Destroy the connector when not needed anymore. See
-                <xref linkend="drm-kms-init"/>.
-              </para>
-            </listitem>
-          </itemizedlist>
-        </sect4>
       </sect3>
     </sect2>
     <sect2>
@@ -1830,83 +1649,7 @@ void intel_crt_init(struct drm_device *dev)
       entities.
     </para>
     <sect2>
-      <title>Helper Functions</title>
-      <itemizedlist>
-        <listitem>
-          <synopsis>int drm_crtc_helper_set_config(struct drm_mode_set *set);</synopsis>
-          <para>
-            The <function>drm_crtc_helper_set_config</function> helper function
-            is a CRTC <methodname>set_config</methodname> implementation. It
-            first tries to locate the best encoder for each connector by calling
-            the connector <methodname>best_encoder</methodname> helper
-            operation.
-          </para>
-          <para>
-            After locating the appropriate encoders, the helper function will
-            call the <methodname>mode_fixup</methodname> encoder and CRTC helper
-            operations to adjust the requested mode, or reject it completely in
-            which case an error will be returned to the application. If the new
-            configuration after mode adjustment is identical to the current
-            configuration the helper function will return without performing any
-            other operation.
-          </para>
-          <para>
-            If the adjusted mode is identical to the current mode but changes to
-            the frame buffer need to be applied, the
-            <function>drm_crtc_helper_set_config</function> function will call
-            the CRTC <methodname>mode_set_base</methodname> helper operation. If
-            the adjusted mode differs from the current mode, or if the
-            <methodname>mode_set_base</methodname> helper operation is not
-            provided, the helper function performs a full mode set sequence by
-            calling the <methodname>prepare</methodname>,
-            <methodname>mode_set</methodname> and
-            <methodname>commit</methodname> CRTC and encoder helper operations,
-            in that order.
-          </para>
-        </listitem>
-        <listitem>
-          <synopsis>void drm_helper_connector_dpms(struct drm_connector *connector, int mode);</synopsis>
-          <para>
-            The <function>drm_helper_connector_dpms</function> helper function
-            is a connector <methodname>dpms</methodname> implementation that
-            tracks power state of connectors. To use the function, drivers must
-            provide <methodname>dpms</methodname> helper operations for CRTCs
-            and encoders to apply the DPMS state to the device.
-          </para>
-          <para>
-            The mid-layer doesn't track the power state of CRTCs and encoders.
-            The <methodname>dpms</methodname> helper operations can thus be
-            called with a mode identical to the currently active mode.
-          </para>
-        </listitem>
-        <listitem>
-          <synopsis>int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
-                                            uint32_t maxX, uint32_t maxY);</synopsis>
-          <para>
-            The <function>drm_helper_probe_single_connector_modes</function> helper
-            function is a connector <methodname>fill_modes</methodname>
-            implementation that updates the connection status for the connector
-            and then retrieves a list of modes by calling the connector
-            <methodname>get_modes</methodname> helper operation.
-          </para>
-         <para>
-            If the helper operation returns no mode, and if the connector status
-            is connector_status_connected, standard VESA DMT modes up to
-            1024x768 are automatically added to the modes list by a call to
-            <function>drm_add_modes_noedid</function>.
-          </para>
-          <para>
-            The function then filters out modes larger than
-            <parameter>max_width</parameter> and <parameter>max_height</parameter>
-            if specified. It finally calls the optional connector
-            <methodname>mode_valid</methodname> helper operation for each mode in
-            the probed list to check whether the mode is valid for the connector.
-          </para>
-        </listitem>
-      </itemizedlist>
-    </sect2>
-    <sect2>
-      <title>CRTC Helper Operations</title>
+      <title>Legacy CRTC Helper Operations</title>
       <itemizedlist>
         <listitem id="drm-helper-crtc-mode-fixup">
           <synopsis>bool (*mode_fixup)(struct drm_crtc *crtc,
@@ -2051,198 +1794,6 @@ void intel_crt_init(struct drm_device *dev)
             connector_status_connected. There is no need to call
             <function>drm_add_edid_modes</function> manually in that case.
           </para>
-          <para>
-            When adding modes manually the driver creates each mode with a call to
-            <function>drm_mode_create</function> and must fill the following fields.
-            <itemizedlist>
-              <listitem>
-                <synopsis>__u32 type;</synopsis>
-                <para>
-                  Mode type bitmask, a combination of
-                  <variablelist>
-                    <varlistentry>
-                      <term>DRM_MODE_TYPE_BUILTIN</term>
-                      <listitem><para>not used?</para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_TYPE_CLOCK_C</term>
-                      <listitem><para>not used?</para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_TYPE_CRTC_C</term>
-                      <listitem><para>not used?</para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>
-        DRM_MODE_TYPE_PREFERRED - The preferred mode for the connector
-                      </term>
-                      <listitem>
-                        <para>not used?</para>
-                      </listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_TYPE_DEFAULT</term>
-                      <listitem><para>not used?</para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_TYPE_USERDEF</term>
-                      <listitem><para>not used?</para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_TYPE_DRIVER</term>
-                      <listitem>
-                        <para>
-                          The mode has been created by the driver (as opposed to
-                          to user-created modes).
-                        </para>
-                      </listitem>
-                    </varlistentry>
-                  </variablelist>
-                  Drivers must set the DRM_MODE_TYPE_DRIVER bit for all modes they
-                  create, and set the DRM_MODE_TYPE_PREFERRED bit for the preferred
-                  mode.
-                </para>
-              </listitem>
-              <listitem>
-                <synopsis>__u32 clock;</synopsis>
-                <para>Pixel clock frequency in kHz unit</para>
-              </listitem>
-              <listitem>
-                <synopsis>__u16 hdisplay, hsync_start, hsync_end, htotal;
-    __u16 vdisplay, vsync_start, vsync_end, vtotal;</synopsis>
-                <para>Horizontal and vertical timing information</para>
-                <screen><![CDATA[
-             Active                 Front           Sync           Back
-             Region                 Porch                          Porch
-    <-----------------------><----------------><-------------><-------------->
-
-      //////////////////////|
-     ////////////////////// |
-    //////////////////////  |..................               ................
-                                               _______________
-
-    <----- [hv]display ----->
-    <------------- [hv]sync_start ------------>
-    <--------------------- [hv]sync_end --------------------->
-    <-------------------------------- [hv]total ----------------------------->
-]]></screen>
-              </listitem>
-              <listitem>
-                <synopsis>__u16 hskew;
-    __u16 vscan;</synopsis>
-                <para>Unknown</para>
-              </listitem>
-              <listitem>
-                <synopsis>__u32 flags;</synopsis>
-                <para>
-                  Mode flags, a combination of
-                  <variablelist>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_PHSYNC</term>
-                      <listitem><para>
-                        Horizontal sync is active high
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_NHSYNC</term>
-                      <listitem><para>
-                        Horizontal sync is active low
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_PVSYNC</term>
-                      <listitem><para>
-                        Vertical sync is active high
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_NVSYNC</term>
-                      <listitem><para>
-                        Vertical sync is active low
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_INTERLACE</term>
-                      <listitem><para>
-                        Mode is interlaced
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_DBLSCAN</term>
-                      <listitem><para>
-                        Mode uses doublescan
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_CSYNC</term>
-                      <listitem><para>
-                        Mode uses composite sync
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_PCSYNC</term>
-                      <listitem><para>
-                        Composite sync is active high
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_NCSYNC</term>
-                      <listitem><para>
-                        Composite sync is active low
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_HSKEW</term>
-                      <listitem><para>
-                        hskew provided (not used?)
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_BCAST</term>
-                      <listitem><para>
-                        not used?
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_PIXMUX</term>
-                      <listitem><para>
-                        not used?
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_DBLCLK</term>
-                      <listitem><para>
-                        not used?
-                      </para></listitem>
-                    </varlistentry>
-                    <varlistentry>
-                      <term>DRM_MODE_FLAG_CLKDIV2</term>
-                      <listitem><para>
-                        ?
-                      </para></listitem>
-                    </varlistentry>
-                  </variablelist>
-                </para>
-                <para>
-                  Note that modes marked with the INTERLACE or DBLSCAN flags will be
-                  filtered out by
-                  <function>drm_helper_probe_single_connector_modes</function> if
-                  the connector's <structfield>interlace_allowed</structfield> or
-                  <structfield>doublescan_allowed</structfield> field is set to 0.
-                </para>
-              </listitem>
-              <listitem>
-                <synopsis>char name[DRM_DISPLAY_MODE_LEN];</synopsis>
-                <para>
-                  Mode name. The driver must call
-                  <function>drm_mode_set_name</function> to fill the mode name from
-                  <structfield>hdisplay</structfield>,
-                  <structfield>vdisplay</structfield> and interlace flag after
-                  filling the corresponding fields.
-                </para>
-              </listitem>
-            </itemizedlist>
-          </para>
           <para>
             The <structfield>vrefresh</structfield> value is computed by
             <function>drm_helper_probe_single_connector_modes</function>.
@@ -2303,8 +1854,12 @@ void intel_crt_init(struct drm_device *dev)
 !Edrivers/gpu/drm/drm_atomic_helper.c
     </sect2>
     <sect2>
-      <title>Modeset Helper Functions Reference</title>
-!Iinclude/drm/drm_crtc_helper.h
+      <title>Modeset Helper Reference for Common Vtables</title>
+!Iinclude/drm/drm_modeset_helper_vtables.h
+!Pinclude/drm/drm_modeset_helper_vtables.h overview
+    </sect2>
+    <sect2>
+      <title>Legacy CRTC/Modeset Helper Functions Reference</title>
 !Edrivers/gpu/drm/drm_crtc_helper.c
 !Pdrivers/gpu/drm/drm_crtc_helper.c overview
     </sect2>
index 64693f2..fe4a7a2 100644 (file)
@@ -1,3 +1,20 @@
+Device-Tree bindings for Samsung Exynos Embedded DisplayPort Transmitter(eDP)
+
+DisplayPort is industry standard to accommodate the growing board adoption
+of digital display technology within the PC and CE industries.
+It consolidates the internal and external connection methods to reduce device
+complexity and cost. It also supports necessary features for important cross
+industry applications and provides performance scalability to enable the next
+generation of displays that feature higher color depths, refresh rates, and
+display resolutions.
+
+eDP (embedded display port) device is compliant with Embedded DisplayPort
+standard as follows,
+- DisplayPort standard 1.1a for Exynos5250 and Exynos5260.
+- DisplayPort standard 1.3 for Exynos5422s and Exynos5800.
+
+eDP resides between FIMD and panel or FIMD and bridge such as LVDS.
+
 The Exynos display port interface should be configured based on
 the type of panel connected to it.
 
@@ -66,8 +83,15 @@ Optional properties for dp-controller:
                Hotplug detect GPIO.
                        Indicates which GPIO should be used for hotplug
                        detection
-       -video interfaces: Device node can contain video interface port
-                           nodes according to [1].
+Video interfaces:
+  Device node can contain video interface port nodes according to [1].
+  The following are properties specific to those nodes:
+
+  endpoint node connected to bridge or panel node:
+   - remote-endpoint: specifies the endpoint in panel or bridge node.
+                     This node is required in all kinds of exynos dp
+                     to represent the connection between dp and bridge
+                     or dp and panel.
 
 [1]: Documentation/devicetree/bindings/media/video-interfaces.txt
 
@@ -111,9 +135,18 @@ Board Specific portion:
                };
 
                ports {
-                       port@0 {
+                       port {
                                dp_out: endpoint {
-                                       remote-endpoint = <&bridge_in>;
+                                       remote-endpoint = <&dp_in>;
+                               };
+                       };
+               };
+
+               panel {
+                       ...
+                       port {
+                               dp_in: endpoint {
+                                       remote-endpoint = <&dp_out>;
                                };
                        };
                };
index f344b9e..e7423be 100644 (file)
@@ -14,17 +14,20 @@ Required properties:
 - clocks: device clocks
   See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details.
 - clock-names: the following clocks are required:
+  * "mdp_core_clk"
+  * "iface_clk"
   * "bus_clk"
-  * "byte_clk"
-  * "core_clk"
   * "core_mmss_clk"
-  * "iface_clk"
-  * "mdp_core_clk"
+  * "byte_clk"
   * "pixel_clk"
+  * "core_clk"
+  For DSIv2, we need an additional clock:
+   * "src_clk"
 - vdd-supply: phandle to vdd regulator device node
 - vddio-supply: phandle to vdd-io regulator device node
 - vdda-supply: phandle to vdda regulator device node
 - qcom,dsi-phy: phandle to DSI PHY device node
+- syscon-sfpb: A phandle to mmss_sfpb syscon node (only for DSIv2)
 
 Optional properties:
 - panel@0: Node of panel connected to this DSI controller.
@@ -51,6 +54,7 @@ Required properties:
   * "qcom,dsi-phy-28nm-hpm"
   * "qcom,dsi-phy-28nm-lp"
   * "qcom,dsi-phy-20nm"
+  * "qcom,dsi-phy-28nm-8960"
 - reg: Physical base address and length of the registers of PLL, PHY and PHY
   regulator
 - reg-names: The names of register regions. The following regions are required:
index 0833eda..a214f6c 100644 (file)
@@ -2,18 +2,28 @@ Qualcomm adreno/snapdragon display controller
 
 Required properties:
 - compatible:
-  * "qcom,mdp" - mdp4
+  * "qcom,mdp4" - mdp4
+  * "qcom,mdp5" - mdp5
 - reg: Physical base address and length of the controller's registers.
 - interrupts: The interrupt signal from the display controller.
 - connectors: array of phandles for output device(s)
 - clocks: device clocks
   See ../clocks/clock-bindings.txt for details.
-- clock-names: the following clocks are required:
-  * "core_clk"
-  * "iface_clk"
-  * "src_clk"
-  * "hdmi_clk"
-  * "mpd_clk"
+- clock-names: the following clocks are required.
+  For MDP4:
+   * "core_clk"
+   * "iface_clk"
+   * "lut_clk"
+   * "src_clk"
+   * "hdmi_clk"
+   * "mdp_clk"
+  For MDP5:
+   * "bus_clk"
+   * "iface_clk"
+   * "core_clk_src"
+   * "core_clk"
+   * "lut_clk" (some MDP5 versions may not need this)
+   * "vsync_clk"
 
 Optional properties:
 - gpus: phandle for gpu device
@@ -26,7 +36,7 @@ Example:
        ...
 
        mdp: qcom,mdp@5100000 {
-               compatible = "qcom,mdp";
+               compatible = "qcom,mdp4";
                reg = <0x05100000 0xf0000>;
                interrupts = <GIC_SPI 75 0>;
                connectors = <&hdmi>;
diff --git a/Documentation/devicetree/bindings/display/panel/boe,tv080wum-nl0.txt b/Documentation/devicetree/bindings/display/panel/boe,tv080wum-nl0.txt
new file mode 100644 (file)
index 0000000..50be5e2
--- /dev/null
@@ -0,0 +1,7 @@
+Boe Corporation 8.0" WUXGA TFT LCD panel
+
+Required properties:
+- compatible: should be "boe,tv080wum-nl0"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/display/panel/innolux,g121x1-l03.txt b/Documentation/devicetree/bindings/display/panel/innolux,g121x1-l03.txt
new file mode 100644 (file)
index 0000000..6497446
--- /dev/null
@@ -0,0 +1,7 @@
+Innolux Corporation 12.1" G121X1-L03 XGA (1024x768) TFT LCD panel
+
+Required properties:
+- compatible: should be "innolux,g121x1-l03"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/display/panel/kyo,tcg121xglp.txt b/Documentation/devicetree/bindings/display/panel/kyo,tcg121xglp.txt
new file mode 100644 (file)
index 0000000..a8e940f
--- /dev/null
@@ -0,0 +1,7 @@
+Kyocera Corporation 12.1" XGA (1024x768) TFT LCD panel
+
+Required properties:
+- compatible: should be "kyo,tcg121xglp"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/display/panel/sharp,ls043t1le01.txt b/Documentation/devicetree/bindings/display/panel/sharp,ls043t1le01.txt
new file mode 100644 (file)
index 0000000..3770a11
--- /dev/null
@@ -0,0 +1,22 @@
+Sharp Microelectronics 4.3" qHD TFT LCD panel
+
+Required properties:
+- compatible: should be "sharp,ls043t1le01-qhd"
+- reg: DSI virtual channel of the peripheral
+- power-supply: phandle of the regulator that provides the supply voltage
+
+Optional properties:
+- backlight: phandle of the backlight device attached to the panel
+- reset-gpios: a GPIO spec for the reset pin
+
+Example:
+
+       mdss_dsi@fd922800 {
+               panel@0 {
+                       compatible = "sharp,ls043t1le01-qhd";
+                       reg = <0>;
+                       avdd-supply = <&pm8941_l22>;
+                       backlight = <&pm8941_wled>;
+                       reset-gpios = <&pm8941_gpios 19 GPIO_ACTIVE_HIGH>;
+               };
+       };
index 0604d42..5fe9372 100644 (file)
@@ -7,6 +7,10 @@ Required properties:
 - reg: should contain G-Scaler physical address location and length.
 - interrupts: should contain G-Scaler interrupt number
 
+Optional properties:
+- samsung,sysreg: handle to syscon used to control the system registers to
+  set writeback input and destination
+
 Example:
 
 gsc_0:  gsc@0x13e00000 {
diff --git a/Documentation/devicetree/bindings/panel/panasonic,vvx10f034n00.txt b/Documentation/devicetree/bindings/panel/panasonic,vvx10f034n00.txt
new file mode 100644 (file)
index 0000000..37dedf6
--- /dev/null
@@ -0,0 +1,20 @@
+Panasonic 10" WUXGA TFT LCD panel
+
+Required properties:
+- compatible: should be "panasonic,vvx10f034n00"
+- reg: DSI virtual channel of the peripheral
+- power-supply: phandle of the regulator that provides the supply voltage
+
+Optional properties:
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+       mdss_dsi@fd922800 {
+               panel@0 {
+                       compatible = "panasonic,vvx10f034n00";
+                       reg = <0>;
+                       power-supply = <&vreg_vsp>;
+                       backlight = <&lp8566_wled>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/panel/qiaodian,qd43003c0-40.txt b/Documentation/devicetree/bindings/panel/qiaodian,qd43003c0-40.txt
new file mode 100644 (file)
index 0000000..0fbdab8
--- /dev/null
@@ -0,0 +1,7 @@
+QiaoDian XianShi Corporation 4"3 TFT LCD panel
+
+Required properties:
+- compatible: should be "qiaodian,qd43003c0-40"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
index 55df1d4..6b0fc4f 100644 (file)
@@ -33,6 +33,7 @@ auo   AU Optronics Corporation
 avago  Avago Technologies
 avic   Shanghai AVIC Optoelectronics Co., Ltd.
 axis   Axis Communications AB
+boe    BOE Technology Group Co., Ltd.
 bosch  Bosch Sensortec GmbH
 boundary       Boundary Devices Inc.
 brcm   Broadcom Corporation
@@ -123,6 +124,7 @@ jedec       JEDEC Solid State Technology Association
 karo   Ka-Ro electronics GmbH
 keymile        Keymile GmbH
 kinetic Kinetic Technologies
+kyo    Kyocera Corporation
 lacie  LaCie
 lantiq Lantiq Semiconductor
 lenovo Lenovo Group Ltd.
@@ -180,6 +182,7 @@ qca Qualcomm Atheros, Inc.
 qcom   Qualcomm Technologies, Inc
 qemu   QEMU, a generic and open source machine emulator and virtualizer
 qi     Qi Hardware
+qiaodian       QiaoDian XianShi Corporation
 qnap   QNAP Systems, Inc.
 radxa  Radxa
 raidsonic      RaidSonic Technology GmbH
index 49a4f43..1cc2e95 100644 (file)
                compatible = "auo,b133htn01";
                power-supply = <&tps65090_fet6>;
                backlight = <&backlight>;
+
+               port {
+                       panel_in: endpoint {
+                               remote-endpoint = <&dp_out>;
+                       };
+               };
        };
 
        mmc1_pwrseq: mmc1_pwrseq {
        samsung,link-rate = <0x0a>;
        samsung,lane-count = <2>;
        samsung,hpd-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>;
-       panel = <&panel>;
+
+       ports {
+               port {
+                       dp_out: endpoint {
+                               remote-endpoint = <&panel_in>;
+                       };
+               };
+       };
 };
 
 &fimd {
index a53d756..fdc1be8 100644 (file)
@@ -35,6 +35,7 @@
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_fixed.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
index 4dcc8fb..093599a 100644 (file)
@@ -3729,7 +3729,7 @@ static void dce_v10_0_encoder_add(struct amdgpu_device *adev,
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
                drm_encoder_init(dev, encoder, &dce_v10_0_encoder_funcs,
-                                DRM_MODE_ENCODER_DAC);
+                                DRM_MODE_ENCODER_DAC, NULL);
                drm_encoder_helper_add(encoder, &dce_v10_0_dac_helper_funcs);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
@@ -3740,15 +3740,15 @@ static void dce_v10_0_encoder_add(struct amdgpu_device *adev,
                if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
                        amdgpu_encoder->rmx_type = RMX_FULL;
                        drm_encoder_init(dev, encoder, &dce_v10_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_LVDS);
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_lcd_info(amdgpu_encoder);
                } else if (amdgpu_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) {
                        drm_encoder_init(dev, encoder, &dce_v10_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_DAC);
+                                        DRM_MODE_ENCODER_DAC, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_dig_info(amdgpu_encoder);
                } else {
                        drm_encoder_init(dev, encoder, &dce_v10_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_TMDS);
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_dig_info(amdgpu_encoder);
                }
                drm_encoder_helper_add(encoder, &dce_v10_0_dig_helper_funcs);
@@ -3766,13 +3766,13 @@ static void dce_v10_0_encoder_add(struct amdgpu_device *adev,
                amdgpu_encoder->is_ext_encoder = true;
                if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
                        drm_encoder_init(dev, encoder, &dce_v10_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_LVDS);
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                else if (amdgpu_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT))
                        drm_encoder_init(dev, encoder, &dce_v10_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_DAC);
+                                        DRM_MODE_ENCODER_DAC, NULL);
                else
                        drm_encoder_init(dev, encoder, &dce_v10_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_TMDS);
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                drm_encoder_helper_add(encoder, &dce_v10_0_ext_helper_funcs);
                break;
        }
index 8f1e511..8701661 100644 (file)
@@ -3722,7 +3722,7 @@ static void dce_v11_0_encoder_add(struct amdgpu_device *adev,
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
                drm_encoder_init(dev, encoder, &dce_v11_0_encoder_funcs,
-                                DRM_MODE_ENCODER_DAC);
+                                DRM_MODE_ENCODER_DAC, NULL);
                drm_encoder_helper_add(encoder, &dce_v11_0_dac_helper_funcs);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
@@ -3733,15 +3733,15 @@ static void dce_v11_0_encoder_add(struct amdgpu_device *adev,
                if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
                        amdgpu_encoder->rmx_type = RMX_FULL;
                        drm_encoder_init(dev, encoder, &dce_v11_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_LVDS);
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_lcd_info(amdgpu_encoder);
                } else if (amdgpu_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) {
                        drm_encoder_init(dev, encoder, &dce_v11_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_DAC);
+                                        DRM_MODE_ENCODER_DAC, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_dig_info(amdgpu_encoder);
                } else {
                        drm_encoder_init(dev, encoder, &dce_v11_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_TMDS);
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_dig_info(amdgpu_encoder);
                }
                drm_encoder_helper_add(encoder, &dce_v11_0_dig_helper_funcs);
@@ -3759,13 +3759,13 @@ static void dce_v11_0_encoder_add(struct amdgpu_device *adev,
                amdgpu_encoder->is_ext_encoder = true;
                if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
                        drm_encoder_init(dev, encoder, &dce_v11_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_LVDS);
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                else if (amdgpu_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT))
                        drm_encoder_init(dev, encoder, &dce_v11_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_DAC);
+                                        DRM_MODE_ENCODER_DAC, NULL);
                else
                        drm_encoder_init(dev, encoder, &dce_v11_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_TMDS);
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                drm_encoder_helper_add(encoder, &dce_v11_0_ext_helper_funcs);
                break;
        }
index 42d954d..d0e128c 100644 (file)
@@ -3659,7 +3659,7 @@ static void dce_v8_0_encoder_add(struct amdgpu_device *adev,
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
                drm_encoder_init(dev, encoder, &dce_v8_0_encoder_funcs,
-                                DRM_MODE_ENCODER_DAC);
+                                DRM_MODE_ENCODER_DAC, NULL);
                drm_encoder_helper_add(encoder, &dce_v8_0_dac_helper_funcs);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
@@ -3670,15 +3670,15 @@ static void dce_v8_0_encoder_add(struct amdgpu_device *adev,
                if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
                        amdgpu_encoder->rmx_type = RMX_FULL;
                        drm_encoder_init(dev, encoder, &dce_v8_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_LVDS);
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_lcd_info(amdgpu_encoder);
                } else if (amdgpu_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) {
                        drm_encoder_init(dev, encoder, &dce_v8_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_DAC);
+                                        DRM_MODE_ENCODER_DAC, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_dig_info(amdgpu_encoder);
                } else {
                        drm_encoder_init(dev, encoder, &dce_v8_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_TMDS);
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                        amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_dig_info(amdgpu_encoder);
                }
                drm_encoder_helper_add(encoder, &dce_v8_0_dig_helper_funcs);
@@ -3696,13 +3696,13 @@ static void dce_v8_0_encoder_add(struct amdgpu_device *adev,
                amdgpu_encoder->is_ext_encoder = true;
                if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
                        drm_encoder_init(dev, encoder, &dce_v8_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_LVDS);
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                else if (amdgpu_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT))
                        drm_encoder_init(dev, encoder, &dce_v8_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_DAC);
+                                        DRM_MODE_ENCODER_DAC, NULL);
                else
                        drm_encoder_init(dev, encoder, &dce_v8_0_encoder_funcs,
-                                        DRM_MODE_ENCODER_TMDS);
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                drm_encoder_helper_add(encoder, &dce_v8_0_ext_helper_funcs);
                break;
        }
index cebcab5..9bdc28c 100644 (file)
@@ -1216,14 +1216,14 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
                                       &armada_primary_plane_funcs,
                                       armada_primary_formats,
                                       ARRAY_SIZE(armada_primary_formats),
-                                      DRM_PLANE_TYPE_PRIMARY);
+                                      DRM_PLANE_TYPE_PRIMARY, NULL);
        if (ret) {
                kfree(primary);
                return ret;
        }
 
        ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, &primary->base, NULL,
-                                       &armada_crtc_funcs);
+                                       &armada_crtc_funcs, NULL);
        if (ret)
                goto err_crtc_init;
 
index 5c22b38..148e8a4 100644 (file)
@@ -460,7 +460,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
                                       &armada_ovl_plane_funcs,
                                       armada_ovl_formats,
                                       ARRAY_SIZE(armada_ovl_formats),
-                                      DRM_PLANE_TYPE_OVERLAY);
+                                      DRM_PLANE_TYPE_OVERLAY, NULL);
        if (ret) {
                kfree(dplane);
                return ret;
index 69d19f3..0123458 100644 (file)
@@ -751,7 +751,7 @@ static int ast_encoder_init(struct drm_device *dev)
                return -ENOMEM;
 
        drm_encoder_init(dev, &ast_encoder->base, &ast_enc_funcs,
-                        DRM_MODE_ENCODER_DAC);
+                        DRM_MODE_ENCODER_DAC, NULL);
        drm_encoder_helper_add(&ast_encoder->base, &ast_enc_helper_funcs);
 
        ast_encoder->base.possible_crtcs = 1;
index 9f6e234..468a14f 100644 (file)
@@ -344,7 +344,7 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
        ret = drm_crtc_init_with_planes(dev, &crtc->base,
                                &planes->primary->base,
                                planes->cursor ? &planes->cursor->base : NULL,
-                               &atmel_hlcdc_crtc_funcs);
+                               &atmel_hlcdc_crtc_funcs, NULL);
        if (ret < 0)
                goto fail;
 
index 067e4c1..d112900 100644 (file)
@@ -256,7 +256,7 @@ static int atmel_hlcdc_create_panel_output(struct drm_device *dev,
                               &atmel_hlcdc_panel_encoder_helper_funcs);
        ret = drm_encoder_init(dev, &panel->base.encoder,
                               &atmel_hlcdc_panel_encoder_funcs,
-                              DRM_MODE_ENCODER_LVDS);
+                              DRM_MODE_ENCODER_LVDS, NULL);
        if (ret)
                return ret;
 
index d0299ae..1ffe9c3 100644 (file)
@@ -941,7 +941,7 @@ atmel_hlcdc_plane_create(struct drm_device *dev,
        ret = drm_universal_plane_init(dev, &plane->base, 0,
                                       &layer_plane_funcs,
                                       desc->formats->formats,
-                                      desc->formats->nformats, type);
+                                      desc->formats->nformats, type, NULL);
        if (ret)
                return ERR_PTR(ret);
 
index 26bcd03..a88be6d 100644 (file)
@@ -196,7 +196,7 @@ static void bochs_encoder_init(struct drm_device *dev)
 
        encoder->possible_crtcs = 0x1;
        drm_encoder_init(dev, encoder, &bochs_encoder_encoder_funcs,
-                        DRM_MODE_ENCODER_DAC);
+                        DRM_MODE_ENCODER_DAC, NULL);
        drm_encoder_helper_add(encoder, &bochs_encoder_helper_funcs);
 }
 
index 6dddd39..27e2022 100644 (file)
@@ -22,7 +22,6 @@ config DRM_DW_HDMI_AHB_AUDIO
          Designware HDMI block.  This is used in conjunction with
          the i.MX6 HDMI driver.
 
-
 config DRM_NXP_PTN3460
        tristate "NXP PTN3460 DP/LVDS bridge"
        depends on OF
index d4e28be..f13c33d 100644 (file)
@@ -1,6 +1,6 @@
 ccflags-y := -Iinclude/drm
 
-obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
-obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
diff --git a/drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c
new file mode 100644 (file)
index 0000000..122bb01
--- /dev/null
@@ -0,0 +1,653 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written and tested against the Designware HDMI Tx found in iMX6.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_edid.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw-hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+       HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+       HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+       HDMI_AHB_DMA_START_START = BIT(0),
+       HDMI_AHB_DMA_STOP_STOP = BIT(0),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+       HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+       HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+       HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+       HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+       HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+       HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+       HDMI_IH_AHBDMAAUD_STAT0_ALL =
+               HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+               HDMI_IH_AHBDMAAUD_STAT0_LOST |
+               HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+               HDMI_IH_AHBDMAAUD_STAT0_DONE |
+               HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+               HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+       HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+       HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+       HDMI_AHB_DMA_CONF0_INCR4 = 0,
+       HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+       HDMI_AHB_DMA_MASK_DONE = BIT(7),
+
+       HDMI_REVISION_ID = 0x0001,
+       HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+       HDMI_FC_AUDICONF2 = 0x1027,
+       HDMI_FC_AUDSCONF = 0x1063,
+       HDMI_FC_AUDSCONF_LAYOUT1 = 1 << 0,
+       HDMI_FC_AUDSCONF_LAYOUT0 = 0 << 0,
+       HDMI_AHB_DMA_CONF0 = 0x3600,
+       HDMI_AHB_DMA_START = 0x3601,
+       HDMI_AHB_DMA_STOP = 0x3602,
+       HDMI_AHB_DMA_THRSLD = 0x3603,
+       HDMI_AHB_DMA_STRADDR0 = 0x3604,
+       HDMI_AHB_DMA_STPADDR0 = 0x3608,
+       HDMI_AHB_DMA_MASK = 0x3614,
+       HDMI_AHB_DMA_POL = 0x3615,
+       HDMI_AHB_DMA_CONF1 = 0x3616,
+       HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct dw_hdmi_channel_conf {
+       u8 conf1;
+       u8 ca;
+};
+
+/*
+ * The default mapping of ALSA channels to HDMI channels and speaker
+ * allocation bits.  Note that we can't do channel remapping here -
+ * channels must be in the same order.
+ *
+ * Mappings for alsa-lib pcm/surround*.conf files:
+ *
+ *             Front   Sur4.0  Sur4.1  Sur5.0  Sur5.1  Sur7.1
+ * Channels    2       4       6       6       6       8
+ *
+ * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
+ *
+ *                             Number of ALSA channels
+ * ALSA Channel        2       3       4       5       6       7       8
+ * 0           FL:0    =       =       =       =       =       =
+ * 1           FR:1    =       =       =       =       =       =
+ * 2                   FC:3    RL:4    LFE:2   =       =       =
+ * 3                           RR:5    RL:4    FC:3    =       =
+ * 4                                   RR:5    RL:4    =       =
+ * 5                                           RR:5    =       =
+ * 6                                                   RC:6    =
+ * 7                                                   RLC/FRC RLC/FRC
+ */
+static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
+       { 0x03, 0x00 }, /* FL,FR */
+       { 0x0b, 0x02 }, /* FL,FR,FC */
+       { 0x33, 0x08 }, /* FL,FR,RL,RR */
+       { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
+       { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
+       { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
+       { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
+};
+
+struct snd_dw_hdmi {
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       spinlock_t lock;
+       struct dw_hdmi_audio_data data;
+       struct snd_pcm_substream *substream;
+       void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+       void *buf_src;
+       void *buf_dst;
+       dma_addr_t buf_addr;
+       unsigned buf_offset;
+       unsigned buf_period;
+       unsigned buf_size;
+       unsigned channels;
+       u8 revision;
+       u8 iec_offset;
+       u8 cs[192][8];
+};
+
+static void dw_hdmi_writel(u32 val, void __iomem *ptr)
+{
+       writeb_relaxed(val, ptr);
+       writeb_relaxed(val >> 8, ptr + 1);
+       writeb_relaxed(val >> 16, ptr + 2);
+       writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+       size_t offset, size_t bytes)
+{
+       u32 *src = dw->buf_src + offset;
+       u32 *dst = dw->buf_dst + offset;
+       u32 *end = dw->buf_src + offset + bytes;
+
+       do {
+               u32 b, sample = *src++;
+
+               b = (sample & 8) << (28 - 3);
+
+               sample >>= 4;
+
+               *dst++ = sample | b;
+       } while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+       sample ^= sample >> 16;
+       sample ^= sample >> 8;
+       sample ^= sample >> 4;
+       sample ^= sample >> 2;
+       sample ^= sample >> 1;
+       return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+       size_t offset, size_t bytes)
+{
+       u32 *src = dw->buf_src + offset;
+       u32 *dst = dw->buf_dst + offset;
+       u32 *end = dw->buf_src + offset + bytes;
+
+       do {
+               unsigned i;
+               u8 *cs;
+
+               cs = dw->cs[dw->iec_offset++];
+               if (dw->iec_offset >= 192)
+                       dw->iec_offset = 0;
+
+               i = dw->channels;
+               do {
+                       u32 sample = *src++;
+
+                       sample &= ~0xff000000;
+                       sample |= *cs++ << 24;
+                       sample |= parity(sample & ~0xf8000000);
+
+                       *dst++ = sample;
+               } while (--i);
+       } while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+       struct snd_pcm_runtime *runtime)
+{
+       u8 cs[4];
+       unsigned ch, i, j;
+
+       snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+       memset(dw->cs, 0, sizeof(dw->cs));
+
+       for (ch = 0; ch < 8; ch++) {
+               cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+               cs[2] |= (ch + 1) << 4;
+
+               for (i = 0; i < ARRAY_SIZE(cs); i++) {
+                       unsigned c = cs[i];
+
+                       for (j = 0; j < 8; j++, c >>= 1)
+                               dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+               }
+       }
+       dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+       void __iomem *base = dw->data.base;
+       unsigned offset = dw->buf_offset;
+       unsigned period = dw->buf_period;
+       u32 start, stop;
+
+       dw->reformat(dw, offset, period);
+
+       /* Clear all irqs before enabling irqs and starting DMA */
+       writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+                      base + HDMI_IH_AHBDMAAUD_STAT0);
+
+       start = dw->buf_addr + offset;
+       stop = start + period - 1;
+
+       /* Setup the hardware start/stop addresses */
+       dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+       dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+       writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+       writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+       offset += period;
+       if (offset >= dw->buf_size)
+               offset = 0;
+       dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+       /* Disable interrupts before disabling DMA */
+       writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+       writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+       struct snd_dw_hdmi *dw = data;
+       struct snd_pcm_substream *substream;
+       unsigned stat;
+
+       stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+       if (!stat)
+               return IRQ_NONE;
+
+       writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+       substream = dw->substream;
+       if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+               snd_pcm_period_elapsed(substream);
+
+               spin_lock(&dw->lock);
+               if (dw->substream)
+                       dw_hdmi_start_dma(dw);
+               spin_unlock(&dw->lock);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID,
+       .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+                  SNDRV_PCM_FMTBIT_S24_LE,
+       .rates = SNDRV_PCM_RATE_32000 |
+                SNDRV_PCM_RATE_44100 |
+                SNDRV_PCM_RATE_48000 |
+                SNDRV_PCM_RATE_88200 |
+                SNDRV_PCM_RATE_96000 |
+                SNDRV_PCM_RATE_176400 |
+                SNDRV_PCM_RATE_192000,
+       .channels_min = 2,
+       .channels_max = 8,
+       .buffer_bytes_max = 1024 * 1024,
+       .period_bytes_min = 256,
+       .period_bytes_max = 8192,       /* ERR004323: must limit to 8k */
+       .periods_min = 2,
+       .periods_max = 16,
+       .fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_dw_hdmi *dw = substream->private_data;
+       void __iomem *base = dw->data.base;
+       int ret;
+
+       runtime->hw = dw_hdmi_hw;
+
+       ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_pcm_limit_hw_rates(runtime);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               return ret;
+
+       /* Limit the buffer size to the size of the preallocated buffer */
+       ret = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                                          0, substream->dma_buffer.bytes);
+       if (ret < 0)
+               return ret;
+
+       /* Clear FIFO */
+       writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+                      base + HDMI_AHB_DMA_CONF0);
+
+       /* Configure interrupt polarities */
+       writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+       writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+       /* Keep interrupts masked, and clear any pending */
+       writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+       writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+       ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+                         "dw-hdmi-audio", dw);
+       if (ret)
+               return ret;
+
+       /* Un-mute done interrupt */
+       writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+                      ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+                      base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+       return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+       struct snd_dw_hdmi *dw = substream->private_data;
+
+       /* Mute all interrupts */
+       writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+                      dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+       free_irq(dw->data.irq, dw);
+
+       return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       /* Allocate the PCM runtime buffer, which is exposed to userspace. */
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_dw_hdmi *dw = substream->private_data;
+       u8 threshold, conf0, conf1, layout, ca;
+
+       /* Setup as per 3.0.5 FSL 4.1.0 BSP */
+       switch (dw->revision) {
+       case 0x0a:
+               conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+                       HDMI_AHB_DMA_CONF0_INCR4;
+               if (runtime->channels == 2)
+                       threshold = 126;
+               else
+                       threshold = 124;
+               break;
+       case 0x1a:
+               conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+                       HDMI_AHB_DMA_CONF0_INCR8;
+               threshold = 128;
+               break;
+       default:
+               /* NOTREACHED */
+               return -EINVAL;
+       }
+
+       dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+       /* Minimum number of bytes in the fifo. */
+       runtime->hw.fifo_size = threshold * 32;
+
+       conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+       conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
+       ca = default_hdmi_channel_config[runtime->channels - 2].ca;
+
+       /*
+        * For >2 channel PCM audio, we need to select layout 1
+        * and set an appropriate channel map.
+        */
+       if (runtime->channels > 2)
+               layout = HDMI_FC_AUDSCONF_LAYOUT1;
+       else
+               layout = HDMI_FC_AUDSCONF_LAYOUT0;
+
+       writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+       writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+       writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+       writeb_relaxed(layout, dw->data.base + HDMI_FC_AUDSCONF);
+       writeb_relaxed(ca, dw->data.base + HDMI_FC_AUDICONF2);
+
+       switch (runtime->format) {
+       case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+               dw->reformat = dw_hdmi_reformat_iec958;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               dw_hdmi_create_cs(dw, runtime);
+               dw->reformat = dw_hdmi_reformat_s24;
+               break;
+       }
+       dw->iec_offset = 0;
+       dw->channels = runtime->channels;
+       dw->buf_src  = runtime->dma_area;
+       dw->buf_dst  = substream->dma_buffer.area;
+       dw->buf_addr = substream->dma_buffer.addr;
+       dw->buf_period = snd_pcm_lib_period_bytes(substream);
+       dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+       return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_dw_hdmi *dw = substream->private_data;
+       unsigned long flags;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               spin_lock_irqsave(&dw->lock, flags);
+               dw->buf_offset = 0;
+               dw->substream = substream;
+               dw_hdmi_start_dma(dw);
+               dw_hdmi_audio_enable(dw->data.hdmi);
+               spin_unlock_irqrestore(&dw->lock, flags);
+               substream->runtime->delay = substream->runtime->period_size;
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+               spin_lock_irqsave(&dw->lock, flags);
+               dw->substream = NULL;
+               dw_hdmi_stop_dma(dw);
+               dw_hdmi_audio_disable(dw->data.hdmi);
+               spin_unlock_irqrestore(&dw->lock, flags);
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_dw_hdmi *dw = substream->private_data;
+
+       /*
+        * We are unable to report the exact hardware position as
+        * reading the 32-bit DMA position using 8-bit reads is racy.
+        */
+       return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+       .open = dw_hdmi_open,
+       .close = dw_hdmi_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = dw_hdmi_hw_params,
+       .hw_free = dw_hdmi_hw_free,
+       .prepare = dw_hdmi_prepare,
+       .trigger = dw_hdmi_trigger,
+       .pointer = dw_hdmi_pointer,
+       .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+       const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+       struct device *dev = pdev->dev.parent;
+       struct snd_dw_hdmi *dw;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       unsigned revision;
+       int ret;
+
+       writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+                      data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+       revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+       if (revision != 0x0a && revision != 0x1a) {
+               dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+                       revision);
+               return -ENXIO;
+       }
+
+       ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+                             THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+       if (ret < 0)
+               return ret;
+
+       strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+       strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+       snprintf(card->longname, sizeof(card->longname),
+                "%s rev 0x%02x, irq %d", card->shortname, revision,
+                data->irq);
+
+       dw = card->private_data;
+       dw->card = card;
+       dw->data = *data;
+       dw->revision = revision;
+
+       spin_lock_init(&dw->lock);
+
+       ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+       if (ret < 0)
+               goto err;
+
+       dw->pcm = pcm;
+       pcm->private_data = dw;
+       strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+       /*
+        * To support 8-channel 96kHz audio reliably, we need 512k
+        * to satisfy alsa with our restricted period (ERR004323).
+        */
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                       dev, 128 * 1024, 1024 * 1024);
+
+       ret = snd_card_register(card);
+       if (ret < 0)
+               goto err;
+
+       platform_set_drvdata(pdev, dw);
+
+       return 0;
+
+err:
+       snd_card_free(card);
+       return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+       struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+       snd_card_free(dw->card);
+
+       return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
+/*
+ * This code is fine, but requires implementation in the dw_hdmi_trigger()
+ * method which is currently missing as I have no way to test this.
+ */
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+       struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+       snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+       snd_pcm_suspend_all(dw->pcm);
+
+       return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+       struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+       snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+                        snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+       .probe  = snd_dw_hdmi_probe,
+       .remove = snd_dw_hdmi_remove,
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .pm = PM_OPS,
+       },
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/dw-hdmi-audio.h
new file mode 100644 (file)
index 0000000..91f631b
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct dw_hdmi;
+
+struct dw_hdmi_audio_data {
+       phys_addr_t phys;
+       void __iomem *base;
+       int irq;
+       struct dw_hdmi *hdmi;
+       u8 *eld;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
new file mode 100644 (file)
index 0000000..298018a
--- /dev/null
@@ -0,0 +1,1849 @@
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Designware High-Definition Multimedia Interface (HDMI) driver
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ */
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/hdmi.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/spinlock.h>
+
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include "dw-hdmi.h"
+#include "dw-hdmi-audio.h"
+
+#define HDMI_EDID_LEN          512
+
+#define RGB                    0
+#define YCBCR444               1
+#define YCBCR422_16BITS                2
+#define YCBCR422_8BITS         3
+#define XVYCC444               4
+
+enum hdmi_datamap {
+       RGB444_8B = 0x01,
+       RGB444_10B = 0x03,
+       RGB444_12B = 0x05,
+       RGB444_16B = 0x07,
+       YCbCr444_8B = 0x09,
+       YCbCr444_10B = 0x0B,
+       YCbCr444_12B = 0x0D,
+       YCbCr444_16B = 0x0F,
+       YCbCr422_8B = 0x16,
+       YCbCr422_10B = 0x14,
+       YCbCr422_12B = 0x12,
+};
+
+static const u16 csc_coeff_default[3][4] = {
+       { 0x2000, 0x0000, 0x0000, 0x0000 },
+       { 0x0000, 0x2000, 0x0000, 0x0000 },
+       { 0x0000, 0x0000, 0x2000, 0x0000 }
+};
+
+static const u16 csc_coeff_rgb_out_eitu601[3][4] = {
+       { 0x2000, 0x6926, 0x74fd, 0x010e },
+       { 0x2000, 0x2cdd, 0x0000, 0x7e9a },
+       { 0x2000, 0x0000, 0x38b4, 0x7e3b }
+};
+
+static const u16 csc_coeff_rgb_out_eitu709[3][4] = {
+       { 0x2000, 0x7106, 0x7a02, 0x00a7 },
+       { 0x2000, 0x3264, 0x0000, 0x7e6d },
+       { 0x2000, 0x0000, 0x3b61, 0x7e25 }
+};
+
+static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
+       { 0x2591, 0x1322, 0x074b, 0x0000 },
+       { 0x6535, 0x2000, 0x7acc, 0x0200 },
+       { 0x6acd, 0x7534, 0x2000, 0x0200 }
+};
+
+static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
+       { 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
+       { 0x62f0, 0x2000, 0x7d11, 0x0200 },
+       { 0x6756, 0x78ab, 0x2000, 0x0200 }
+};
+
+struct hdmi_vmode {
+       bool mdataenablepolarity;
+
+       unsigned int mpixelclock;
+       unsigned int mpixelrepetitioninput;
+       unsigned int mpixelrepetitionoutput;
+};
+
+struct hdmi_data_info {
+       unsigned int enc_in_format;
+       unsigned int enc_out_format;
+       unsigned int enc_color_depth;
+       unsigned int colorimetry;
+       unsigned int pix_repet_factor;
+       unsigned int hdcp_enable;
+       struct hdmi_vmode video_mode;
+};
+
+struct dw_hdmi {
+       struct drm_connector connector;
+       struct drm_encoder *encoder;
+       struct drm_bridge *bridge;
+
+       struct platform_device *audio;
+       enum dw_hdmi_devtype dev_type;
+       struct device *dev;
+       struct clk *isfr_clk;
+       struct clk *iahb_clk;
+
+       struct hdmi_data_info hdmi_data;
+       const struct dw_hdmi_plat_data *plat_data;
+
+       int vic;
+
+       u8 edid[HDMI_EDID_LEN];
+       bool cable_plugin;
+
+       bool phy_enabled;
+       struct drm_display_mode previous_mode;
+
+       struct i2c_adapter *ddc;
+       void __iomem *regs;
+       bool sink_is_hdmi;
+       bool sink_has_audio;
+
+       struct mutex mutex;             /* for state below and previous_mode */
+       enum drm_connector_force force; /* mutex-protected force state */
+       bool disabled;                  /* DRM has disabled our bridge */
+       bool bridge_is_on;              /* indicates the bridge is on */
+       bool rxsense;                   /* rxsense state */
+       u8 phy_mask;                    /* desired phy int mask settings */
+
+       spinlock_t audio_lock;
+       struct mutex audio_mutex;
+       unsigned int sample_rate;
+       unsigned int audio_cts;
+       unsigned int audio_n;
+       bool audio_enable;
+
+       void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
+       u8 (*read)(struct dw_hdmi *hdmi, int offset);
+};
+
+#define HDMI_IH_PHY_STAT0_RX_SENSE \
+       (HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
+        HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
+
+#define HDMI_PHY_RX_SENSE \
+       (HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | \
+        HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3)
+
+static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset)
+{
+       writel(val, hdmi->regs + (offset << 2));
+}
+
+static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset)
+{
+       return readl(hdmi->regs + (offset << 2));
+}
+
+static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
+{
+       writeb(val, hdmi->regs + offset);
+}
+
+static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset)
+{
+       return readb(hdmi->regs + offset);
+}
+
+static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
+{
+       hdmi->write(hdmi, val, offset);
+}
+
+static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
+{
+       return hdmi->read(hdmi, offset);
+}
+
+static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
+{
+       u8 val = hdmi_readb(hdmi, reg) & ~mask;
+
+       val |= data & mask;
+       hdmi_writeb(hdmi, val, reg);
+}
+
+static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
+                            u8 shift, u8 mask)
+{
+       hdmi_modb(hdmi, data << shift, mask, reg);
+}
+
+static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
+                          unsigned int n)
+{
+       /* Must be set/cleared first */
+       hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+
+       /* nshift factor = 0 */
+       hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
+
+       hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+                   HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+       hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
+       hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
+
+       hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3);
+       hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
+       hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
+}
+
+static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
+{
+       unsigned int n = (128 * freq) / 1000;
+       unsigned int mult = 1;
+
+       while (freq > 48000) {
+               mult *= 2;
+               freq /= 2;
+       }
+
+       switch (freq) {
+       case 32000:
+               if (pixel_clk == 25175000)
+                       n = 4576;
+               else if (pixel_clk == 27027000)
+                       n = 4096;
+               else if (pixel_clk == 74176000 || pixel_clk == 148352000)
+                       n = 11648;
+               else
+                       n = 4096;
+               n *= mult;
+               break;
+
+       case 44100:
+               if (pixel_clk == 25175000)
+                       n = 7007;
+               else if (pixel_clk == 74176000)
+                       n = 17836;
+               else if (pixel_clk == 148352000)
+                       n = 8918;
+               else
+                       n = 6272;
+               n *= mult;
+               break;
+
+       case 48000:
+               if (pixel_clk == 25175000)
+                       n = 6864;
+               else if (pixel_clk == 27027000)
+                       n = 6144;
+               else if (pixel_clk == 74176000)
+                       n = 11648;
+               else if (pixel_clk == 148352000)
+                       n = 5824;
+               else
+                       n = 6144;
+               n *= mult;
+               break;
+
+       default:
+               break;
+       }
+
+       return n;
+}
+
+static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
+       unsigned long pixel_clk, unsigned int sample_rate)
+{
+       unsigned long ftdms = pixel_clk;
+       unsigned int n, cts;
+       u64 tmp;
+
+       n = hdmi_compute_n(sample_rate, pixel_clk);
+
+       /*
+        * Compute the CTS value from the N value.  Note that CTS and N
+        * can be up to 20 bits in total, so we need 64-bit math.  Also
+        * note that our TDMS clock is not fully accurate; it is accurate
+        * to kHz.  This can introduce an unnecessary remainder in the
+        * calculation below, so we don't try to warn about that.
+        */
+       tmp = (u64)ftdms * n;
+       do_div(tmp, 128 * sample_rate);
+       cts = tmp;
+
+       dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
+               __func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000,
+               n, cts);
+
+       spin_lock_irq(&hdmi->audio_lock);
+       hdmi->audio_n = n;
+       hdmi->audio_cts = cts;
+       hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
+       spin_unlock_irq(&hdmi->audio_lock);
+}
+
+static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
+{
+       mutex_lock(&hdmi->audio_mutex);
+       hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate);
+       mutex_unlock(&hdmi->audio_mutex);
+}
+
+static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
+{
+       mutex_lock(&hdmi->audio_mutex);
+       hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
+                                hdmi->sample_rate);
+       mutex_unlock(&hdmi->audio_mutex);
+}
+
+void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
+{
+       mutex_lock(&hdmi->audio_mutex);
+       hdmi->sample_rate = rate;
+       hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
+                                hdmi->sample_rate);
+       mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
+
+void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi->audio_lock, flags);
+       hdmi->audio_enable = true;
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+       spin_unlock_irqrestore(&hdmi->audio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
+
+void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi->audio_lock, flags);
+       hdmi->audio_enable = false;
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+       spin_unlock_irqrestore(&hdmi->audio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
+
+/*
+ * this submodule is responsible for the video data synchronization.
+ * for example, for RGB 4:4:4 input, the data map is defined as
+ *                     pin{47~40} <==> R[7:0]
+ *                     pin{31~24} <==> G[7:0]
+ *                     pin{15~8}  <==> B[7:0]
+ */
+static void hdmi_video_sample(struct dw_hdmi *hdmi)
+{
+       int color_format = 0;
+       u8 val;
+
+       if (hdmi->hdmi_data.enc_in_format == RGB) {
+               if (hdmi->hdmi_data.enc_color_depth == 8)
+                       color_format = 0x01;
+               else if (hdmi->hdmi_data.enc_color_depth == 10)
+                       color_format = 0x03;
+               else if (hdmi->hdmi_data.enc_color_depth == 12)
+                       color_format = 0x05;
+               else if (hdmi->hdmi_data.enc_color_depth == 16)
+                       color_format = 0x07;
+               else
+                       return;
+       } else if (hdmi->hdmi_data.enc_in_format == YCBCR444) {
+               if (hdmi->hdmi_data.enc_color_depth == 8)
+                       color_format = 0x09;
+               else if (hdmi->hdmi_data.enc_color_depth == 10)
+                       color_format = 0x0B;
+               else if (hdmi->hdmi_data.enc_color_depth == 12)
+                       color_format = 0x0D;
+               else if (hdmi->hdmi_data.enc_color_depth == 16)
+                       color_format = 0x0F;
+               else
+                       return;
+       } else if (hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) {
+               if (hdmi->hdmi_data.enc_color_depth == 8)
+                       color_format = 0x16;
+               else if (hdmi->hdmi_data.enc_color_depth == 10)
+                       color_format = 0x14;
+               else if (hdmi->hdmi_data.enc_color_depth == 12)
+                       color_format = 0x12;
+               else
+                       return;
+       }
+
+       val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
+               ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
+               HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
+       hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
+
+       /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
+       val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
+               HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
+               HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
+       hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
+       hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
+       hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
+       hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
+       hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
+       hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
+       hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
+}
+
+static int is_color_space_conversion(struct dw_hdmi *hdmi)
+{
+       return hdmi->hdmi_data.enc_in_format != hdmi->hdmi_data.enc_out_format;
+}
+
+static int is_color_space_decimation(struct dw_hdmi *hdmi)
+{
+       if (hdmi->hdmi_data.enc_out_format != YCBCR422_8BITS)
+               return 0;
+       if (hdmi->hdmi_data.enc_in_format == RGB ||
+           hdmi->hdmi_data.enc_in_format == YCBCR444)
+               return 1;
+       return 0;
+}
+
+static int is_color_space_interpolation(struct dw_hdmi *hdmi)
+{
+       if (hdmi->hdmi_data.enc_in_format != YCBCR422_8BITS)
+               return 0;
+       if (hdmi->hdmi_data.enc_out_format == RGB ||
+           hdmi->hdmi_data.enc_out_format == YCBCR444)
+               return 1;
+       return 0;
+}
+
+static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
+{
+       const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
+       unsigned i;
+       u32 csc_scale = 1;
+
+       if (is_color_space_conversion(hdmi)) {
+               if (hdmi->hdmi_data.enc_out_format == RGB) {
+                       if (hdmi->hdmi_data.colorimetry ==
+                                       HDMI_COLORIMETRY_ITU_601)
+                               csc_coeff = &csc_coeff_rgb_out_eitu601;
+                       else
+                               csc_coeff = &csc_coeff_rgb_out_eitu709;
+               } else if (hdmi->hdmi_data.enc_in_format == RGB) {
+                       if (hdmi->hdmi_data.colorimetry ==
+                                       HDMI_COLORIMETRY_ITU_601)
+                               csc_coeff = &csc_coeff_rgb_in_eitu601;
+                       else
+                               csc_coeff = &csc_coeff_rgb_in_eitu709;
+                       csc_scale = 0;
+               }
+       }
+
+       /* The CSC registers are sequential, alternating MSB then LSB */
+       for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) {
+               u16 coeff_a = (*csc_coeff)[0][i];
+               u16 coeff_b = (*csc_coeff)[1][i];
+               u16 coeff_c = (*csc_coeff)[2][i];
+
+               hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2);
+               hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
+               hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
+               hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
+               hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2);
+               hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
+       }
+
+       hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
+                 HDMI_CSC_SCALE);
+}
+
+static void hdmi_video_csc(struct dw_hdmi *hdmi)
+{
+       int color_depth = 0;
+       int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
+       int decimation = 0;
+
+       /* YCC422 interpolation to 444 mode */
+       if (is_color_space_interpolation(hdmi))
+               interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
+       else if (is_color_space_decimation(hdmi))
+               decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
+
+       if (hdmi->hdmi_data.enc_color_depth == 8)
+               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
+       else if (hdmi->hdmi_data.enc_color_depth == 10)
+               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
+       else if (hdmi->hdmi_data.enc_color_depth == 12)
+               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
+       else if (hdmi->hdmi_data.enc_color_depth == 16)
+               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
+       else
+               return;
+
+       /* Configure the CSC registers */
+       hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
+       hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
+                 HDMI_CSC_SCALE);
+
+       dw_hdmi_update_csc_coeffs(hdmi);
+}
+
+/*
+ * HDMI video packetizer is used to packetize the data.
+ * for example, if input is YCC422 mode or repeater is used,
+ * data should be repacked this module can be bypassed.
+ */
+static void hdmi_video_packetize(struct dw_hdmi *hdmi)
+{
+       unsigned int color_depth = 0;
+       unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
+       unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
+       struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
+       u8 val, vp_conf;
+
+       if (hdmi_data->enc_out_format == RGB ||
+           hdmi_data->enc_out_format == YCBCR444) {
+               if (!hdmi_data->enc_color_depth) {
+                       output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+               } else if (hdmi_data->enc_color_depth == 8) {
+                       color_depth = 4;
+                       output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+               } else if (hdmi_data->enc_color_depth == 10) {
+                       color_depth = 5;
+               } else if (hdmi_data->enc_color_depth == 12) {
+                       color_depth = 6;
+               } else if (hdmi_data->enc_color_depth == 16) {
+                       color_depth = 7;
+               } else {
+                       return;
+               }
+       } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
+               if (!hdmi_data->enc_color_depth ||
+                   hdmi_data->enc_color_depth == 8)
+                       remap_size = HDMI_VP_REMAP_YCC422_16bit;
+               else if (hdmi_data->enc_color_depth == 10)
+                       remap_size = HDMI_VP_REMAP_YCC422_20bit;
+               else if (hdmi_data->enc_color_depth == 12)
+                       remap_size = HDMI_VP_REMAP_YCC422_24bit;
+               else
+                       return;
+               output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
+       } else {
+               return;
+       }
+
+       /* set the packetizer registers */
+       val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
+               HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
+               ((hdmi_data->pix_repet_factor <<
+               HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
+               HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
+       hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
+
+       hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
+                 HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
+
+       /* Data from pixel repeater block */
+       if (hdmi_data->pix_repet_factor > 1) {
+               vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
+                         HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
+       } else { /* data from packetizer block */
+               vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
+                         HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
+       }
+
+       hdmi_modb(hdmi, vp_conf,
+                 HDMI_VP_CONF_PR_EN_MASK |
+                 HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
+
+       hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+                 HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
+
+       hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
+
+       if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
+               vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
+                         HDMI_VP_CONF_PP_EN_ENABLE |
+                         HDMI_VP_CONF_YCC422_EN_DISABLE;
+       } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
+               vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
+                         HDMI_VP_CONF_PP_EN_DISABLE |
+                         HDMI_VP_CONF_YCC422_EN_ENABLE;
+       } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
+               vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
+                         HDMI_VP_CONF_PP_EN_DISABLE |
+                         HDMI_VP_CONF_YCC422_EN_DISABLE;
+       } else {
+               return;
+       }
+
+       hdmi_modb(hdmi, vp_conf,
+                 HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
+                 HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
+
+       hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
+                       HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
+                 HDMI_VP_STUFF_PP_STUFFING_MASK |
+                 HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
+
+       hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
+                 HDMI_VP_CONF);
+}
+
+static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi,
+                                      unsigned char bit)
+{
+       hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
+                 HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi,
+                                       unsigned char bit)
+{
+       hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
+                 HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi,
+                                      unsigned char bit)
+{
+       hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
+                 HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi,
+                                    unsigned char bit)
+{
+       hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
+}
+
+static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi,
+                                     unsigned char bit)
+{
+       hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
+}
+
+static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
+{
+       u32 val;
+
+       while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
+               if (msec-- == 0)
+                       return false;
+               udelay(1000);
+       }
+       hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0);
+
+       return true;
+}
+
+static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
+                                unsigned char addr)
+{
+       hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+       hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+       hdmi_writeb(hdmi, (unsigned char)(data >> 8),
+                   HDMI_PHY_I2CM_DATAO_1_ADDR);
+       hdmi_writeb(hdmi, (unsigned char)(data >> 0),
+                   HDMI_PHY_I2CM_DATAO_0_ADDR);
+       hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
+                   HDMI_PHY_I2CM_OPERATION_ADDR);
+       hdmi_phy_wait_i2c_done(hdmi, 1000);
+}
+
+static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
+                             unsigned char addr)
+{
+       __hdmi_phy_i2c_write(hdmi, data, addr);
+       return 0;
+}
+
+static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
+{
+       hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
+                        HDMI_PHY_CONF0_PDZ_OFFSET,
+                        HDMI_PHY_CONF0_PDZ_MASK);
+}
+
+static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable)
+{
+       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+                        HDMI_PHY_CONF0_ENTMDS_OFFSET,
+                        HDMI_PHY_CONF0_ENTMDS_MASK);
+}
+
+static void dw_hdmi_phy_enable_spare(struct dw_hdmi *hdmi, u8 enable)
+{
+       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+                        HDMI_PHY_CONF0_SPARECTRL_OFFSET,
+                        HDMI_PHY_CONF0_SPARECTRL_MASK);
+}
+
+static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
+{
+       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+                        HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
+                        HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
+}
+
+static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
+{
+       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+                        HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
+                        HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
+}
+
+static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
+{
+       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+                        HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
+                        HDMI_PHY_CONF0_SELDATAENPOL_MASK);
+}
+
+static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
+{
+       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+                        HDMI_PHY_CONF0_SELDIPIF_OFFSET,
+                        HDMI_PHY_CONF0_SELDIPIF_MASK);
+}
+
+static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
+                             unsigned char res, int cscon)
+{
+       unsigned res_idx;
+       u8 val, msec;
+       const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+       const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
+       const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
+       const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+
+       if (prep)
+               return -EINVAL;
+
+       switch (res) {
+       case 0: /* color resolution 0 is 8 bit colour depth */
+       case 8:
+               res_idx = DW_HDMI_RES_8;
+               break;
+       case 10:
+               res_idx = DW_HDMI_RES_10;
+               break;
+       case 12:
+               res_idx = DW_HDMI_RES_12;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* PLL/MPLL Cfg - always match on final entry */
+       for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
+               if (hdmi->hdmi_data.video_mode.mpixelclock <=
+                   mpll_config->mpixelclock)
+                       break;
+
+       for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
+               if (hdmi->hdmi_data.video_mode.mpixelclock <=
+                   curr_ctrl->mpixelclock)
+                       break;
+
+       for (; phy_config->mpixelclock != ~0UL; phy_config++)
+               if (hdmi->hdmi_data.video_mode.mpixelclock <=
+                   phy_config->mpixelclock)
+                       break;
+
+       if (mpll_config->mpixelclock == ~0UL ||
+           curr_ctrl->mpixelclock == ~0UL ||
+           phy_config->mpixelclock == ~0UL) {
+               dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
+                       hdmi->hdmi_data.video_mode.mpixelclock);
+               return -EINVAL;
+       }
+
+       /* Enable csc path */
+       if (cscon)
+               val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH;
+       else
+               val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;
+
+       hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL);
+
+       /* gen2 tx power off */
+       dw_hdmi_phy_gen2_txpwron(hdmi, 0);
+
+       /* gen2 pddq */
+       dw_hdmi_phy_gen2_pddq(hdmi, 1);
+
+       /* PHY reset */
+       hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
+       hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
+
+       hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
+
+       hdmi_phy_test_clear(hdmi, 1);
+       hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
+                   HDMI_PHY_I2CM_SLAVE_ADDR);
+       hdmi_phy_test_clear(hdmi, 0);
+
+       hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].cpce, 0x06);
+       hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15);
+
+       /* CURRCTRL */
+       hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10);
+
+       hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
+       hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
+
+       hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19);  /* TXTERM */
+       hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */
+       hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */
+
+       /* REMOVE CLK TERM */
+       hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
+
+       dw_hdmi_phy_enable_powerdown(hdmi, false);
+
+       /* toggle TMDS enable */
+       dw_hdmi_phy_enable_tmds(hdmi, 0);
+       dw_hdmi_phy_enable_tmds(hdmi, 1);
+
+       /* gen2 tx power on */
+       dw_hdmi_phy_gen2_txpwron(hdmi, 1);
+       dw_hdmi_phy_gen2_pddq(hdmi, 0);
+
+       if (hdmi->dev_type == RK3288_HDMI)
+               dw_hdmi_phy_enable_spare(hdmi, 1);
+
+       /*Wait for PHY PLL lock */
+       msec = 5;
+       do {
+               val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+               if (!val)
+                       break;
+
+               if (msec == 0) {
+                       dev_err(hdmi->dev, "PHY PLL not locked\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1000);
+               msec--;
+       } while (1);
+
+       return 0;
+}
+
+static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
+{
+       int i, ret;
+       bool cscon;
+
+       /*check csc whether needed activated in HDMI mode */
+       cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
+
+       /* HDMI Phy spec says to do the phy initialization sequence twice */
+       for (i = 0; i < 2; i++) {
+               dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
+               dw_hdmi_phy_sel_interface_control(hdmi, 0);
+               dw_hdmi_phy_enable_tmds(hdmi, 0);
+               dw_hdmi_phy_enable_powerdown(hdmi, true);
+
+               /* Enable CSC */
+               ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
+               if (ret)
+                       return ret;
+       }
+
+       hdmi->phy_enabled = true;
+       return 0;
+}
+
+static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
+{
+       u8 de;
+
+       if (hdmi->hdmi_data.video_mode.mdataenablepolarity)
+               de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
+       else
+               de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
+
+       /* disable rx detect */
+       hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
+                 HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
+
+       hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
+
+       hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
+                 HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
+}
+
+static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
+{
+       struct hdmi_avi_infoframe frame;
+       u8 val;
+
+       /* Initialise info frame from DRM mode */
+       drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
+
+       if (hdmi->hdmi_data.enc_out_format == YCBCR444)
+               frame.colorspace = HDMI_COLORSPACE_YUV444;
+       else if (hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS)
+               frame.colorspace = HDMI_COLORSPACE_YUV422;
+       else
+               frame.colorspace = HDMI_COLORSPACE_RGB;
+
+       /* Set up colorimetry */
+       if (hdmi->hdmi_data.enc_out_format == XVYCC444) {
+               frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
+               if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601)
+                       frame.extended_colorimetry =
+                               HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+               else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/
+                       frame.extended_colorimetry =
+                               HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
+       } else if (hdmi->hdmi_data.enc_out_format != RGB) {
+               frame.colorimetry = hdmi->hdmi_data.colorimetry;
+               frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+       } else { /* Carries no data */
+               frame.colorimetry = HDMI_COLORIMETRY_NONE;
+               frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+       }
+
+       frame.scan_mode = HDMI_SCAN_MODE_NONE;
+
+       /*
+        * The Designware IP uses a different byte format from standard
+        * AVI info frames, though generally the bits are in the correct
+        * bytes.
+        */
+
+       /*
+        * AVI data byte 1 differences: Colorspace in bits 4,5 rather than 5,6,
+        * active aspect present in bit 6 rather than 4.
+        */
+       val = (frame.colorspace & 3) << 4 | (frame.scan_mode & 0x3);
+       if (frame.active_aspect & 15)
+               val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT;
+       if (frame.top_bar || frame.bottom_bar)
+               val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR;
+       if (frame.left_bar || frame.right_bar)
+               val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR;
+       hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
+
+       /* AVI data byte 2 differences: none */
+       val = ((frame.colorimetry & 0x3) << 6) |
+             ((frame.picture_aspect & 0x3) << 4) |
+             (frame.active_aspect & 0xf);
+       hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
+
+       /* AVI data byte 3 differences: none */
+       val = ((frame.extended_colorimetry & 0x7) << 4) |
+             ((frame.quantization_range & 0x3) << 2) |
+             (frame.nups & 0x3);
+       if (frame.itc)
+               val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID;
+       hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
+
+       /* AVI data byte 4 differences: none */
+       val = frame.video_code & 0x7f;
+       hdmi_writeb(hdmi, val, HDMI_FC_AVIVID);
+
+       /* AVI Data Byte 5- set up input and output pixel repetition */
+       val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
+               HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) &
+               HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) |
+               ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
+               HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
+               HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
+       hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
+
+       /*
+        * AVI data byte 5 differences: content type in 0,1 rather than 4,5,
+        * ycc range in bits 2,3 rather than 6,7
+        */
+       val = ((frame.ycc_quantization_range & 0x3) << 2) |
+             (frame.content_type & 0x3);
+       hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
+
+       /* AVI Data Bytes 6-13 */
+       hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0);
+       hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1);
+       hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0);
+       hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1);
+       hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0);
+       hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1);
+       hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0);
+       hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1);
+}
+
+static void hdmi_av_composer(struct dw_hdmi *hdmi,
+                            const struct drm_display_mode *mode)
+{
+       u8 inv_val;
+       struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+       int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
+       unsigned int vdisplay;
+
+       vmode->mpixelclock = mode->clock * 1000;
+
+       dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
+
+       /* Set up HDMI_FC_INVIDCONF */
+       inv_val = (hdmi->hdmi_data.hdcp_enable ?
+               HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
+               HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
+
+       inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+               HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
+               HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW;
+
+       inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+               HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
+               HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW;
+
+       inv_val |= (vmode->mdataenablepolarity ?
+               HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
+               HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
+
+       if (hdmi->vic == 39)
+               inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
+       else
+               inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+                       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
+                       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
+
+       inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+               HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
+               HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
+
+       inv_val |= hdmi->sink_is_hdmi ?
+               HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
+               HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
+
+       hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
+
+       vdisplay = mode->vdisplay;
+       vblank = mode->vtotal - mode->vdisplay;
+       v_de_vs = mode->vsync_start - mode->vdisplay;
+       vsync_len = mode->vsync_end - mode->vsync_start;
+
+       /*
+        * When we're setting an interlaced mode, we need
+        * to adjust the vertical timing to suit.
+        */
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+               vdisplay /= 2;
+               vblank /= 2;
+               v_de_vs /= 2;
+               vsync_len /= 2;
+       }
+
+       /* Set up horizontal active pixel width */
+       hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
+       hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
+
+       /* Set up vertical active lines */
+       hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
+       hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
+
+       /* Set up horizontal blanking pixel region width */
+       hblank = mode->htotal - mode->hdisplay;
+       hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
+       hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
+
+       /* Set up vertical blanking pixel region width */
+       hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
+
+       /* Set up HSYNC active edge delay width (in pixel clks) */
+       h_de_hs = mode->hsync_start - mode->hdisplay;
+       hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
+       hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
+
+       /* Set up VSYNC active edge delay (in lines) */
+       hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
+
+       /* Set up HSYNC active pulse width (in pixel clks) */
+       hsync_len = mode->hsync_end - mode->hsync_start;
+       hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+       hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
+
+       /* Set up VSYNC active edge delay (in lines) */
+       hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
+}
+
+static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
+{
+       if (!hdmi->phy_enabled)
+               return;
+
+       dw_hdmi_phy_enable_tmds(hdmi, 0);
+       dw_hdmi_phy_enable_powerdown(hdmi, true);
+
+       hdmi->phy_enabled = false;
+}
+
+/* HDMI Initialization Step B.4 */
+static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
+{
+       u8 clkdis;
+
+       /* control period minimum duration */
+       hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
+       hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
+       hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
+
+       /* Set to fill TMDS data channels */
+       hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
+       hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
+       hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
+
+       /* Enable pixel clock and tmds data path */
+       clkdis = 0x7F;
+       clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+       hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+
+       clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+       hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+
+       /* Enable csc path */
+       if (is_color_space_conversion(hdmi)) {
+               clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+               hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+       }
+}
+
+static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
+{
+       hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+}
+
+/* Workaround to clear the overflow condition */
+static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
+{
+       int count;
+       u8 val;
+
+       /* TMDS software reset */
+       hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+
+       val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
+       if (hdmi->dev_type == IMX6DL_HDMI) {
+               hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+               return;
+       }
+
+       for (count = 0; count < 4; count++)
+               hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+}
+
+static void hdmi_enable_overflow_interrupts(struct dw_hdmi *hdmi)
+{
+       hdmi_writeb(hdmi, 0, HDMI_FC_MASK2);
+       hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
+}
+
+static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
+{
+       hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
+                   HDMI_IH_MUTE_FC_STAT2);
+}
+
+static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
+{
+       int ret;
+
+       hdmi_disable_overflow_interrupts(hdmi);
+
+       hdmi->vic = drm_match_cea_mode(mode);
+
+       if (!hdmi->vic) {
+               dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
+       } else {
+               dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
+       }
+
+       if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
+           (hdmi->vic == 21) || (hdmi->vic == 22) ||
+           (hdmi->vic == 2) || (hdmi->vic == 3) ||
+           (hdmi->vic == 17) || (hdmi->vic == 18))
+               hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+       else
+               hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+       hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
+       hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
+
+       /* TODO: Get input format from IPU (via FB driver interface) */
+       hdmi->hdmi_data.enc_in_format = RGB;
+
+       hdmi->hdmi_data.enc_out_format = RGB;
+
+       hdmi->hdmi_data.enc_color_depth = 8;
+       hdmi->hdmi_data.pix_repet_factor = 0;
+       hdmi->hdmi_data.hdcp_enable = 0;
+       hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
+
+       /* HDMI Initialization Step B.1 */
+       hdmi_av_composer(hdmi, mode);
+
+       /* HDMI Initializateion Step B.2 */
+       ret = dw_hdmi_phy_init(hdmi);
+       if (ret)
+               return ret;
+
+       /* HDMI Initialization Step B.3 */
+       dw_hdmi_enable_video_path(hdmi);
+
+       if (hdmi->sink_has_audio) {
+               dev_dbg(hdmi->dev, "sink has audio support\n");
+
+               /* HDMI Initialization Step E - Configure audio */
+               hdmi_clk_regenerator_update_pixel_clock(hdmi);
+               hdmi_enable_audio_clk(hdmi);
+       }
+
+       /* not for DVI mode */
+       if (hdmi->sink_is_hdmi) {
+               dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
+
+               /* HDMI Initialization Step F - Configure AVI InfoFrame */
+               hdmi_config_AVI(hdmi, mode);
+       } else {
+               dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
+       }
+
+       hdmi_video_packetize(hdmi);
+       hdmi_video_csc(hdmi);
+       hdmi_video_sample(hdmi);
+       hdmi_tx_hdcp_config(hdmi);
+
+       dw_hdmi_clear_overflow(hdmi);
+       if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
+               hdmi_enable_overflow_interrupts(hdmi);
+
+       return 0;
+}
+
+/* Wait until we are registered to enable interrupts */
+static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi)
+{
+       hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+                   HDMI_PHY_I2CM_INT_ADDR);
+
+       hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+                   HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+                   HDMI_PHY_I2CM_CTLINT_ADDR);
+
+       /* enable cable hot plug irq */
+       hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+
+       /* Clear Hotplug interrupts */
+       hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+                   HDMI_IH_PHY_STAT0);
+
+       return 0;
+}
+
+static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
+{
+       u8 ih_mute;
+
+       /*
+        * Boot up defaults are:
+        * HDMI_IH_MUTE   = 0x03 (disabled)
+        * HDMI_IH_MUTE_* = 0x00 (enabled)
+        *
+        * Disable top level interrupt bits in HDMI block
+        */
+       ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
+                 HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
+                 HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
+
+       hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+
+       /* by default mask all interrupts */
+       hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
+       hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
+       hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
+       hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
+       hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
+       hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
+       hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+       hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
+       hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
+       hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
+       hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
+       hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
+       hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
+       hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
+       hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
+
+       /* Disable interrupts in the IH_MUTE_* registers */
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
+       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+       /* Enable top level interrupt bits in HDMI block */
+       ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
+                   HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
+       hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+}
+
+static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
+{
+       hdmi->bridge_is_on = true;
+       dw_hdmi_setup(hdmi, &hdmi->previous_mode);
+}
+
+static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
+{
+       dw_hdmi_phy_disable(hdmi);
+       hdmi->bridge_is_on = false;
+}
+
+static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
+{
+       int force = hdmi->force;
+
+       if (hdmi->disabled) {
+               force = DRM_FORCE_OFF;
+       } else if (force == DRM_FORCE_UNSPECIFIED) {
+               if (hdmi->rxsense)
+                       force = DRM_FORCE_ON;
+               else
+                       force = DRM_FORCE_OFF;
+       }
+
+       if (force == DRM_FORCE_OFF) {
+               if (hdmi->bridge_is_on)
+                       dw_hdmi_poweroff(hdmi);
+       } else {
+               if (!hdmi->bridge_is_on)
+                       dw_hdmi_poweron(hdmi);
+       }
+}
+
+/*
+ * Adjust the detection of RXSENSE according to whether we have a forced
+ * connection mode enabled, or whether we have been disabled.  There is
+ * no point processing RXSENSE interrupts if we have a forced connection
+ * state, or DRM has us disabled.
+ *
+ * We also disable rxsense interrupts when we think we're disconnected
+ * to avoid floating TDMS signals giving false rxsense interrupts.
+ *
+ * Note: we still need to listen for HPD interrupts even when DRM has us
+ * disabled so that we can detect a connect event.
+ */
+static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
+{
+       u8 old_mask = hdmi->phy_mask;
+
+       if (hdmi->force || hdmi->disabled || !hdmi->rxsense)
+               hdmi->phy_mask |= HDMI_PHY_RX_SENSE;
+       else
+               hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE;
+
+       if (old_mask != hdmi->phy_mask)
+               hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+}
+
+static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
+                                   struct drm_display_mode *orig_mode,
+                                   struct drm_display_mode *mode)
+{
+       struct dw_hdmi *hdmi = bridge->driver_private;
+
+       mutex_lock(&hdmi->mutex);
+
+       /* Store the display mode for plugin/DKMS poweron events */
+       memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+
+       mutex_unlock(&hdmi->mutex);
+}
+
+static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
+                                     const struct drm_display_mode *mode,
+                                     struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
+{
+       struct dw_hdmi *hdmi = bridge->driver_private;
+
+       mutex_lock(&hdmi->mutex);
+       hdmi->disabled = true;
+       dw_hdmi_update_power(hdmi);
+       dw_hdmi_update_phy_mask(hdmi);
+       mutex_unlock(&hdmi->mutex);
+}
+
+static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
+{
+       struct dw_hdmi *hdmi = bridge->driver_private;
+
+       mutex_lock(&hdmi->mutex);
+       hdmi->disabled = false;
+       dw_hdmi_update_power(hdmi);
+       dw_hdmi_update_phy_mask(hdmi);
+       mutex_unlock(&hdmi->mutex);
+}
+
+static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
+{
+       /* do nothing */
+}
+
+static enum drm_connector_status
+dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+                                            connector);
+
+       mutex_lock(&hdmi->mutex);
+       hdmi->force = DRM_FORCE_UNSPECIFIED;
+       dw_hdmi_update_power(hdmi);
+       dw_hdmi_update_phy_mask(hdmi);
+       mutex_unlock(&hdmi->mutex);
+
+       return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
+               connector_status_connected : connector_status_disconnected;
+}
+
+static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+                                            connector);
+       struct edid *edid;
+       int ret = 0;
+
+       if (!hdmi->ddc)
+               return 0;
+
+       edid = drm_get_edid(connector, hdmi->ddc);
+       if (edid) {
+               dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
+                       edid->width_cm, edid->height_cm);
+
+               hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+               hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
+               drm_mode_connector_update_edid_property(connector, edid);
+               ret = drm_add_edid_modes(connector, edid);
+               /* Store the ELD */
+               drm_edid_to_eld(connector, edid);
+               kfree(edid);
+       } else {
+               dev_dbg(hdmi->dev, "failed to get edid\n");
+       }
+
+       return ret;
+}
+
+static enum drm_mode_status
+dw_hdmi_connector_mode_valid(struct drm_connector *connector,
+                            struct drm_display_mode *mode)
+{
+       struct dw_hdmi *hdmi = container_of(connector,
+                                          struct dw_hdmi, connector);
+       enum drm_mode_status mode_status = MODE_OK;
+
+       /* We don't support double-clocked modes */
+       if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+               return MODE_BAD;
+
+       if (hdmi->plat_data->mode_valid)
+               mode_status = hdmi->plat_data->mode_valid(connector, mode);
+
+       return mode_status;
+}
+
+static struct drm_encoder *dw_hdmi_connector_best_encoder(struct drm_connector
+                                                          *connector)
+{
+       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+                                            connector);
+
+       return hdmi->encoder;
+}
+
+static void dw_hdmi_connector_destroy(struct drm_connector *connector)
+{
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static void dw_hdmi_connector_force(struct drm_connector *connector)
+{
+       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+                                            connector);
+
+       mutex_lock(&hdmi->mutex);
+       hdmi->force = connector->force;
+       dw_hdmi_update_power(hdmi);
+       dw_hdmi_update_phy_mask(hdmi);
+       mutex_unlock(&hdmi->mutex);
+}
+
+static struct drm_connector_funcs dw_hdmi_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = dw_hdmi_connector_detect,
+       .destroy = dw_hdmi_connector_destroy,
+       .force = dw_hdmi_connector_force,
+};
+
+static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
+       .get_modes = dw_hdmi_connector_get_modes,
+       .mode_valid = dw_hdmi_connector_mode_valid,
+       .best_encoder = dw_hdmi_connector_best_encoder,
+};
+
+static struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
+       .enable = dw_hdmi_bridge_enable,
+       .disable = dw_hdmi_bridge_disable,
+       .pre_enable = dw_hdmi_bridge_nop,
+       .post_disable = dw_hdmi_bridge_nop,
+       .mode_set = dw_hdmi_bridge_mode_set,
+       .mode_fixup = dw_hdmi_bridge_mode_fixup,
+};
+
+static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
+{
+       struct dw_hdmi *hdmi = dev_id;
+       u8 intr_stat;
+
+       intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+       if (intr_stat)
+               hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+
+       return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE;
+}
+
+static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
+{
+       struct dw_hdmi *hdmi = dev_id;
+       u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
+
+       intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+       phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+       phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+
+       phy_pol_mask = 0;
+       if (intr_stat & HDMI_IH_PHY_STAT0_HPD)
+               phy_pol_mask |= HDMI_PHY_HPD;
+       if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE0)
+               phy_pol_mask |= HDMI_PHY_RX_SENSE0;
+       if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE1)
+               phy_pol_mask |= HDMI_PHY_RX_SENSE1;
+       if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE2)
+               phy_pol_mask |= HDMI_PHY_RX_SENSE2;
+       if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE3)
+               phy_pol_mask |= HDMI_PHY_RX_SENSE3;
+
+       if (phy_pol_mask)
+               hdmi_modb(hdmi, ~phy_int_pol, phy_pol_mask, HDMI_PHY_POL0);
+
+       /*
+        * RX sense tells us whether the TDMS transmitters are detecting
+        * load - in other words, there's something listening on the
+        * other end of the link.  Use this to decide whether we should
+        * power on the phy as HPD may be toggled by the sink to merely
+        * ask the source to re-read the EDID.
+        */
+       if (intr_stat &
+           (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) {
+               mutex_lock(&hdmi->mutex);
+               if (!hdmi->disabled && !hdmi->force) {
+                       /*
+                        * If the RX sense status indicates we're disconnected,
+                        * clear the software rxsense status.
+                        */
+                       if (!(phy_stat & HDMI_PHY_RX_SENSE))
+                               hdmi->rxsense = false;
+
+                       /*
+                        * Only set the software rxsense status when both
+                        * rxsense and hpd indicates we're connected.
+                        * This avoids what seems to be bad behaviour in
+                        * at least iMX6S versions of the phy.
+                        */
+                       if (phy_stat & HDMI_PHY_HPD)
+                               hdmi->rxsense = true;
+
+                       dw_hdmi_update_power(hdmi);
+                       dw_hdmi_update_phy_mask(hdmi);
+               }
+               mutex_unlock(&hdmi->mutex);
+       }
+
+       if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+               dev_dbg(hdmi->dev, "EVENT=%s\n",
+                       phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout");
+               drm_helper_hpd_irq_event(hdmi->bridge->dev);
+       }
+
+       hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+       hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+                   HDMI_IH_MUTE_PHY_STAT0);
+
+       return IRQ_HANDLED;
+}
+
+static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
+{
+       struct drm_encoder *encoder = hdmi->encoder;
+       struct drm_bridge *bridge;
+       int ret;
+
+       bridge = devm_kzalloc(drm->dev, sizeof(*bridge), GFP_KERNEL);
+       if (!bridge) {
+               DRM_ERROR("Failed to allocate drm bridge\n");
+               return -ENOMEM;
+       }
+
+       hdmi->bridge = bridge;
+       bridge->driver_private = hdmi;
+       bridge->funcs = &dw_hdmi_bridge_funcs;
+       ret = drm_bridge_attach(drm, bridge);
+       if (ret) {
+               DRM_ERROR("Failed to initialize bridge with drm\n");
+               return -EINVAL;
+       }
+
+       encoder->bridge = bridge;
+       hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+       drm_connector_helper_add(&hdmi->connector,
+                                &dw_hdmi_connector_helper_funcs);
+       drm_connector_init(drm, &hdmi->connector, &dw_hdmi_connector_funcs,
+                          DRM_MODE_CONNECTOR_HDMIA);
+
+       hdmi->connector.encoder = encoder;
+
+       drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
+
+       return 0;
+}
+
+int dw_hdmi_bind(struct device *dev, struct device *master,
+                void *data, struct drm_encoder *encoder,
+                struct resource *iores, int irq,
+                const struct dw_hdmi_plat_data *plat_data)
+{
+       struct drm_device *drm = data;
+       struct device_node *np = dev->of_node;
+       struct platform_device_info pdevinfo;
+       struct device_node *ddc_node;
+       struct dw_hdmi_audio_data audio;
+       struct dw_hdmi *hdmi;
+       int ret;
+       u32 val = 1;
+
+       hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+       if (!hdmi)
+               return -ENOMEM;
+
+       hdmi->connector.interlace_allowed = 1;
+
+       hdmi->plat_data = plat_data;
+       hdmi->dev = dev;
+       hdmi->dev_type = plat_data->dev_type;
+       hdmi->sample_rate = 48000;
+       hdmi->encoder = encoder;
+       hdmi->disabled = true;
+       hdmi->rxsense = true;
+       hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
+
+       mutex_init(&hdmi->mutex);
+       mutex_init(&hdmi->audio_mutex);
+       spin_lock_init(&hdmi->audio_lock);
+
+       of_property_read_u32(np, "reg-io-width", &val);
+
+       switch (val) {
+       case 4:
+               hdmi->write = dw_hdmi_writel;
+               hdmi->read = dw_hdmi_readl;
+               break;
+       case 1:
+               hdmi->write = dw_hdmi_writeb;
+               hdmi->read = dw_hdmi_readb;
+               break;
+       default:
+               dev_err(dev, "reg-io-width must be 1 or 4\n");
+               return -EINVAL;
+       }
+
+       ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
+       if (ddc_node) {
+               hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
+               of_node_put(ddc_node);
+               if (!hdmi->ddc) {
+                       dev_dbg(hdmi->dev, "failed to read ddc node\n");
+                       return -EPROBE_DEFER;
+               }
+
+       } else {
+               dev_dbg(hdmi->dev, "no ddc property found\n");
+       }
+
+       hdmi->regs = devm_ioremap_resource(dev, iores);
+       if (IS_ERR(hdmi->regs))
+               return PTR_ERR(hdmi->regs);
+
+       hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
+       if (IS_ERR(hdmi->isfr_clk)) {
+               ret = PTR_ERR(hdmi->isfr_clk);
+               dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(hdmi->isfr_clk);
+       if (ret) {
+               dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
+               return ret;
+       }
+
+       hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
+       if (IS_ERR(hdmi->iahb_clk)) {
+               ret = PTR_ERR(hdmi->iahb_clk);
+               dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret);
+               goto err_isfr;
+       }
+
+       ret = clk_prepare_enable(hdmi->iahb_clk);
+       if (ret) {
+               dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
+               goto err_isfr;
+       }
+
+       /* Product and revision IDs */
+       dev_info(dev,
+                "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
+                hdmi_readb(hdmi, HDMI_DESIGN_ID),
+                hdmi_readb(hdmi, HDMI_REVISION_ID),
+                hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
+                hdmi_readb(hdmi, HDMI_PRODUCT_ID1));
+
+       initialize_hdmi_ih_mutes(hdmi);
+
+       ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
+                                       dw_hdmi_irq, IRQF_SHARED,
+                                       dev_name(dev), hdmi);
+       if (ret)
+               goto err_iahb;
+
+       /*
+        * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
+        * N and cts values before enabling phy
+        */
+       hdmi_init_clk_regenerator(hdmi);
+
+       /*
+        * Configure registers related to HDMI interrupt
+        * generation before registering IRQ.
+        */
+       hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0);
+
+       /* Clear Hotplug interrupts */
+       hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+                   HDMI_IH_PHY_STAT0);
+
+       ret = dw_hdmi_fb_registered(hdmi);
+       if (ret)
+               goto err_iahb;
+
+       ret = dw_hdmi_register(drm, hdmi);
+       if (ret)
+               goto err_iahb;
+
+       /* Unmute interrupts */
+       hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+                   HDMI_IH_MUTE_PHY_STAT0);
+
+       memset(&pdevinfo, 0, sizeof(pdevinfo));
+       pdevinfo.parent = dev;
+       pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+       if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
+               audio.phys = iores->start;
+               audio.base = hdmi->regs;
+               audio.irq = irq;
+               audio.hdmi = hdmi;
+               audio.eld = hdmi->connector.eld;
+
+               pdevinfo.name = "dw-hdmi-ahb-audio";
+               pdevinfo.data = &audio;
+               pdevinfo.size_data = sizeof(audio);
+               pdevinfo.dma_mask = DMA_BIT_MASK(32);
+               hdmi->audio = platform_device_register_full(&pdevinfo);
+       }
+
+       dev_set_drvdata(dev, hdmi);
+
+       return 0;
+
+err_iahb:
+       clk_disable_unprepare(hdmi->iahb_clk);
+err_isfr:
+       clk_disable_unprepare(hdmi->isfr_clk);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_bind);
+
+void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
+{
+       struct dw_hdmi *hdmi = dev_get_drvdata(dev);
+
+       if (hdmi->audio && !IS_ERR(hdmi->audio))
+               platform_device_unregister(hdmi->audio);
+
+       /* Disable all interrupts */
+       hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+
+       hdmi->connector.funcs->destroy(&hdmi->connector);
+       hdmi->encoder->funcs->destroy(hdmi->encoder);
+
+       clk_disable_unprepare(hdmi->iahb_clk);
+       clk_disable_unprepare(hdmi->isfr_clk);
+       i2c_put_adapter(hdmi->ddc);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
+MODULE_DESCRIPTION("DW HDMI transmitter driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dw-hdmi");
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
new file mode 100644 (file)
index 0000000..fc9a560
--- /dev/null
@@ -0,0 +1,1037 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __DW_HDMI_H__
+#define __DW_HDMI_H__
+
+/* Identification Registers */
+#define HDMI_DESIGN_ID                          0x0000
+#define HDMI_REVISION_ID                        0x0001
+#define HDMI_PRODUCT_ID0                        0x0002
+#define HDMI_PRODUCT_ID1                        0x0003
+#define HDMI_CONFIG0_ID                         0x0004
+#define HDMI_CONFIG1_ID                         0x0005
+#define HDMI_CONFIG2_ID                         0x0006
+#define HDMI_CONFIG3_ID                         0x0007
+
+/* Interrupt Registers */
+#define HDMI_IH_FC_STAT0                        0x0100
+#define HDMI_IH_FC_STAT1                        0x0101
+#define HDMI_IH_FC_STAT2                        0x0102
+#define HDMI_IH_AS_STAT0                        0x0103
+#define HDMI_IH_PHY_STAT0                       0x0104
+#define HDMI_IH_I2CM_STAT0                      0x0105
+#define HDMI_IH_CEC_STAT0                       0x0106
+#define HDMI_IH_VP_STAT0                        0x0107
+#define HDMI_IH_I2CMPHY_STAT0                   0x0108
+#define HDMI_IH_AHBDMAAUD_STAT0                 0x0109
+
+#define HDMI_IH_MUTE_FC_STAT0                   0x0180
+#define HDMI_IH_MUTE_FC_STAT1                   0x0181
+#define HDMI_IH_MUTE_FC_STAT2                   0x0182
+#define HDMI_IH_MUTE_AS_STAT0                   0x0183
+#define HDMI_IH_MUTE_PHY_STAT0                  0x0184
+#define HDMI_IH_MUTE_I2CM_STAT0                 0x0185
+#define HDMI_IH_MUTE_CEC_STAT0                  0x0186
+#define HDMI_IH_MUTE_VP_STAT0                   0x0187
+#define HDMI_IH_MUTE_I2CMPHY_STAT0              0x0188
+#define HDMI_IH_MUTE_AHBDMAAUD_STAT0            0x0189
+#define HDMI_IH_MUTE                            0x01FF
+
+/* Video Sample Registers */
+#define HDMI_TX_INVID0                          0x0200
+#define HDMI_TX_INSTUFFING                      0x0201
+#define HDMI_TX_GYDATA0                         0x0202
+#define HDMI_TX_GYDATA1                         0x0203
+#define HDMI_TX_RCRDATA0                        0x0204
+#define HDMI_TX_RCRDATA1                        0x0205
+#define HDMI_TX_BCBDATA0                        0x0206
+#define HDMI_TX_BCBDATA1                        0x0207
+
+/* Video Packetizer Registers */
+#define HDMI_VP_STATUS                          0x0800
+#define HDMI_VP_PR_CD                           0x0801
+#define HDMI_VP_STUFF                           0x0802
+#define HDMI_VP_REMAP                           0x0803
+#define HDMI_VP_CONF                            0x0804
+#define HDMI_VP_STAT                            0x0805
+#define HDMI_VP_INT                             0x0806
+#define HDMI_VP_MASK                            0x0807
+#define HDMI_VP_POL                             0x0808
+
+/* Frame Composer Registers */
+#define HDMI_FC_INVIDCONF                       0x1000
+#define HDMI_FC_INHACTV0                        0x1001
+#define HDMI_FC_INHACTV1                        0x1002
+#define HDMI_FC_INHBLANK0                       0x1003
+#define HDMI_FC_INHBLANK1                       0x1004
+#define HDMI_FC_INVACTV0                        0x1005
+#define HDMI_FC_INVACTV1                        0x1006
+#define HDMI_FC_INVBLANK                        0x1007
+#define HDMI_FC_HSYNCINDELAY0                   0x1008
+#define HDMI_FC_HSYNCINDELAY1                   0x1009
+#define HDMI_FC_HSYNCINWIDTH0                   0x100A
+#define HDMI_FC_HSYNCINWIDTH1                   0x100B
+#define HDMI_FC_VSYNCINDELAY                    0x100C
+#define HDMI_FC_VSYNCINWIDTH                    0x100D
+#define HDMI_FC_INFREQ0                         0x100E
+#define HDMI_FC_INFREQ1                         0x100F
+#define HDMI_FC_INFREQ2                         0x1010
+#define HDMI_FC_CTRLDUR                         0x1011
+#define HDMI_FC_EXCTRLDUR                       0x1012
+#define HDMI_FC_EXCTRLSPAC                      0x1013
+#define HDMI_FC_CH0PREAM                        0x1014
+#define HDMI_FC_CH1PREAM                        0x1015
+#define HDMI_FC_CH2PREAM                        0x1016
+#define HDMI_FC_AVICONF3                        0x1017
+#define HDMI_FC_GCP                             0x1018
+#define HDMI_FC_AVICONF0                        0x1019
+#define HDMI_FC_AVICONF1                        0x101A
+#define HDMI_FC_AVICONF2                        0x101B
+#define HDMI_FC_AVIVID                          0x101C
+#define HDMI_FC_AVIETB0                         0x101D
+#define HDMI_FC_AVIETB1                         0x101E
+#define HDMI_FC_AVISBB0                         0x101F
+#define HDMI_FC_AVISBB1                         0x1020
+#define HDMI_FC_AVIELB0                         0x1021
+#define HDMI_FC_AVIELB1                         0x1022
+#define HDMI_FC_AVISRB0                         0x1023
+#define HDMI_FC_AVISRB1                         0x1024
+#define HDMI_FC_AUDICONF0                       0x1025
+#define HDMI_FC_AUDICONF1                       0x1026
+#define HDMI_FC_AUDICONF2                       0x1027
+#define HDMI_FC_AUDICONF3                       0x1028
+#define HDMI_FC_VSDIEEEID0                      0x1029
+#define HDMI_FC_VSDSIZE                         0x102A
+#define HDMI_FC_VSDIEEEID1                      0x1030
+#define HDMI_FC_VSDIEEEID2                      0x1031
+#define HDMI_FC_VSDPAYLOAD0                     0x1032
+#define HDMI_FC_VSDPAYLOAD1                     0x1033
+#define HDMI_FC_VSDPAYLOAD2                     0x1034
+#define HDMI_FC_VSDPAYLOAD3                     0x1035
+#define HDMI_FC_VSDPAYLOAD4                     0x1036
+#define HDMI_FC_VSDPAYLOAD5                     0x1037
+#define HDMI_FC_VSDPAYLOAD6                     0x1038
+#define HDMI_FC_VSDPAYLOAD7                     0x1039
+#define HDMI_FC_VSDPAYLOAD8                     0x103A
+#define HDMI_FC_VSDPAYLOAD9                     0x103B
+#define HDMI_FC_VSDPAYLOAD10                    0x103C
+#define HDMI_FC_VSDPAYLOAD11                    0x103D
+#define HDMI_FC_VSDPAYLOAD12                    0x103E
+#define HDMI_FC_VSDPAYLOAD13                    0x103F
+#define HDMI_FC_VSDPAYLOAD14                    0x1040
+#define HDMI_FC_VSDPAYLOAD15                    0x1041
+#define HDMI_FC_VSDPAYLOAD16                    0x1042
+#define HDMI_FC_VSDPAYLOAD17                    0x1043
+#define HDMI_FC_VSDPAYLOAD18                    0x1044
+#define HDMI_FC_VSDPAYLOAD19                    0x1045
+#define HDMI_FC_VSDPAYLOAD20                    0x1046
+#define HDMI_FC_VSDPAYLOAD21                    0x1047
+#define HDMI_FC_VSDPAYLOAD22                    0x1048
+#define HDMI_FC_VSDPAYLOAD23                    0x1049
+#define HDMI_FC_SPDVENDORNAME0                  0x104A
+#define HDMI_FC_SPDVENDORNAME1                  0x104B
+#define HDMI_FC_SPDVENDORNAME2                  0x104C
+#define HDMI_FC_SPDVENDORNAME3                  0x104D
+#define HDMI_FC_SPDVENDORNAME4                  0x104E
+#define HDMI_FC_SPDVENDORNAME5                  0x104F
+#define HDMI_FC_SPDVENDORNAME6                  0x1050
+#define HDMI_FC_SPDVENDORNAME7                  0x1051
+#define HDMI_FC_SDPPRODUCTNAME0                 0x1052
+#define HDMI_FC_SDPPRODUCTNAME1                 0x1053
+#define HDMI_FC_SDPPRODUCTNAME2                 0x1054
+#define HDMI_FC_SDPPRODUCTNAME3                 0x1055
+#define HDMI_FC_SDPPRODUCTNAME4                 0x1056
+#define HDMI_FC_SDPPRODUCTNAME5                 0x1057
+#define HDMI_FC_SDPPRODUCTNAME6                 0x1058
+#define HDMI_FC_SDPPRODUCTNAME7                 0x1059
+#define HDMI_FC_SDPPRODUCTNAME8                 0x105A
+#define HDMI_FC_SDPPRODUCTNAME9                 0x105B
+#define HDMI_FC_SDPPRODUCTNAME10                0x105C
+#define HDMI_FC_SDPPRODUCTNAME11                0x105D
+#define HDMI_FC_SDPPRODUCTNAME12                0x105E
+#define HDMI_FC_SDPPRODUCTNAME13                0x105F
+#define HDMI_FC_SDPPRODUCTNAME14                0x1060
+#define HDMI_FC_SPDPRODUCTNAME15                0x1061
+#define HDMI_FC_SPDDEVICEINF                    0x1062
+#define HDMI_FC_AUDSCONF                        0x1063
+#define HDMI_FC_AUDSSTAT                        0x1064
+#define HDMI_FC_DATACH0FILL                     0x1070
+#define HDMI_FC_DATACH1FILL                     0x1071
+#define HDMI_FC_DATACH2FILL                     0x1072
+#define HDMI_FC_CTRLQHIGH                       0x1073
+#define HDMI_FC_CTRLQLOW                        0x1074
+#define HDMI_FC_ACP0                            0x1075
+#define HDMI_FC_ACP28                           0x1076
+#define HDMI_FC_ACP27                           0x1077
+#define HDMI_FC_ACP26                           0x1078
+#define HDMI_FC_ACP25                           0x1079
+#define HDMI_FC_ACP24                           0x107A
+#define HDMI_FC_ACP23                           0x107B
+#define HDMI_FC_ACP22                           0x107C
+#define HDMI_FC_ACP21                           0x107D
+#define HDMI_FC_ACP20                           0x107E
+#define HDMI_FC_ACP19                           0x107F
+#define HDMI_FC_ACP18                           0x1080
+#define HDMI_FC_ACP17                           0x1081
+#define HDMI_FC_ACP16                           0x1082
+#define HDMI_FC_ACP15                           0x1083
+#define HDMI_FC_ACP14                           0x1084
+#define HDMI_FC_ACP13                           0x1085
+#define HDMI_FC_ACP12                           0x1086
+#define HDMI_FC_ACP11                           0x1087
+#define HDMI_FC_ACP10                           0x1088
+#define HDMI_FC_ACP9                            0x1089
+#define HDMI_FC_ACP8                            0x108A
+#define HDMI_FC_ACP7                            0x108B
+#define HDMI_FC_ACP6                            0x108C
+#define HDMI_FC_ACP5                            0x108D
+#define HDMI_FC_ACP4                            0x108E
+#define HDMI_FC_ACP3                            0x108F
+#define HDMI_FC_ACP2                            0x1090
+#define HDMI_FC_ACP1                            0x1091
+#define HDMI_FC_ISCR1_0                         0x1092
+#define HDMI_FC_ISCR1_16                        0x1093
+#define HDMI_FC_ISCR1_15                        0x1094
+#define HDMI_FC_ISCR1_14                        0x1095
+#define HDMI_FC_ISCR1_13                        0x1096
+#define HDMI_FC_ISCR1_12                        0x1097
+#define HDMI_FC_ISCR1_11                        0x1098
+#define HDMI_FC_ISCR1_10                        0x1099
+#define HDMI_FC_ISCR1_9                         0x109A
+#define HDMI_FC_ISCR1_8                         0x109B
+#define HDMI_FC_ISCR1_7                         0x109C
+#define HDMI_FC_ISCR1_6                         0x109D
+#define HDMI_FC_ISCR1_5                         0x109E
+#define HDMI_FC_ISCR1_4                         0x109F
+#define HDMI_FC_ISCR1_3                         0x10A0
+#define HDMI_FC_ISCR1_2                         0x10A1
+#define HDMI_FC_ISCR1_1                         0x10A2
+#define HDMI_FC_ISCR2_15                        0x10A3
+#define HDMI_FC_ISCR2_14                        0x10A4
+#define HDMI_FC_ISCR2_13                        0x10A5
+#define HDMI_FC_ISCR2_12                        0x10A6
+#define HDMI_FC_ISCR2_11                        0x10A7
+#define HDMI_FC_ISCR2_10                        0x10A8
+#define HDMI_FC_ISCR2_9                         0x10A9
+#define HDMI_FC_ISCR2_8                         0x10AA
+#define HDMI_FC_ISCR2_7                         0x10AB
+#define HDMI_FC_ISCR2_6                         0x10AC
+#define HDMI_FC_ISCR2_5                         0x10AD
+#define HDMI_FC_ISCR2_4                         0x10AE
+#define HDMI_FC_ISCR2_3                         0x10AF
+#define HDMI_FC_ISCR2_2                         0x10B0
+#define HDMI_FC_ISCR2_1                         0x10B1
+#define HDMI_FC_ISCR2_0                         0x10B2
+#define HDMI_FC_DATAUTO0                        0x10B3
+#define HDMI_FC_DATAUTO1                        0x10B4
+#define HDMI_FC_DATAUTO2                        0x10B5
+#define HDMI_FC_DATMAN                          0x10B6
+#define HDMI_FC_DATAUTO3                        0x10B7
+#define HDMI_FC_RDRB0                           0x10B8
+#define HDMI_FC_RDRB1                           0x10B9
+#define HDMI_FC_RDRB2                           0x10BA
+#define HDMI_FC_RDRB3                           0x10BB
+#define HDMI_FC_RDRB4                           0x10BC
+#define HDMI_FC_RDRB5                           0x10BD
+#define HDMI_FC_RDRB6                           0x10BE
+#define HDMI_FC_RDRB7                           0x10BF
+#define HDMI_FC_STAT0                           0x10D0
+#define HDMI_FC_INT0                            0x10D1
+#define HDMI_FC_MASK0                           0x10D2
+#define HDMI_FC_POL0                            0x10D3
+#define HDMI_FC_STAT1                           0x10D4
+#define HDMI_FC_INT1                            0x10D5
+#define HDMI_FC_MASK1                           0x10D6
+#define HDMI_FC_POL1                            0x10D7
+#define HDMI_FC_STAT2                           0x10D8
+#define HDMI_FC_INT2                            0x10D9
+#define HDMI_FC_MASK2                           0x10DA
+#define HDMI_FC_POL2                            0x10DB
+#define HDMI_FC_PRCONF                          0x10E0
+
+#define HDMI_FC_GMD_STAT                        0x1100
+#define HDMI_FC_GMD_EN                          0x1101
+#define HDMI_FC_GMD_UP                          0x1102
+#define HDMI_FC_GMD_CONF                        0x1103
+#define HDMI_FC_GMD_HB                          0x1104
+#define HDMI_FC_GMD_PB0                         0x1105
+#define HDMI_FC_GMD_PB1                         0x1106
+#define HDMI_FC_GMD_PB2                         0x1107
+#define HDMI_FC_GMD_PB3                         0x1108
+#define HDMI_FC_GMD_PB4                         0x1109
+#define HDMI_FC_GMD_PB5                         0x110A
+#define HDMI_FC_GMD_PB6                         0x110B
+#define HDMI_FC_GMD_PB7                         0x110C
+#define HDMI_FC_GMD_PB8                         0x110D
+#define HDMI_FC_GMD_PB9                         0x110E
+#define HDMI_FC_GMD_PB10                        0x110F
+#define HDMI_FC_GMD_PB11                        0x1110
+#define HDMI_FC_GMD_PB12                        0x1111
+#define HDMI_FC_GMD_PB13                        0x1112
+#define HDMI_FC_GMD_PB14                        0x1113
+#define HDMI_FC_GMD_PB15                        0x1114
+#define HDMI_FC_GMD_PB16                        0x1115
+#define HDMI_FC_GMD_PB17                        0x1116
+#define HDMI_FC_GMD_PB18                        0x1117
+#define HDMI_FC_GMD_PB19                        0x1118
+#define HDMI_FC_GMD_PB20                        0x1119
+#define HDMI_FC_GMD_PB21                        0x111A
+#define HDMI_FC_GMD_PB22                        0x111B
+#define HDMI_FC_GMD_PB23                        0x111C
+#define HDMI_FC_GMD_PB24                        0x111D
+#define HDMI_FC_GMD_PB25                        0x111E
+#define HDMI_FC_GMD_PB26                        0x111F
+#define HDMI_FC_GMD_PB27                        0x1120
+
+#define HDMI_FC_DBGFORCE                        0x1200
+#define HDMI_FC_DBGAUD0CH0                      0x1201
+#define HDMI_FC_DBGAUD1CH0                      0x1202
+#define HDMI_FC_DBGAUD2CH0                      0x1203
+#define HDMI_FC_DBGAUD0CH1                      0x1204
+#define HDMI_FC_DBGAUD1CH1                      0x1205
+#define HDMI_FC_DBGAUD2CH1                      0x1206
+#define HDMI_FC_DBGAUD0CH2                      0x1207
+#define HDMI_FC_DBGAUD1CH2                      0x1208
+#define HDMI_FC_DBGAUD2CH2                      0x1209
+#define HDMI_FC_DBGAUD0CH3                      0x120A
+#define HDMI_FC_DBGAUD1CH3                      0x120B
+#define HDMI_FC_DBGAUD2CH3                      0x120C
+#define HDMI_FC_DBGAUD0CH4                      0x120D
+#define HDMI_FC_DBGAUD1CH4                      0x120E
+#define HDMI_FC_DBGAUD2CH4                      0x120F
+#define HDMI_FC_DBGAUD0CH5                      0x1210
+#define HDMI_FC_DBGAUD1CH5                      0x1211
+#define HDMI_FC_DBGAUD2CH5                      0x1212
+#define HDMI_FC_DBGAUD0CH6                      0x1213
+#define HDMI_FC_DBGAUD1CH6                      0x1214
+#define HDMI_FC_DBGAUD2CH6                      0x1215
+#define HDMI_FC_DBGAUD0CH7                      0x1216
+#define HDMI_FC_DBGAUD1CH7                      0x1217
+#define HDMI_FC_DBGAUD2CH7                      0x1218
+#define HDMI_FC_DBGTMDS0                        0x1219
+#define HDMI_FC_DBGTMDS1                        0x121A
+#define HDMI_FC_DBGTMDS2                        0x121B
+
+/* HDMI Source PHY Registers */
+#define HDMI_PHY_CONF0                          0x3000
+#define HDMI_PHY_TST0                           0x3001
+#define HDMI_PHY_TST1                           0x3002
+#define HDMI_PHY_TST2                           0x3003
+#define HDMI_PHY_STAT0                          0x3004
+#define HDMI_PHY_INT0                           0x3005
+#define HDMI_PHY_MASK0                          0x3006
+#define HDMI_PHY_POL0                           0x3007
+
+/* HDMI Master PHY Registers */
+#define HDMI_PHY_I2CM_SLAVE_ADDR                0x3020
+#define HDMI_PHY_I2CM_ADDRESS_ADDR              0x3021
+#define HDMI_PHY_I2CM_DATAO_1_ADDR              0x3022
+#define HDMI_PHY_I2CM_DATAO_0_ADDR              0x3023
+#define HDMI_PHY_I2CM_DATAI_1_ADDR              0x3024
+#define HDMI_PHY_I2CM_DATAI_0_ADDR              0x3025
+#define HDMI_PHY_I2CM_OPERATION_ADDR            0x3026
+#define HDMI_PHY_I2CM_INT_ADDR                  0x3027
+#define HDMI_PHY_I2CM_CTLINT_ADDR               0x3028
+#define HDMI_PHY_I2CM_DIV_ADDR                  0x3029
+#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR             0x302a
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR        0x302b
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR        0x302c
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR        0x302d
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR        0x302e
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR        0x302f
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR        0x3030
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR        0x3031
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR        0x3032
+
+/* Audio Sampler Registers */
+#define HDMI_AUD_CONF0                          0x3100
+#define HDMI_AUD_CONF1                          0x3101
+#define HDMI_AUD_INT                            0x3102
+#define HDMI_AUD_CONF2                          0x3103
+#define HDMI_AUD_N1                             0x3200
+#define HDMI_AUD_N2                             0x3201
+#define HDMI_AUD_N3                             0x3202
+#define HDMI_AUD_CTS1                           0x3203
+#define HDMI_AUD_CTS2                           0x3204
+#define HDMI_AUD_CTS3                           0x3205
+#define HDMI_AUD_INPUTCLKFS                     0x3206
+#define HDMI_AUD_SPDIFINT                      0x3302
+#define HDMI_AUD_CONF0_HBR                      0x3400
+#define HDMI_AUD_HBR_STATUS                     0x3401
+#define HDMI_AUD_HBR_INT                        0x3402
+#define HDMI_AUD_HBR_POL                        0x3403
+#define HDMI_AUD_HBR_MASK                       0x3404
+
+/*
+ * Generic Parallel Audio Interface Registers
+ * Not used as GPAUD interface is not enabled in hw
+ */
+#define HDMI_GP_CONF0                           0x3500
+#define HDMI_GP_CONF1                           0x3501
+#define HDMI_GP_CONF2                           0x3502
+#define HDMI_GP_STAT                            0x3503
+#define HDMI_GP_INT                             0x3504
+#define HDMI_GP_MASK                            0x3505
+#define HDMI_GP_POL                             0x3506
+
+/* Audio DMA Registers */
+#define HDMI_AHB_DMA_CONF0                      0x3600
+#define HDMI_AHB_DMA_START                      0x3601
+#define HDMI_AHB_DMA_STOP                       0x3602
+#define HDMI_AHB_DMA_THRSLD                     0x3603
+#define HDMI_AHB_DMA_STRADDR0                   0x3604
+#define HDMI_AHB_DMA_STRADDR1                   0x3605
+#define HDMI_AHB_DMA_STRADDR2                   0x3606
+#define HDMI_AHB_DMA_STRADDR3                   0x3607
+#define HDMI_AHB_DMA_STPADDR0                   0x3608
+#define HDMI_AHB_DMA_STPADDR1                   0x3609
+#define HDMI_AHB_DMA_STPADDR2                   0x360a
+#define HDMI_AHB_DMA_STPADDR3                   0x360b
+#define HDMI_AHB_DMA_BSTADDR0                   0x360c
+#define HDMI_AHB_DMA_BSTADDR1                   0x360d
+#define HDMI_AHB_DMA_BSTADDR2                   0x360e
+#define HDMI_AHB_DMA_BSTADDR3                   0x360f
+#define HDMI_AHB_DMA_MBLENGTH0                  0x3610
+#define HDMI_AHB_DMA_MBLENGTH1                  0x3611
+#define HDMI_AHB_DMA_STAT                       0x3612
+#define HDMI_AHB_DMA_INT                        0x3613
+#define HDMI_AHB_DMA_MASK                       0x3614
+#define HDMI_AHB_DMA_POL                        0x3615
+#define HDMI_AHB_DMA_CONF1                      0x3616
+#define HDMI_AHB_DMA_BUFFSTAT                   0x3617
+#define HDMI_AHB_DMA_BUFFINT                    0x3618
+#define HDMI_AHB_DMA_BUFFMASK                   0x3619
+#define HDMI_AHB_DMA_BUFFPOL                    0x361a
+
+/* Main Controller Registers */
+#define HDMI_MC_SFRDIV                          0x4000
+#define HDMI_MC_CLKDIS                          0x4001
+#define HDMI_MC_SWRSTZ                          0x4002
+#define HDMI_MC_OPCTRL                          0x4003
+#define HDMI_MC_FLOWCTRL                        0x4004
+#define HDMI_MC_PHYRSTZ                         0x4005
+#define HDMI_MC_LOCKONCLOCK                     0x4006
+#define HDMI_MC_HEACPHY_RST                     0x4007
+
+/* Color Space  Converter Registers */
+#define HDMI_CSC_CFG                            0x4100
+#define HDMI_CSC_SCALE                          0x4101
+#define HDMI_CSC_COEF_A1_MSB                    0x4102
+#define HDMI_CSC_COEF_A1_LSB                    0x4103
+#define HDMI_CSC_COEF_A2_MSB                    0x4104
+#define HDMI_CSC_COEF_A2_LSB                    0x4105
+#define HDMI_CSC_COEF_A3_MSB                    0x4106
+#define HDMI_CSC_COEF_A3_LSB                    0x4107
+#define HDMI_CSC_COEF_A4_MSB                    0x4108
+#define HDMI_CSC_COEF_A4_LSB                    0x4109
+#define HDMI_CSC_COEF_B1_MSB                    0x410A
+#define HDMI_CSC_COEF_B1_LSB                    0x410B
+#define HDMI_CSC_COEF_B2_MSB                    0x410C
+#define HDMI_CSC_COEF_B2_LSB                    0x410D
+#define HDMI_CSC_COEF_B3_MSB                    0x410E
+#define HDMI_CSC_COEF_B3_LSB                    0x410F
+#define HDMI_CSC_COEF_B4_MSB                    0x4110
+#define HDMI_CSC_COEF_B4_LSB                    0x4111
+#define HDMI_CSC_COEF_C1_MSB                    0x4112
+#define HDMI_CSC_COEF_C1_LSB                    0x4113
+#define HDMI_CSC_COEF_C2_MSB                    0x4114
+#define HDMI_CSC_COEF_C2_LSB                    0x4115
+#define HDMI_CSC_COEF_C3_MSB                    0x4116
+#define HDMI_CSC_COEF_C3_LSB                    0x4117
+#define HDMI_CSC_COEF_C4_MSB                    0x4118
+#define HDMI_CSC_COEF_C4_LSB                    0x4119
+
+/* HDCP Encryption Engine Registers */
+#define HDMI_A_HDCPCFG0                         0x5000
+#define HDMI_A_HDCPCFG1                         0x5001
+#define HDMI_A_HDCPOBS0                         0x5002
+#define HDMI_A_HDCPOBS1                         0x5003
+#define HDMI_A_HDCPOBS2                         0x5004
+#define HDMI_A_HDCPOBS3                         0x5005
+#define HDMI_A_APIINTCLR                        0x5006
+#define HDMI_A_APIINTSTAT                       0x5007
+#define HDMI_A_APIINTMSK                        0x5008
+#define HDMI_A_VIDPOLCFG                        0x5009
+#define HDMI_A_OESSWCFG                         0x500A
+#define HDMI_A_TIMER1SETUP0                     0x500B
+#define HDMI_A_TIMER1SETUP1                     0x500C
+#define HDMI_A_TIMER2SETUP0                     0x500D
+#define HDMI_A_TIMER2SETUP1                     0x500E
+#define HDMI_A_100MSCFG                         0x500F
+#define HDMI_A_2SCFG0                           0x5010
+#define HDMI_A_2SCFG1                           0x5011
+#define HDMI_A_5SCFG0                           0x5012
+#define HDMI_A_5SCFG1                           0x5013
+#define HDMI_A_SRMVERLSB                        0x5014
+#define HDMI_A_SRMVERMSB                        0x5015
+#define HDMI_A_SRMCTRL                          0x5016
+#define HDMI_A_SFRSETUP                         0x5017
+#define HDMI_A_I2CHSETUP                        0x5018
+#define HDMI_A_INTSETUP                         0x5019
+#define HDMI_A_PRESETUP                         0x501A
+#define HDMI_A_SRM_BASE                         0x5020
+
+/* CEC Engine Registers */
+#define HDMI_CEC_CTRL                           0x7D00
+#define HDMI_CEC_STAT                           0x7D01
+#define HDMI_CEC_MASK                           0x7D02
+#define HDMI_CEC_POLARITY                       0x7D03
+#define HDMI_CEC_INT                            0x7D04
+#define HDMI_CEC_ADDR_L                         0x7D05
+#define HDMI_CEC_ADDR_H                         0x7D06
+#define HDMI_CEC_TX_CNT                         0x7D07
+#define HDMI_CEC_RX_CNT                         0x7D08
+#define HDMI_CEC_TX_DATA0                       0x7D10
+#define HDMI_CEC_TX_DATA1                       0x7D11
+#define HDMI_CEC_TX_DATA2                       0x7D12
+#define HDMI_CEC_TX_DATA3                       0x7D13
+#define HDMI_CEC_TX_DATA4                       0x7D14
+#define HDMI_CEC_TX_DATA5                       0x7D15
+#define HDMI_CEC_TX_DATA6                       0x7D16
+#define HDMI_CEC_TX_DATA7                       0x7D17
+#define HDMI_CEC_TX_DATA8                       0x7D18
+#define HDMI_CEC_TX_DATA9                       0x7D19
+#define HDMI_CEC_TX_DATA10                      0x7D1a
+#define HDMI_CEC_TX_DATA11                      0x7D1b
+#define HDMI_CEC_TX_DATA12                      0x7D1c
+#define HDMI_CEC_TX_DATA13                      0x7D1d
+#define HDMI_CEC_TX_DATA14                      0x7D1e
+#define HDMI_CEC_TX_DATA15                      0x7D1f
+#define HDMI_CEC_RX_DATA0                       0x7D20
+#define HDMI_CEC_RX_DATA1                       0x7D21
+#define HDMI_CEC_RX_DATA2                       0x7D22
+#define HDMI_CEC_RX_DATA3                       0x7D23
+#define HDMI_CEC_RX_DATA4                       0x7D24
+#define HDMI_CEC_RX_DATA5                       0x7D25
+#define HDMI_CEC_RX_DATA6                       0x7D26
+#define HDMI_CEC_RX_DATA7                       0x7D27
+#define HDMI_CEC_RX_DATA8                       0x7D28
+#define HDMI_CEC_RX_DATA9                       0x7D29
+#define HDMI_CEC_RX_DATA10                      0x7D2a
+#define HDMI_CEC_RX_DATA11                      0x7D2b
+#define HDMI_CEC_RX_DATA12                      0x7D2c
+#define HDMI_CEC_RX_DATA13                      0x7D2d
+#define HDMI_CEC_RX_DATA14                      0x7D2e
+#define HDMI_CEC_RX_DATA15                      0x7D2f
+#define HDMI_CEC_LOCK                           0x7D30
+#define HDMI_CEC_WKUPCTRL                       0x7D31
+
+/* I2C Master Registers (E-DDC) */
+#define HDMI_I2CM_SLAVE                         0x7E00
+#define HDMI_I2CM_ADDRESS                       0x7E01
+#define HDMI_I2CM_DATAO                         0x7E02
+#define HDMI_I2CM_DATAI                         0x7E03
+#define HDMI_I2CM_OPERATION                     0x7E04
+#define HDMI_I2CM_INT                           0x7E05
+#define HDMI_I2CM_CTLINT                        0x7E06
+#define HDMI_I2CM_DIV                           0x7E07
+#define HDMI_I2CM_SEGADDR                       0x7E08
+#define HDMI_I2CM_SOFTRSTZ                      0x7E09
+#define HDMI_I2CM_SEGPTR                        0x7E0A
+#define HDMI_I2CM_SS_SCL_HCNT_1_ADDR            0x7E0B
+#define HDMI_I2CM_SS_SCL_HCNT_0_ADDR            0x7E0C
+#define HDMI_I2CM_SS_SCL_LCNT_1_ADDR            0x7E0D
+#define HDMI_I2CM_SS_SCL_LCNT_0_ADDR            0x7E0E
+#define HDMI_I2CM_FS_SCL_HCNT_1_ADDR            0x7E0F
+#define HDMI_I2CM_FS_SCL_HCNT_0_ADDR            0x7E10
+#define HDMI_I2CM_FS_SCL_LCNT_1_ADDR            0x7E11
+#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
+
+enum {
+/* CONFIG1_ID field values */
+       HDMI_CONFIG1_AHB = 0x01,
+
+/* IH_FC_INT2 field values */
+       HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
+       HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_FC_STAT2 field values */
+       HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03,
+       HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_PHY_STAT0 field values */
+       HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20,
+       HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10,
+       HDMI_IH_PHY_STAT0_RX_SENSE1 = 0x8,
+       HDMI_IH_PHY_STAT0_RX_SENSE0 = 0x4,
+       HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2,
+       HDMI_IH_PHY_STAT0_HPD = 0x1,
+
+/* IH_MUTE_I2CMPHY_STAT0 field values */
+       HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2,
+       HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1,
+
+/* IH_AHBDMAAUD_STAT0 field values */
+       HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20,
+       HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10,
+       HDMI_IH_AHBDMAAUD_STAT0_RETRY = 0x08,
+       HDMI_IH_AHBDMAAUD_STAT0_DONE = 0x04,
+       HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
+       HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+
+/* IH_MUTE_FC_STAT2 field values */
+       HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03,
+       HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_MUTE_AHBDMAAUD_STAT0 field values */
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = 0x08,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = 0x04,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+
+/* IH_MUTE field values */
+       HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2,
+       HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1,
+
+/* TX_INVID0 field values */
+       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_MASK = 0x80,
+       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_ENABLE = 0x80,
+       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00,
+       HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1F,
+       HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0,
+
+/* TX_INSTUFFING field values */
+       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_MASK = 0x4,
+       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4,
+       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_DISABLE = 0x0,
+       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_MASK = 0x2,
+       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2,
+       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_DISABLE = 0x0,
+       HDMI_TX_INSTUFFING_GYDATA_STUFFING_MASK = 0x1,
+       HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1,
+       HDMI_TX_INSTUFFING_GYDATA_STUFFING_DISABLE = 0x0,
+
+/* VP_PR_CD field values */
+       HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xF0,
+       HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4,
+       HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0F,
+       HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0,
+
+/* VP_STUFF field values */
+       HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20,
+       HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5,
+       HDMI_VP_STUFF_IFIX_PP_TO_LAST_MASK = 0x10,
+       HDMI_VP_STUFF_IFIX_PP_TO_LAST_OFFSET = 4,
+       HDMI_VP_STUFF_ICX_GOTO_P0_ST_MASK = 0x8,
+       HDMI_VP_STUFF_ICX_GOTO_P0_ST_OFFSET = 3,
+       HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4,
+       HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4,
+       HDMI_VP_STUFF_YCC422_STUFFING_DIRECT_MODE = 0x0,
+       HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2,
+       HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2,
+       HDMI_VP_STUFF_PP_STUFFING_DIRECT_MODE = 0x0,
+       HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1,
+       HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1,
+       HDMI_VP_STUFF_PR_STUFFING_DIRECT_MODE = 0x0,
+
+/* VP_CONF field values */
+       HDMI_VP_CONF_BYPASS_EN_MASK = 0x40,
+       HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40,
+       HDMI_VP_CONF_BYPASS_EN_DISABLE = 0x00,
+       HDMI_VP_CONF_PP_EN_ENMASK = 0x20,
+       HDMI_VP_CONF_PP_EN_ENABLE = 0x20,
+       HDMI_VP_CONF_PP_EN_DISABLE = 0x00,
+       HDMI_VP_CONF_PR_EN_MASK = 0x10,
+       HDMI_VP_CONF_PR_EN_ENABLE = 0x10,
+       HDMI_VP_CONF_PR_EN_DISABLE = 0x00,
+       HDMI_VP_CONF_YCC422_EN_MASK = 0x8,
+       HDMI_VP_CONF_YCC422_EN_ENABLE = 0x8,
+       HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0,
+       HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4,
+       HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4,
+       HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER = 0x0,
+       HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3,
+       HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3,
+       HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422 = 0x1,
+       HDMI_VP_CONF_OUTPUT_SELECTOR_PP = 0x0,
+
+/* VP_REMAP field values */
+       HDMI_VP_REMAP_MASK = 0x3,
+       HDMI_VP_REMAP_YCC422_24bit = 0x2,
+       HDMI_VP_REMAP_YCC422_20bit = 0x1,
+       HDMI_VP_REMAP_YCC422_16bit = 0x0,
+
+/* FC_INVIDCONF field values */
+       HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK = 0x80,
+       HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE = 0x80,
+       HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE = 0x00,
+       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40,
+       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40,
+       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
+       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20,
+       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20,
+       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
+       HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10,
+       HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10,
+       HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00,
+       HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8,
+       HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8,
+       HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0,
+       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2,
+       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2,
+       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0,
+       HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1,
+       HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1,
+       HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0,
+
+/* FC_AUDICONF0 field values */
+       HDMI_FC_AUDICONF0_CC_OFFSET = 4,
+       HDMI_FC_AUDICONF0_CC_MASK = 0x70,
+       HDMI_FC_AUDICONF0_CT_OFFSET = 0,
+       HDMI_FC_AUDICONF0_CT_MASK = 0xF,
+
+/* FC_AUDICONF1 field values */
+       HDMI_FC_AUDICONF1_SS_OFFSET = 3,
+       HDMI_FC_AUDICONF1_SS_MASK = 0x18,
+       HDMI_FC_AUDICONF1_SF_OFFSET = 0,
+       HDMI_FC_AUDICONF1_SF_MASK = 0x7,
+
+/* FC_AUDICONF3 field values */
+       HDMI_FC_AUDICONF3_LFEPBL_OFFSET = 5,
+       HDMI_FC_AUDICONF3_LFEPBL_MASK = 0x60,
+       HDMI_FC_AUDICONF3_DM_INH_OFFSET = 4,
+       HDMI_FC_AUDICONF3_DM_INH_MASK = 0x10,
+       HDMI_FC_AUDICONF3_LSV_OFFSET = 0,
+       HDMI_FC_AUDICONF3_LSV_MASK = 0xF,
+
+/* FC_AUDSCHNLS0 field values */
+       HDMI_FC_AUDSCHNLS0_CGMSA_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS0_CGMSA_MASK = 0x30,
+       HDMI_FC_AUDSCHNLS0_COPYRIGHT_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS0_COPYRIGHT_MASK = 0x01,
+
+/* FC_AUDSCHNLS3-6 field values */
+       HDMI_FC_AUDSCHNLS3_OIEC_CH0_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS3_OIEC_CH0_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS3_OIEC_CH1_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS3_OIEC_CH1_MASK = 0xf0,
+       HDMI_FC_AUDSCHNLS4_OIEC_CH2_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS4_OIEC_CH2_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS4_OIEC_CH3_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS4_OIEC_CH3_MASK = 0xf0,
+
+       HDMI_FC_AUDSCHNLS5_OIEC_CH0_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS5_OIEC_CH0_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS5_OIEC_CH1_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS5_OIEC_CH1_MASK = 0xf0,
+       HDMI_FC_AUDSCHNLS6_OIEC_CH2_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS6_OIEC_CH2_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS6_OIEC_CH3_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS6_OIEC_CH3_MASK = 0xf0,
+
+/* HDMI_FC_AUDSCHNLS7 field values */
+       HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30,
+
+/* HDMI_FC_AUDSCHNLS8 field values */
+       HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0,
+       HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0,
+
+/* FC_AUDSCONF field values */
+       HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0,
+       HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4,
+       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK = 0x1,
+       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_OFFSET = 0,
+       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1,
+       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0,
+
+/* FC_STAT2 field values */
+       HDMI_FC_STAT2_OVERFLOW_MASK = 0x03,
+       HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_INT2 field values */
+       HDMI_FC_INT2_OVERFLOW_MASK = 0x03,
+       HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_MASK2 field values */
+       HDMI_FC_MASK2_OVERFLOW_MASK = 0x03,
+       HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_PRCONF field values */
+       HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0,
+       HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4,
+       HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F,
+       HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0,
+
+/* FC_AVICONF0-FC_AVICONF3 field values */
+       HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03,
+       HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00,
+       HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x01,
+       HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x02,
+       HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x40,
+       HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x40,
+       HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00,
+       HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0C,
+       HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00,
+       HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x04,
+       HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x08,
+       HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0x0C,
+       HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x30,
+       HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x10,
+       HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x20,
+       HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00,
+
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0F,
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x08,
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09,
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0A,
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0B,
+       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30,
+       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00,
+       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10,
+       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20,
+       HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xC0,
+       HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00,
+       HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40,
+       HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80,
+       HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xC0,
+
+       HDMI_FC_AVICONF2_SCALING_MASK = 0x03,
+       HDMI_FC_AVICONF2_SCALING_NONE = 0x00,
+       HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01,
+       HDMI_FC_AVICONF2_SCALING_VERT = 0x02,
+       HDMI_FC_AVICONF2_SCALING_HORIZ_VERT = 0x03,
+       HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0C,
+       HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00,
+       HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04,
+       HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40,
+       HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80,
+       HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00,
+       HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80,
+
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03,
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS = 0x00,
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_PHOTO = 0x01,
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_CINEMA = 0x02,
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GAME = 0x03,
+       HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0C,
+       HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
+       HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
+
+/* FC_DBGFORCE field values */
+       HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
+       HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
+
+/* PHY_CONF0 field values */
+       HDMI_PHY_CONF0_PDZ_MASK = 0x80,
+       HDMI_PHY_CONF0_PDZ_OFFSET = 7,
+       HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
+       HDMI_PHY_CONF0_ENTMDS_OFFSET = 6,
+       HDMI_PHY_CONF0_SPARECTRL_MASK = 0x20,
+       HDMI_PHY_CONF0_SPARECTRL_OFFSET = 5,
+       HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10,
+       HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
+       HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
+       HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3,
+       HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4,
+       HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2,
+       HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2,
+       HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1,
+       HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1,
+       HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0,
+
+/* PHY_TST0 field values */
+       HDMI_PHY_TST0_TSTCLR_MASK = 0x20,
+       HDMI_PHY_TST0_TSTCLR_OFFSET = 5,
+       HDMI_PHY_TST0_TSTEN_MASK = 0x10,
+       HDMI_PHY_TST0_TSTEN_OFFSET = 4,
+       HDMI_PHY_TST0_TSTCLK_MASK = 0x1,
+       HDMI_PHY_TST0_TSTCLK_OFFSET = 0,
+
+/* PHY_STAT0 field values */
+       HDMI_PHY_RX_SENSE3 = 0x80,
+       HDMI_PHY_RX_SENSE2 = 0x40,
+       HDMI_PHY_RX_SENSE1 = 0x20,
+       HDMI_PHY_RX_SENSE0 = 0x10,
+       HDMI_PHY_HPD = 0x02,
+       HDMI_PHY_TX_PHY_LOCK = 0x01,
+
+/* PHY_I2CM_SLAVE_ADDR field values */
+       HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69,
+       HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49,
+
+/* PHY_I2CM_OPERATION_ADDR field values */
+       HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10,
+       HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1,
+
+/* HDMI_PHY_I2CM_INT_ADDR */
+       HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08,
+       HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04,
+
+/* HDMI_PHY_I2CM_CTLINT_ADDR */
+       HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80,
+       HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40,
+       HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08,
+       HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
+
+/* AUD_CTS3 field values */
+       HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
+       HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
+       HDMI_AUD_CTS3_N_SHIFT_1 = 0,
+       HDMI_AUD_CTS3_N_SHIFT_16 = 0x20,
+       HDMI_AUD_CTS3_N_SHIFT_32 = 0x40,
+       HDMI_AUD_CTS3_N_SHIFT_64 = 0x60,
+       HDMI_AUD_CTS3_N_SHIFT_128 = 0x80,
+       HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0,
+       /* note that the CTS3 MANUAL bit has been removed
+          from our part. Can't set it, will read as 0. */
+       HDMI_AUD_CTS3_CTS_MANUAL = 0x10,
+       HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f,
+
+/* AHB_DMA_CONF0 field values */
+       HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7,
+       HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80,
+       HDMI_AHB_DMA_CONF0_HBR = 0x10,
+       HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3,
+       HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08,
+       HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1,
+       HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK = 0x06,
+       HDMI_AHB_DMA_CONF0_INCR4 = 0x0,
+       HDMI_AHB_DMA_CONF0_INCR8 = 0x2,
+       HDMI_AHB_DMA_CONF0_INCR16 = 0x4,
+       HDMI_AHB_DMA_CONF0_BURST_MODE = 0x1,
+
+/* HDMI_AHB_DMA_START field values */
+       HDMI_AHB_DMA_START_START_OFFSET = 0,
+       HDMI_AHB_DMA_START_START_MASK = 0x01,
+
+/* HDMI_AHB_DMA_STOP field values */
+       HDMI_AHB_DMA_STOP_STOP_OFFSET = 0,
+       HDMI_AHB_DMA_STOP_STOP_MASK = 0x01,
+
+/* AHB_DMA_STAT, AHB_DMA_INT, AHB_DMA_MASK, AHB_DMA_POL field values */
+       HDMI_AHB_DMA_DONE = 0x80,
+       HDMI_AHB_DMA_RETRY_SPLIT = 0x40,
+       HDMI_AHB_DMA_LOSTOWNERSHIP = 0x20,
+       HDMI_AHB_DMA_ERROR = 0x10,
+       HDMI_AHB_DMA_FIFO_THREMPTY = 0x04,
+       HDMI_AHB_DMA_FIFO_FULL = 0x02,
+       HDMI_AHB_DMA_FIFO_EMPTY = 0x01,
+
+/* AHB_DMA_BUFFSTAT, AHB_DMA_BUFFINT,AHB_DMA_BUFFMASK,AHB_DMA_BUFFPOL values */
+       HDMI_AHB_DMA_BUFFSTAT_FULL = 0x02,
+       HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01,
+
+/* MC_CLKDIS field values */
+       HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40,
+       HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20,
+       HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10,
+       HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8,
+       HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4,
+       HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2,
+       HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1,
+
+/* MC_SWRSTZ field values */
+       HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02,
+
+/* MC_FLOWCTRL field values */
+       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1,
+       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1,
+       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0,
+
+/* MC_PHYRSTZ field values */
+       HDMI_MC_PHYRSTZ_ASSERT = 0x0,
+       HDMI_MC_PHYRSTZ_DEASSERT = 0x1,
+
+/* MC_HEACPHY_RST field values */
+       HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
+       HDMI_MC_HEACPHY_RST_DEASSERT = 0x0,
+
+/* CSC_CFG field values */
+       HDMI_CSC_CFG_INTMODE_MASK = 0x30,
+       HDMI_CSC_CFG_INTMODE_OFFSET = 4,
+       HDMI_CSC_CFG_INTMODE_DISABLE = 0x00,
+       HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1 = 0x10,
+       HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA2 = 0x20,
+       HDMI_CSC_CFG_DECMODE_MASK = 0x3,
+       HDMI_CSC_CFG_DECMODE_OFFSET = 0,
+       HDMI_CSC_CFG_DECMODE_DISABLE = 0x0,
+       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1 = 0x1,
+       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA2 = 0x2,
+       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3 = 0x3,
+
+/* CSC_SCALE field values */
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xF0,
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00,
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50,
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60,
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70,
+       HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03,
+
+/* A_HDCPCFG0 field values */
+       HDMI_A_HDCPCFG0_ELVENA_MASK = 0x80,
+       HDMI_A_HDCPCFG0_ELVENA_ENABLE = 0x80,
+       HDMI_A_HDCPCFG0_ELVENA_DISABLE = 0x00,
+       HDMI_A_HDCPCFG0_I2CFASTMODE_MASK = 0x40,
+       HDMI_A_HDCPCFG0_I2CFASTMODE_ENABLE = 0x40,
+       HDMI_A_HDCPCFG0_I2CFASTMODE_DISABLE = 0x00,
+       HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK = 0x20,
+       HDMI_A_HDCPCFG0_BYPENCRYPTION_ENABLE = 0x20,
+       HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE = 0x00,
+       HDMI_A_HDCPCFG0_SYNCRICHECK_MASK = 0x10,
+       HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE = 0x10,
+       HDMI_A_HDCPCFG0_SYNCRICHECK_DISABLE = 0x00,
+       HDMI_A_HDCPCFG0_AVMUTE_MASK = 0x8,
+       HDMI_A_HDCPCFG0_AVMUTE_ENABLE = 0x8,
+       HDMI_A_HDCPCFG0_AVMUTE_DISABLE = 0x0,
+       HDMI_A_HDCPCFG0_RXDETECT_MASK = 0x4,
+       HDMI_A_HDCPCFG0_RXDETECT_ENABLE = 0x4,
+       HDMI_A_HDCPCFG0_RXDETECT_DISABLE = 0x0,
+       HDMI_A_HDCPCFG0_EN11FEATURE_MASK = 0x2,
+       HDMI_A_HDCPCFG0_EN11FEATURE_ENABLE = 0x2,
+       HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE = 0x0,
+       HDMI_A_HDCPCFG0_HDMIDVI_MASK = 0x1,
+       HDMI_A_HDCPCFG0_HDMIDVI_HDMI = 0x1,
+       HDMI_A_HDCPCFG0_HDMIDVI_DVI = 0x0,
+
+/* A_HDCPCFG1 field values */
+       HDMI_A_HDCPCFG1_DISSHA1CHECK_MASK = 0x8,
+       HDMI_A_HDCPCFG1_DISSHA1CHECK_DISABLE = 0x8,
+       HDMI_A_HDCPCFG1_DISSHA1CHECK_ENABLE = 0x0,
+       HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK = 0x4,
+       HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE = 0x4,
+       HDMI_A_HDCPCFG1_PH2UPSHFTENC_DISABLE = 0x0,
+       HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK = 0x2,
+       HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE = 0x2,
+       HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE = 0x0,
+       HDMI_A_HDCPCFG1_SWRESET_MASK = 0x1,
+       HDMI_A_HDCPCFG1_SWRESET_ASSERT = 0x0,
+
+/* A_VIDPOLCFG field values */
+       HDMI_A_VIDPOLCFG_UNENCRYPTCONF_MASK = 0x60,
+       HDMI_A_VIDPOLCFG_UNENCRYPTCONF_OFFSET = 5,
+       HDMI_A_VIDPOLCFG_DATAENPOL_MASK = 0x10,
+       HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH = 0x10,
+       HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW = 0x0,
+       HDMI_A_VIDPOLCFG_VSYNCPOL_MASK = 0x8,
+       HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH = 0x8,
+       HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW = 0x0,
+       HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2,
+       HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2,
+       HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0,
+};
+
+#endif /* __DW_HDMI_H__ */
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c
deleted file mode 100644 (file)
index 59f630f..0000000
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * DesignWare HDMI audio driver
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Written and tested against the Designware HDMI Tx found in iMX6.
- */
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <drm/bridge/dw_hdmi.h>
-#include <drm/drm_edid.h>
-
-#include <sound/asoundef.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_drm_eld.h>
-#include <sound/pcm_iec958.h>
-
-#include "dw_hdmi-audio.h"
-
-#define DRIVER_NAME "dw-hdmi-ahb-audio"
-
-/* Provide some bits rather than bit offsets */
-enum {
-       HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
-       HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
-       HDMI_AHB_DMA_START_START = BIT(0),
-       HDMI_AHB_DMA_STOP_STOP = BIT(0),
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
-               HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
-               HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
-               HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
-               HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
-               HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
-               HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
-       HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
-       HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
-       HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
-       HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
-       HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
-       HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
-       HDMI_IH_AHBDMAAUD_STAT0_ALL =
-               HDMI_IH_AHBDMAAUD_STAT0_ERROR |
-               HDMI_IH_AHBDMAAUD_STAT0_LOST |
-               HDMI_IH_AHBDMAAUD_STAT0_RETRY |
-               HDMI_IH_AHBDMAAUD_STAT0_DONE |
-               HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
-               HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
-       HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
-       HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
-       HDMI_AHB_DMA_CONF0_INCR4 = 0,
-       HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
-       HDMI_AHB_DMA_MASK_DONE = BIT(7),
-
-       HDMI_REVISION_ID = 0x0001,
-       HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
-       HDMI_FC_AUDICONF2 = 0x1027,
-       HDMI_FC_AUDSCONF = 0x1063,
-       HDMI_FC_AUDSCONF_LAYOUT1 = 1 << 0,
-       HDMI_FC_AUDSCONF_LAYOUT0 = 0 << 0,
-       HDMI_AHB_DMA_CONF0 = 0x3600,
-       HDMI_AHB_DMA_START = 0x3601,
-       HDMI_AHB_DMA_STOP = 0x3602,
-       HDMI_AHB_DMA_THRSLD = 0x3603,
-       HDMI_AHB_DMA_STRADDR0 = 0x3604,
-       HDMI_AHB_DMA_STPADDR0 = 0x3608,
-       HDMI_AHB_DMA_MASK = 0x3614,
-       HDMI_AHB_DMA_POL = 0x3615,
-       HDMI_AHB_DMA_CONF1 = 0x3616,
-       HDMI_AHB_DMA_BUFFPOL = 0x361a,
-};
-
-struct dw_hdmi_channel_conf {
-       u8 conf1;
-       u8 ca;
-};
-
-/*
- * The default mapping of ALSA channels to HDMI channels and speaker
- * allocation bits.  Note that we can't do channel remapping here -
- * channels must be in the same order.
- *
- * Mappings for alsa-lib pcm/surround*.conf files:
- *
- *             Front   Sur4.0  Sur4.1  Sur5.0  Sur5.1  Sur7.1
- * Channels    2       4       6       6       6       8
- *
- * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
- *
- *                             Number of ALSA channels
- * ALSA Channel        2       3       4       5       6       7       8
- * 0           FL:0    =       =       =       =       =       =
- * 1           FR:1    =       =       =       =       =       =
- * 2                   FC:3    RL:4    LFE:2   =       =       =
- * 3                           RR:5    RL:4    FC:3    =       =
- * 4                                   RR:5    RL:4    =       =
- * 5                                           RR:5    =       =
- * 6                                                   RC:6    =
- * 7                                                   RLC/FRC RLC/FRC
- */
-static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
-       { 0x03, 0x00 }, /* FL,FR */
-       { 0x0b, 0x02 }, /* FL,FR,FC */
-       { 0x33, 0x08 }, /* FL,FR,RL,RR */
-       { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
-       { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
-       { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
-       { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
-};
-
-struct snd_dw_hdmi {
-       struct snd_card *card;
-       struct snd_pcm *pcm;
-       spinlock_t lock;
-       struct dw_hdmi_audio_data data;
-       struct snd_pcm_substream *substream;
-       void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
-       void *buf_src;
-       void *buf_dst;
-       dma_addr_t buf_addr;
-       unsigned buf_offset;
-       unsigned buf_period;
-       unsigned buf_size;
-       unsigned channels;
-       u8 revision;
-       u8 iec_offset;
-       u8 cs[192][8];
-};
-
-static void dw_hdmi_writel(u32 val, void __iomem *ptr)
-{
-       writeb_relaxed(val, ptr);
-       writeb_relaxed(val >> 8, ptr + 1);
-       writeb_relaxed(val >> 16, ptr + 2);
-       writeb_relaxed(val >> 24, ptr + 3);
-}
-
-/*
- * Convert to hardware format: The userspace buffer contains IEC958 samples,
- * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
- * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
- * samples in 23..0.
- *
- * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
- *
- * Ideally, we could do with having the data properly formatted in userspace.
- */
-static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
-       size_t offset, size_t bytes)
-{
-       u32 *src = dw->buf_src + offset;
-       u32 *dst = dw->buf_dst + offset;
-       u32 *end = dw->buf_src + offset + bytes;
-
-       do {
-               u32 b, sample = *src++;
-
-               b = (sample & 8) << (28 - 3);
-
-               sample >>= 4;
-
-               *dst++ = sample | b;
-       } while (src < end);
-}
-
-static u32 parity(u32 sample)
-{
-       sample ^= sample >> 16;
-       sample ^= sample >> 8;
-       sample ^= sample >> 4;
-       sample ^= sample >> 2;
-       sample ^= sample >> 1;
-       return (sample & 1) << 27;
-}
-
-static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
-       size_t offset, size_t bytes)
-{
-       u32 *src = dw->buf_src + offset;
-       u32 *dst = dw->buf_dst + offset;
-       u32 *end = dw->buf_src + offset + bytes;
-
-       do {
-               unsigned i;
-               u8 *cs;
-
-               cs = dw->cs[dw->iec_offset++];
-               if (dw->iec_offset >= 192)
-                       dw->iec_offset = 0;
-
-               i = dw->channels;
-               do {
-                       u32 sample = *src++;
-
-                       sample &= ~0xff000000;
-                       sample |= *cs++ << 24;
-                       sample |= parity(sample & ~0xf8000000);
-
-                       *dst++ = sample;
-               } while (--i);
-       } while (src < end);
-}
-
-static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
-       struct snd_pcm_runtime *runtime)
-{
-       u8 cs[4];
-       unsigned ch, i, j;
-
-       snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
-
-       memset(dw->cs, 0, sizeof(dw->cs));
-
-       for (ch = 0; ch < 8; ch++) {
-               cs[2] &= ~IEC958_AES2_CON_CHANNEL;
-               cs[2] |= (ch + 1) << 4;
-
-               for (i = 0; i < ARRAY_SIZE(cs); i++) {
-                       unsigned c = cs[i];
-
-                       for (j = 0; j < 8; j++, c >>= 1)
-                               dw->cs[i * 8 + j][ch] = (c & 1) << 2;
-               }
-       }
-       dw->cs[0][0] |= BIT(4);
-}
-
-static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
-{
-       void __iomem *base = dw->data.base;
-       unsigned offset = dw->buf_offset;
-       unsigned period = dw->buf_period;
-       u32 start, stop;
-
-       dw->reformat(dw, offset, period);
-
-       /* Clear all irqs before enabling irqs and starting DMA */
-       writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
-                      base + HDMI_IH_AHBDMAAUD_STAT0);
-
-       start = dw->buf_addr + offset;
-       stop = start + period - 1;
-
-       /* Setup the hardware start/stop addresses */
-       dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
-       dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
-
-       writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
-       writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
-
-       offset += period;
-       if (offset >= dw->buf_size)
-               offset = 0;
-       dw->buf_offset = offset;
-}
-
-static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
-{
-       /* Disable interrupts before disabling DMA */
-       writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
-       writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
-}
-
-static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
-{
-       struct snd_dw_hdmi *dw = data;
-       struct snd_pcm_substream *substream;
-       unsigned stat;
-
-       stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
-       if (!stat)
-               return IRQ_NONE;
-
-       writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
-
-       substream = dw->substream;
-       if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
-               snd_pcm_period_elapsed(substream);
-
-               spin_lock(&dw->lock);
-               if (dw->substream)
-                       dw_hdmi_start_dma(dw);
-               spin_unlock(&dw->lock);
-       }
-
-       return IRQ_HANDLED;
-}
-
-static struct snd_pcm_hardware dw_hdmi_hw = {
-       .info = SNDRV_PCM_INFO_INTERLEAVED |
-               SNDRV_PCM_INFO_BLOCK_TRANSFER |
-               SNDRV_PCM_INFO_MMAP |
-               SNDRV_PCM_INFO_MMAP_VALID,
-       .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
-                  SNDRV_PCM_FMTBIT_S24_LE,
-       .rates = SNDRV_PCM_RATE_32000 |
-                SNDRV_PCM_RATE_44100 |
-                SNDRV_PCM_RATE_48000 |
-                SNDRV_PCM_RATE_88200 |
-                SNDRV_PCM_RATE_96000 |
-                SNDRV_PCM_RATE_176400 |
-                SNDRV_PCM_RATE_192000,
-       .channels_min = 2,
-       .channels_max = 8,
-       .buffer_bytes_max = 1024 * 1024,
-       .period_bytes_min = 256,
-       .period_bytes_max = 8192,       /* ERR004323: must limit to 8k */
-       .periods_min = 2,
-       .periods_max = 16,
-       .fifo_size = 0,
-};
-
-static int dw_hdmi_open(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_dw_hdmi *dw = substream->private_data;
-       void __iomem *base = dw->data.base;
-       int ret;
-
-       runtime->hw = dw_hdmi_hw;
-
-       ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld);
-       if (ret < 0)
-               return ret;
-
-       ret = snd_pcm_limit_hw_rates(runtime);
-       if (ret < 0)
-               return ret;
-
-       ret = snd_pcm_hw_constraint_integer(runtime,
-                                           SNDRV_PCM_HW_PARAM_PERIODS);
-       if (ret < 0)
-               return ret;
-
-       /* Limit the buffer size to the size of the preallocated buffer */
-       ret = snd_pcm_hw_constraint_minmax(runtime,
-                                          SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
-                                          0, substream->dma_buffer.bytes);
-       if (ret < 0)
-               return ret;
-
-       /* Clear FIFO */
-       writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
-                      base + HDMI_AHB_DMA_CONF0);
-
-       /* Configure interrupt polarities */
-       writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
-       writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
-
-       /* Keep interrupts masked, and clear any pending */
-       writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
-       writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
-
-       ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
-                         "dw-hdmi-audio", dw);
-       if (ret)
-               return ret;
-
-       /* Un-mute done interrupt */
-       writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
-                      ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
-                      base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
-
-       return 0;
-}
-
-static int dw_hdmi_close(struct snd_pcm_substream *substream)
-{
-       struct snd_dw_hdmi *dw = substream->private_data;
-
-       /* Mute all interrupts */
-       writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
-                      dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
-
-       free_irq(dw->data.irq, dw);
-
-       return 0;
-}
-
-static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
-{
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       /* Allocate the PCM runtime buffer, which is exposed to userspace. */
-       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                               params_buffer_bytes(params));
-}
-
-static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_dw_hdmi *dw = substream->private_data;
-       u8 threshold, conf0, conf1, layout, ca;
-
-       /* Setup as per 3.0.5 FSL 4.1.0 BSP */
-       switch (dw->revision) {
-       case 0x0a:
-               conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
-                       HDMI_AHB_DMA_CONF0_INCR4;
-               if (runtime->channels == 2)
-                       threshold = 126;
-               else
-                       threshold = 124;
-               break;
-       case 0x1a:
-               conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
-                       HDMI_AHB_DMA_CONF0_INCR8;
-               threshold = 128;
-               break;
-       default:
-               /* NOTREACHED */
-               return -EINVAL;
-       }
-
-       dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
-
-       /* Minimum number of bytes in the fifo. */
-       runtime->hw.fifo_size = threshold * 32;
-
-       conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
-       conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
-       ca = default_hdmi_channel_config[runtime->channels - 2].ca;
-
-       /*
-        * For >2 channel PCM audio, we need to select layout 1
-        * and set an appropriate channel map.
-        */
-       if (runtime->channels > 2)
-               layout = HDMI_FC_AUDSCONF_LAYOUT1;
-       else
-               layout = HDMI_FC_AUDSCONF_LAYOUT0;
-
-       writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
-       writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
-       writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
-       writeb_relaxed(layout, dw->data.base + HDMI_FC_AUDSCONF);
-       writeb_relaxed(ca, dw->data.base + HDMI_FC_AUDICONF2);
-
-       switch (runtime->format) {
-       case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
-               dw->reformat = dw_hdmi_reformat_iec958;
-               break;
-       case SNDRV_PCM_FORMAT_S24_LE:
-               dw_hdmi_create_cs(dw, runtime);
-               dw->reformat = dw_hdmi_reformat_s24;
-               break;
-       }
-       dw->iec_offset = 0;
-       dw->channels = runtime->channels;
-       dw->buf_src  = runtime->dma_area;
-       dw->buf_dst  = substream->dma_buffer.area;
-       dw->buf_addr = substream->dma_buffer.addr;
-       dw->buf_period = snd_pcm_lib_period_bytes(substream);
-       dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
-
-       return 0;
-}
-
-static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct snd_dw_hdmi *dw = substream->private_data;
-       unsigned long flags;
-       int ret = 0;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               spin_lock_irqsave(&dw->lock, flags);
-               dw->buf_offset = 0;
-               dw->substream = substream;
-               dw_hdmi_start_dma(dw);
-               dw_hdmi_audio_enable(dw->data.hdmi);
-               spin_unlock_irqrestore(&dw->lock, flags);
-               substream->runtime->delay = substream->runtime->period_size;
-               break;
-
-       case SNDRV_PCM_TRIGGER_STOP:
-               spin_lock_irqsave(&dw->lock, flags);
-               dw->substream = NULL;
-               dw_hdmi_stop_dma(dw);
-               dw_hdmi_audio_disable(dw->data.hdmi);
-               spin_unlock_irqrestore(&dw->lock, flags);
-               break;
-
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_dw_hdmi *dw = substream->private_data;
-
-       /*
-        * We are unable to report the exact hardware position as
-        * reading the 32-bit DMA position using 8-bit reads is racy.
-        */
-       return bytes_to_frames(runtime, dw->buf_offset);
-}
-
-static struct snd_pcm_ops snd_dw_hdmi_ops = {
-       .open = dw_hdmi_open,
-       .close = dw_hdmi_close,
-       .ioctl = snd_pcm_lib_ioctl,
-       .hw_params = dw_hdmi_hw_params,
-       .hw_free = dw_hdmi_hw_free,
-       .prepare = dw_hdmi_prepare,
-       .trigger = dw_hdmi_trigger,
-       .pointer = dw_hdmi_pointer,
-       .page = snd_pcm_lib_get_vmalloc_page,
-};
-
-static int snd_dw_hdmi_probe(struct platform_device *pdev)
-{
-       const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
-       struct device *dev = pdev->dev.parent;
-       struct snd_dw_hdmi *dw;
-       struct snd_card *card;
-       struct snd_pcm *pcm;
-       unsigned revision;
-       int ret;
-
-       writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
-                      data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
-       revision = readb_relaxed(data->base + HDMI_REVISION_ID);
-       if (revision != 0x0a && revision != 0x1a) {
-               dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
-                       revision);
-               return -ENXIO;
-       }
-
-       ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
-                             THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
-       if (ret < 0)
-               return ret;
-
-       strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
-       strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
-       snprintf(card->longname, sizeof(card->longname),
-                "%s rev 0x%02x, irq %d", card->shortname, revision,
-                data->irq);
-
-       dw = card->private_data;
-       dw->card = card;
-       dw->data = *data;
-       dw->revision = revision;
-
-       spin_lock_init(&dw->lock);
-
-       ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
-       if (ret < 0)
-               goto err;
-
-       dw->pcm = pcm;
-       pcm->private_data = dw;
-       strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
-
-       /*
-        * To support 8-channel 96kHz audio reliably, we need 512k
-        * to satisfy alsa with our restricted period (ERR004323).
-        */
-       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-                       dev, 128 * 1024, 1024 * 1024);
-
-       ret = snd_card_register(card);
-       if (ret < 0)
-               goto err;
-
-       platform_set_drvdata(pdev, dw);
-
-       return 0;
-
-err:
-       snd_card_free(card);
-       return ret;
-}
-
-static int snd_dw_hdmi_remove(struct platform_device *pdev)
-{
-       struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
-
-       snd_card_free(dw->card);
-
-       return 0;
-}
-
-#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
-/*
- * This code is fine, but requires implementation in the dw_hdmi_trigger()
- * method which is currently missing as I have no way to test this.
- */
-static int snd_dw_hdmi_suspend(struct device *dev)
-{
-       struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
-
-       snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
-       snd_pcm_suspend_all(dw->pcm);
-
-       return 0;
-}
-
-static int snd_dw_hdmi_resume(struct device *dev)
-{
-       struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
-
-       snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
-
-       return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
-                        snd_dw_hdmi_resume);
-#define PM_OPS &snd_dw_hdmi_pm
-#else
-#define PM_OPS NULL
-#endif
-
-static struct platform_driver snd_dw_hdmi_driver = {
-       .probe  = snd_dw_hdmi_probe,
-       .remove = snd_dw_hdmi_remove,
-       .driver = {
-               .name = DRIVER_NAME,
-               .owner = THIS_MODULE,
-               .pm = PM_OPS,
-       },
-};
-
-module_platform_driver(snd_dw_hdmi_driver);
-
-MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
-MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h
deleted file mode 100644 (file)
index 91f631b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef DW_HDMI_AUDIO_H
-#define DW_HDMI_AUDIO_H
-
-struct dw_hdmi;
-
-struct dw_hdmi_audio_data {
-       phys_addr_t phys;
-       void __iomem *base;
-       int irq;
-       struct dw_hdmi *hdmi;
-       u8 *eld;
-};
-
-#endif
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
deleted file mode 100644 (file)
index 56de9f1..0000000
+++ /dev/null
@@ -1,1849 +0,0 @@
-/*
- * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Designware High-Definition Multimedia Interface (HDMI) driver
- *
- * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- */
-#include <linux/module.h>
-#include <linux/irq.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/hdmi.h>
-#include <linux/mutex.h>
-#include <linux/of_device.h>
-#include <linux/spinlock.h>
-
-#include <drm/drm_of.h>
-#include <drm/drmP.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_encoder_slave.h>
-#include <drm/bridge/dw_hdmi.h>
-
-#include "dw_hdmi.h"
-#include "dw_hdmi-audio.h"
-
-#define HDMI_EDID_LEN          512
-
-#define RGB                    0
-#define YCBCR444               1
-#define YCBCR422_16BITS                2
-#define YCBCR422_8BITS         3
-#define XVYCC444               4
-
-enum hdmi_datamap {
-       RGB444_8B = 0x01,
-       RGB444_10B = 0x03,
-       RGB444_12B = 0x05,
-       RGB444_16B = 0x07,
-       YCbCr444_8B = 0x09,
-       YCbCr444_10B = 0x0B,
-       YCbCr444_12B = 0x0D,
-       YCbCr444_16B = 0x0F,
-       YCbCr422_8B = 0x16,
-       YCbCr422_10B = 0x14,
-       YCbCr422_12B = 0x12,
-};
-
-static const u16 csc_coeff_default[3][4] = {
-       { 0x2000, 0x0000, 0x0000, 0x0000 },
-       { 0x0000, 0x2000, 0x0000, 0x0000 },
-       { 0x0000, 0x0000, 0x2000, 0x0000 }
-};
-
-static const u16 csc_coeff_rgb_out_eitu601[3][4] = {
-       { 0x2000, 0x6926, 0x74fd, 0x010e },
-       { 0x2000, 0x2cdd, 0x0000, 0x7e9a },
-       { 0x2000, 0x0000, 0x38b4, 0x7e3b }
-};
-
-static const u16 csc_coeff_rgb_out_eitu709[3][4] = {
-       { 0x2000, 0x7106, 0x7a02, 0x00a7 },
-       { 0x2000, 0x3264, 0x0000, 0x7e6d },
-       { 0x2000, 0x0000, 0x3b61, 0x7e25 }
-};
-
-static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
-       { 0x2591, 0x1322, 0x074b, 0x0000 },
-       { 0x6535, 0x2000, 0x7acc, 0x0200 },
-       { 0x6acd, 0x7534, 0x2000, 0x0200 }
-};
-
-static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
-       { 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
-       { 0x62f0, 0x2000, 0x7d11, 0x0200 },
-       { 0x6756, 0x78ab, 0x2000, 0x0200 }
-};
-
-struct hdmi_vmode {
-       bool mdataenablepolarity;
-
-       unsigned int mpixelclock;
-       unsigned int mpixelrepetitioninput;
-       unsigned int mpixelrepetitionoutput;
-};
-
-struct hdmi_data_info {
-       unsigned int enc_in_format;
-       unsigned int enc_out_format;
-       unsigned int enc_color_depth;
-       unsigned int colorimetry;
-       unsigned int pix_repet_factor;
-       unsigned int hdcp_enable;
-       struct hdmi_vmode video_mode;
-};
-
-struct dw_hdmi {
-       struct drm_connector connector;
-       struct drm_encoder *encoder;
-       struct drm_bridge *bridge;
-
-       struct platform_device *audio;
-       enum dw_hdmi_devtype dev_type;
-       struct device *dev;
-       struct clk *isfr_clk;
-       struct clk *iahb_clk;
-
-       struct hdmi_data_info hdmi_data;
-       const struct dw_hdmi_plat_data *plat_data;
-
-       int vic;
-
-       u8 edid[HDMI_EDID_LEN];
-       bool cable_plugin;
-
-       bool phy_enabled;
-       struct drm_display_mode previous_mode;
-
-       struct i2c_adapter *ddc;
-       void __iomem *regs;
-       bool sink_is_hdmi;
-       bool sink_has_audio;
-
-       struct mutex mutex;             /* for state below and previous_mode */
-       enum drm_connector_force force; /* mutex-protected force state */
-       bool disabled;                  /* DRM has disabled our bridge */
-       bool bridge_is_on;              /* indicates the bridge is on */
-       bool rxsense;                   /* rxsense state */
-       u8 phy_mask;                    /* desired phy int mask settings */
-
-       spinlock_t audio_lock;
-       struct mutex audio_mutex;
-       unsigned int sample_rate;
-       unsigned int audio_cts;
-       unsigned int audio_n;
-       bool audio_enable;
-
-       void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
-       u8 (*read)(struct dw_hdmi *hdmi, int offset);
-};
-
-#define HDMI_IH_PHY_STAT0_RX_SENSE \
-       (HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
-        HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
-
-#define HDMI_PHY_RX_SENSE \
-       (HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | \
-        HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3)
-
-static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset)
-{
-       writel(val, hdmi->regs + (offset << 2));
-}
-
-static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset)
-{
-       return readl(hdmi->regs + (offset << 2));
-}
-
-static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
-{
-       writeb(val, hdmi->regs + offset);
-}
-
-static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset)
-{
-       return readb(hdmi->regs + offset);
-}
-
-static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
-{
-       hdmi->write(hdmi, val, offset);
-}
-
-static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
-{
-       return hdmi->read(hdmi, offset);
-}
-
-static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
-{
-       u8 val = hdmi_readb(hdmi, reg) & ~mask;
-
-       val |= data & mask;
-       hdmi_writeb(hdmi, val, reg);
-}
-
-static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
-                            u8 shift, u8 mask)
-{
-       hdmi_modb(hdmi, data << shift, mask, reg);
-}
-
-static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
-                          unsigned int n)
-{
-       /* Must be set/cleared first */
-       hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
-
-       /* nshift factor = 0 */
-       hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
-
-       hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
-                   HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
-       hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
-       hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
-
-       hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3);
-       hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
-       hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
-}
-
-static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
-{
-       unsigned int n = (128 * freq) / 1000;
-       unsigned int mult = 1;
-
-       while (freq > 48000) {
-               mult *= 2;
-               freq /= 2;
-       }
-
-       switch (freq) {
-       case 32000:
-               if (pixel_clk == 25175000)
-                       n = 4576;
-               else if (pixel_clk == 27027000)
-                       n = 4096;
-               else if (pixel_clk == 74176000 || pixel_clk == 148352000)
-                       n = 11648;
-               else
-                       n = 4096;
-               n *= mult;
-               break;
-
-       case 44100:
-               if (pixel_clk == 25175000)
-                       n = 7007;
-               else if (pixel_clk == 74176000)
-                       n = 17836;
-               else if (pixel_clk == 148352000)
-                       n = 8918;
-               else
-                       n = 6272;
-               n *= mult;
-               break;
-
-       case 48000:
-               if (pixel_clk == 25175000)
-                       n = 6864;
-               else if (pixel_clk == 27027000)
-                       n = 6144;
-               else if (pixel_clk == 74176000)
-                       n = 11648;
-               else if (pixel_clk == 148352000)
-                       n = 5824;
-               else
-                       n = 6144;
-               n *= mult;
-               break;
-
-       default:
-               break;
-       }
-
-       return n;
-}
-
-static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
-       unsigned long pixel_clk, unsigned int sample_rate)
-{
-       unsigned long ftdms = pixel_clk;
-       unsigned int n, cts;
-       u64 tmp;
-
-       n = hdmi_compute_n(sample_rate, pixel_clk);
-
-       /*
-        * Compute the CTS value from the N value.  Note that CTS and N
-        * can be up to 20 bits in total, so we need 64-bit math.  Also
-        * note that our TDMS clock is not fully accurate; it is accurate
-        * to kHz.  This can introduce an unnecessary remainder in the
-        * calculation below, so we don't try to warn about that.
-        */
-       tmp = (u64)ftdms * n;
-       do_div(tmp, 128 * sample_rate);
-       cts = tmp;
-
-       dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
-               __func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000,
-               n, cts);
-
-       spin_lock_irq(&hdmi->audio_lock);
-       hdmi->audio_n = n;
-       hdmi->audio_cts = cts;
-       hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
-       spin_unlock_irq(&hdmi->audio_lock);
-}
-
-static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
-{
-       mutex_lock(&hdmi->audio_mutex);
-       hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate);
-       mutex_unlock(&hdmi->audio_mutex);
-}
-
-static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
-{
-       mutex_lock(&hdmi->audio_mutex);
-       hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
-                                hdmi->sample_rate);
-       mutex_unlock(&hdmi->audio_mutex);
-}
-
-void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
-{
-       mutex_lock(&hdmi->audio_mutex);
-       hdmi->sample_rate = rate;
-       hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
-                                hdmi->sample_rate);
-       mutex_unlock(&hdmi->audio_mutex);
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
-
-void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&hdmi->audio_lock, flags);
-       hdmi->audio_enable = true;
-       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
-       spin_unlock_irqrestore(&hdmi->audio_lock, flags);
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
-
-void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&hdmi->audio_lock, flags);
-       hdmi->audio_enable = false;
-       hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
-       spin_unlock_irqrestore(&hdmi->audio_lock, flags);
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
-
-/*
- * this submodule is responsible for the video data synchronization.
- * for example, for RGB 4:4:4 input, the data map is defined as
- *                     pin{47~40} <==> R[7:0]
- *                     pin{31~24} <==> G[7:0]
- *                     pin{15~8}  <==> B[7:0]
- */
-static void hdmi_video_sample(struct dw_hdmi *hdmi)
-{
-       int color_format = 0;
-       u8 val;
-
-       if (hdmi->hdmi_data.enc_in_format == RGB) {
-               if (hdmi->hdmi_data.enc_color_depth == 8)
-                       color_format = 0x01;
-               else if (hdmi->hdmi_data.enc_color_depth == 10)
-                       color_format = 0x03;
-               else if (hdmi->hdmi_data.enc_color_depth == 12)
-                       color_format = 0x05;
-               else if (hdmi->hdmi_data.enc_color_depth == 16)
-                       color_format = 0x07;
-               else
-                       return;
-       } else if (hdmi->hdmi_data.enc_in_format == YCBCR444) {
-               if (hdmi->hdmi_data.enc_color_depth == 8)
-                       color_format = 0x09;
-               else if (hdmi->hdmi_data.enc_color_depth == 10)
-                       color_format = 0x0B;
-               else if (hdmi->hdmi_data.enc_color_depth == 12)
-                       color_format = 0x0D;
-               else if (hdmi->hdmi_data.enc_color_depth == 16)
-                       color_format = 0x0F;
-               else
-                       return;
-       } else if (hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) {
-               if (hdmi->hdmi_data.enc_color_depth == 8)
-                       color_format = 0x16;
-               else if (hdmi->hdmi_data.enc_color_depth == 10)
-                       color_format = 0x14;
-               else if (hdmi->hdmi_data.enc_color_depth == 12)
-                       color_format = 0x12;
-               else
-                       return;
-       }
-
-       val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
-               ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
-               HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
-       hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
-
-       /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
-       val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
-               HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
-               HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
-       hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
-       hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
-       hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
-       hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
-       hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
-       hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
-       hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
-}
-
-static int is_color_space_conversion(struct dw_hdmi *hdmi)
-{
-       return hdmi->hdmi_data.enc_in_format != hdmi->hdmi_data.enc_out_format;
-}
-
-static int is_color_space_decimation(struct dw_hdmi *hdmi)
-{
-       if (hdmi->hdmi_data.enc_out_format != YCBCR422_8BITS)
-               return 0;
-       if (hdmi->hdmi_data.enc_in_format == RGB ||
-           hdmi->hdmi_data.enc_in_format == YCBCR444)
-               return 1;
-       return 0;
-}
-
-static int is_color_space_interpolation(struct dw_hdmi *hdmi)
-{
-       if (hdmi->hdmi_data.enc_in_format != YCBCR422_8BITS)
-               return 0;
-       if (hdmi->hdmi_data.enc_out_format == RGB ||
-           hdmi->hdmi_data.enc_out_format == YCBCR444)
-               return 1;
-       return 0;
-}
-
-static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
-{
-       const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
-       unsigned i;
-       u32 csc_scale = 1;
-
-       if (is_color_space_conversion(hdmi)) {
-               if (hdmi->hdmi_data.enc_out_format == RGB) {
-                       if (hdmi->hdmi_data.colorimetry ==
-                                       HDMI_COLORIMETRY_ITU_601)
-                               csc_coeff = &csc_coeff_rgb_out_eitu601;
-                       else
-                               csc_coeff = &csc_coeff_rgb_out_eitu709;
-               } else if (hdmi->hdmi_data.enc_in_format == RGB) {
-                       if (hdmi->hdmi_data.colorimetry ==
-                                       HDMI_COLORIMETRY_ITU_601)
-                               csc_coeff = &csc_coeff_rgb_in_eitu601;
-                       else
-                               csc_coeff = &csc_coeff_rgb_in_eitu709;
-                       csc_scale = 0;
-               }
-       }
-
-       /* The CSC registers are sequential, alternating MSB then LSB */
-       for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) {
-               u16 coeff_a = (*csc_coeff)[0][i];
-               u16 coeff_b = (*csc_coeff)[1][i];
-               u16 coeff_c = (*csc_coeff)[2][i];
-
-               hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2);
-               hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
-               hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
-               hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
-               hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2);
-               hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
-       }
-
-       hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
-                 HDMI_CSC_SCALE);
-}
-
-static void hdmi_video_csc(struct dw_hdmi *hdmi)
-{
-       int color_depth = 0;
-       int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
-       int decimation = 0;
-
-       /* YCC422 interpolation to 444 mode */
-       if (is_color_space_interpolation(hdmi))
-               interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
-       else if (is_color_space_decimation(hdmi))
-               decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
-
-       if (hdmi->hdmi_data.enc_color_depth == 8)
-               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
-       else if (hdmi->hdmi_data.enc_color_depth == 10)
-               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
-       else if (hdmi->hdmi_data.enc_color_depth == 12)
-               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
-       else if (hdmi->hdmi_data.enc_color_depth == 16)
-               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
-       else
-               return;
-
-       /* Configure the CSC registers */
-       hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
-       hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
-                 HDMI_CSC_SCALE);
-
-       dw_hdmi_update_csc_coeffs(hdmi);
-}
-
-/*
- * HDMI video packetizer is used to packetize the data.
- * for example, if input is YCC422 mode or repeater is used,
- * data should be repacked this module can be bypassed.
- */
-static void hdmi_video_packetize(struct dw_hdmi *hdmi)
-{
-       unsigned int color_depth = 0;
-       unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
-       unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
-       struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
-       u8 val, vp_conf;
-
-       if (hdmi_data->enc_out_format == RGB ||
-           hdmi_data->enc_out_format == YCBCR444) {
-               if (!hdmi_data->enc_color_depth) {
-                       output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-               } else if (hdmi_data->enc_color_depth == 8) {
-                       color_depth = 4;
-                       output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-               } else if (hdmi_data->enc_color_depth == 10) {
-                       color_depth = 5;
-               } else if (hdmi_data->enc_color_depth == 12) {
-                       color_depth = 6;
-               } else if (hdmi_data->enc_color_depth == 16) {
-                       color_depth = 7;
-               } else {
-                       return;
-               }
-       } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
-               if (!hdmi_data->enc_color_depth ||
-                   hdmi_data->enc_color_depth == 8)
-                       remap_size = HDMI_VP_REMAP_YCC422_16bit;
-               else if (hdmi_data->enc_color_depth == 10)
-                       remap_size = HDMI_VP_REMAP_YCC422_20bit;
-               else if (hdmi_data->enc_color_depth == 12)
-                       remap_size = HDMI_VP_REMAP_YCC422_24bit;
-               else
-                       return;
-               output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
-       } else {
-               return;
-       }
-
-       /* set the packetizer registers */
-       val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
-               HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
-               ((hdmi_data->pix_repet_factor <<
-               HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
-               HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
-       hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
-
-       hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
-                 HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
-
-       /* Data from pixel repeater block */
-       if (hdmi_data->pix_repet_factor > 1) {
-               vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
-                         HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
-       } else { /* data from packetizer block */
-               vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
-                         HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
-       }
-
-       hdmi_modb(hdmi, vp_conf,
-                 HDMI_VP_CONF_PR_EN_MASK |
-                 HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
-
-       hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
-                 HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
-
-       hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
-
-       if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
-               vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
-                         HDMI_VP_CONF_PP_EN_ENABLE |
-                         HDMI_VP_CONF_YCC422_EN_DISABLE;
-       } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
-               vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
-                         HDMI_VP_CONF_PP_EN_DISABLE |
-                         HDMI_VP_CONF_YCC422_EN_ENABLE;
-       } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
-               vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
-                         HDMI_VP_CONF_PP_EN_DISABLE |
-                         HDMI_VP_CONF_YCC422_EN_DISABLE;
-       } else {
-               return;
-       }
-
-       hdmi_modb(hdmi, vp_conf,
-                 HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
-                 HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
-
-       hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
-                       HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
-                 HDMI_VP_STUFF_PP_STUFFING_MASK |
-                 HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
-
-       hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
-                 HDMI_VP_CONF);
-}
-
-static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi,
-                                      unsigned char bit)
-{
-       hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
-                 HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
-}
-
-static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi,
-                                       unsigned char bit)
-{
-       hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
-                 HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
-}
-
-static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi,
-                                      unsigned char bit)
-{
-       hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
-                 HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
-}
-
-static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi,
-                                    unsigned char bit)
-{
-       hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
-}
-
-static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi,
-                                     unsigned char bit)
-{
-       hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
-}
-
-static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
-{
-       u32 val;
-
-       while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
-               if (msec-- == 0)
-                       return false;
-               udelay(1000);
-       }
-       hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0);
-
-       return true;
-}
-
-static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
-                                unsigned char addr)
-{
-       hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
-       hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
-       hdmi_writeb(hdmi, (unsigned char)(data >> 8),
-                   HDMI_PHY_I2CM_DATAO_1_ADDR);
-       hdmi_writeb(hdmi, (unsigned char)(data >> 0),
-                   HDMI_PHY_I2CM_DATAO_0_ADDR);
-       hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
-                   HDMI_PHY_I2CM_OPERATION_ADDR);
-       hdmi_phy_wait_i2c_done(hdmi, 1000);
-}
-
-static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
-                             unsigned char addr)
-{
-       __hdmi_phy_i2c_write(hdmi, data, addr);
-       return 0;
-}
-
-static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
-{
-       hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
-                        HDMI_PHY_CONF0_PDZ_OFFSET,
-                        HDMI_PHY_CONF0_PDZ_MASK);
-}
-
-static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable)
-{
-       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
-                        HDMI_PHY_CONF0_ENTMDS_OFFSET,
-                        HDMI_PHY_CONF0_ENTMDS_MASK);
-}
-
-static void dw_hdmi_phy_enable_spare(struct dw_hdmi *hdmi, u8 enable)
-{
-       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
-                        HDMI_PHY_CONF0_SPARECTRL_OFFSET,
-                        HDMI_PHY_CONF0_SPARECTRL_MASK);
-}
-
-static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
-{
-       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
-                        HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
-                        HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
-}
-
-static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
-{
-       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
-                        HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
-                        HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
-}
-
-static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
-{
-       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
-                        HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
-                        HDMI_PHY_CONF0_SELDATAENPOL_MASK);
-}
-
-static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
-{
-       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
-                        HDMI_PHY_CONF0_SELDIPIF_OFFSET,
-                        HDMI_PHY_CONF0_SELDIPIF_MASK);
-}
-
-static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
-                             unsigned char res, int cscon)
-{
-       unsigned res_idx;
-       u8 val, msec;
-       const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
-       const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
-       const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
-       const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
-
-       if (prep)
-               return -EINVAL;
-
-       switch (res) {
-       case 0: /* color resolution 0 is 8 bit colour depth */
-       case 8:
-               res_idx = DW_HDMI_RES_8;
-               break;
-       case 10:
-               res_idx = DW_HDMI_RES_10;
-               break;
-       case 12:
-               res_idx = DW_HDMI_RES_12;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       /* PLL/MPLL Cfg - always match on final entry */
-       for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
-               if (hdmi->hdmi_data.video_mode.mpixelclock <=
-                   mpll_config->mpixelclock)
-                       break;
-
-       for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
-               if (hdmi->hdmi_data.video_mode.mpixelclock <=
-                   curr_ctrl->mpixelclock)
-                       break;
-
-       for (; phy_config->mpixelclock != ~0UL; phy_config++)
-               if (hdmi->hdmi_data.video_mode.mpixelclock <=
-                   phy_config->mpixelclock)
-                       break;
-
-       if (mpll_config->mpixelclock == ~0UL ||
-           curr_ctrl->mpixelclock == ~0UL ||
-           phy_config->mpixelclock == ~0UL) {
-               dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
-                       hdmi->hdmi_data.video_mode.mpixelclock);
-               return -EINVAL;
-       }
-
-       /* Enable csc path */
-       if (cscon)
-               val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH;
-       else
-               val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;
-
-       hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL);
-
-       /* gen2 tx power off */
-       dw_hdmi_phy_gen2_txpwron(hdmi, 0);
-
-       /* gen2 pddq */
-       dw_hdmi_phy_gen2_pddq(hdmi, 1);
-
-       /* PHY reset */
-       hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
-       hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
-
-       hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
-
-       hdmi_phy_test_clear(hdmi, 1);
-       hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
-                   HDMI_PHY_I2CM_SLAVE_ADDR);
-       hdmi_phy_test_clear(hdmi, 0);
-
-       hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].cpce, 0x06);
-       hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15);
-
-       /* CURRCTRL */
-       hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10);
-
-       hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
-       hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
-
-       hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19);  /* TXTERM */
-       hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */
-       hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */
-
-       /* REMOVE CLK TERM */
-       hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
-
-       dw_hdmi_phy_enable_powerdown(hdmi, false);
-
-       /* toggle TMDS enable */
-       dw_hdmi_phy_enable_tmds(hdmi, 0);
-       dw_hdmi_phy_enable_tmds(hdmi, 1);
-
-       /* gen2 tx power on */
-       dw_hdmi_phy_gen2_txpwron(hdmi, 1);
-       dw_hdmi_phy_gen2_pddq(hdmi, 0);
-
-       if (hdmi->dev_type == RK3288_HDMI)
-               dw_hdmi_phy_enable_spare(hdmi, 1);
-
-       /*Wait for PHY PLL lock */
-       msec = 5;
-       do {
-               val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
-               if (!val)
-                       break;
-
-               if (msec == 0) {
-                       dev_err(hdmi->dev, "PHY PLL not locked\n");
-                       return -ETIMEDOUT;
-               }
-
-               udelay(1000);
-               msec--;
-       } while (1);
-
-       return 0;
-}
-
-static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
-{
-       int i, ret;
-       bool cscon;
-
-       /*check csc whether needed activated in HDMI mode */
-       cscon = hdmi->sink_is_hdmi && is_color_space_conversion(hdmi);
-
-       /* HDMI Phy spec says to do the phy initialization sequence twice */
-       for (i = 0; i < 2; i++) {
-               dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
-               dw_hdmi_phy_sel_interface_control(hdmi, 0);
-               dw_hdmi_phy_enable_tmds(hdmi, 0);
-               dw_hdmi_phy_enable_powerdown(hdmi, true);
-
-               /* Enable CSC */
-               ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
-               if (ret)
-                       return ret;
-       }
-
-       hdmi->phy_enabled = true;
-       return 0;
-}
-
-static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
-{
-       u8 de;
-
-       if (hdmi->hdmi_data.video_mode.mdataenablepolarity)
-               de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
-       else
-               de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
-
-       /* disable rx detect */
-       hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
-                 HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
-
-       hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
-
-       hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
-                 HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
-}
-
-static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
-{
-       struct hdmi_avi_infoframe frame;
-       u8 val;
-
-       /* Initialise info frame from DRM mode */
-       drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
-
-       if (hdmi->hdmi_data.enc_out_format == YCBCR444)
-               frame.colorspace = HDMI_COLORSPACE_YUV444;
-       else if (hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS)
-               frame.colorspace = HDMI_COLORSPACE_YUV422;
-       else
-               frame.colorspace = HDMI_COLORSPACE_RGB;
-
-       /* Set up colorimetry */
-       if (hdmi->hdmi_data.enc_out_format == XVYCC444) {
-               frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
-               if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601)
-                       frame.extended_colorimetry =
-                               HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
-               else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/
-                       frame.extended_colorimetry =
-                               HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
-       } else if (hdmi->hdmi_data.enc_out_format != RGB) {
-               frame.colorimetry = hdmi->hdmi_data.colorimetry;
-               frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
-       } else { /* Carries no data */
-               frame.colorimetry = HDMI_COLORIMETRY_NONE;
-               frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
-       }
-
-       frame.scan_mode = HDMI_SCAN_MODE_NONE;
-
-       /*
-        * The Designware IP uses a different byte format from standard
-        * AVI info frames, though generally the bits are in the correct
-        * bytes.
-        */
-
-       /*
-        * AVI data byte 1 differences: Colorspace in bits 4,5 rather than 5,6,
-        * active aspect present in bit 6 rather than 4.
-        */
-       val = (frame.colorspace & 3) << 4 | (frame.scan_mode & 0x3);
-       if (frame.active_aspect & 15)
-               val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT;
-       if (frame.top_bar || frame.bottom_bar)
-               val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR;
-       if (frame.left_bar || frame.right_bar)
-               val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR;
-       hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
-
-       /* AVI data byte 2 differences: none */
-       val = ((frame.colorimetry & 0x3) << 6) |
-             ((frame.picture_aspect & 0x3) << 4) |
-             (frame.active_aspect & 0xf);
-       hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
-
-       /* AVI data byte 3 differences: none */
-       val = ((frame.extended_colorimetry & 0x7) << 4) |
-             ((frame.quantization_range & 0x3) << 2) |
-             (frame.nups & 0x3);
-       if (frame.itc)
-               val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID;
-       hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
-
-       /* AVI data byte 4 differences: none */
-       val = frame.video_code & 0x7f;
-       hdmi_writeb(hdmi, val, HDMI_FC_AVIVID);
-
-       /* AVI Data Byte 5- set up input and output pixel repetition */
-       val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
-               HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) &
-               HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) |
-               ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
-               HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
-               HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
-       hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
-
-       /*
-        * AVI data byte 5 differences: content type in 0,1 rather than 4,5,
-        * ycc range in bits 2,3 rather than 6,7
-        */
-       val = ((frame.ycc_quantization_range & 0x3) << 2) |
-             (frame.content_type & 0x3);
-       hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
-
-       /* AVI Data Bytes 6-13 */
-       hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0);
-       hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1);
-       hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0);
-       hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1);
-       hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0);
-       hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1);
-       hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0);
-       hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1);
-}
-
-static void hdmi_av_composer(struct dw_hdmi *hdmi,
-                            const struct drm_display_mode *mode)
-{
-       u8 inv_val;
-       struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
-       int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
-       unsigned int vdisplay;
-
-       vmode->mpixelclock = mode->clock * 1000;
-
-       dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
-
-       /* Set up HDMI_FC_INVIDCONF */
-       inv_val = (hdmi->hdmi_data.hdcp_enable ?
-               HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
-               HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
-
-       inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
-               HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
-               HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW;
-
-       inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
-               HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
-               HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW;
-
-       inv_val |= (vmode->mdataenablepolarity ?
-               HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
-               HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
-
-       if (hdmi->vic == 39)
-               inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
-       else
-               inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
-                       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
-                       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
-
-       inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
-               HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
-               HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
-
-       inv_val |= hdmi->sink_is_hdmi ?
-               HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
-               HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
-
-       hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
-
-       vdisplay = mode->vdisplay;
-       vblank = mode->vtotal - mode->vdisplay;
-       v_de_vs = mode->vsync_start - mode->vdisplay;
-       vsync_len = mode->vsync_end - mode->vsync_start;
-
-       /*
-        * When we're setting an interlaced mode, we need
-        * to adjust the vertical timing to suit.
-        */
-       if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
-               vdisplay /= 2;
-               vblank /= 2;
-               v_de_vs /= 2;
-               vsync_len /= 2;
-       }
-
-       /* Set up horizontal active pixel width */
-       hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
-       hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
-
-       /* Set up vertical active lines */
-       hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
-       hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
-
-       /* Set up horizontal blanking pixel region width */
-       hblank = mode->htotal - mode->hdisplay;
-       hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
-       hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
-
-       /* Set up vertical blanking pixel region width */
-       hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
-
-       /* Set up HSYNC active edge delay width (in pixel clks) */
-       h_de_hs = mode->hsync_start - mode->hdisplay;
-       hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
-       hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
-
-       /* Set up VSYNC active edge delay (in lines) */
-       hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
-
-       /* Set up HSYNC active pulse width (in pixel clks) */
-       hsync_len = mode->hsync_end - mode->hsync_start;
-       hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
-       hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
-
-       /* Set up VSYNC active edge delay (in lines) */
-       hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
-}
-
-static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
-{
-       if (!hdmi->phy_enabled)
-               return;
-
-       dw_hdmi_phy_enable_tmds(hdmi, 0);
-       dw_hdmi_phy_enable_powerdown(hdmi, true);
-
-       hdmi->phy_enabled = false;
-}
-
-/* HDMI Initialization Step B.4 */
-static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
-{
-       u8 clkdis;
-
-       /* control period minimum duration */
-       hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
-       hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
-       hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
-
-       /* Set to fill TMDS data channels */
-       hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
-       hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
-       hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
-
-       /* Enable pixel clock and tmds data path */
-       clkdis = 0x7F;
-       clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-       hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
-
-       clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-       hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
-
-       /* Enable csc path */
-       if (is_color_space_conversion(hdmi)) {
-               clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-               hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
-       }
-}
-
-static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
-{
-       hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
-}
-
-/* Workaround to clear the overflow condition */
-static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
-{
-       int count;
-       u8 val;
-
-       /* TMDS software reset */
-       hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
-
-       val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
-       if (hdmi->dev_type == IMX6DL_HDMI) {
-               hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
-               return;
-       }
-
-       for (count = 0; count < 4; count++)
-               hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
-}
-
-static void hdmi_enable_overflow_interrupts(struct dw_hdmi *hdmi)
-{
-       hdmi_writeb(hdmi, 0, HDMI_FC_MASK2);
-       hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
-}
-
-static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
-{
-       hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
-                   HDMI_IH_MUTE_FC_STAT2);
-}
-
-static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
-{
-       int ret;
-
-       hdmi_disable_overflow_interrupts(hdmi);
-
-       hdmi->vic = drm_match_cea_mode(mode);
-
-       if (!hdmi->vic) {
-               dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
-       } else {
-               dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
-       }
-
-       if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
-           (hdmi->vic == 21) || (hdmi->vic == 22) ||
-           (hdmi->vic == 2) || (hdmi->vic == 3) ||
-           (hdmi->vic == 17) || (hdmi->vic == 18))
-               hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
-       else
-               hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
-
-       hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
-       hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
-
-       /* TODO: Get input format from IPU (via FB driver interface) */
-       hdmi->hdmi_data.enc_in_format = RGB;
-
-       hdmi->hdmi_data.enc_out_format = RGB;
-
-       hdmi->hdmi_data.enc_color_depth = 8;
-       hdmi->hdmi_data.pix_repet_factor = 0;
-       hdmi->hdmi_data.hdcp_enable = 0;
-       hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
-
-       /* HDMI Initialization Step B.1 */
-       hdmi_av_composer(hdmi, mode);
-
-       /* HDMI Initializateion Step B.2 */
-       ret = dw_hdmi_phy_init(hdmi);
-       if (ret)
-               return ret;
-
-       /* HDMI Initialization Step B.3 */
-       dw_hdmi_enable_video_path(hdmi);
-
-       if (hdmi->sink_has_audio) {
-               dev_dbg(hdmi->dev, "sink has audio support\n");
-
-               /* HDMI Initialization Step E - Configure audio */
-               hdmi_clk_regenerator_update_pixel_clock(hdmi);
-               hdmi_enable_audio_clk(hdmi);
-       }
-
-       /* not for DVI mode */
-       if (hdmi->sink_is_hdmi) {
-               dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
-
-               /* HDMI Initialization Step F - Configure AVI InfoFrame */
-               hdmi_config_AVI(hdmi, mode);
-       } else {
-               dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
-       }
-
-       hdmi_video_packetize(hdmi);
-       hdmi_video_csc(hdmi);
-       hdmi_video_sample(hdmi);
-       hdmi_tx_hdcp_config(hdmi);
-
-       dw_hdmi_clear_overflow(hdmi);
-       if (hdmi->cable_plugin && hdmi->sink_is_hdmi)
-               hdmi_enable_overflow_interrupts(hdmi);
-
-       return 0;
-}
-
-/* Wait until we are registered to enable interrupts */
-static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi)
-{
-       hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
-                   HDMI_PHY_I2CM_INT_ADDR);
-
-       hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
-                   HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
-                   HDMI_PHY_I2CM_CTLINT_ADDR);
-
-       /* enable cable hot plug irq */
-       hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
-
-       /* Clear Hotplug interrupts */
-       hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
-                   HDMI_IH_PHY_STAT0);
-
-       return 0;
-}
-
-static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
-{
-       u8 ih_mute;
-
-       /*
-        * Boot up defaults are:
-        * HDMI_IH_MUTE   = 0x03 (disabled)
-        * HDMI_IH_MUTE_* = 0x00 (enabled)
-        *
-        * Disable top level interrupt bits in HDMI block
-        */
-       ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
-                 HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
-                 HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
-
-       hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
-
-       /* by default mask all interrupts */
-       hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
-       hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
-       hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
-       hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
-       hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
-       hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
-       hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
-       hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
-       hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
-       hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
-       hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
-       hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-       hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
-       hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
-       hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
-
-       /* Disable interrupts in the IH_MUTE_* registers */
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
-       hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
-
-       /* Enable top level interrupt bits in HDMI block */
-       ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
-                   HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
-       hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
-}
-
-static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
-{
-       hdmi->bridge_is_on = true;
-       dw_hdmi_setup(hdmi, &hdmi->previous_mode);
-}
-
-static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
-{
-       dw_hdmi_phy_disable(hdmi);
-       hdmi->bridge_is_on = false;
-}
-
-static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
-{
-       int force = hdmi->force;
-
-       if (hdmi->disabled) {
-               force = DRM_FORCE_OFF;
-       } else if (force == DRM_FORCE_UNSPECIFIED) {
-               if (hdmi->rxsense)
-                       force = DRM_FORCE_ON;
-               else
-                       force = DRM_FORCE_OFF;
-       }
-
-       if (force == DRM_FORCE_OFF) {
-               if (hdmi->bridge_is_on)
-                       dw_hdmi_poweroff(hdmi);
-       } else {
-               if (!hdmi->bridge_is_on)
-                       dw_hdmi_poweron(hdmi);
-       }
-}
-
-/*
- * Adjust the detection of RXSENSE according to whether we have a forced
- * connection mode enabled, or whether we have been disabled.  There is
- * no point processing RXSENSE interrupts if we have a forced connection
- * state, or DRM has us disabled.
- *
- * We also disable rxsense interrupts when we think we're disconnected
- * to avoid floating TDMS signals giving false rxsense interrupts.
- *
- * Note: we still need to listen for HPD interrupts even when DRM has us
- * disabled so that we can detect a connect event.
- */
-static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
-{
-       u8 old_mask = hdmi->phy_mask;
-
-       if (hdmi->force || hdmi->disabled || !hdmi->rxsense)
-               hdmi->phy_mask |= HDMI_PHY_RX_SENSE;
-       else
-               hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE;
-
-       if (old_mask != hdmi->phy_mask)
-               hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
-}
-
-static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
-                                   struct drm_display_mode *orig_mode,
-                                   struct drm_display_mode *mode)
-{
-       struct dw_hdmi *hdmi = bridge->driver_private;
-
-       mutex_lock(&hdmi->mutex);
-
-       /* Store the display mode for plugin/DKMS poweron events */
-       memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
-
-       mutex_unlock(&hdmi->mutex);
-}
-
-static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
-                                     const struct drm_display_mode *mode,
-                                     struct drm_display_mode *adjusted_mode)
-{
-       return true;
-}
-
-static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
-{
-       struct dw_hdmi *hdmi = bridge->driver_private;
-
-       mutex_lock(&hdmi->mutex);
-       hdmi->disabled = true;
-       dw_hdmi_update_power(hdmi);
-       dw_hdmi_update_phy_mask(hdmi);
-       mutex_unlock(&hdmi->mutex);
-}
-
-static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
-{
-       struct dw_hdmi *hdmi = bridge->driver_private;
-
-       mutex_lock(&hdmi->mutex);
-       hdmi->disabled = false;
-       dw_hdmi_update_power(hdmi);
-       dw_hdmi_update_phy_mask(hdmi);
-       mutex_unlock(&hdmi->mutex);
-}
-
-static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
-{
-       /* do nothing */
-}
-
-static enum drm_connector_status
-dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
-{
-       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
-                                            connector);
-
-       mutex_lock(&hdmi->mutex);
-       hdmi->force = DRM_FORCE_UNSPECIFIED;
-       dw_hdmi_update_power(hdmi);
-       dw_hdmi_update_phy_mask(hdmi);
-       mutex_unlock(&hdmi->mutex);
-
-       return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
-               connector_status_connected : connector_status_disconnected;
-}
-
-static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
-{
-       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
-                                            connector);
-       struct edid *edid;
-       int ret = 0;
-
-       if (!hdmi->ddc)
-               return 0;
-
-       edid = drm_get_edid(connector, hdmi->ddc);
-       if (edid) {
-               dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
-                       edid->width_cm, edid->height_cm);
-
-               hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
-               hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
-               drm_mode_connector_update_edid_property(connector, edid);
-               ret = drm_add_edid_modes(connector, edid);
-               /* Store the ELD */
-               drm_edid_to_eld(connector, edid);
-               kfree(edid);
-       } else {
-               dev_dbg(hdmi->dev, "failed to get edid\n");
-       }
-
-       return ret;
-}
-
-static enum drm_mode_status
-dw_hdmi_connector_mode_valid(struct drm_connector *connector,
-                            struct drm_display_mode *mode)
-{
-       struct dw_hdmi *hdmi = container_of(connector,
-                                          struct dw_hdmi, connector);
-       enum drm_mode_status mode_status = MODE_OK;
-
-       /* We don't support double-clocked modes */
-       if (mode->flags & DRM_MODE_FLAG_DBLCLK)
-               return MODE_BAD;
-
-       if (hdmi->plat_data->mode_valid)
-               mode_status = hdmi->plat_data->mode_valid(connector, mode);
-
-       return mode_status;
-}
-
-static struct drm_encoder *dw_hdmi_connector_best_encoder(struct drm_connector
-                                                          *connector)
-{
-       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
-                                            connector);
-
-       return hdmi->encoder;
-}
-
-static void dw_hdmi_connector_destroy(struct drm_connector *connector)
-{
-       drm_connector_unregister(connector);
-       drm_connector_cleanup(connector);
-}
-
-static void dw_hdmi_connector_force(struct drm_connector *connector)
-{
-       struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
-                                            connector);
-
-       mutex_lock(&hdmi->mutex);
-       hdmi->force = connector->force;
-       dw_hdmi_update_power(hdmi);
-       dw_hdmi_update_phy_mask(hdmi);
-       mutex_unlock(&hdmi->mutex);
-}
-
-static struct drm_connector_funcs dw_hdmi_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
-       .fill_modes = drm_helper_probe_single_connector_modes,
-       .detect = dw_hdmi_connector_detect,
-       .destroy = dw_hdmi_connector_destroy,
-       .force = dw_hdmi_connector_force,
-};
-
-static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
-       .get_modes = dw_hdmi_connector_get_modes,
-       .mode_valid = dw_hdmi_connector_mode_valid,
-       .best_encoder = dw_hdmi_connector_best_encoder,
-};
-
-static struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
-       .enable = dw_hdmi_bridge_enable,
-       .disable = dw_hdmi_bridge_disable,
-       .pre_enable = dw_hdmi_bridge_nop,
-       .post_disable = dw_hdmi_bridge_nop,
-       .mode_set = dw_hdmi_bridge_mode_set,
-       .mode_fixup = dw_hdmi_bridge_mode_fixup,
-};
-
-static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
-{
-       struct dw_hdmi *hdmi = dev_id;
-       u8 intr_stat;
-
-       intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
-       if (intr_stat)
-               hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
-
-       return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE;
-}
-
-static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
-{
-       struct dw_hdmi *hdmi = dev_id;
-       u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
-
-       intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
-       phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
-       phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
-
-       phy_pol_mask = 0;
-       if (intr_stat & HDMI_IH_PHY_STAT0_HPD)
-               phy_pol_mask |= HDMI_PHY_HPD;
-       if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE0)
-               phy_pol_mask |= HDMI_PHY_RX_SENSE0;
-       if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE1)
-               phy_pol_mask |= HDMI_PHY_RX_SENSE1;
-       if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE2)
-               phy_pol_mask |= HDMI_PHY_RX_SENSE2;
-       if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE3)
-               phy_pol_mask |= HDMI_PHY_RX_SENSE3;
-
-       if (phy_pol_mask)
-               hdmi_modb(hdmi, ~phy_int_pol, phy_pol_mask, HDMI_PHY_POL0);
-
-       /*
-        * RX sense tells us whether the TDMS transmitters are detecting
-        * load - in other words, there's something listening on the
-        * other end of the link.  Use this to decide whether we should
-        * power on the phy as HPD may be toggled by the sink to merely
-        * ask the source to re-read the EDID.
-        */
-       if (intr_stat &
-           (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) {
-               mutex_lock(&hdmi->mutex);
-               if (!hdmi->disabled && !hdmi->force) {
-                       /*
-                        * If the RX sense status indicates we're disconnected,
-                        * clear the software rxsense status.
-                        */
-                       if (!(phy_stat & HDMI_PHY_RX_SENSE))
-                               hdmi->rxsense = false;
-
-                       /*
-                        * Only set the software rxsense status when both
-                        * rxsense and hpd indicates we're connected.
-                        * This avoids what seems to be bad behaviour in
-                        * at least iMX6S versions of the phy.
-                        */
-                       if (phy_stat & HDMI_PHY_HPD)
-                               hdmi->rxsense = true;
-
-                       dw_hdmi_update_power(hdmi);
-                       dw_hdmi_update_phy_mask(hdmi);
-               }
-               mutex_unlock(&hdmi->mutex);
-       }
-
-       if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
-               dev_dbg(hdmi->dev, "EVENT=%s\n",
-                       phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout");
-               drm_helper_hpd_irq_event(hdmi->bridge->dev);
-       }
-
-       hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-       hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
-                   HDMI_IH_MUTE_PHY_STAT0);
-
-       return IRQ_HANDLED;
-}
-
-static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
-{
-       struct drm_encoder *encoder = hdmi->encoder;
-       struct drm_bridge *bridge;
-       int ret;
-
-       bridge = devm_kzalloc(drm->dev, sizeof(*bridge), GFP_KERNEL);
-       if (!bridge) {
-               DRM_ERROR("Failed to allocate drm bridge\n");
-               return -ENOMEM;
-       }
-
-       hdmi->bridge = bridge;
-       bridge->driver_private = hdmi;
-       bridge->funcs = &dw_hdmi_bridge_funcs;
-       ret = drm_bridge_attach(drm, bridge);
-       if (ret) {
-               DRM_ERROR("Failed to initialize bridge with drm\n");
-               return -EINVAL;
-       }
-
-       encoder->bridge = bridge;
-       hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
-
-       drm_connector_helper_add(&hdmi->connector,
-                                &dw_hdmi_connector_helper_funcs);
-       drm_connector_init(drm, &hdmi->connector, &dw_hdmi_connector_funcs,
-                          DRM_MODE_CONNECTOR_HDMIA);
-
-       hdmi->connector.encoder = encoder;
-
-       drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
-
-       return 0;
-}
-
-int dw_hdmi_bind(struct device *dev, struct device *master,
-                void *data, struct drm_encoder *encoder,
-                struct resource *iores, int irq,
-                const struct dw_hdmi_plat_data *plat_data)
-{
-       struct drm_device *drm = data;
-       struct device_node *np = dev->of_node;
-       struct platform_device_info pdevinfo;
-       struct device_node *ddc_node;
-       struct dw_hdmi_audio_data audio;
-       struct dw_hdmi *hdmi;
-       int ret;
-       u32 val = 1;
-
-       hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
-       if (!hdmi)
-               return -ENOMEM;
-
-       hdmi->connector.interlace_allowed = 1;
-
-       hdmi->plat_data = plat_data;
-       hdmi->dev = dev;
-       hdmi->dev_type = plat_data->dev_type;
-       hdmi->sample_rate = 48000;
-       hdmi->encoder = encoder;
-       hdmi->disabled = true;
-       hdmi->rxsense = true;
-       hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
-
-       mutex_init(&hdmi->mutex);
-       mutex_init(&hdmi->audio_mutex);
-       spin_lock_init(&hdmi->audio_lock);
-
-       of_property_read_u32(np, "reg-io-width", &val);
-
-       switch (val) {
-       case 4:
-               hdmi->write = dw_hdmi_writel;
-               hdmi->read = dw_hdmi_readl;
-               break;
-       case 1:
-               hdmi->write = dw_hdmi_writeb;
-               hdmi->read = dw_hdmi_readb;
-               break;
-       default:
-               dev_err(dev, "reg-io-width must be 1 or 4\n");
-               return -EINVAL;
-       }
-
-       ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
-       if (ddc_node) {
-               hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
-               of_node_put(ddc_node);
-               if (!hdmi->ddc) {
-                       dev_dbg(hdmi->dev, "failed to read ddc node\n");
-                       return -EPROBE_DEFER;
-               }
-
-       } else {
-               dev_dbg(hdmi->dev, "no ddc property found\n");
-       }
-
-       hdmi->regs = devm_ioremap_resource(dev, iores);
-       if (IS_ERR(hdmi->regs))
-               return PTR_ERR(hdmi->regs);
-
-       hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
-       if (IS_ERR(hdmi->isfr_clk)) {
-               ret = PTR_ERR(hdmi->isfr_clk);
-               dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret);
-               return ret;
-       }
-
-       ret = clk_prepare_enable(hdmi->isfr_clk);
-       if (ret) {
-               dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
-               return ret;
-       }
-
-       hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
-       if (IS_ERR(hdmi->iahb_clk)) {
-               ret = PTR_ERR(hdmi->iahb_clk);
-               dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret);
-               goto err_isfr;
-       }
-
-       ret = clk_prepare_enable(hdmi->iahb_clk);
-       if (ret) {
-               dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
-               goto err_isfr;
-       }
-
-       /* Product and revision IDs */
-       dev_info(dev,
-                "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
-                hdmi_readb(hdmi, HDMI_DESIGN_ID),
-                hdmi_readb(hdmi, HDMI_REVISION_ID),
-                hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
-                hdmi_readb(hdmi, HDMI_PRODUCT_ID1));
-
-       initialize_hdmi_ih_mutes(hdmi);
-
-       ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
-                                       dw_hdmi_irq, IRQF_SHARED,
-                                       dev_name(dev), hdmi);
-       if (ret)
-               goto err_iahb;
-
-       /*
-        * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
-        * N and cts values before enabling phy
-        */
-       hdmi_init_clk_regenerator(hdmi);
-
-       /*
-        * Configure registers related to HDMI interrupt
-        * generation before registering IRQ.
-        */
-       hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0);
-
-       /* Clear Hotplug interrupts */
-       hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
-                   HDMI_IH_PHY_STAT0);
-
-       ret = dw_hdmi_fb_registered(hdmi);
-       if (ret)
-               goto err_iahb;
-
-       ret = dw_hdmi_register(drm, hdmi);
-       if (ret)
-               goto err_iahb;
-
-       /* Unmute interrupts */
-       hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
-                   HDMI_IH_MUTE_PHY_STAT0);
-
-       memset(&pdevinfo, 0, sizeof(pdevinfo));
-       pdevinfo.parent = dev;
-       pdevinfo.id = PLATFORM_DEVID_AUTO;
-
-       if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) {
-               audio.phys = iores->start;
-               audio.base = hdmi->regs;
-               audio.irq = irq;
-               audio.hdmi = hdmi;
-               audio.eld = hdmi->connector.eld;
-
-               pdevinfo.name = "dw-hdmi-ahb-audio";
-               pdevinfo.data = &audio;
-               pdevinfo.size_data = sizeof(audio);
-               pdevinfo.dma_mask = DMA_BIT_MASK(32);
-               hdmi->audio = platform_device_register_full(&pdevinfo);
-       }
-
-       dev_set_drvdata(dev, hdmi);
-
-       return 0;
-
-err_iahb:
-       clk_disable_unprepare(hdmi->iahb_clk);
-err_isfr:
-       clk_disable_unprepare(hdmi->isfr_clk);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_bind);
-
-void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
-{
-       struct dw_hdmi *hdmi = dev_get_drvdata(dev);
-
-       if (hdmi->audio && !IS_ERR(hdmi->audio))
-               platform_device_unregister(hdmi->audio);
-
-       /* Disable all interrupts */
-       hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
-
-       hdmi->connector.funcs->destroy(&hdmi->connector);
-       hdmi->encoder->funcs->destroy(hdmi->encoder);
-
-       clk_disable_unprepare(hdmi->iahb_clk);
-       clk_disable_unprepare(hdmi->isfr_clk);
-       i2c_put_adapter(hdmi->ddc);
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
-
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
-MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
-MODULE_DESCRIPTION("DW HDMI transmitter driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:dw-hdmi");
diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h
deleted file mode 100644 (file)
index fc9a560..0000000
+++ /dev/null
@@ -1,1037 +0,0 @@
-/*
- * Copyright (C) 2011 Freescale Semiconductor, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __DW_HDMI_H__
-#define __DW_HDMI_H__
-
-/* Identification Registers */
-#define HDMI_DESIGN_ID                          0x0000
-#define HDMI_REVISION_ID                        0x0001
-#define HDMI_PRODUCT_ID0                        0x0002
-#define HDMI_PRODUCT_ID1                        0x0003
-#define HDMI_CONFIG0_ID                         0x0004
-#define HDMI_CONFIG1_ID                         0x0005
-#define HDMI_CONFIG2_ID                         0x0006
-#define HDMI_CONFIG3_ID                         0x0007
-
-/* Interrupt Registers */
-#define HDMI_IH_FC_STAT0                        0x0100
-#define HDMI_IH_FC_STAT1                        0x0101
-#define HDMI_IH_FC_STAT2                        0x0102
-#define HDMI_IH_AS_STAT0                        0x0103
-#define HDMI_IH_PHY_STAT0                       0x0104
-#define HDMI_IH_I2CM_STAT0                      0x0105
-#define HDMI_IH_CEC_STAT0                       0x0106
-#define HDMI_IH_VP_STAT0                        0x0107
-#define HDMI_IH_I2CMPHY_STAT0                   0x0108
-#define HDMI_IH_AHBDMAAUD_STAT0                 0x0109
-
-#define HDMI_IH_MUTE_FC_STAT0                   0x0180
-#define HDMI_IH_MUTE_FC_STAT1                   0x0181
-#define HDMI_IH_MUTE_FC_STAT2                   0x0182
-#define HDMI_IH_MUTE_AS_STAT0                   0x0183
-#define HDMI_IH_MUTE_PHY_STAT0                  0x0184
-#define HDMI_IH_MUTE_I2CM_STAT0                 0x0185
-#define HDMI_IH_MUTE_CEC_STAT0                  0x0186
-#define HDMI_IH_MUTE_VP_STAT0                   0x0187
-#define HDMI_IH_MUTE_I2CMPHY_STAT0              0x0188
-#define HDMI_IH_MUTE_AHBDMAAUD_STAT0            0x0189
-#define HDMI_IH_MUTE                            0x01FF
-
-/* Video Sample Registers */
-#define HDMI_TX_INVID0                          0x0200
-#define HDMI_TX_INSTUFFING                      0x0201
-#define HDMI_TX_GYDATA0                         0x0202
-#define HDMI_TX_GYDATA1                         0x0203
-#define HDMI_TX_RCRDATA0                        0x0204
-#define HDMI_TX_RCRDATA1                        0x0205
-#define HDMI_TX_BCBDATA0                        0x0206
-#define HDMI_TX_BCBDATA1                        0x0207
-
-/* Video Packetizer Registers */
-#define HDMI_VP_STATUS                          0x0800
-#define HDMI_VP_PR_CD                           0x0801
-#define HDMI_VP_STUFF                           0x0802
-#define HDMI_VP_REMAP                           0x0803
-#define HDMI_VP_CONF                            0x0804
-#define HDMI_VP_STAT                            0x0805
-#define HDMI_VP_INT                             0x0806
-#define HDMI_VP_MASK                            0x0807
-#define HDMI_VP_POL                             0x0808
-
-/* Frame Composer Registers */
-#define HDMI_FC_INVIDCONF                       0x1000
-#define HDMI_FC_INHACTV0                        0x1001
-#define HDMI_FC_INHACTV1                        0x1002
-#define HDMI_FC_INHBLANK0                       0x1003
-#define HDMI_FC_INHBLANK1                       0x1004
-#define HDMI_FC_INVACTV0                        0x1005
-#define HDMI_FC_INVACTV1                        0x1006
-#define HDMI_FC_INVBLANK                        0x1007
-#define HDMI_FC_HSYNCINDELAY0                   0x1008
-#define HDMI_FC_HSYNCINDELAY1                   0x1009
-#define HDMI_FC_HSYNCINWIDTH0                   0x100A
-#define HDMI_FC_HSYNCINWIDTH1                   0x100B
-#define HDMI_FC_VSYNCINDELAY                    0x100C
-#define HDMI_FC_VSYNCINWIDTH                    0x100D
-#define HDMI_FC_INFREQ0                         0x100E
-#define HDMI_FC_INFREQ1                         0x100F
-#define HDMI_FC_INFREQ2                         0x1010
-#define HDMI_FC_CTRLDUR                         0x1011
-#define HDMI_FC_EXCTRLDUR                       0x1012
-#define HDMI_FC_EXCTRLSPAC                      0x1013
-#define HDMI_FC_CH0PREAM                        0x1014
-#define HDMI_FC_CH1PREAM                        0x1015
-#define HDMI_FC_CH2PREAM                        0x1016
-#define HDMI_FC_AVICONF3                        0x1017
-#define HDMI_FC_GCP                             0x1018
-#define HDMI_FC_AVICONF0                        0x1019
-#define HDMI_FC_AVICONF1                        0x101A
-#define HDMI_FC_AVICONF2                        0x101B
-#define HDMI_FC_AVIVID                          0x101C
-#define HDMI_FC_AVIETB0                         0x101D
-#define HDMI_FC_AVIETB1                         0x101E
-#define HDMI_FC_AVISBB0                         0x101F
-#define HDMI_FC_AVISBB1                         0x1020
-#define HDMI_FC_AVIELB0                         0x1021
-#define HDMI_FC_AVIELB1                         0x1022
-#define HDMI_FC_AVISRB0                         0x1023
-#define HDMI_FC_AVISRB1                         0x1024
-#define HDMI_FC_AUDICONF0                       0x1025
-#define HDMI_FC_AUDICONF1                       0x1026
-#define HDMI_FC_AUDICONF2                       0x1027
-#define HDMI_FC_AUDICONF3                       0x1028
-#define HDMI_FC_VSDIEEEID0                      0x1029
-#define HDMI_FC_VSDSIZE                         0x102A
-#define HDMI_FC_VSDIEEEID1                      0x1030
-#define HDMI_FC_VSDIEEEID2                      0x1031
-#define HDMI_FC_VSDPAYLOAD0                     0x1032
-#define HDMI_FC_VSDPAYLOAD1                     0x1033
-#define HDMI_FC_VSDPAYLOAD2                     0x1034
-#define HDMI_FC_VSDPAYLOAD3                     0x1035
-#define HDMI_FC_VSDPAYLOAD4                     0x1036
-#define HDMI_FC_VSDPAYLOAD5                     0x1037
-#define HDMI_FC_VSDPAYLOAD6                     0x1038
-#define HDMI_FC_VSDPAYLOAD7                     0x1039
-#define HDMI_FC_VSDPAYLOAD8                     0x103A
-#define HDMI_FC_VSDPAYLOAD9                     0x103B
-#define HDMI_FC_VSDPAYLOAD10                    0x103C
-#define HDMI_FC_VSDPAYLOAD11                    0x103D
-#define HDMI_FC_VSDPAYLOAD12                    0x103E
-#define HDMI_FC_VSDPAYLOAD13                    0x103F
-#define HDMI_FC_VSDPAYLOAD14                    0x1040
-#define HDMI_FC_VSDPAYLOAD15                    0x1041
-#define HDMI_FC_VSDPAYLOAD16                    0x1042
-#define HDMI_FC_VSDPAYLOAD17                    0x1043
-#define HDMI_FC_VSDPAYLOAD18                    0x1044
-#define HDMI_FC_VSDPAYLOAD19                    0x1045
-#define HDMI_FC_VSDPAYLOAD20                    0x1046
-#define HDMI_FC_VSDPAYLOAD21                    0x1047
-#define HDMI_FC_VSDPAYLOAD22                    0x1048
-#define HDMI_FC_VSDPAYLOAD23                    0x1049
-#define HDMI_FC_SPDVENDORNAME0                  0x104A
-#define HDMI_FC_SPDVENDORNAME1                  0x104B
-#define HDMI_FC_SPDVENDORNAME2                  0x104C
-#define HDMI_FC_SPDVENDORNAME3                  0x104D
-#define HDMI_FC_SPDVENDORNAME4                  0x104E
-#define HDMI_FC_SPDVENDORNAME5                  0x104F
-#define HDMI_FC_SPDVENDORNAME6                  0x1050
-#define HDMI_FC_SPDVENDORNAME7                  0x1051
-#define HDMI_FC_SDPPRODUCTNAME0                 0x1052
-#define HDMI_FC_SDPPRODUCTNAME1                 0x1053
-#define HDMI_FC_SDPPRODUCTNAME2                 0x1054
-#define HDMI_FC_SDPPRODUCTNAME3                 0x1055
-#define HDMI_FC_SDPPRODUCTNAME4                 0x1056
-#define HDMI_FC_SDPPRODUCTNAME5                 0x1057
-#define HDMI_FC_SDPPRODUCTNAME6                 0x1058
-#define HDMI_FC_SDPPRODUCTNAME7                 0x1059
-#define HDMI_FC_SDPPRODUCTNAME8                 0x105A
-#define HDMI_FC_SDPPRODUCTNAME9                 0x105B
-#define HDMI_FC_SDPPRODUCTNAME10                0x105C
-#define HDMI_FC_SDPPRODUCTNAME11                0x105D
-#define HDMI_FC_SDPPRODUCTNAME12                0x105E
-#define HDMI_FC_SDPPRODUCTNAME13                0x105F
-#define HDMI_FC_SDPPRODUCTNAME14                0x1060
-#define HDMI_FC_SPDPRODUCTNAME15                0x1061
-#define HDMI_FC_SPDDEVICEINF                    0x1062
-#define HDMI_FC_AUDSCONF                        0x1063
-#define HDMI_FC_AUDSSTAT                        0x1064
-#define HDMI_FC_DATACH0FILL                     0x1070
-#define HDMI_FC_DATACH1FILL                     0x1071
-#define HDMI_FC_DATACH2FILL                     0x1072
-#define HDMI_FC_CTRLQHIGH                       0x1073
-#define HDMI_FC_CTRLQLOW                        0x1074
-#define HDMI_FC_ACP0                            0x1075
-#define HDMI_FC_ACP28                           0x1076
-#define HDMI_FC_ACP27                           0x1077
-#define HDMI_FC_ACP26                           0x1078
-#define HDMI_FC_ACP25                           0x1079
-#define HDMI_FC_ACP24                           0x107A
-#define HDMI_FC_ACP23                           0x107B
-#define HDMI_FC_ACP22                           0x107C
-#define HDMI_FC_ACP21                           0x107D
-#define HDMI_FC_ACP20                           0x107E
-#define HDMI_FC_ACP19                           0x107F
-#define HDMI_FC_ACP18                           0x1080
-#define HDMI_FC_ACP17                           0x1081
-#define HDMI_FC_ACP16                           0x1082
-#define HDMI_FC_ACP15                           0x1083
-#define HDMI_FC_ACP14                           0x1084
-#define HDMI_FC_ACP13                           0x1085
-#define HDMI_FC_ACP12                           0x1086
-#define HDMI_FC_ACP11                           0x1087
-#define HDMI_FC_ACP10                           0x1088
-#define HDMI_FC_ACP9                            0x1089
-#define HDMI_FC_ACP8                            0x108A
-#define HDMI_FC_ACP7                            0x108B
-#define HDMI_FC_ACP6                            0x108C
-#define HDMI_FC_ACP5                            0x108D
-#define HDMI_FC_ACP4                            0x108E
-#define HDMI_FC_ACP3                            0x108F
-#define HDMI_FC_ACP2                            0x1090
-#define HDMI_FC_ACP1                            0x1091
-#define HDMI_FC_ISCR1_0                         0x1092
-#define HDMI_FC_ISCR1_16                        0x1093
-#define HDMI_FC_ISCR1_15                        0x1094
-#define HDMI_FC_ISCR1_14                        0x1095
-#define HDMI_FC_ISCR1_13                        0x1096
-#define HDMI_FC_ISCR1_12                        0x1097
-#define HDMI_FC_ISCR1_11                        0x1098
-#define HDMI_FC_ISCR1_10                        0x1099
-#define HDMI_FC_ISCR1_9                         0x109A
-#define HDMI_FC_ISCR1_8                         0x109B
-#define HDMI_FC_ISCR1_7                         0x109C
-#define HDMI_FC_ISCR1_6                         0x109D
-#define HDMI_FC_ISCR1_5                         0x109E
-#define HDMI_FC_ISCR1_4                         0x109F
-#define HDMI_FC_ISCR1_3                         0x10A0
-#define HDMI_FC_ISCR1_2                         0x10A1
-#define HDMI_FC_ISCR1_1                         0x10A2
-#define HDMI_FC_ISCR2_15                        0x10A3
-#define HDMI_FC_ISCR2_14                        0x10A4
-#define HDMI_FC_ISCR2_13                        0x10A5
-#define HDMI_FC_ISCR2_12                        0x10A6
-#define HDMI_FC_ISCR2_11                        0x10A7
-#define HDMI_FC_ISCR2_10                        0x10A8
-#define HDMI_FC_ISCR2_9                         0x10A9
-#define HDMI_FC_ISCR2_8                         0x10AA
-#define HDMI_FC_ISCR2_7                         0x10AB
-#define HDMI_FC_ISCR2_6                         0x10AC
-#define HDMI_FC_ISCR2_5                         0x10AD
-#define HDMI_FC_ISCR2_4                         0x10AE
-#define HDMI_FC_ISCR2_3                         0x10AF
-#define HDMI_FC_ISCR2_2                         0x10B0
-#define HDMI_FC_ISCR2_1                         0x10B1
-#define HDMI_FC_ISCR2_0                         0x10B2
-#define HDMI_FC_DATAUTO0                        0x10B3
-#define HDMI_FC_DATAUTO1                        0x10B4
-#define HDMI_FC_DATAUTO2                        0x10B5
-#define HDMI_FC_DATMAN                          0x10B6
-#define HDMI_FC_DATAUTO3                        0x10B7
-#define HDMI_FC_RDRB0                           0x10B8
-#define HDMI_FC_RDRB1                           0x10B9
-#define HDMI_FC_RDRB2                           0x10BA
-#define HDMI_FC_RDRB3                           0x10BB
-#define HDMI_FC_RDRB4                           0x10BC
-#define HDMI_FC_RDRB5                           0x10BD
-#define HDMI_FC_RDRB6                           0x10BE
-#define HDMI_FC_RDRB7                           0x10BF
-#define HDMI_FC_STAT0                           0x10D0
-#define HDMI_FC_INT0                            0x10D1
-#define HDMI_FC_MASK0                           0x10D2
-#define HDMI_FC_POL0                            0x10D3
-#define HDMI_FC_STAT1                           0x10D4
-#define HDMI_FC_INT1                            0x10D5
-#define HDMI_FC_MASK1                           0x10D6
-#define HDMI_FC_POL1                            0x10D7
-#define HDMI_FC_STAT2                           0x10D8
-#define HDMI_FC_INT2                            0x10D9
-#define HDMI_FC_MASK2                           0x10DA
-#define HDMI_FC_POL2                            0x10DB
-#define HDMI_FC_PRCONF                          0x10E0
-
-#define HDMI_FC_GMD_STAT                        0x1100
-#define HDMI_FC_GMD_EN                          0x1101
-#define HDMI_FC_GMD_UP                          0x1102
-#define HDMI_FC_GMD_CONF                        0x1103
-#define HDMI_FC_GMD_HB                          0x1104
-#define HDMI_FC_GMD_PB0                         0x1105
-#define HDMI_FC_GMD_PB1                         0x1106
-#define HDMI_FC_GMD_PB2                         0x1107
-#define HDMI_FC_GMD_PB3                         0x1108
-#define HDMI_FC_GMD_PB4                         0x1109
-#define HDMI_FC_GMD_PB5                         0x110A
-#define HDMI_FC_GMD_PB6                         0x110B
-#define HDMI_FC_GMD_PB7                         0x110C
-#define HDMI_FC_GMD_PB8                         0x110D
-#define HDMI_FC_GMD_PB9                         0x110E
-#define HDMI_FC_GMD_PB10                        0x110F
-#define HDMI_FC_GMD_PB11                        0x1110
-#define HDMI_FC_GMD_PB12                        0x1111
-#define HDMI_FC_GMD_PB13                        0x1112
-#define HDMI_FC_GMD_PB14                        0x1113
-#define HDMI_FC_GMD_PB15                        0x1114
-#define HDMI_FC_GMD_PB16                        0x1115
-#define HDMI_FC_GMD_PB17                        0x1116
-#define HDMI_FC_GMD_PB18                        0x1117
-#define HDMI_FC_GMD_PB19                        0x1118
-#define HDMI_FC_GMD_PB20                        0x1119
-#define HDMI_FC_GMD_PB21                        0x111A
-#define HDMI_FC_GMD_PB22                        0x111B
-#define HDMI_FC_GMD_PB23                        0x111C
-#define HDMI_FC_GMD_PB24                        0x111D
-#define HDMI_FC_GMD_PB25                        0x111E
-#define HDMI_FC_GMD_PB26                        0x111F
-#define HDMI_FC_GMD_PB27                        0x1120
-
-#define HDMI_FC_DBGFORCE                        0x1200
-#define HDMI_FC_DBGAUD0CH0                      0x1201
-#define HDMI_FC_DBGAUD1CH0                      0x1202
-#define HDMI_FC_DBGAUD2CH0                      0x1203
-#define HDMI_FC_DBGAUD0CH1                      0x1204
-#define HDMI_FC_DBGAUD1CH1                      0x1205
-#define HDMI_FC_DBGAUD2CH1                      0x1206
-#define HDMI_FC_DBGAUD0CH2                      0x1207
-#define HDMI_FC_DBGAUD1CH2                      0x1208
-#define HDMI_FC_DBGAUD2CH2                      0x1209
-#define HDMI_FC_DBGAUD0CH3                      0x120A
-#define HDMI_FC_DBGAUD1CH3                      0x120B
-#define HDMI_FC_DBGAUD2CH3                      0x120C
-#define HDMI_FC_DBGAUD0CH4                      0x120D
-#define HDMI_FC_DBGAUD1CH4                      0x120E
-#define HDMI_FC_DBGAUD2CH4                      0x120F
-#define HDMI_FC_DBGAUD0CH5                      0x1210
-#define HDMI_FC_DBGAUD1CH5                      0x1211
-#define HDMI_FC_DBGAUD2CH5                      0x1212
-#define HDMI_FC_DBGAUD0CH6                      0x1213
-#define HDMI_FC_DBGAUD1CH6                      0x1214
-#define HDMI_FC_DBGAUD2CH6                      0x1215
-#define HDMI_FC_DBGAUD0CH7                      0x1216
-#define HDMI_FC_DBGAUD1CH7                      0x1217
-#define HDMI_FC_DBGAUD2CH7                      0x1218
-#define HDMI_FC_DBGTMDS0                        0x1219
-#define HDMI_FC_DBGTMDS1                        0x121A
-#define HDMI_FC_DBGTMDS2                        0x121B
-
-/* HDMI Source PHY Registers */
-#define HDMI_PHY_CONF0                          0x3000
-#define HDMI_PHY_TST0                           0x3001
-#define HDMI_PHY_TST1                           0x3002
-#define HDMI_PHY_TST2                           0x3003
-#define HDMI_PHY_STAT0                          0x3004
-#define HDMI_PHY_INT0                           0x3005
-#define HDMI_PHY_MASK0                          0x3006
-#define HDMI_PHY_POL0                           0x3007
-
-/* HDMI Master PHY Registers */
-#define HDMI_PHY_I2CM_SLAVE_ADDR                0x3020
-#define HDMI_PHY_I2CM_ADDRESS_ADDR              0x3021
-#define HDMI_PHY_I2CM_DATAO_1_ADDR              0x3022
-#define HDMI_PHY_I2CM_DATAO_0_ADDR              0x3023
-#define HDMI_PHY_I2CM_DATAI_1_ADDR              0x3024
-#define HDMI_PHY_I2CM_DATAI_0_ADDR              0x3025
-#define HDMI_PHY_I2CM_OPERATION_ADDR            0x3026
-#define HDMI_PHY_I2CM_INT_ADDR                  0x3027
-#define HDMI_PHY_I2CM_CTLINT_ADDR               0x3028
-#define HDMI_PHY_I2CM_DIV_ADDR                  0x3029
-#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR             0x302a
-#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR        0x302b
-#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR        0x302c
-#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR        0x302d
-#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR        0x302e
-#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR        0x302f
-#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR        0x3030
-#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR        0x3031
-#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR        0x3032
-
-/* Audio Sampler Registers */
-#define HDMI_AUD_CONF0                          0x3100
-#define HDMI_AUD_CONF1                          0x3101
-#define HDMI_AUD_INT                            0x3102
-#define HDMI_AUD_CONF2                          0x3103
-#define HDMI_AUD_N1                             0x3200
-#define HDMI_AUD_N2                             0x3201
-#define HDMI_AUD_N3                             0x3202
-#define HDMI_AUD_CTS1                           0x3203
-#define HDMI_AUD_CTS2                           0x3204
-#define HDMI_AUD_CTS3                           0x3205
-#define HDMI_AUD_INPUTCLKFS                     0x3206
-#define HDMI_AUD_SPDIFINT                      0x3302
-#define HDMI_AUD_CONF0_HBR                      0x3400
-#define HDMI_AUD_HBR_STATUS                     0x3401
-#define HDMI_AUD_HBR_INT                        0x3402
-#define HDMI_AUD_HBR_POL                        0x3403
-#define HDMI_AUD_HBR_MASK                       0x3404
-
-/*
- * Generic Parallel Audio Interface Registers
- * Not used as GPAUD interface is not enabled in hw
- */
-#define HDMI_GP_CONF0                           0x3500
-#define HDMI_GP_CONF1                           0x3501
-#define HDMI_GP_CONF2                           0x3502
-#define HDMI_GP_STAT                            0x3503
-#define HDMI_GP_INT                             0x3504
-#define HDMI_GP_MASK                            0x3505
-#define HDMI_GP_POL                             0x3506
-
-/* Audio DMA Registers */
-#define HDMI_AHB_DMA_CONF0                      0x3600
-#define HDMI_AHB_DMA_START                      0x3601
-#define HDMI_AHB_DMA_STOP                       0x3602
-#define HDMI_AHB_DMA_THRSLD                     0x3603
-#define HDMI_AHB_DMA_STRADDR0                   0x3604
-#define HDMI_AHB_DMA_STRADDR1                   0x3605
-#define HDMI_AHB_DMA_STRADDR2                   0x3606
-#define HDMI_AHB_DMA_STRADDR3                   0x3607
-#define HDMI_AHB_DMA_STPADDR0                   0x3608
-#define HDMI_AHB_DMA_STPADDR1                   0x3609
-#define HDMI_AHB_DMA_STPADDR2                   0x360a
-#define HDMI_AHB_DMA_STPADDR3                   0x360b
-#define HDMI_AHB_DMA_BSTADDR0                   0x360c
-#define HDMI_AHB_DMA_BSTADDR1                   0x360d
-#define HDMI_AHB_DMA_BSTADDR2                   0x360e
-#define HDMI_AHB_DMA_BSTADDR3                   0x360f
-#define HDMI_AHB_DMA_MBLENGTH0                  0x3610
-#define HDMI_AHB_DMA_MBLENGTH1                  0x3611
-#define HDMI_AHB_DMA_STAT                       0x3612
-#define HDMI_AHB_DMA_INT                        0x3613
-#define HDMI_AHB_DMA_MASK                       0x3614
-#define HDMI_AHB_DMA_POL                        0x3615
-#define HDMI_AHB_DMA_CONF1                      0x3616
-#define HDMI_AHB_DMA_BUFFSTAT                   0x3617
-#define HDMI_AHB_DMA_BUFFINT                    0x3618
-#define HDMI_AHB_DMA_BUFFMASK                   0x3619
-#define HDMI_AHB_DMA_BUFFPOL                    0x361a
-
-/* Main Controller Registers */
-#define HDMI_MC_SFRDIV                          0x4000
-#define HDMI_MC_CLKDIS                          0x4001
-#define HDMI_MC_SWRSTZ                          0x4002
-#define HDMI_MC_OPCTRL                          0x4003
-#define HDMI_MC_FLOWCTRL                        0x4004
-#define HDMI_MC_PHYRSTZ                         0x4005
-#define HDMI_MC_LOCKONCLOCK                     0x4006
-#define HDMI_MC_HEACPHY_RST                     0x4007
-
-/* Color Space  Converter Registers */
-#define HDMI_CSC_CFG                            0x4100
-#define HDMI_CSC_SCALE                          0x4101
-#define HDMI_CSC_COEF_A1_MSB                    0x4102
-#define HDMI_CSC_COEF_A1_LSB                    0x4103
-#define HDMI_CSC_COEF_A2_MSB                    0x4104
-#define HDMI_CSC_COEF_A2_LSB                    0x4105
-#define HDMI_CSC_COEF_A3_MSB                    0x4106
-#define HDMI_CSC_COEF_A3_LSB                    0x4107
-#define HDMI_CSC_COEF_A4_MSB                    0x4108
-#define HDMI_CSC_COEF_A4_LSB                    0x4109
-#define HDMI_CSC_COEF_B1_MSB                    0x410A
-#define HDMI_CSC_COEF_B1_LSB                    0x410B
-#define HDMI_CSC_COEF_B2_MSB                    0x410C
-#define HDMI_CSC_COEF_B2_LSB                    0x410D
-#define HDMI_CSC_COEF_B3_MSB                    0x410E
-#define HDMI_CSC_COEF_B3_LSB                    0x410F
-#define HDMI_CSC_COEF_B4_MSB                    0x4110
-#define HDMI_CSC_COEF_B4_LSB                    0x4111
-#define HDMI_CSC_COEF_C1_MSB                    0x4112
-#define HDMI_CSC_COEF_C1_LSB                    0x4113
-#define HDMI_CSC_COEF_C2_MSB                    0x4114
-#define HDMI_CSC_COEF_C2_LSB                    0x4115
-#define HDMI_CSC_COEF_C3_MSB                    0x4116
-#define HDMI_CSC_COEF_C3_LSB                    0x4117
-#define HDMI_CSC_COEF_C4_MSB                    0x4118
-#define HDMI_CSC_COEF_C4_LSB                    0x4119
-
-/* HDCP Encryption Engine Registers */
-#define HDMI_A_HDCPCFG0                         0x5000
-#define HDMI_A_HDCPCFG1                         0x5001
-#define HDMI_A_HDCPOBS0                         0x5002
-#define HDMI_A_HDCPOBS1                         0x5003
-#define HDMI_A_HDCPOBS2                         0x5004
-#define HDMI_A_HDCPOBS3                         0x5005
-#define HDMI_A_APIINTCLR                        0x5006
-#define HDMI_A_APIINTSTAT                       0x5007
-#define HDMI_A_APIINTMSK                        0x5008
-#define HDMI_A_VIDPOLCFG                        0x5009
-#define HDMI_A_OESSWCFG                         0x500A
-#define HDMI_A_TIMER1SETUP0                     0x500B
-#define HDMI_A_TIMER1SETUP1                     0x500C
-#define HDMI_A_TIMER2SETUP0                     0x500D
-#define HDMI_A_TIMER2SETUP1                     0x500E
-#define HDMI_A_100MSCFG                         0x500F
-#define HDMI_A_2SCFG0                           0x5010
-#define HDMI_A_2SCFG1                           0x5011
-#define HDMI_A_5SCFG0                           0x5012
-#define HDMI_A_5SCFG1                           0x5013
-#define HDMI_A_SRMVERLSB                        0x5014
-#define HDMI_A_SRMVERMSB                        0x5015
-#define HDMI_A_SRMCTRL                          0x5016
-#define HDMI_A_SFRSETUP                         0x5017
-#define HDMI_A_I2CHSETUP                        0x5018
-#define HDMI_A_INTSETUP                         0x5019
-#define HDMI_A_PRESETUP                         0x501A
-#define HDMI_A_SRM_BASE                         0x5020
-
-/* CEC Engine Registers */
-#define HDMI_CEC_CTRL                           0x7D00
-#define HDMI_CEC_STAT                           0x7D01
-#define HDMI_CEC_MASK                           0x7D02
-#define HDMI_CEC_POLARITY                       0x7D03
-#define HDMI_CEC_INT                            0x7D04
-#define HDMI_CEC_ADDR_L                         0x7D05
-#define HDMI_CEC_ADDR_H                         0x7D06
-#define HDMI_CEC_TX_CNT                         0x7D07
-#define HDMI_CEC_RX_CNT                         0x7D08
-#define HDMI_CEC_TX_DATA0                       0x7D10
-#define HDMI_CEC_TX_DATA1                       0x7D11
-#define HDMI_CEC_TX_DATA2                       0x7D12
-#define HDMI_CEC_TX_DATA3                       0x7D13
-#define HDMI_CEC_TX_DATA4                       0x7D14
-#define HDMI_CEC_TX_DATA5                       0x7D15
-#define HDMI_CEC_TX_DATA6                       0x7D16
-#define HDMI_CEC_TX_DATA7                       0x7D17
-#define HDMI_CEC_TX_DATA8                       0x7D18
-#define HDMI_CEC_TX_DATA9                       0x7D19
-#define HDMI_CEC_TX_DATA10                      0x7D1a
-#define HDMI_CEC_TX_DATA11                      0x7D1b
-#define HDMI_CEC_TX_DATA12                      0x7D1c
-#define HDMI_CEC_TX_DATA13                      0x7D1d
-#define HDMI_CEC_TX_DATA14                      0x7D1e
-#define HDMI_CEC_TX_DATA15                      0x7D1f
-#define HDMI_CEC_RX_DATA0                       0x7D20
-#define HDMI_CEC_RX_DATA1                       0x7D21
-#define HDMI_CEC_RX_DATA2                       0x7D22
-#define HDMI_CEC_RX_DATA3                       0x7D23
-#define HDMI_CEC_RX_DATA4                       0x7D24
-#define HDMI_CEC_RX_DATA5                       0x7D25
-#define HDMI_CEC_RX_DATA6                       0x7D26
-#define HDMI_CEC_RX_DATA7                       0x7D27
-#define HDMI_CEC_RX_DATA8                       0x7D28
-#define HDMI_CEC_RX_DATA9                       0x7D29
-#define HDMI_CEC_RX_DATA10                      0x7D2a
-#define HDMI_CEC_RX_DATA11                      0x7D2b
-#define HDMI_CEC_RX_DATA12                      0x7D2c
-#define HDMI_CEC_RX_DATA13                      0x7D2d
-#define HDMI_CEC_RX_DATA14                      0x7D2e
-#define HDMI_CEC_RX_DATA15                      0x7D2f
-#define HDMI_CEC_LOCK                           0x7D30
-#define HDMI_CEC_WKUPCTRL                       0x7D31
-
-/* I2C Master Registers (E-DDC) */
-#define HDMI_I2CM_SLAVE                         0x7E00
-#define HDMI_I2CM_ADDRESS                       0x7E01
-#define HDMI_I2CM_DATAO                         0x7E02
-#define HDMI_I2CM_DATAI                         0x7E03
-#define HDMI_I2CM_OPERATION                     0x7E04
-#define HDMI_I2CM_INT                           0x7E05
-#define HDMI_I2CM_CTLINT                        0x7E06
-#define HDMI_I2CM_DIV                           0x7E07
-#define HDMI_I2CM_SEGADDR                       0x7E08
-#define HDMI_I2CM_SOFTRSTZ                      0x7E09
-#define HDMI_I2CM_SEGPTR                        0x7E0A
-#define HDMI_I2CM_SS_SCL_HCNT_1_ADDR            0x7E0B
-#define HDMI_I2CM_SS_SCL_HCNT_0_ADDR            0x7E0C
-#define HDMI_I2CM_SS_SCL_LCNT_1_ADDR            0x7E0D
-#define HDMI_I2CM_SS_SCL_LCNT_0_ADDR            0x7E0E
-#define HDMI_I2CM_FS_SCL_HCNT_1_ADDR            0x7E0F
-#define HDMI_I2CM_FS_SCL_HCNT_0_ADDR            0x7E10
-#define HDMI_I2CM_FS_SCL_LCNT_1_ADDR            0x7E11
-#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
-
-enum {
-/* CONFIG1_ID field values */
-       HDMI_CONFIG1_AHB = 0x01,
-
-/* IH_FC_INT2 field values */
-       HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
-       HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-       HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
-
-/* IH_FC_STAT2 field values */
-       HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03,
-       HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
-       HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
-
-/* IH_PHY_STAT0 field values */
-       HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20,
-       HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10,
-       HDMI_IH_PHY_STAT0_RX_SENSE1 = 0x8,
-       HDMI_IH_PHY_STAT0_RX_SENSE0 = 0x4,
-       HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2,
-       HDMI_IH_PHY_STAT0_HPD = 0x1,
-
-/* IH_MUTE_I2CMPHY_STAT0 field values */
-       HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2,
-       HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1,
-
-/* IH_AHBDMAAUD_STAT0 field values */
-       HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20,
-       HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10,
-       HDMI_IH_AHBDMAAUD_STAT0_RETRY = 0x08,
-       HDMI_IH_AHBDMAAUD_STAT0_DONE = 0x04,
-       HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
-       HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
-
-/* IH_MUTE_FC_STAT2 field values */
-       HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03,
-       HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
-       HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
-
-/* IH_MUTE_AHBDMAAUD_STAT0 field values */
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20,
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10,
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = 0x08,
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = 0x04,
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
-       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
-
-/* IH_MUTE field values */
-       HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2,
-       HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1,
-
-/* TX_INVID0 field values */
-       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_MASK = 0x80,
-       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_ENABLE = 0x80,
-       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00,
-       HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1F,
-       HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0,
-
-/* TX_INSTUFFING field values */
-       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_MASK = 0x4,
-       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4,
-       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_DISABLE = 0x0,
-       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_MASK = 0x2,
-       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2,
-       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_DISABLE = 0x0,
-       HDMI_TX_INSTUFFING_GYDATA_STUFFING_MASK = 0x1,
-       HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1,
-       HDMI_TX_INSTUFFING_GYDATA_STUFFING_DISABLE = 0x0,
-
-/* VP_PR_CD field values */
-       HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xF0,
-       HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4,
-       HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0F,
-       HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0,
-
-/* VP_STUFF field values */
-       HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20,
-       HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5,
-       HDMI_VP_STUFF_IFIX_PP_TO_LAST_MASK = 0x10,
-       HDMI_VP_STUFF_IFIX_PP_TO_LAST_OFFSET = 4,
-       HDMI_VP_STUFF_ICX_GOTO_P0_ST_MASK = 0x8,
-       HDMI_VP_STUFF_ICX_GOTO_P0_ST_OFFSET = 3,
-       HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4,
-       HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4,
-       HDMI_VP_STUFF_YCC422_STUFFING_DIRECT_MODE = 0x0,
-       HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2,
-       HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2,
-       HDMI_VP_STUFF_PP_STUFFING_DIRECT_MODE = 0x0,
-       HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1,
-       HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1,
-       HDMI_VP_STUFF_PR_STUFFING_DIRECT_MODE = 0x0,
-
-/* VP_CONF field values */
-       HDMI_VP_CONF_BYPASS_EN_MASK = 0x40,
-       HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40,
-       HDMI_VP_CONF_BYPASS_EN_DISABLE = 0x00,
-       HDMI_VP_CONF_PP_EN_ENMASK = 0x20,
-       HDMI_VP_CONF_PP_EN_ENABLE = 0x20,
-       HDMI_VP_CONF_PP_EN_DISABLE = 0x00,
-       HDMI_VP_CONF_PR_EN_MASK = 0x10,
-       HDMI_VP_CONF_PR_EN_ENABLE = 0x10,
-       HDMI_VP_CONF_PR_EN_DISABLE = 0x00,
-       HDMI_VP_CONF_YCC422_EN_MASK = 0x8,
-       HDMI_VP_CONF_YCC422_EN_ENABLE = 0x8,
-       HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0,
-       HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4,
-       HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4,
-       HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER = 0x0,
-       HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3,
-       HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3,
-       HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422 = 0x1,
-       HDMI_VP_CONF_OUTPUT_SELECTOR_PP = 0x0,
-
-/* VP_REMAP field values */
-       HDMI_VP_REMAP_MASK = 0x3,
-       HDMI_VP_REMAP_YCC422_24bit = 0x2,
-       HDMI_VP_REMAP_YCC422_20bit = 0x1,
-       HDMI_VP_REMAP_YCC422_16bit = 0x0,
-
-/* FC_INVIDCONF field values */
-       HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK = 0x80,
-       HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE = 0x80,
-       HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE = 0x00,
-       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40,
-       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40,
-       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
-       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20,
-       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20,
-       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
-       HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10,
-       HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10,
-       HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00,
-       HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8,
-       HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8,
-       HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0,
-       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2,
-       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2,
-       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0,
-       HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1,
-       HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1,
-       HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0,
-
-/* FC_AUDICONF0 field values */
-       HDMI_FC_AUDICONF0_CC_OFFSET = 4,
-       HDMI_FC_AUDICONF0_CC_MASK = 0x70,
-       HDMI_FC_AUDICONF0_CT_OFFSET = 0,
-       HDMI_FC_AUDICONF0_CT_MASK = 0xF,
-
-/* FC_AUDICONF1 field values */
-       HDMI_FC_AUDICONF1_SS_OFFSET = 3,
-       HDMI_FC_AUDICONF1_SS_MASK = 0x18,
-       HDMI_FC_AUDICONF1_SF_OFFSET = 0,
-       HDMI_FC_AUDICONF1_SF_MASK = 0x7,
-
-/* FC_AUDICONF3 field values */
-       HDMI_FC_AUDICONF3_LFEPBL_OFFSET = 5,
-       HDMI_FC_AUDICONF3_LFEPBL_MASK = 0x60,
-       HDMI_FC_AUDICONF3_DM_INH_OFFSET = 4,
-       HDMI_FC_AUDICONF3_DM_INH_MASK = 0x10,
-       HDMI_FC_AUDICONF3_LSV_OFFSET = 0,
-       HDMI_FC_AUDICONF3_LSV_MASK = 0xF,
-
-/* FC_AUDSCHNLS0 field values */
-       HDMI_FC_AUDSCHNLS0_CGMSA_OFFSET = 4,
-       HDMI_FC_AUDSCHNLS0_CGMSA_MASK = 0x30,
-       HDMI_FC_AUDSCHNLS0_COPYRIGHT_OFFSET = 0,
-       HDMI_FC_AUDSCHNLS0_COPYRIGHT_MASK = 0x01,
-
-/* FC_AUDSCHNLS3-6 field values */
-       HDMI_FC_AUDSCHNLS3_OIEC_CH0_OFFSET = 0,
-       HDMI_FC_AUDSCHNLS3_OIEC_CH0_MASK = 0x0f,
-       HDMI_FC_AUDSCHNLS3_OIEC_CH1_OFFSET = 4,
-       HDMI_FC_AUDSCHNLS3_OIEC_CH1_MASK = 0xf0,
-       HDMI_FC_AUDSCHNLS4_OIEC_CH2_OFFSET = 0,
-       HDMI_FC_AUDSCHNLS4_OIEC_CH2_MASK = 0x0f,
-       HDMI_FC_AUDSCHNLS4_OIEC_CH3_OFFSET = 4,
-       HDMI_FC_AUDSCHNLS4_OIEC_CH3_MASK = 0xf0,
-
-       HDMI_FC_AUDSCHNLS5_OIEC_CH0_OFFSET = 0,
-       HDMI_FC_AUDSCHNLS5_OIEC_CH0_MASK = 0x0f,
-       HDMI_FC_AUDSCHNLS5_OIEC_CH1_OFFSET = 4,
-       HDMI_FC_AUDSCHNLS5_OIEC_CH1_MASK = 0xf0,
-       HDMI_FC_AUDSCHNLS6_OIEC_CH2_OFFSET = 0,
-       HDMI_FC_AUDSCHNLS6_OIEC_CH2_MASK = 0x0f,
-       HDMI_FC_AUDSCHNLS6_OIEC_CH3_OFFSET = 4,
-       HDMI_FC_AUDSCHNLS6_OIEC_CH3_MASK = 0xf0,
-
-/* HDMI_FC_AUDSCHNLS7 field values */
-       HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4,
-       HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30,
-
-/* HDMI_FC_AUDSCHNLS8 field values */
-       HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0,
-       HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET = 4,
-       HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f,
-       HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0,
-
-/* FC_AUDSCONF field values */
-       HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0,
-       HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4,
-       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK = 0x1,
-       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_OFFSET = 0,
-       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1,
-       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0,
-
-/* FC_STAT2 field values */
-       HDMI_FC_STAT2_OVERFLOW_MASK = 0x03,
-       HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
-       HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
-
-/* FC_INT2 field values */
-       HDMI_FC_INT2_OVERFLOW_MASK = 0x03,
-       HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
-       HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
-
-/* FC_MASK2 field values */
-       HDMI_FC_MASK2_OVERFLOW_MASK = 0x03,
-       HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02,
-       HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01,
-
-/* FC_PRCONF field values */
-       HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0,
-       HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4,
-       HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F,
-       HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0,
-
-/* FC_AVICONF0-FC_AVICONF3 field values */
-       HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03,
-       HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00,
-       HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x01,
-       HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x02,
-       HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x40,
-       HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x40,
-       HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00,
-       HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0C,
-       HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00,
-       HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x04,
-       HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x08,
-       HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0x0C,
-       HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x30,
-       HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x10,
-       HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x20,
-       HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00,
-
-       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0F,
-       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x08,
-       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09,
-       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0A,
-       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0B,
-       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30,
-       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00,
-       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10,
-       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20,
-       HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xC0,
-       HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00,
-       HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40,
-       HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80,
-       HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xC0,
-
-       HDMI_FC_AVICONF2_SCALING_MASK = 0x03,
-       HDMI_FC_AVICONF2_SCALING_NONE = 0x00,
-       HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01,
-       HDMI_FC_AVICONF2_SCALING_VERT = 0x02,
-       HDMI_FC_AVICONF2_SCALING_HORIZ_VERT = 0x03,
-       HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0C,
-       HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00,
-       HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04,
-       HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08,
-       HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70,
-       HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00,
-       HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10,
-       HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20,
-       HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30,
-       HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40,
-       HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80,
-       HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00,
-       HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80,
-
-       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03,
-       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS = 0x00,
-       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_PHOTO = 0x01,
-       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_CINEMA = 0x02,
-       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GAME = 0x03,
-       HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0C,
-       HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
-       HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
-
-/* FC_DBGFORCE field values */
-       HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
-       HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
-
-/* PHY_CONF0 field values */
-       HDMI_PHY_CONF0_PDZ_MASK = 0x80,
-       HDMI_PHY_CONF0_PDZ_OFFSET = 7,
-       HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
-       HDMI_PHY_CONF0_ENTMDS_OFFSET = 6,
-       HDMI_PHY_CONF0_SPARECTRL_MASK = 0x20,
-       HDMI_PHY_CONF0_SPARECTRL_OFFSET = 5,
-       HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10,
-       HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
-       HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
-       HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3,
-       HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4,
-       HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2,
-       HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2,
-       HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1,
-       HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1,
-       HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0,
-
-/* PHY_TST0 field values */
-       HDMI_PHY_TST0_TSTCLR_MASK = 0x20,
-       HDMI_PHY_TST0_TSTCLR_OFFSET = 5,
-       HDMI_PHY_TST0_TSTEN_MASK = 0x10,
-       HDMI_PHY_TST0_TSTEN_OFFSET = 4,
-       HDMI_PHY_TST0_TSTCLK_MASK = 0x1,
-       HDMI_PHY_TST0_TSTCLK_OFFSET = 0,
-
-/* PHY_STAT0 field values */
-       HDMI_PHY_RX_SENSE3 = 0x80,
-       HDMI_PHY_RX_SENSE2 = 0x40,
-       HDMI_PHY_RX_SENSE1 = 0x20,
-       HDMI_PHY_RX_SENSE0 = 0x10,
-       HDMI_PHY_HPD = 0x02,
-       HDMI_PHY_TX_PHY_LOCK = 0x01,
-
-/* PHY_I2CM_SLAVE_ADDR field values */
-       HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69,
-       HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49,
-
-/* PHY_I2CM_OPERATION_ADDR field values */
-       HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10,
-       HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1,
-
-/* HDMI_PHY_I2CM_INT_ADDR */
-       HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08,
-       HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04,
-
-/* HDMI_PHY_I2CM_CTLINT_ADDR */
-       HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80,
-       HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40,
-       HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08,
-       HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
-
-/* AUD_CTS3 field values */
-       HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
-       HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
-       HDMI_AUD_CTS3_N_SHIFT_1 = 0,
-       HDMI_AUD_CTS3_N_SHIFT_16 = 0x20,
-       HDMI_AUD_CTS3_N_SHIFT_32 = 0x40,
-       HDMI_AUD_CTS3_N_SHIFT_64 = 0x60,
-       HDMI_AUD_CTS3_N_SHIFT_128 = 0x80,
-       HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0,
-       /* note that the CTS3 MANUAL bit has been removed
-          from our part. Can't set it, will read as 0. */
-       HDMI_AUD_CTS3_CTS_MANUAL = 0x10,
-       HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f,
-
-/* AHB_DMA_CONF0 field values */
-       HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7,
-       HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80,
-       HDMI_AHB_DMA_CONF0_HBR = 0x10,
-       HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3,
-       HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08,
-       HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1,
-       HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK = 0x06,
-       HDMI_AHB_DMA_CONF0_INCR4 = 0x0,
-       HDMI_AHB_DMA_CONF0_INCR8 = 0x2,
-       HDMI_AHB_DMA_CONF0_INCR16 = 0x4,
-       HDMI_AHB_DMA_CONF0_BURST_MODE = 0x1,
-
-/* HDMI_AHB_DMA_START field values */
-       HDMI_AHB_DMA_START_START_OFFSET = 0,
-       HDMI_AHB_DMA_START_START_MASK = 0x01,
-
-/* HDMI_AHB_DMA_STOP field values */
-       HDMI_AHB_DMA_STOP_STOP_OFFSET = 0,
-       HDMI_AHB_DMA_STOP_STOP_MASK = 0x01,
-
-/* AHB_DMA_STAT, AHB_DMA_INT, AHB_DMA_MASK, AHB_DMA_POL field values */
-       HDMI_AHB_DMA_DONE = 0x80,
-       HDMI_AHB_DMA_RETRY_SPLIT = 0x40,
-       HDMI_AHB_DMA_LOSTOWNERSHIP = 0x20,
-       HDMI_AHB_DMA_ERROR = 0x10,
-       HDMI_AHB_DMA_FIFO_THREMPTY = 0x04,
-       HDMI_AHB_DMA_FIFO_FULL = 0x02,
-       HDMI_AHB_DMA_FIFO_EMPTY = 0x01,
-
-/* AHB_DMA_BUFFSTAT, AHB_DMA_BUFFINT,AHB_DMA_BUFFMASK,AHB_DMA_BUFFPOL values */
-       HDMI_AHB_DMA_BUFFSTAT_FULL = 0x02,
-       HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01,
-
-/* MC_CLKDIS field values */
-       HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40,
-       HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20,
-       HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10,
-       HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8,
-       HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4,
-       HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2,
-       HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1,
-
-/* MC_SWRSTZ field values */
-       HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02,
-
-/* MC_FLOWCTRL field values */
-       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1,
-       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1,
-       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0,
-
-/* MC_PHYRSTZ field values */
-       HDMI_MC_PHYRSTZ_ASSERT = 0x0,
-       HDMI_MC_PHYRSTZ_DEASSERT = 0x1,
-
-/* MC_HEACPHY_RST field values */
-       HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
-       HDMI_MC_HEACPHY_RST_DEASSERT = 0x0,
-
-/* CSC_CFG field values */
-       HDMI_CSC_CFG_INTMODE_MASK = 0x30,
-       HDMI_CSC_CFG_INTMODE_OFFSET = 4,
-       HDMI_CSC_CFG_INTMODE_DISABLE = 0x00,
-       HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1 = 0x10,
-       HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA2 = 0x20,
-       HDMI_CSC_CFG_DECMODE_MASK = 0x3,
-       HDMI_CSC_CFG_DECMODE_OFFSET = 0,
-       HDMI_CSC_CFG_DECMODE_DISABLE = 0x0,
-       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1 = 0x1,
-       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA2 = 0x2,
-       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3 = 0x3,
-
-/* CSC_SCALE field values */
-       HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xF0,
-       HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00,
-       HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50,
-       HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60,
-       HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70,
-       HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03,
-
-/* A_HDCPCFG0 field values */
-       HDMI_A_HDCPCFG0_ELVENA_MASK = 0x80,
-       HDMI_A_HDCPCFG0_ELVENA_ENABLE = 0x80,
-       HDMI_A_HDCPCFG0_ELVENA_DISABLE = 0x00,
-       HDMI_A_HDCPCFG0_I2CFASTMODE_MASK = 0x40,
-       HDMI_A_HDCPCFG0_I2CFASTMODE_ENABLE = 0x40,
-       HDMI_A_HDCPCFG0_I2CFASTMODE_DISABLE = 0x00,
-       HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK = 0x20,
-       HDMI_A_HDCPCFG0_BYPENCRYPTION_ENABLE = 0x20,
-       HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE = 0x00,
-       HDMI_A_HDCPCFG0_SYNCRICHECK_MASK = 0x10,
-       HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE = 0x10,
-       HDMI_A_HDCPCFG0_SYNCRICHECK_DISABLE = 0x00,
-       HDMI_A_HDCPCFG0_AVMUTE_MASK = 0x8,
-       HDMI_A_HDCPCFG0_AVMUTE_ENABLE = 0x8,
-       HDMI_A_HDCPCFG0_AVMUTE_DISABLE = 0x0,
-       HDMI_A_HDCPCFG0_RXDETECT_MASK = 0x4,
-       HDMI_A_HDCPCFG0_RXDETECT_ENABLE = 0x4,
-       HDMI_A_HDCPCFG0_RXDETECT_DISABLE = 0x0,
-       HDMI_A_HDCPCFG0_EN11FEATURE_MASK = 0x2,
-       HDMI_A_HDCPCFG0_EN11FEATURE_ENABLE = 0x2,
-       HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE = 0x0,
-       HDMI_A_HDCPCFG0_HDMIDVI_MASK = 0x1,
-       HDMI_A_HDCPCFG0_HDMIDVI_HDMI = 0x1,
-       HDMI_A_HDCPCFG0_HDMIDVI_DVI = 0x0,
-
-/* A_HDCPCFG1 field values */
-       HDMI_A_HDCPCFG1_DISSHA1CHECK_MASK = 0x8,
-       HDMI_A_HDCPCFG1_DISSHA1CHECK_DISABLE = 0x8,
-       HDMI_A_HDCPCFG1_DISSHA1CHECK_ENABLE = 0x0,
-       HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK = 0x4,
-       HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE = 0x4,
-       HDMI_A_HDCPCFG1_PH2UPSHFTENC_DISABLE = 0x0,
-       HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK = 0x2,
-       HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE = 0x2,
-       HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE = 0x0,
-       HDMI_A_HDCPCFG1_SWRESET_MASK = 0x1,
-       HDMI_A_HDCPCFG1_SWRESET_ASSERT = 0x0,
-
-/* A_VIDPOLCFG field values */
-       HDMI_A_VIDPOLCFG_UNENCRYPTCONF_MASK = 0x60,
-       HDMI_A_VIDPOLCFG_UNENCRYPTCONF_OFFSET = 5,
-       HDMI_A_VIDPOLCFG_DATAENPOL_MASK = 0x10,
-       HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH = 0x10,
-       HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW = 0x0,
-       HDMI_A_VIDPOLCFG_VSYNCPOL_MASK = 0x8,
-       HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH = 0x8,
-       HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW = 0x0,
-       HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2,
-       HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2,
-       HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0,
-};
-
-#endif /* __DW_HDMI_H__ */
index 61385f2..276719e 100644 (file)
@@ -489,7 +489,7 @@ static struct drm_encoder *cirrus_encoder_init(struct drm_device *dev)
        encoder->possible_crtcs = 0x1;
 
        drm_encoder_init(dev, encoder, &cirrus_encoder_encoder_funcs,
-                        DRM_MODE_ENCODER_DAC);
+                        DRM_MODE_ENCODER_DAC, NULL);
        drm_encoder_helper_add(encoder, &cirrus_encoder_helper_funcs);
 
        return encoder;
index ef5f766..6a21e5c 100644 (file)
@@ -288,8 +288,8 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
        state->crtcs[index] = crtc;
        crtc_state->state = state;
 
-       DRM_DEBUG_ATOMIC("Added [CRTC:%d] %p state to %p\n",
-                        crtc->base.id, crtc_state, state);
+       DRM_DEBUG_ATOMIC("Added [CRTC:%d:%s] %p state to %p\n",
+                        crtc->base.id, crtc->name, crtc_state, state);
 
        return crtc_state;
 }
@@ -429,11 +429,20 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
 }
 EXPORT_SYMBOL(drm_atomic_crtc_set_property);
 
-/*
+/**
+ * drm_atomic_crtc_get_property - get property value from CRTC state
+ * @crtc: the drm CRTC to set a property on
+ * @state: the state object to get the property value from
+ * @property: the property to set
+ * @val: return location for the property value
+ *
  * This function handles generic/core properties and calls out to
  * driver's ->atomic_get_property() for driver properties.  To ensure
  * consistent behavior you must call this function rather than the
  * driver hook directly.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
  */
 static int
 drm_atomic_crtc_get_property(struct drm_crtc *crtc,
@@ -477,8 +486,8 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
         */
 
        if (state->active && !state->enable) {
-               DRM_DEBUG_ATOMIC("[CRTC:%d] active without enabled\n",
-                                crtc->base.id);
+               DRM_DEBUG_ATOMIC("[CRTC:%d:%s] active without enabled\n",
+                                crtc->base.id, crtc->name);
                return -EINVAL;
        }
 
@@ -487,15 +496,15 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
         * be able to trigger. */
        if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) &&
            WARN_ON(state->enable && !state->mode_blob)) {
-               DRM_DEBUG_ATOMIC("[CRTC:%d] enabled without mode blob\n",
-                                crtc->base.id);
+               DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled without mode blob\n",
+                                crtc->base.id, crtc->name);
                return -EINVAL;
        }
 
        if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) &&
            WARN_ON(!state->enable && state->mode_blob)) {
-               DRM_DEBUG_ATOMIC("[CRTC:%d] disabled with mode blob\n",
-                                crtc->base.id);
+               DRM_DEBUG_ATOMIC("[CRTC:%d:%s] disabled with mode blob\n",
+                                crtc->base.id, crtc->name);
                return -EINVAL;
        }
 
@@ -540,8 +549,8 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
        state->planes[index] = plane;
        plane_state->state = state;
 
-       DRM_DEBUG_ATOMIC("Added [PLANE:%d] %p state to %p\n",
-                        plane->base.id, plane_state, state);
+       DRM_DEBUG_ATOMIC("Added [PLANE:%d:%s] %p state to %p\n",
+                        plane->base.id, plane->name, plane_state, state);
 
        if (plane_state->crtc) {
                struct drm_crtc_state *crtc_state;
@@ -616,11 +625,20 @@ int drm_atomic_plane_set_property(struct drm_plane *plane,
 }
 EXPORT_SYMBOL(drm_atomic_plane_set_property);
 
-/*
+/**
+ * drm_atomic_plane_get_property - get property value from plane state
+ * @plane: the drm plane to set a property on
+ * @state: the state object to get the property value from
+ * @property: the property to set
+ * @val: return location for the property value
+ *
  * This function handles generic/core properties and calls out to
  * driver's ->atomic_get_property() for driver properties.  To ensure
  * consistent behavior you must call this function rather than the
  * driver hook directly.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
  */
 static int
 drm_atomic_plane_get_property(struct drm_plane *plane,
@@ -752,8 +770,8 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
        }
 
        if (plane_switching_crtc(state->state, plane, state)) {
-               DRM_DEBUG_ATOMIC("[PLANE:%d] switching CRTC directly\n",
-                                plane->base.id);
+               DRM_DEBUG_ATOMIC("[PLANE:%d:%s] switching CRTC directly\n",
+                                plane->base.id, plane->name);
                return -EINVAL;
        }
 
@@ -872,11 +890,20 @@ int drm_atomic_connector_set_property(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_atomic_connector_set_property);
 
-/*
+/**
+ * drm_atomic_connector_get_property - get property value from connector state
+ * @connector: the drm connector to set a property on
+ * @state: the state object to get the property value from
+ * @property: the property to set
+ * @val: return location for the property value
+ *
  * This function handles generic/core properties and calls out to
  * driver's ->atomic_get_property() for driver properties.  To ensure
  * consistent behavior you must call this function rather than the
  * driver hook directly.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
  */
 static int
 drm_atomic_connector_get_property(struct drm_connector *connector,
@@ -977,8 +1004,8 @@ drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state,
        }
 
        if (crtc)
-               DRM_DEBUG_ATOMIC("Link plane state %p to [CRTC:%d]\n",
-                                plane_state, crtc->base.id);
+               DRM_DEBUG_ATOMIC("Link plane state %p to [CRTC:%d:%s]\n",
+                                plane_state, crtc->base.id, crtc->name);
        else
                DRM_DEBUG_ATOMIC("Link plane state %p to [NOCRTC]\n",
                                 plane_state);
@@ -1045,8 +1072,8 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
        conn_state->crtc = crtc;
 
        if (crtc)
-               DRM_DEBUG_ATOMIC("Link connector state %p to [CRTC:%d]\n",
-                                conn_state, crtc->base.id);
+               DRM_DEBUG_ATOMIC("Link connector state %p to [CRTC:%d:%s]\n",
+                                conn_state, crtc->base.id, crtc->name);
        else
                DRM_DEBUG_ATOMIC("Link connector state %p to [NOCRTC]\n",
                                 conn_state);
@@ -1085,8 +1112,8 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
        if (ret)
                return ret;
 
-       DRM_DEBUG_ATOMIC("Adding all current connectors for [CRTC:%d] to %p\n",
-                        crtc->base.id, state);
+       DRM_DEBUG_ATOMIC("Adding all current connectors for [CRTC:%d:%s] to %p\n",
+                        crtc->base.id, crtc->name, state);
 
        /*
         * Changed connectors are already in @state, so only need to look at the
@@ -1166,8 +1193,9 @@ drm_atomic_connectors_for_crtc(struct drm_atomic_state *state,
                        num_connected_connectors++;
        }
 
-       DRM_DEBUG_ATOMIC("State %p has %i connectors for [CRTC:%d]\n",
-                        state, num_connected_connectors, crtc->base.id);
+       DRM_DEBUG_ATOMIC("State %p has %i connectors for [CRTC:%d:%s]\n",
+                        state, num_connected_connectors,
+                        crtc->base.id, crtc->name);
 
        return num_connected_connectors;
 }
@@ -1220,8 +1248,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
        for_each_plane_in_state(state, plane, plane_state, i) {
                ret = drm_atomic_plane_check(plane, plane_state);
                if (ret) {
-                       DRM_DEBUG_ATOMIC("[PLANE:%d] atomic core check failed\n",
-                                        plane->base.id);
+                       DRM_DEBUG_ATOMIC("[PLANE:%d:%s] atomic core check failed\n",
+                                        plane->base.id, plane->name);
                        return ret;
                }
        }
@@ -1229,8 +1257,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
        for_each_crtc_in_state(state, crtc, crtc_state, i) {
                ret = drm_atomic_crtc_check(crtc, crtc_state);
                if (ret) {
-                       DRM_DEBUG_ATOMIC("[CRTC:%d] atomic core check failed\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic core check failed\n",
+                                        crtc->base.id, crtc->name);
                        return ret;
                }
        }
@@ -1241,8 +1269,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
        if (!state->allow_modeset) {
                for_each_crtc_in_state(state, crtc, crtc_state, i) {
                        if (drm_atomic_crtc_needs_modeset(crtc_state)) {
-                               DRM_DEBUG_ATOMIC("[CRTC:%d] requires full modeset\n",
-                                                crtc->base.id);
+                               DRM_DEBUG_ATOMIC("[CRTC:%d:%s] requires full modeset\n",
+                                                crtc->base.id, crtc->name);
                                return -EINVAL;
                        }
                }
index 74a5fc4..63f925b 100644 (file)
  * drm_atomic_helper_disable_plane(), drm_atomic_helper_disable_plane() and the
  * various functions to implement set_property callbacks. New drivers must not
  * implement these functions themselves but must use the provided helpers.
+ *
+ * The atomic helper uses the same function table structures as all other
+ * modesetting helpers. See the documentation for struct &drm_crtc_helper_funcs,
+ * struct &drm_encoder_helper_funcs and struct &drm_connector_helper_funcs. It
+ * also shares the struct &drm_plane_helper_funcs function table with the plane
+ * helpers.
  */
 static void
 drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
@@ -137,9 +143,9 @@ steal_encoder(struct drm_atomic_state *state,
         */
        WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
 
-       DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n",
+       DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], stealing it\n",
                         encoder->base.id, encoder->name,
-                        encoder_crtc->base.id);
+                        encoder_crtc->base.id, encoder_crtc->name);
 
        crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc);
        if (IS_ERR(crtc_state))
@@ -240,12 +246,13 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
        }
 
        if (new_encoder == connector_state->best_encoder) {
-               DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n",
+               DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d:%s]\n",
                                 connector->base.id,
                                 connector->name,
                                 new_encoder->base.id,
                                 new_encoder->name,
-                                connector_state->crtc->base.id);
+                                connector_state->crtc->base.id,
+                                connector_state->crtc->name);
 
                return 0;
        }
@@ -279,12 +286,13 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
        crtc_state = state->crtc_states[idx];
        crtc_state->connectors_changed = true;
 
-       DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n",
+       DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n",
                         connector->base.id,
                         connector->name,
                         new_encoder->base.id,
                         new_encoder->name,
-                        connector_state->crtc->base.id);
+                        connector_state->crtc->base.id,
+                        connector_state->crtc->name);
 
        return 0;
 }
@@ -368,8 +376,8 @@ mode_fixup(struct drm_atomic_state *state)
                ret = funcs->mode_fixup(crtc, &crtc_state->mode,
                                        &crtc_state->adjusted_mode);
                if (!ret) {
-                       DRM_DEBUG_ATOMIC("[CRTC:%d] fixup failed\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d:%s] fixup failed\n",
+                                        crtc->base.id, crtc->name);
                        return -EINVAL;
                }
        }
@@ -416,14 +424,14 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
 
        for_each_crtc_in_state(state, crtc, crtc_state, i) {
                if (!drm_mode_equal(&crtc->state->mode, &crtc_state->mode)) {
-                       DRM_DEBUG_ATOMIC("[CRTC:%d] mode changed\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode changed\n",
+                                        crtc->base.id, crtc->name);
                        crtc_state->mode_changed = true;
                }
 
                if (crtc->state->enable != crtc_state->enable) {
-                       DRM_DEBUG_ATOMIC("[CRTC:%d] enable changed\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enable changed\n",
+                                        crtc->base.id, crtc->name);
 
                        /*
                         * For clarity this assignment is done here, but
@@ -464,18 +472,18 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
                 * a full modeset because update_connector_routing force that.
                 */
                if (crtc->state->active != crtc_state->active) {
-                       DRM_DEBUG_ATOMIC("[CRTC:%d] active changed\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d:%s] active changed\n",
+                                        crtc->base.id, crtc->name);
                        crtc_state->active_changed = true;
                }
 
                if (!drm_atomic_crtc_needs_modeset(crtc_state))
                        continue;
 
-               DRM_DEBUG_ATOMIC("[CRTC:%d] needs all connectors, enable: %c, active: %c\n",
-                                crtc->base.id,
+               DRM_DEBUG_ATOMIC("[CRTC:%d:%s] needs all connectors, enable: %c, active: %c\n",
+                                crtc->base.id, crtc->name,
                                 crtc_state->enable ? 'y' : 'n',
-                             crtc_state->active ? 'y' : 'n');
+                                crtc_state->active ? 'y' : 'n');
 
                ret = drm_atomic_add_affected_connectors(state, crtc);
                if (ret != 0)
@@ -489,8 +497,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
                                                                crtc);
 
                if (crtc_state->enable != !!num_connectors) {
-                       DRM_DEBUG_ATOMIC("[CRTC:%d] enabled/connectors mismatch\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled/connectors mismatch\n",
+                                        crtc->base.id, crtc->name);
 
                        return -EINVAL;
                }
@@ -537,8 +545,8 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
 
                ret = funcs->atomic_check(plane, plane_state);
                if (ret) {
-                       DRM_DEBUG_ATOMIC("[PLANE:%d] atomic driver check failed\n",
-                                        plane->base.id);
+                       DRM_DEBUG_ATOMIC("[PLANE:%d:%s] atomic driver check failed\n",
+                                        plane->base.id, plane->name);
                        return ret;
                }
        }
@@ -553,8 +561,8 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
 
                ret = funcs->atomic_check(crtc, state->crtc_states[i]);
                if (ret) {
-                       DRM_DEBUG_ATOMIC("[CRTC:%d] atomic driver check failed\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic driver check failed\n",
+                                        crtc->base.id, crtc->name);
                        return ret;
                }
        }
@@ -667,8 +675,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 
                funcs = crtc->helper_private;
 
-               DRM_DEBUG_ATOMIC("disabling [CRTC:%d]\n",
-                                crtc->base.id);
+               DRM_DEBUG_ATOMIC("disabling [CRTC:%d:%s]\n",
+                                crtc->base.id, crtc->name);
 
 
                /* Right function depends upon target state. */
@@ -779,8 +787,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
                funcs = crtc->helper_private;
 
                if (crtc->state->enable && funcs->mode_set_nofb) {
-                       DRM_DEBUG_ATOMIC("modeset on [CRTC:%d]\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("modeset on [CRTC:%d:%s]\n",
+                                        crtc->base.id, crtc->name);
 
                        funcs->mode_set_nofb(crtc);
                }
@@ -879,8 +887,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
                funcs = crtc->helper_private;
 
                if (crtc->state->enable) {
-                       DRM_DEBUG_ATOMIC("enabling [CRTC:%d]\n",
-                                        crtc->base.id);
+                       DRM_DEBUG_ATOMIC("enabling [CRTC:%d:%s]\n",
+                                        crtc->base.id, crtc->name);
 
                        if (funcs->enable)
                                funcs->enable(crtc);
@@ -2399,6 +2407,12 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_dpms);
  * The simpler solution is to just reset the software state to everything off,
  * which is easiest to do by calling drm_mode_config_reset(). To facilitate this
  * the atomic helpers provide default reset implementations for all hooks.
+ *
+ * On the upside the precise state tracking of atomic simplifies system suspend
+ * and resume a lot. For drivers using drm_mode_config_reset() a complete recipe
+ * is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume().
+ * For other drivers the building blocks are split out, see the documentation
+ * for these functions.
  */
 
 /**
index 6b8f721..bd93453 100644 (file)
 /**
  * DOC: overview
  *
- * drm_bridge represents a device that hangs on to an encoder. These are handy
- * when a regular drm_encoder entity isn't enough to represent the entire
+ * struct &drm_bridge represents a device that hangs on to an encoder. These are
+ * handy when a regular &drm_encoder entity isn't enough to represent the entire
  * encoder chain.
  *
- * A bridge is always associated to a single drm_encoder at a time, but can be
+ * A bridge is always attached to a single &drm_encoder at a time, but can be
  * either connected to it directly, or through an intermediate bridge:
  *
- * encoder ---> bridge B ---> bridge A
+ *     encoder ---> bridge B ---> bridge A
  *
  * Here, the output of the encoder feeds to bridge B, and that furthers feeds to
  * bridge A.
  * The driver using the bridge is responsible to make the associations between
  * the encoder and bridges. Once these links are made, the bridges will
  * participate along with encoder functions to perform mode_set/enable/disable
- * through the ops provided in drm_bridge_funcs.
+ * through the ops provided in &drm_bridge_funcs.
  *
  * drm_bridge, like drm_panel, aren't drm_mode_object entities like planes,
- * crtcs, encoders or connectors. They just provide additional hooks to get the
- * desired output at the end of the encoder chain.
+ * CRTCs, encoders or connectors and hence are not visible to userspace. They
+ * just provide additional hooks to get the desired output at the end of the
+ * encoder chain.
+ *
+ * Bridges can also be chained up using the next pointer in struct &drm_bridge.
+ *
+ * Both legacy CRTC helpers and the new atomic modeset helpers support bridges.
  */
 
 static DEFINE_MUTEX(bridge_lock);
@@ -122,34 +127,12 @@ EXPORT_SYMBOL(drm_bridge_attach);
 /**
  * DOC: bridge callbacks
  *
- * The drm_bridge_funcs ops are populated by the bridge driver. The drm
- * internals(atomic and crtc helpers) use the helpers defined in drm_bridge.c
- * These helpers call a specific drm_bridge_funcs op for all the bridges
+ * The &drm_bridge_funcs ops are populated by the bridge driver. The DRM
+ * internals (atomic and CRTC helpers) use the helpers defined in drm_bridge.c
+ * These helpers call a specific &drm_bridge_funcs op for all the bridges
  * during encoder configuration.
  *
- * When creating a bridge driver, one can implement drm_bridge_funcs op with
- * the help of these rough rules:
- *
- * pre_enable: this contains things needed to be done for the bridge before
- * its clock and timings are enabled by its source. For a bridge, its source
- * is generally the encoder or bridge just before it in the encoder chain.
- *
- * enable: this contains things needed to be done for the bridge once its
- * source is enabled. In other words, enable is called once the source is
- * ready with clock and timing needed by the bridge.
- *
- * disable: this contains things needed to be done for the bridge assuming
- * that its source is still enabled, i.e. clock and timings are still on.
- *
- * post_disable: this contains things needed to be done for the bridge once
- * its source is disabled, i.e. once clocks and timings are off.
- *
- * mode_fixup: this should fixup the given mode for the bridge. It is called
- * after the encoder's mode fixup. mode_fixup can also reject a mode completely
- * if it's unsuitable for the hardware.
- *
- * mode_set: this sets up the mode for the bridge. It assumes that its source
- * (an encoder or a bridge) has set the mode too.
+ * For detailed specification of the bridge callbacks see &drm_bridge_funcs.
  */
 
 /**
@@ -159,7 +142,7 @@ EXPORT_SYMBOL(drm_bridge_attach);
  * @mode: desired mode to be set for the bridge
  * @adjusted_mode: updated mode that works for this bridge
  *
- * Calls 'mode_fixup' drm_bridge_funcs op for all the bridges in the
+ * Calls ->mode_fixup() &drm_bridge_funcs op for all the bridges in the
  * encoder chain, starting from the first bridge to the last.
  *
  * Note: the bridge passed should be the one closest to the encoder
@@ -186,11 +169,11 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
 EXPORT_SYMBOL(drm_bridge_mode_fixup);
 
 /**
- * drm_bridge_disable - calls 'disable' drm_bridge_funcs op for all
+ * drm_bridge_disable - calls ->disable() &drm_bridge_funcs op for all
  *                     bridges in the encoder chain.
  * @bridge: bridge control structure
  *
- * Calls 'disable' drm_bridge_funcs op for all the bridges in the encoder
+ * Calls ->disable() &drm_bridge_funcs op for all the bridges in the encoder
  * chain, starting from the last bridge to the first. These are called before
  * calling the encoder's prepare op.
  *
@@ -208,11 +191,11 @@ void drm_bridge_disable(struct drm_bridge *bridge)
 EXPORT_SYMBOL(drm_bridge_disable);
 
 /**
- * drm_bridge_post_disable - calls 'post_disable' drm_bridge_funcs op for
+ * drm_bridge_post_disable - calls ->post_disable() &drm_bridge_funcs op for
  *                          all bridges in the encoder chain.
  * @bridge: bridge control structure
  *
- * Calls 'post_disable' drm_bridge_funcs op for all the bridges in the
+ * Calls ->post_disable() &drm_bridge_funcs op for all the bridges in the
  * encoder chain, starting from the first bridge to the last. These are called
  * after completing the encoder's prepare op.
  *
@@ -236,7 +219,7 @@ EXPORT_SYMBOL(drm_bridge_post_disable);
  * @mode: desired mode to be set for the bridge
  * @adjusted_mode: updated mode that works for this bridge
  *
- * Calls 'mode_set' drm_bridge_funcs op for all the bridges in the
+ * Calls ->mode_set() &drm_bridge_funcs op for all the bridges in the
  * encoder chain, starting from the first bridge to the last.
  *
  * Note: the bridge passed should be the one closest to the encoder
@@ -256,11 +239,11 @@ void drm_bridge_mode_set(struct drm_bridge *bridge,
 EXPORT_SYMBOL(drm_bridge_mode_set);
 
 /**
- * drm_bridge_pre_enable - calls 'pre_enable' drm_bridge_funcs op for all
+ * drm_bridge_pre_enable - calls ->pre_enable() &drm_bridge_funcs op for all
  *                        bridges in the encoder chain.
  * @bridge: bridge control structure
  *
- * Calls 'pre_enable' drm_bridge_funcs op for all the bridges in the encoder
+ * Calls ->pre_enable() &drm_bridge_funcs op for all the bridges in the encoder
  * chain, starting from the last bridge to the first. These are called
  * before calling the encoder's commit op.
  *
@@ -278,11 +261,11 @@ void drm_bridge_pre_enable(struct drm_bridge *bridge)
 EXPORT_SYMBOL(drm_bridge_pre_enable);
 
 /**
- * drm_bridge_enable - calls 'enable' drm_bridge_funcs op for all bridges
+ * drm_bridge_enable - calls ->enable() &drm_bridge_funcs op for all bridges
  *                    in the encoder chain.
  * @bridge: bridge control structure
  *
- * Calls 'enable' drm_bridge_funcs op for all the bridges in the encoder
+ * Calls ->enable() &drm_bridge_funcs op for all the bridges in the encoder
  * chain, starting from the first bridge to the last. These are called
  * after completing the encoder's commit op.
  *
index 32dd134..62fa95f 100644 (file)
@@ -649,6 +649,18 @@ EXPORT_SYMBOL(drm_framebuffer_remove);
 
 DEFINE_WW_CLASS(crtc_ww_class);
 
+static unsigned int drm_num_crtcs(struct drm_device *dev)
+{
+       unsigned int num = 0;
+       struct drm_crtc *tmp;
+
+       drm_for_each_crtc(tmp, dev) {
+               num++;
+       }
+
+       return num;
+}
+
 /**
  * drm_crtc_init_with_planes - Initialise a new CRTC object with
  *    specified primary and cursor planes.
@@ -657,6 +669,7 @@ DEFINE_WW_CLASS(crtc_ww_class);
  * @primary: Primary plane for CRTC
  * @cursor: Cursor plane for CRTC
  * @funcs: callbacks for the new CRTC
+ * @name: printf style format string for the CRTC name, or NULL for default name
  *
  * Inits a new object created as base part of a driver crtc object.
  *
@@ -666,7 +679,8 @@ DEFINE_WW_CLASS(crtc_ww_class);
 int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
                              struct drm_plane *primary,
                              struct drm_plane *cursor,
-                             const struct drm_crtc_funcs *funcs)
+                             const struct drm_crtc_funcs *funcs,
+                             const char *name, ...)
 {
        struct drm_mode_config *config = &dev->mode_config;
        int ret;
@@ -682,6 +696,21 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
        if (ret)
                return ret;
 
+       if (name) {
+               va_list ap;
+
+               va_start(ap, name);
+               crtc->name = kvasprintf(GFP_KERNEL, name, ap);
+               va_end(ap);
+       } else {
+               crtc->name = kasprintf(GFP_KERNEL, "crtc-%d",
+                                      drm_num_crtcs(dev));
+       }
+       if (!crtc->name) {
+               drm_mode_object_put(dev, &crtc->base);
+               return -ENOMEM;
+       }
+
        crtc->base.properties = &crtc->properties;
 
        list_add_tail(&crtc->head, &config->crtc_list);
@@ -728,6 +757,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
        if (crtc->state && crtc->funcs->atomic_destroy_state)
                crtc->funcs->atomic_destroy_state(crtc, crtc->state);
 
+       kfree(crtc->name);
+
        memset(crtc, 0, sizeof(*crtc));
 }
 EXPORT_SYMBOL(drm_crtc_cleanup);
@@ -1075,6 +1106,7 @@ EXPORT_SYMBOL(drm_connector_unplug_all);
  * @encoder: the encoder to init
  * @funcs: callbacks for this encoder
  * @encoder_type: user visible type of the encoder
+ * @name: printf style format string for the encoder name, or NULL for default name
  *
  * Initialises a preallocated encoder. Encoder should be
  * subclassed as part of driver encoder objects.
@@ -1085,7 +1117,7 @@ EXPORT_SYMBOL(drm_connector_unplug_all);
 int drm_encoder_init(struct drm_device *dev,
                      struct drm_encoder *encoder,
                      const struct drm_encoder_funcs *funcs,
-                     int encoder_type)
+                     int encoder_type, const char *name, ...)
 {
        int ret;
 
@@ -1098,9 +1130,17 @@ int drm_encoder_init(struct drm_device *dev,
        encoder->dev = dev;
        encoder->encoder_type = encoder_type;
        encoder->funcs = funcs;
-       encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
-                                 drm_encoder_enum_list[encoder_type].name,
-                                 encoder->base.id);
+       if (name) {
+               va_list ap;
+
+               va_start(ap, name);
+               encoder->name = kvasprintf(GFP_KERNEL, name, ap);
+               va_end(ap);
+       } else {
+               encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
+                                         drm_encoder_enum_list[encoder_type].name,
+                                         encoder->base.id);
+       }
        if (!encoder->name) {
                ret = -ENOMEM;
                goto out_put;
@@ -1141,6 +1181,18 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 }
 EXPORT_SYMBOL(drm_encoder_cleanup);
 
+static unsigned int drm_num_planes(struct drm_device *dev)
+{
+       unsigned int num = 0;
+       struct drm_plane *tmp;
+
+       drm_for_each_plane(tmp, dev) {
+               num++;
+       }
+
+       return num;
+}
+
 /**
  * drm_universal_plane_init - Initialize a new universal plane object
  * @dev: DRM device
@@ -1150,6 +1202,7 @@ EXPORT_SYMBOL(drm_encoder_cleanup);
  * @formats: array of supported formats (%DRM_FORMAT_*)
  * @format_count: number of elements in @formats
  * @type: type of plane (overlay, primary, cursor)
+ * @name: printf style format string for the plane name, or NULL for default name
  *
  * Initializes a plane object of type @type.
  *
@@ -1160,7 +1213,8 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
                             unsigned long possible_crtcs,
                             const struct drm_plane_funcs *funcs,
                             const uint32_t *formats, unsigned int format_count,
-                            enum drm_plane_type type)
+                            enum drm_plane_type type,
+                            const char *name, ...)
 {
        struct drm_mode_config *config = &dev->mode_config;
        int ret;
@@ -1182,6 +1236,22 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
                return -ENOMEM;
        }
 
+       if (name) {
+               va_list ap;
+
+               va_start(ap, name);
+               plane->name = kvasprintf(GFP_KERNEL, name, ap);
+               va_end(ap);
+       } else {
+               plane->name = kasprintf(GFP_KERNEL, "plane-%d",
+                                       drm_num_planes(dev));
+       }
+       if (!plane->name) {
+               kfree(plane->format_types);
+               drm_mode_object_put(dev, &plane->base);
+               return -ENOMEM;
+       }
+
        memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
        plane->format_count = format_count;
        plane->possible_crtcs = possible_crtcs;
@@ -1240,7 +1310,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 
        type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
        return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
-                                       formats, format_count, type);
+                                       formats, format_count, type, NULL);
 }
 EXPORT_SYMBOL(drm_plane_init);
 
@@ -1272,6 +1342,8 @@ void drm_plane_cleanup(struct drm_plane *plane)
        if (plane->state && plane->funcs->atomic_destroy_state)
                plane->funcs->atomic_destroy_state(plane, plane->state);
 
+       kfree(plane->name);
+
        memset(plane, 0, sizeof(*plane));
 }
 EXPORT_SYMBOL(drm_plane_cleanup);
@@ -1801,7 +1873,8 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
                copied = 0;
                crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
                drm_for_each_crtc(crtc, dev) {
-                       DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
+                       DRM_DEBUG_KMS("[CRTC:%d:%s]\n",
+                                     crtc->base.id, crtc->name);
                        if (put_user(crtc->base.id, crtc_id + copied)) {
                                ret = -EFAULT;
                                goto out;
@@ -2646,7 +2719,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                ret = -ENOENT;
                goto out;
        }
-       DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
+       DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
 
        if (crtc_req->mode_valid) {
                /* If we have a mode we need a framebuffer. */
@@ -4785,9 +4858,7 @@ static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
 
        /* Do DPMS ourselves */
        if (property == connector->dev->mode_config.dpms_property) {
-               ret = 0;
-               if (connector->funcs->dpms)
-                       ret = (*connector->funcs->dpms)(connector, (int)value);
+               ret = (*connector->funcs->dpms)(connector, (int)value);
        } else if (connector->funcs->set_property)
                ret = connector->funcs->set_property(connector, property, value);
 
index 10d0989..a02a7f9 100644 (file)
  * the same callbacks which drivers can use to e.g. restore the modeset
  * configuration on resume with drm_helper_resume_force_mode().
  *
+ * Note that this helper library doesn't track the current power state of CRTCs
+ * and encoders. It can call callbacks like ->dpms() even though the hardware is
+ * already in the desired state. This deficiency has been fixed in the atomic
+ * helpers.
+ *
  * The driver callbacks are mostly compatible with the atomic modeset helpers,
  * except for the handling of the primary plane: Atomic helpers require that the
  * primary plane is implemented as a real standalone plane and not directly tied
  * converting to the plane helpers). New drivers must not use these functions
  * but need to implement the atomic interface instead, potentially using the
  * atomic helpers for that.
+ *
+ * These legacy modeset helpers use the same function table structures as
+ * all other modesetting helpers. See the documentation for struct
+ * &drm_crtc_helper_funcs, struct &drm_encoder_helper_funcs and struct
+ * &drm_connector_helper_funcs.
  */
 MODULE_AUTHOR("David Airlie, Jesse Barnes");
 MODULE_DESCRIPTION("DRM KMS helper");
@@ -206,8 +216,8 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev)
  * @dev: DRM device
  *
  * This function walks through the entire mode setting configuration of @dev. It
- * will remove any crtc links of unused encoders and encoder links of
- * disconnected connectors. Then it will disable all unused encoders and crtcs
+ * will remove any CRTC links of unused encoders and encoder links of
+ * disconnected connectors. Then it will disable all unused encoders and CRTCs
  * either by calling their disable callback if available or by calling their
  * dpms callback with DRM_MODE_DPMS_OFF.
  */
@@ -329,7 +339,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                DRM_DEBUG_KMS("CRTC fixup failed\n");
                goto done;
        }
-       DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
+       DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
 
        crtc->hwmode = *adjusted_mode;
 
@@ -445,11 +455,36 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
  * drm_crtc_helper_set_config - set a new config from userspace
  * @set: mode set configuration
  *
- * Setup a new configuration, provided by the upper layers (either an ioctl call
- * from userspace or internally e.g. from the fbdev support code) in @set, and
- * enable it. This is the main helper functions for drivers that implement
- * kernel mode setting with the crtc helper functions and the assorted
- * ->prepare(), ->modeset() and ->commit() helper callbacks.
+ * The drm_crtc_helper_set_config() helper function implements the set_config
+ * callback of struct &drm_crtc_funcs for drivers using the legacy CRTC helpers.
+ *
+ * It first tries to locate the best encoder for each connector by calling the
+ * connector ->best_encoder() (struct &drm_connector_helper_funcs) helper
+ * operation.
+ *
+ * After locating the appropriate encoders, the helper function will call the
+ * mode_fixup encoder and CRTC helper operations to adjust the requested mode,
+ * or reject it completely in which case an error will be returned to the
+ * application. If the new configuration after mode adjustment is identical to
+ * the current configuration the helper function will return without performing
+ * any other operation.
+ *
+ * If the adjusted mode is identical to the current mode but changes to the
+ * frame buffer need to be applied, the drm_crtc_helper_set_config() function
+ * will call the CRTC ->mode_set_base() (struct &drm_crtc_helper_funcs) helper
+ * operation.
+ *
+ * If the adjusted mode differs from the current mode, or if the
+ * ->mode_set_base() helper operation is not provided, the helper function
+ * performs a full mode set sequence by calling the ->prepare(), ->mode_set()
+ * and ->commit() CRTC and encoder helper operations, in that order.
+ * Alternatively it can also use the dpms and disable helper operations. For
+ * details see struct &drm_crtc_helper_funcs and struct
+ * &drm_encoder_helper_funcs.
+ *
+ * This function is deprecated.  New drivers must implement atomic modeset
+ * support, for which this function is unsuitable. Instead drivers should use
+ * drm_atomic_helper_set_config().
  *
  * Returns:
  * Returns 0 on success, negative errno numbers on failure.
@@ -484,11 +519,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                set->fb = NULL;
 
        if (set->fb) {
-               DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n",
-                               set->crtc->base.id, set->fb->base.id,
-                               (int)set->num_connectors, set->x, set->y);
+               DRM_DEBUG_KMS("[CRTC:%d:%s] [FB:%d] #connectors=%d (x y) (%i %i)\n",
+                             set->crtc->base.id, set->crtc->name,
+                             set->fb->base.id,
+                             (int)set->num_connectors, set->x, set->y);
        } else {
-               DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
+               DRM_DEBUG_KMS("[CRTC:%d:%s] [NOFB]\n",
+                             set->crtc->base.id, set->crtc->name);
                drm_crtc_helper_disable(set->crtc);
                return 0;
        }
@@ -628,12 +665,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        connector->encoder->crtc = new_crtc;
                }
                if (new_crtc) {
-                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
-                               connector->base.id, connector->name,
-                               new_crtc->base.id);
+                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d:%s]\n",
+                                     connector->base.id, connector->name,
+                                     new_crtc->base.id, new_crtc->name);
                } else {
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
-                               connector->base.id, connector->name);
+                                     connector->base.id, connector->name);
                }
        }
 
@@ -650,8 +687,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
                                                      set->x, set->y,
                                                      save_set.fb)) {
-                               DRM_ERROR("failed to set mode on [CRTC:%d]\n",
-                                         set->crtc->base.id);
+                               DRM_ERROR("failed to set mode on [CRTC:%d:%s]\n",
+                                         set->crtc->base.id, set->crtc->name);
                                set->crtc->primary->fb = save_set.fb;
                                ret = -EINVAL;
                                goto fail;
@@ -758,10 +795,18 @@ static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)
  * @connector: affected connector
  * @mode: DPMS mode
  *
- * This is the main helper function provided by the crtc helper framework for
+ * The drm_helper_connector_dpms() helper function implements the ->dpms()
+ * callback of struct &drm_connector_funcs for drivers using the legacy CRTC helpers.
+ *
+ * This is the main helper function provided by the CRTC helper framework for
  * implementing the DPMS connector attribute. It computes the new desired DPMS
- * state for all encoders and crtcs in the output mesh and calls the ->dpms()
- * callback provided by the driver appropriately.
+ * state for all encoders and CRTCs in the output mesh and calls the ->dpms()
+ * callbacks provided by the driver in struct &drm_crtc_helper_funcs and struct
+ * &drm_encoder_helper_funcs appropriately.
+ *
+ * This function is deprecated.  New drivers must implement atomic modeset
+ * support, for which this function is unsuitable. Instead drivers should use
+ * drm_atomic_helper_connector_dpms().
  *
  * Returns:
  * Always returns 0.
@@ -919,9 +964,9 @@ EXPORT_SYMBOL(drm_helper_resume_force_mode);
  * @old_fb: previous framebuffer
  *
  * This function implements a callback useable as the ->mode_set callback
- * required by the crtc helpers. Besides the atomic plane helper functions for
+ * required by the CRTC helpers. Besides the atomic plane helper functions for
  * the primary plane the driver must also provide the ->mode_set_nofb callback
- * to set up the crtc.
+ * to set up the CRTC.
  *
  * This is a transitional helper useful for converting drivers to the atomic
  * interfaces.
@@ -985,7 +1030,7 @@ EXPORT_SYMBOL(drm_helper_crtc_mode_set);
  * @old_fb: previous framebuffer
  *
  * This function implements a callback useable as the ->mode_set_base used
- * required by the crtc helpers. The driver must provide the atomic plane helper
+ * required by the CRTC helpers. The driver must provide the atomic plane helper
  * functions for the primary plane.
  *
  * This is a transitional helper useful for converting drivers to the atomic
index e109b49..0f7b00b 100644 (file)
@@ -59,11 +59,13 @@ __drm_gem_cma_create(struct drm_device *drm, size_t size)
        struct drm_gem_object *gem_obj;
        int ret;
 
-       cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
-       if (!cma_obj)
+       if (drm->driver->gem_create_object)
+               gem_obj = drm->driver->gem_create_object(drm, size);
+       else
+               gem_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
+       if (!gem_obj)
                return ERR_PTR(-ENOMEM);
-
-       gem_obj = &cma_obj->base;
+       cma_obj = container_of(gem_obj, struct drm_gem_cma_object, base);
 
        ret = drm_gem_object_init(drm, gem_obj, size);
        if (ret)
index 2d5ca8e..6e6a9c5 100644 (file)
@@ -365,6 +365,44 @@ int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
 }
 EXPORT_SYMBOL(mipi_dsi_create_packet);
 
+/**
+ * mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi)
+{
+       struct mipi_dsi_msg msg = {
+               .channel = dsi->channel,
+               .type = MIPI_DSI_SHUTDOWN_PERIPHERAL,
+               .tx_buf = (u8 [2]) { 0, 0 },
+               .tx_len = 2,
+       };
+
+       return mipi_dsi_device_transfer(dsi, &msg);
+}
+EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral);
+
+/**
+ * mipi_dsi_turn_on_peripheral() - sends a Turn On Peripheral command
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi)
+{
+       struct mipi_dsi_msg msg = {
+               .channel = dsi->channel,
+               .type = MIPI_DSI_TURN_ON_PERIPHERAL,
+               .tx_buf = (u8 [2]) { 0, 0 },
+               .tx_len = 2,
+       };
+
+       return mipi_dsi_device_transfer(dsi, &msg);
+}
+EXPORT_SYMBOL(mipi_dsi_turn_on_peripheral);
+
 /*
  * mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
  *    the payload in a long packet transmitted from the peripheral back to the
index ef6bd36..5a8a78d 100644 (file)
@@ -708,7 +708,8 @@ void drm_mode_set_name(struct drm_display_mode *mode)
 }
 EXPORT_SYMBOL(drm_mode_set_name);
 
-/** drm_mode_hsync - get the hsync of a mode
+/**
+ * drm_mode_hsync - get the hsync of a mode
  * @mode: mode
  *
  * Returns:
@@ -1073,7 +1074,7 @@ static const char * const drm_mode_status_names[] = {
        MODE_STATUS(ONE_SIZE),
        MODE_STATUS(NO_REDUCED),
        MODE_STATUS(NO_STEREO),
-       MODE_STATUS(UNVERIFIED),
+       MODE_STATUS(STALE),
        MODE_STATUS(BAD),
        MODE_STATUS(ERROR),
 };
@@ -1171,7 +1172,6 @@ EXPORT_SYMBOL(drm_mode_sort);
 /**
  * drm_mode_connector_list_update - update the mode list for the connector
  * @connector: the connector to update
- * @merge_type_bits: whether to merge or overwrite type bits
  *
  * This moves the modes from the @connector probed_modes list
  * to the actual mode list. It compares the probed mode against the current
@@ -1180,33 +1180,48 @@ EXPORT_SYMBOL(drm_mode_sort);
  * This is just a helper functions doesn't validate any modes itself and also
  * doesn't prune any invalid modes. Callers need to do that themselves.
  */
-void drm_mode_connector_list_update(struct drm_connector *connector,
-                                   bool merge_type_bits)
+void drm_mode_connector_list_update(struct drm_connector *connector)
 {
-       struct drm_display_mode *mode;
        struct drm_display_mode *pmode, *pt;
-       int found_it;
 
        WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
 
-       list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
-                                head) {
-               found_it = 0;
+       list_for_each_entry_safe(pmode, pt, &connector->probed_modes, head) {
+               struct drm_display_mode *mode;
+               bool found_it = false;
+
                /* go through current modes checking for the new probed mode */
                list_for_each_entry(mode, &connector->modes, head) {
-                       if (drm_mode_equal(pmode, mode)) {
-                               found_it = 1;
-                               /* if equal delete the probed mode */
-                               mode->status = pmode->status;
-                               /* Merge type bits together */
-                               if (merge_type_bits)
-                                       mode->type |= pmode->type;
-                               else
-                                       mode->type = pmode->type;
-                               list_del(&pmode->head);
-                               drm_mode_destroy(connector->dev, pmode);
-                               break;
+                       if (!drm_mode_equal(pmode, mode))
+                               continue;
+
+                       found_it = true;
+
+                       /*
+                        * If the old matching mode is stale (ie. left over
+                        * from a previous probe) just replace it outright.
+                        * Otherwise just merge the type bits between all
+                        * equal probed modes.
+                        *
+                        * If two probed modes are considered equal, pick the
+                        * actual timings from the one that's marked as
+                        * preferred (in case the match isn't 100%). If
+                        * multiple or zero preferred modes are present, favor
+                        * the mode added to the probed_modes list first.
+                        */
+                       if (mode->status == MODE_STALE) {
+                               drm_mode_copy(mode, pmode);
+                       } else if ((mode->type & DRM_MODE_TYPE_PREFERRED) == 0 &&
+                                  (pmode->type & DRM_MODE_TYPE_PREFERRED) != 0) {
+                               pmode->type |= mode->type;
+                               drm_mode_copy(mode, pmode);
+                       } else {
+                               mode->type |= pmode->type;
                        }
+
+                       list_del(&pmode->head);
+                       drm_mode_destroy(connector->dev, pmode);
+                       break;
                }
 
                if (!found_it) {
@@ -1247,7 +1262,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
        unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
        bool yres_specified = false, cvt = false, rb = false;
        bool interlace = false, margins = false, was_digit = false;
-       int i, err;
+       int i;
        enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
 
 #ifdef CONFIG_FB
@@ -1267,9 +1282,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
                case '@':
                        if (!refresh_specified && !bpp_specified &&
                            !yres_specified && !cvt && !rb && was_digit) {
-                               err = kstrtouint(&name[i + 1], 10, &refresh);
-                               if (err)
-                                       return false;
+                               refresh = simple_strtol(&name[i+1], NULL, 10);
                                refresh_specified = true;
                                was_digit = false;
                        } else
@@ -1278,9 +1291,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
                case '-':
                        if (!bpp_specified && !yres_specified && !cvt &&
                            !rb && was_digit) {
-                               err = kstrtouint(&name[i + 1], 10, &bpp);
-                               if (err)
-                                       return false;
+                               bpp = simple_strtol(&name[i+1], NULL, 10);
                                bpp_specified = true;
                                was_digit = false;
                        } else
@@ -1288,9 +1299,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
                        break;
                case 'x':
                        if (!yres_specified && was_digit) {
-                               err = kstrtouint(&name[i + 1], 10, &yres);
-                               if (err)
-                                       return false;
+                               yres = simple_strtol(&name[i+1], NULL, 10);
                                yres_specified = true;
                                was_digit = false;
                        } else
@@ -1514,4 +1523,4 @@ int drm_mode_convert_umode(struct drm_display_mode *out,
 
 out:
        return ret;
-}
+}
\ No newline at end of file
index a6983d4..369d289 100644 (file)
  * by the atomic helpers.
  *
  * Again drivers are strongly urged to switch to the new interfaces.
+ *
+ * The plane helpers share the function table structures with other helpers,
+ * specifically also the atomic helpers. See struct &drm_plane_helper_funcs for
+ * the details.
  */
 
 /*
@@ -371,7 +375,7 @@ static struct drm_plane *create_primary_plane(struct drm_device *dev)
                                       &drm_primary_helper_funcs,
                                       safe_modeset_formats,
                                       ARRAY_SIZE(safe_modeset_formats),
-                                      DRM_PLANE_TYPE_PRIMARY);
+                                      DRM_PLANE_TYPE_PRIMARY, NULL);
        if (ret) {
                kfree(primary);
                primary = NULL;
@@ -398,7 +402,8 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
        struct drm_plane *primary;
 
        primary = create_primary_plane(dev);
-       return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs);
+       return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs,
+                                        NULL);
 }
 EXPORT_SYMBOL(drm_crtc_init);
 
index eee3b6f..e714b5a 100644 (file)
@@ -53,6 +53,9 @@
  * This helper library can be used independently of the modeset helper library.
  * Drivers can also overwrite different parts e.g. use their own hotplug
  * handling code to avoid probing unrelated outputs.
+ *
+ * The probe helpers share the function table structures with other display
+ * helper libraries. See struct &drm_connector_helper_funcs for the details.
  */
 
 static bool drm_kms_helper_poll = true;
@@ -126,9 +129,64 @@ void drm_kms_helper_poll_enable_locked(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_kms_helper_poll_enable_locked);
 
-
-static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connector *connector,
-                                                             uint32_t maxX, uint32_t maxY, bool merge_type_bits)
+/**
+ * drm_helper_probe_single_connector_modes - get complete set of display modes
+ * @connector: connector to probe
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * Based on the helper callbacks implemented by @connector in struct
+ * &drm_connector_helper_funcs try to detect all valid modes.  Modes will first
+ * be added to the connector's probed_modes list, then culled (based on validity
+ * and the @maxX, @maxY parameters) and put into the normal modes list.
+ *
+ * Intended to be used as a generic implementation of the ->fill_modes()
+ * @connector vfunc for drivers that use the CRTC helpers for output mode
+ * filtering and detection.
+ *
+ * The basic procedure is as follows
+ *
+ * 1. All modes currently on the connector's modes list are marked as stale
+ *
+ * 2. New modes are added to the connector's probed_modes list with
+ *    drm_mode_probed_add(). New modes start their life with status as OK.
+ *    Modes are added from a single source using the following priority order.
+ *
+ *    - debugfs 'override_edid' (used for testing only)
+ *    - firmware EDID (drm_load_edid_firmware())
+ *    - connector helper ->get_modes() vfunc
+ *    - if the connector status is connector_status_connected, standard
+ *      VESA DMT modes up to 1024x768 are automatically added
+ *      (drm_add_modes_noedid())
+ *
+ *    Finally modes specified via the kernel command line (video=...) are
+ *    added in addition to what the earlier probes produced
+ *    (drm_helper_probe_add_cmdline_mode()). These modes are generated
+ *    using the VESA GTF/CVT formulas.
+ *
+ * 3. Modes are moved from the probed_modes list to the modes list. Potential
+ *    duplicates are merged together (see drm_mode_connector_list_update()).
+ *    After this step the probed_modes list will be empty again.
+ *
+ * 4. Any non-stale mode on the modes list then undergoes validation
+ *
+ *    - drm_mode_validate_basic() performs basic sanity checks
+ *    - drm_mode_validate_size() filters out modes larger than @maxX and @maxY
+ *      (if specified)
+ *    - drm_mode_validate_flag() checks the modes againt basic connector
+ *      capabilites (interlace_allowed,doublescan_allowed,stereo_allowed)
+ *    - the optional connector ->mode_valid() helper can perform driver and/or
+ *      hardware specific checks
+ *
+ * 5. Any mode whose status is not OK is pruned from the connector's modes list,
+ *    accompanied by a debug message indicating the reason for the mode's
+ *    rejection (see drm_mode_prune_invalid()).
+ *
+ * Returns:
+ * The number of modes found on @connector.
+ */
+int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
+                                           uint32_t maxX, uint32_t maxY)
 {
        struct drm_device *dev = connector->dev;
        struct drm_display_mode *mode;
@@ -143,9 +201,9 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
                        connector->name);
-       /* set all modes to the unverified state */
+       /* set all old modes to the stale state */
        list_for_each_entry(mode, &connector->modes, head)
-               mode->status = MODE_UNVERIFIED;
+               mode->status = MODE_STALE;
 
        old_status = connector->status;
 
@@ -200,17 +258,16 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
                goto prune;
        }
 
+       if (connector->override_edid) {
+               struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
+
+               count = drm_add_edid_modes(connector, edid);
+               drm_edid_to_eld(connector, edid);
+       } else {
 #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
-       count = drm_load_edid_firmware(connector);
-       if (count == 0)
+               count = drm_load_edid_firmware(connector);
+               if (count == 0)
 #endif
-       {
-               if (connector->override_edid) {
-                       struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
-
-                       count = drm_add_edid_modes(connector, edid);
-                       drm_edid_to_eld(connector, edid);
-               } else
                        count = (*connector_funcs->get_modes)(connector);
        }
 
@@ -220,7 +277,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
        if (count == 0)
                goto prune;
 
-       drm_mode_connector_list_update(connector, merge_type_bits);
+       drm_mode_connector_list_update(connector);
 
        if (connector->interlace_allowed)
                mode_flags |= DRM_MODE_FLAG_INTERLACE;
@@ -230,7 +287,8 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
                mode_flags |= DRM_MODE_FLAG_3D_MASK;
 
        list_for_each_entry(mode, &connector->modes, head) {
-               mode->status = drm_mode_validate_basic(mode);
+               if (mode->status == MODE_OK)
+                       mode->status = drm_mode_validate_basic(mode);
 
                if (mode->status == MODE_OK)
                        mode->status = drm_mode_validate_size(mode, maxX, maxY);
@@ -263,48 +321,8 @@ prune:
 
        return count;
 }
-
-/**
- * drm_helper_probe_single_connector_modes - get complete set of display modes
- * @connector: connector to probe
- * @maxX: max width for modes
- * @maxY: max height for modes
- *
- * Based on the helper callbacks implemented by @connector try to detect all
- * valid modes.  Modes will first be added to the connector's probed_modes list,
- * then culled (based on validity and the @maxX, @maxY parameters) and put into
- * the normal modes list.
- *
- * Intended to be use as a generic implementation of the ->fill_modes()
- * @connector vfunc for drivers that use the crtc helpers for output mode
- * filtering and detection.
- *
- * Returns:
- * The number of modes found on @connector.
- */
-int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
-                                           uint32_t maxX, uint32_t maxY)
-{
-       return drm_helper_probe_single_connector_modes_merge_bits(connector, maxX, maxY, true);
-}
 EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
 
-/**
- * drm_helper_probe_single_connector_modes_nomerge - get complete set of display modes
- * @connector: connector to probe
- * @maxX: max width for modes
- * @maxY: max height for modes
- *
- * This operates like drm_hehlper_probe_single_connector_modes except it
- * replaces the mode bits instead of merging them for preferred modes.
- */
-int drm_helper_probe_single_connector_modes_nomerge(struct drm_connector *connector,
-                                           uint32_t maxX, uint32_t maxY)
-{
-       return drm_helper_probe_single_connector_modes_merge_bits(connector, maxX, maxY, false);
-}
-EXPORT_SYMBOL(drm_helper_probe_single_connector_modes_nomerge);
-
 /**
  * drm_kms_helper_hotplug_event - fire off KMS hotplug events
  * @dev: drm_device whose connector state changed
index 96e86cf..83efca9 100644 (file)
@@ -118,7 +118,7 @@ config DRM_EXYNOS_ROTATOR
 
 config DRM_EXYNOS_GSC
        bool "GScaler"
-       depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !ARCH_MULTIPLATFORM
+       depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !VIDEO_SAMSUNG_EXYNOS_GSC
        help
          Choose this option if you want to use Exynos GSC for DRM.
 
index fbe1b31..c7362b9 100644 (file)
 
 #include "exynos_drm_drv.h"
 #include "exynos_drm_crtc.h"
+#include "exynos_drm_fb.h"
 #include "exynos_drm_plane.h"
 #include "exynos_drm_iommu.h"
 
 #define WINDOWS_NR     3
-#define CURSOR_WIN     2
 #define MIN_FB_WIDTH_FOR_16WORD_BURST  128
 
 static const char * const decon_clks_name[] = {
@@ -56,6 +56,7 @@ struct decon_context {
        struct drm_device               *drm_dev;
        struct exynos_drm_crtc          *crtc;
        struct exynos_drm_plane         planes[WINDOWS_NR];
+       struct exynos_drm_plane_config  configs[WINDOWS_NR];
        void __iomem                    *addr;
        struct clk                      *clks[ARRAY_SIZE(decon_clks_name)];
        int                             pipe;
@@ -71,6 +72,12 @@ static const uint32_t decon_formats[] = {
        DRM_FORMAT_ARGB8888,
 };
 
+static const enum drm_plane_type decon_win_types[WINDOWS_NR] = {
+       DRM_PLANE_TYPE_PRIMARY,
+       DRM_PLANE_TYPE_OVERLAY,
+       DRM_PLANE_TYPE_CURSOR,
+};
+
 static inline void decon_set_bits(struct decon_context *ctx, u32 reg, u32 mask,
                                  u32 val)
 {
@@ -259,21 +266,24 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
 static void decon_update_plane(struct exynos_drm_crtc *crtc,
                               struct exynos_drm_plane *plane)
 {
+       struct exynos_drm_plane_state *state =
+                               to_exynos_plane_state(plane->base.state);
        struct decon_context *ctx = crtc->ctx;
-       struct drm_plane_state *state = plane->base.state;
+       struct drm_framebuffer *fb = state->base.fb;
        unsigned int win = plane->zpos;
-       unsigned int bpp = state->fb->bits_per_pixel >> 3;
-       unsigned int pitch = state->fb->pitches[0];
+       unsigned int bpp = fb->bits_per_pixel >> 3;
+       unsigned int pitch = fb->pitches[0];
+       dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0);
        u32 val;
 
        if (test_bit(BIT_SUSPENDED, &ctx->flags))
                return;
 
-       val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y);
+       val = COORDINATE_X(state->crtc.x) | COORDINATE_Y(state->crtc.y);
        writel(val, ctx->addr + DECON_VIDOSDxA(win));
 
-       val = COORDINATE_X(plane->crtc_x + plane->crtc_w - 1) |
-               COORDINATE_Y(plane->crtc_y + plane->crtc_h - 1);
+       val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) |
+               COORDINATE_Y(state->crtc.y + state->crtc.h - 1);
        writel(val, ctx->addr + DECON_VIDOSDxB(win));
 
        val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) |
@@ -284,20 +294,20 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
                VIDOSD_Wx_ALPHA_B_F(0x0);
        writel(val, ctx->addr + DECON_VIDOSDxD(win));
 
-       writel(plane->dma_addr[0], ctx->addr + DECON_VIDW0xADD0B0(win));
+       writel(dma_addr, ctx->addr + DECON_VIDW0xADD0B0(win));
 
-       val = plane->dma_addr[0] + pitch * plane->crtc_h;
+       val = dma_addr + pitch * state->src.h;
        writel(val, ctx->addr + DECON_VIDW0xADD1B0(win));
 
        if (ctx->out_type != IFTYPE_HDMI)
-               val = BIT_VAL(pitch - plane->crtc_w * bpp, 27, 14)
-                       | BIT_VAL(plane->crtc_w * bpp, 13, 0);
+               val = BIT_VAL(pitch - state->crtc.w * bpp, 27, 14)
+                       | BIT_VAL(state->crtc.w * bpp, 13, 0);
        else
-               val = BIT_VAL(pitch - plane->crtc_w * bpp, 29, 15)
-                       | BIT_VAL(plane->crtc_w * bpp, 14, 0);
+               val = BIT_VAL(pitch - state->crtc.w * bpp, 29, 15)
+                       | BIT_VAL(state->crtc.w * bpp, 14, 0);
        writel(val, ctx->addr + DECON_VIDW0xADD2(win));
 
-       decon_win_set_pixfmt(ctx, win, state->fb);
+       decon_win_set_pixfmt(ctx, win, fb);
 
        /* window enable */
        decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0);
@@ -377,20 +387,12 @@ static void decon_swreset(struct decon_context *ctx)
 static void decon_enable(struct exynos_drm_crtc *crtc)
 {
        struct decon_context *ctx = crtc->ctx;
-       int ret;
-       int i;
 
        if (!test_and_clear_bit(BIT_SUSPENDED, &ctx->flags))
                return;
 
        pm_runtime_get_sync(ctx->dev);
 
-       for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
-               ret = clk_prepare_enable(ctx->clks[i]);
-               if (ret < 0)
-                       goto err;
-       }
-
        set_bit(BIT_CLKS_ENABLED, &ctx->flags);
 
        /* if vblank was enabled status, enable it again. */
@@ -399,11 +401,6 @@ static void decon_enable(struct exynos_drm_crtc *crtc)
 
        decon_commit(ctx->crtc);
 
-       return;
-err:
-       while (--i >= 0)
-               clk_disable_unprepare(ctx->clks[i]);
-
        set_bit(BIT_SUSPENDED, &ctx->flags);
 }
 
@@ -425,9 +422,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
 
        decon_swreset(ctx);
 
-       for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++)
-               clk_disable_unprepare(ctx->clks[i]);
-
        clear_bit(BIT_CLKS_ENABLED, &ctx->flags);
 
        pm_runtime_put_sync(ctx->dev);
@@ -478,7 +472,6 @@ err:
 static struct exynos_drm_crtc_ops decon_crtc_ops = {
        .enable                 = decon_enable,
        .disable                = decon_disable,
-       .commit                 = decon_commit,
        .enable_vblank          = decon_enable_vblank,
        .disable_vblank         = decon_disable_vblank,
        .atomic_begin           = decon_atomic_begin,
@@ -495,7 +488,6 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
        struct exynos_drm_private *priv = drm_dev->dev_private;
        struct exynos_drm_plane *exynos_plane;
        enum exynos_drm_output_type out_type;
-       enum drm_plane_type type;
        unsigned int win;
        int ret;
 
@@ -505,10 +497,13 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
        for (win = ctx->first_win; win < WINDOWS_NR; win++) {
                int tmp = (win == ctx->first_win) ? 0 : win;
 
-               type = exynos_plane_get_type(tmp, CURSOR_WIN);
+               ctx->configs[win].pixel_formats = decon_formats;
+               ctx->configs[win].num_pixel_formats = ARRAY_SIZE(decon_formats);
+               ctx->configs[win].zpos = win;
+               ctx->configs[win].type = decon_win_types[tmp];
+
                ret = exynos_plane_init(drm_dev, &ctx->planes[win],
-                               1 << ctx->pipe, type, decon_formats,
-                               ARRAY_SIZE(decon_formats), win);
+                                       1 << ctx->pipe, &ctx->configs[win]);
                if (ret)
                        return ret;
        }
@@ -581,6 +576,44 @@ out:
        return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_PM
+static int exynos5433_decon_suspend(struct device *dev)
+{
+       struct decon_context *ctx = dev_get_drvdata(dev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++)
+               clk_disable_unprepare(ctx->clks[i]);
+
+       return 0;
+}
+
+static int exynos5433_decon_resume(struct device *dev)
+{
+       struct decon_context *ctx = dev_get_drvdata(dev);
+       int i, ret;
+
+       for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
+               ret = clk_prepare_enable(ctx->clks[i]);
+               if (ret < 0)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       while (--i >= 0)
+               clk_disable_unprepare(ctx->clks[i]);
+
+       return ret;
+}
+#endif
+
+static const struct dev_pm_ops exynos5433_decon_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos5433_decon_suspend, exynos5433_decon_resume,
+                          NULL)
+};
+
 static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
        {
                .compatible = "samsung,exynos5433-decon",
@@ -684,6 +717,7 @@ struct platform_driver exynos5433_decon_driver = {
        .remove         = exynos5433_decon_remove,
        .driver         = {
                .name   = "exynos5433-decon",
+               .pm     = &exynos5433_decon_pm_ops,
                .of_match_table = exynos5433_decon_driver_dt_match,
        },
 };
index ead2b16..c47f9af 100644 (file)
@@ -30,6 +30,7 @@
 #include "exynos_drm_crtc.h"
 #include "exynos_drm_plane.h"
 #include "exynos_drm_drv.h"
+#include "exynos_drm_fb.h"
 #include "exynos_drm_fbdev.h"
 #include "exynos_drm_iommu.h"
 
 #define MIN_FB_WIDTH_FOR_16WORD_BURST 128
 
 #define WINDOWS_NR     2
-#define CURSOR_WIN     1
 
 struct decon_context {
        struct device                   *dev;
        struct drm_device               *drm_dev;
        struct exynos_drm_crtc          *crtc;
        struct exynos_drm_plane         planes[WINDOWS_NR];
+       struct exynos_drm_plane_config  configs[WINDOWS_NR];
        struct clk                      *pclk;
        struct clk                      *aclk;
        struct clk                      *eclk;
@@ -81,6 +82,11 @@ static const uint32_t decon_formats[] = {
        DRM_FORMAT_BGRA8888,
 };
 
+static const enum drm_plane_type decon_win_types[WINDOWS_NR] = {
+       DRM_PLANE_TYPE_PRIMARY,
+       DRM_PLANE_TYPE_CURSOR,
+};
+
 static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
 {
        struct decon_context *ctx = crtc->ctx;
@@ -119,13 +125,8 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc)
        }
 
        /* Wait for vsync, as disable channel takes effect at next vsync */
-       if (ch_enabled) {
-               unsigned int state = ctx->suspended;
-
-               ctx->suspended = 0;
+       if (ch_enabled)
                decon_wait_for_vblank(ctx->crtc);
-               ctx->suspended = state;
-       }
 }
 
 static int decon_ctx_initialize(struct decon_context *ctx,
@@ -398,16 +399,17 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
 static void decon_update_plane(struct exynos_drm_crtc *crtc,
                               struct exynos_drm_plane *plane)
 {
+       struct exynos_drm_plane_state *state =
+                               to_exynos_plane_state(plane->base.state);
        struct decon_context *ctx = crtc->ctx;
-       struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
-       struct drm_plane_state *state = plane->base.state;
+       struct drm_framebuffer *fb = state->base.fb;
        int padding;
        unsigned long val, alpha;
        unsigned int last_x;
        unsigned int last_y;
        unsigned int win = plane->zpos;
-       unsigned int bpp = state->fb->bits_per_pixel >> 3;
-       unsigned int pitch = state->fb->pitches[0];
+       unsigned int bpp = fb->bits_per_pixel >> 3;
+       unsigned int pitch = fb->pitches[0];
 
        if (ctx->suspended)
                return;
@@ -423,41 +425,32 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
         */
 
        /* buffer start address */
-       val = (unsigned long)plane->dma_addr[0];
+       val = (unsigned long)exynos_drm_fb_dma_addr(fb, 0);
        writel(val, ctx->regs + VIDW_BUF_START(win));
 
-       padding = (pitch / bpp) - state->fb->width;
+       padding = (pitch / bpp) - fb->width;
 
        /* buffer size */
-       writel(state->fb->width + padding, ctx->regs + VIDW_WHOLE_X(win));
-       writel(state->fb->height, ctx->regs + VIDW_WHOLE_Y(win));
+       writel(fb->width + padding, ctx->regs + VIDW_WHOLE_X(win));
+       writel(fb->height, ctx->regs + VIDW_WHOLE_Y(win));
 
        /* offset from the start of the buffer to read */
-       writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win));
-       writel(plane->src_y, ctx->regs + VIDW_OFFSET_Y(win));
+       writel(state->src.x, ctx->regs + VIDW_OFFSET_X(win));
+       writel(state->src.y, ctx->regs + VIDW_OFFSET_Y(win));
 
        DRM_DEBUG_KMS("start addr = 0x%lx\n",
                        (unsigned long)val);
        DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
-                       plane->crtc_w, plane->crtc_h);
+                       state->crtc.w, state->crtc.h);
 
-       /*
-        * OSD position.
-        * In case the window layout goes of LCD layout, DECON fails.
-        */
-       if ((plane->crtc_x + plane->crtc_w) > mode->hdisplay)
-               plane->crtc_x = mode->hdisplay - plane->crtc_w;
-       if ((plane->crtc_y + plane->crtc_h) > mode->vdisplay)
-               plane->crtc_y = mode->vdisplay - plane->crtc_h;
-
-       val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |
-               VIDOSDxA_TOPLEFT_Y(plane->crtc_y);
+       val = VIDOSDxA_TOPLEFT_X(state->crtc.x) |
+               VIDOSDxA_TOPLEFT_Y(state->crtc.y);
        writel(val, ctx->regs + VIDOSD_A(win));
 
-       last_x = plane->crtc_x + plane->crtc_w;
+       last_x = state->crtc.x + state->crtc.w;
        if (last_x)
                last_x--;
-       last_y = plane->crtc_y + plane->crtc_h;
+       last_y = state->crtc.y + state->crtc.h;
        if (last_y)
                last_y--;
 
@@ -466,7 +459,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
        writel(val, ctx->regs + VIDOSD_B(win));
 
        DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
-                       plane->crtc_x, plane->crtc_y, last_x, last_y);
+                       state->crtc.x, state->crtc.y, last_x, last_y);
 
        /* OSD alpha */
        alpha = VIDOSDxC_ALPHA0_R_F(0x0) |
@@ -481,7 +474,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
 
        writel(alpha, ctx->regs + VIDOSD_D(win));
 
-       decon_win_set_pixfmt(ctx, win, state->fb);
+       decon_win_set_pixfmt(ctx, win, fb);
 
        /* hardware window 0 doesn't support color key. */
        if (win != 0)
@@ -555,39 +548,12 @@ static void decon_init(struct decon_context *ctx)
 static void decon_enable(struct exynos_drm_crtc *crtc)
 {
        struct decon_context *ctx = crtc->ctx;
-       int ret;
 
        if (!ctx->suspended)
                return;
 
-       ctx->suspended = false;
-
        pm_runtime_get_sync(ctx->dev);
 
-       ret = clk_prepare_enable(ctx->pclk);
-       if (ret < 0) {
-               DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret);
-               return;
-       }
-
-       ret = clk_prepare_enable(ctx->aclk);
-       if (ret < 0) {
-               DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret);
-               return;
-       }
-
-       ret = clk_prepare_enable(ctx->eclk);
-       if  (ret < 0) {
-               DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret);
-               return;
-       }
-
-       ret = clk_prepare_enable(ctx->vclk);
-       if  (ret < 0) {
-               DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret);
-               return;
-       }
-
        decon_init(ctx);
 
        /* if vblank was enabled status, enable it again. */
@@ -595,6 +561,8 @@ static void decon_enable(struct exynos_drm_crtc *crtc)
                decon_enable_vblank(ctx->crtc);
 
        decon_commit(ctx->crtc);
+
+       ctx->suspended = false;
 }
 
 static void decon_disable(struct exynos_drm_crtc *crtc)
@@ -613,11 +581,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
        for (i = 0; i < WINDOWS_NR; i++)
                decon_disable_plane(crtc, &ctx->planes[i]);
 
-       clk_disable_unprepare(ctx->vclk);
-       clk_disable_unprepare(ctx->eclk);
-       clk_disable_unprepare(ctx->aclk);
-       clk_disable_unprepare(ctx->pclk);
-
        pm_runtime_put_sync(ctx->dev);
 
        ctx->suspended = true;
@@ -679,8 +642,7 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
        struct decon_context *ctx = dev_get_drvdata(dev);
        struct drm_device *drm_dev = data;
        struct exynos_drm_plane *exynos_plane;
-       enum drm_plane_type type;
-       unsigned int zpos;
+       unsigned int i;
        int ret;
 
        ret = decon_ctx_initialize(ctx, drm_dev);
@@ -689,11 +651,14 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
                return ret;
        }
 
-       for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
-               type = exynos_plane_get_type(zpos, CURSOR_WIN);
-               ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
-                                       1 << ctx->pipe, type, decon_formats,
-                                       ARRAY_SIZE(decon_formats), zpos);
+       for (i = 0; i < WINDOWS_NR; i++) {
+               ctx->configs[i].pixel_formats = decon_formats;
+               ctx->configs[i].num_pixel_formats = ARRAY_SIZE(decon_formats);
+               ctx->configs[i].zpos = i;
+               ctx->configs[i].type = decon_win_types[i];
+
+               ret = exynos_plane_init(drm_dev, &ctx->planes[i],
+                                       1 << ctx->pipe, &ctx->configs[i]);
                if (ret)
                        return ret;
        }
@@ -843,11 +808,63 @@ static int decon_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int exynos7_decon_suspend(struct device *dev)
+{
+       struct decon_context *ctx = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(ctx->vclk);
+       clk_disable_unprepare(ctx->eclk);
+       clk_disable_unprepare(ctx->aclk);
+       clk_disable_unprepare(ctx->pclk);
+
+       return 0;
+}
+
+static int exynos7_decon_resume(struct device *dev)
+{
+       struct decon_context *ctx = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(ctx->pclk);
+       if (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(ctx->aclk);
+       if (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(ctx->eclk);
+       if  (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(ctx->vclk);
+       if  (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos7_decon_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos7_decon_suspend, exynos7_decon_resume,
+                          NULL)
+};
+
 struct platform_driver decon_driver = {
        .probe          = decon_probe,
        .remove         = decon_remove,
        .driver         = {
                .name   = "exynos-decon",
+               .pm     = &exynos7_decon_pm_ops,
                .of_match_table = decon_driver_dt_match,
        },
 };
index 124fb9a..793e497 100644 (file)
@@ -1009,9 +1009,9 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
 {
        int ret;
 
-       encoder->bridge = dp->bridge;
-       dp->bridge->encoder = encoder;
-       ret = drm_bridge_attach(encoder->dev, dp->bridge);
+       encoder->bridge->next = dp->ptn_bridge;
+       dp->ptn_bridge->encoder = encoder;
+       ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge);
        if (ret) {
                DRM_ERROR("Failed to attach bridge to drm\n");
                return ret;
@@ -1020,14 +1020,15 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
        return 0;
 }
 
-static int exynos_dp_create_connector(struct drm_encoder *encoder)
+static int exynos_dp_bridge_attach(struct drm_bridge *bridge)
 {
-       struct exynos_dp_device *dp = encoder_to_dp(encoder);
+       struct exynos_dp_device *dp = bridge->driver_private;
+       struct drm_encoder *encoder = &dp->encoder;
        struct drm_connector *connector = &dp->connector;
        int ret;
 
        /* Pre-empt DP connector creation if there's a bridge */
-       if (dp->bridge) {
+       if (dp->ptn_bridge) {
                ret = exynos_drm_attach_lcd_bridge(dp, encoder);
                if (!ret)
                        return 0;
@@ -1052,27 +1053,16 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder)
        return ret;
 }
 
-static bool exynos_dp_mode_fixup(struct drm_encoder *encoder,
-                                const struct drm_display_mode *mode,
-                                struct drm_display_mode *adjusted_mode)
-{
-       return true;
-}
-
-static void exynos_dp_mode_set(struct drm_encoder *encoder,
-                              struct drm_display_mode *mode,
-                              struct drm_display_mode *adjusted_mode)
-{
-}
-
-static void exynos_dp_enable(struct drm_encoder *encoder)
+static void exynos_dp_bridge_enable(struct drm_bridge *bridge)
 {
-       struct exynos_dp_device *dp = encoder_to_dp(encoder);
+       struct exynos_dp_device *dp = bridge->driver_private;
        struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
 
        if (dp->dpms_mode == DRM_MODE_DPMS_ON)
                return;
 
+       pm_runtime_get_sync(dp->dev);
+
        if (dp->panel) {
                if (drm_panel_prepare(dp->panel)) {
                        DRM_ERROR("failed to setup the panel\n");
@@ -1083,7 +1073,6 @@ static void exynos_dp_enable(struct drm_encoder *encoder)
        if (crtc->ops->clock_enable)
                crtc->ops->clock_enable(dp_to_crtc(dp), true);
 
-       clk_prepare_enable(dp->clock);
        phy_power_on(dp->phy);
        exynos_dp_init_dp(dp);
        enable_irq(dp->irq);
@@ -1092,9 +1081,9 @@ static void exynos_dp_enable(struct drm_encoder *encoder)
        dp->dpms_mode = DRM_MODE_DPMS_ON;
 }
 
-static void exynos_dp_disable(struct drm_encoder *encoder)
+static void exynos_dp_bridge_disable(struct drm_bridge *bridge)
 {
-       struct exynos_dp_device *dp = encoder_to_dp(encoder);
+       struct exynos_dp_device *dp = bridge->driver_private;
        struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
 
        if (dp->dpms_mode != DRM_MODE_DPMS_ON)
@@ -1110,7 +1099,6 @@ static void exynos_dp_disable(struct drm_encoder *encoder)
        disable_irq(dp->irq);
        flush_work(&dp->hotplug_work);
        phy_power_off(dp->phy);
-       clk_disable_unprepare(dp->clock);
 
        if (crtc->ops->clock_enable)
                crtc->ops->clock_enable(dp_to_crtc(dp), false);
@@ -1120,9 +1108,74 @@ static void exynos_dp_disable(struct drm_encoder *encoder)
                        DRM_ERROR("failed to turnoff the panel\n");
        }
 
+       pm_runtime_put_sync(dp->dev);
+
        dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
+static void exynos_dp_bridge_nop(struct drm_bridge *bridge)
+{
+       /* do nothing */
+}
+
+static const struct drm_bridge_funcs exynos_dp_bridge_funcs = {
+       .enable = exynos_dp_bridge_enable,
+       .disable = exynos_dp_bridge_disable,
+       .pre_enable = exynos_dp_bridge_nop,
+       .post_disable = exynos_dp_bridge_nop,
+       .attach = exynos_dp_bridge_attach,
+};
+
+static int exynos_dp_create_connector(struct drm_encoder *encoder)
+{
+       struct exynos_dp_device *dp = encoder_to_dp(encoder);
+       struct drm_device *drm_dev = dp->drm_dev;
+       struct drm_bridge *bridge;
+       int ret;
+
+       bridge = devm_kzalloc(drm_dev->dev, sizeof(*bridge), GFP_KERNEL);
+       if (!bridge) {
+               DRM_ERROR("failed to allocate for drm bridge\n");
+               return -ENOMEM;
+       }
+
+       dp->bridge = bridge;
+
+       encoder->bridge = bridge;
+       bridge->driver_private = dp;
+       bridge->encoder = encoder;
+       bridge->funcs = &exynos_dp_bridge_funcs;
+
+       ret = drm_bridge_attach(drm_dev, bridge);
+       if (ret) {
+               DRM_ERROR("failed to attach drm bridge\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static bool exynos_dp_mode_fixup(struct drm_encoder *encoder,
+                                const struct drm_display_mode *mode,
+                                struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static void exynos_dp_mode_set(struct drm_encoder *encoder,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void exynos_dp_enable(struct drm_encoder *encoder)
+{
+}
+
+static void exynos_dp_disable(struct drm_encoder *encoder)
+{
+}
+
 static struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
        .mode_fixup = exynos_dp_mode_fixup,
        .mode_set = exynos_dp_mode_set,
@@ -1238,7 +1291,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
                }
        }
 
-       if (!dp->panel && !dp->bridge) {
+       if (!dp->panel && !dp->ptn_bridge) {
                ret = exynos_dp_dt_parse_panel(dp);
                if (ret)
                        return ret;
@@ -1289,10 +1342,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 
        INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
 
-       phy_power_on(dp->phy);
-
-       exynos_dp_init_dp(dp);
-
        ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
                        irq_flags, "exynos-dp", dp);
        if (ret) {
@@ -1313,7 +1362,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
        DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
        drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
 
@@ -1343,8 +1392,9 @@ static const struct component_ops exynos_dp_ops = {
 static int exynos_dp_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct device_node *panel_node, *bridge_node, *endpoint;
+       struct device_node *panel_node = NULL, *bridge_node, *endpoint = NULL;
        struct exynos_dp_device *dp;
+       int ret;
 
        dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
                                GFP_KERNEL);
@@ -1353,36 +1403,96 @@ static int exynos_dp_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, dp);
 
+       /* This is for the backward compatibility. */
        panel_node = of_parse_phandle(dev->of_node, "panel", 0);
        if (panel_node) {
                dp->panel = of_drm_find_panel(panel_node);
                of_node_put(panel_node);
                if (!dp->panel)
                        return -EPROBE_DEFER;
+       } else {
+               endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+               if (endpoint) {
+                       panel_node = of_graph_get_remote_port_parent(endpoint);
+                       if (panel_node) {
+                               dp->panel = of_drm_find_panel(panel_node);
+                               of_node_put(panel_node);
+                               if (!dp->panel)
+                                       return -EPROBE_DEFER;
+                       } else {
+                               DRM_ERROR("no port node for panel device.\n");
+                               return -EINVAL;
+                       }
+               }
        }
 
+       if (endpoint)
+               goto out;
+
        endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
        if (endpoint) {
                bridge_node = of_graph_get_remote_port_parent(endpoint);
                if (bridge_node) {
-                       dp->bridge = of_drm_find_bridge(bridge_node);
+                       dp->ptn_bridge = of_drm_find_bridge(bridge_node);
                        of_node_put(bridge_node);
-                       if (!dp->bridge)
+                       if (!dp->ptn_bridge)
                                return -EPROBE_DEFER;
                } else
                        return -EPROBE_DEFER;
        }
 
-       return component_add(&pdev->dev, &exynos_dp_ops);
+out:
+       pm_runtime_enable(dev);
+
+       ret = component_add(&pdev->dev, &exynos_dp_ops);
+       if (ret)
+               goto err_disable_pm_runtime;
+
+       return ret;
+
+err_disable_pm_runtime:
+       pm_runtime_disable(dev);
+
+       return ret;
 }
 
 static int exynos_dp_remove(struct platform_device *pdev)
 {
+       pm_runtime_disable(&pdev->dev);
        component_del(&pdev->dev, &exynos_dp_ops);
 
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int exynos_dp_suspend(struct device *dev)
+{
+       struct exynos_dp_device *dp = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(dp->clock);
+
+       return 0;
+}
+
+static int exynos_dp_resume(struct device *dev)
+{
+       struct exynos_dp_device *dp = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(dp->clock);
+       if (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos_dp_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL)
+};
+
 static const struct of_device_id exynos_dp_match[] = {
        { .compatible = "samsung,exynos5-dp" },
        {},
@@ -1395,6 +1505,7 @@ struct platform_driver dp_driver = {
        .driver         = {
                .name   = "exynos-dp",
                .owner  = THIS_MODULE,
+               .pm     = &exynos_dp_pm_ops,
                .of_match_table = exynos_dp_match,
        },
 };
index e413b6f..66eec4b 100644 (file)
@@ -153,6 +153,7 @@ struct exynos_dp_device {
        struct drm_connector    connector;
        struct drm_panel        *panel;
        struct drm_bridge       *bridge;
+       struct drm_bridge       *ptn_bridge;
        struct clk              *clock;
        unsigned int            irq;
        void __iomem            *reg_base;
index b3ba27f..9d30a0f 100644 (file)
@@ -150,7 +150,7 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
        private->crtc[pipe] = crtc;
 
        ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
-                                       &exynos_crtc_funcs);
+                                       &exynos_crtc_funcs, NULL);
        if (ret < 0)
                goto err_crtc;
 
index c748b87..1dbf8dc 100644 (file)
@@ -309,7 +309,7 @@ int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder)
        DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
        drm_encoder_init(dev, encoder, &exynos_dpi_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs);
 
index 2c6019d..9756797 100644 (file)
@@ -304,45 +304,6 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state)
-{
-       struct drm_connector *connector;
-
-       drm_modeset_lock_all(dev);
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               int old_dpms = connector->dpms;
-
-               if (connector->funcs->dpms)
-                       connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
-
-               /* Set the old mode back to the connector for resume */
-               connector->dpms = old_dpms;
-       }
-       drm_modeset_unlock_all(dev);
-
-       return 0;
-}
-
-static int exynos_drm_resume(struct drm_device *dev)
-{
-       struct drm_connector *connector;
-
-       drm_modeset_lock_all(dev);
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               if (connector->funcs->dpms) {
-                       int dpms = connector->dpms;
-
-                       connector->dpms = DRM_MODE_DPMS_OFF;
-                       connector->funcs->dpms(connector, dpms);
-               }
-       }
-       drm_modeset_unlock_all(dev);
-
-       return 0;
-}
-#endif
-
 static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_exynos_file_private *file_priv;
@@ -476,31 +437,54 @@ static struct drm_driver exynos_drm_driver = {
 };
 
 #ifdef CONFIG_PM_SLEEP
-static int exynos_drm_sys_suspend(struct device *dev)
+static int exynos_drm_suspend(struct device *dev)
 {
        struct drm_device *drm_dev = dev_get_drvdata(dev);
-       pm_message_t message;
+       struct drm_connector *connector;
 
        if (pm_runtime_suspended(dev) || !drm_dev)
                return 0;
 
-       message.event = PM_EVENT_SUSPEND;
-       return exynos_drm_suspend(drm_dev, message);
+       drm_modeset_lock_all(drm_dev);
+       drm_for_each_connector(connector, drm_dev) {
+               int old_dpms = connector->dpms;
+
+               if (connector->funcs->dpms)
+                       connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+               /* Set the old mode back to the connector for resume */
+               connector->dpms = old_dpms;
+       }
+       drm_modeset_unlock_all(drm_dev);
+
+       return 0;
 }
 
-static int exynos_drm_sys_resume(struct device *dev)
+static int exynos_drm_resume(struct device *dev)
 {
        struct drm_device *drm_dev = dev_get_drvdata(dev);
+       struct drm_connector *connector;
 
        if (pm_runtime_suspended(dev) || !drm_dev)
                return 0;
 
-       return exynos_drm_resume(drm_dev);
+       drm_modeset_lock_all(drm_dev);
+       drm_for_each_connector(connector, drm_dev) {
+               if (connector->funcs->dpms) {
+                       int dpms = connector->dpms;
+
+                       connector->dpms = DRM_MODE_DPMS_OFF;
+                       connector->funcs->dpms(connector, dpms);
+               }
+       }
+       drm_modeset_unlock_all(drm_dev);
+
+       return 0;
 }
 #endif
 
 static const struct dev_pm_ops exynos_drm_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
+       SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_suspend, exynos_drm_resume)
 };
 
 /* forward declaration */
index f1eda7f..82bbd7f 100644 (file)
@@ -38,24 +38,44 @@ enum exynos_drm_output_type {
        EXYNOS_DISPLAY_TYPE_VIDI,
 };
 
+struct exynos_drm_rect {
+       unsigned int x, y;
+       unsigned int w, h;
+};
+
 /*
- * Exynos drm common overlay structure.
+ * Exynos drm plane state structure.
  *
- * @base: plane object
- * @src_x: offset x on a framebuffer to be displayed.
- *     - the unit is screen coordinates.
- * @src_y: offset y on a framebuffer to be displayed.
- *     - the unit is screen coordinates.
- * @src_w: width of a partial image to be displayed from framebuffer.
- * @src_h: height of a partial image to be displayed from framebuffer.
- * @crtc_x: offset x on hardware screen.
- * @crtc_y: offset y on hardware screen.
- * @crtc_w: window width to be displayed (hardware screen).
- * @crtc_h: window height to be displayed (hardware screen).
+ * @base: plane_state object (contains drm_framebuffer pointer)
+ * @src: rectangle of the source image data to be displayed (clipped to
+ *       visible part).
+ * @crtc: rectangle of the target image position on hardware screen
+ *       (clipped to visible part).
  * @h_ratio: horizontal scaling ratio, 16.16 fixed point
  * @v_ratio: vertical scaling ratio, 16.16 fixed point
- * @dma_addr: array of bus(accessed by dma) address to the memory region
- *           allocated for a overlay.
+ *
+ * this structure consists plane state data that will be applied to hardware
+ * specific overlay info.
+ */
+
+struct exynos_drm_plane_state {
+       struct drm_plane_state base;
+       struct exynos_drm_rect crtc;
+       struct exynos_drm_rect src;
+       unsigned int h_ratio;
+       unsigned int v_ratio;
+};
+
+static inline struct exynos_drm_plane_state *
+to_exynos_plane_state(struct drm_plane_state *state)
+{
+       return container_of(state, struct exynos_drm_plane_state, base);
+}
+
+/*
+ * Exynos drm common overlay structure.
+ *
+ * @base: plane object
  * @zpos: order of overlay layer(z position).
  *
  * this structure is common to exynos SoC and its contents would be copied
@@ -64,21 +84,32 @@ enum exynos_drm_output_type {
 
 struct exynos_drm_plane {
        struct drm_plane base;
-       unsigned int src_x;
-       unsigned int src_y;
-       unsigned int src_w;
-       unsigned int src_h;
-       unsigned int crtc_x;
-       unsigned int crtc_y;
-       unsigned int crtc_w;
-       unsigned int crtc_h;
-       unsigned int h_ratio;
-       unsigned int v_ratio;
-       dma_addr_t dma_addr[MAX_FB_BUFFER];
+       const struct exynos_drm_plane_config *config;
        unsigned int zpos;
        struct drm_framebuffer *pending_fb;
 };
 
+#define EXYNOS_DRM_PLANE_CAP_DOUBLE    (1 << 0)
+#define EXYNOS_DRM_PLANE_CAP_SCALE     (1 << 1)
+
+/*
+ * Exynos DRM plane configuration structure.
+ *
+ * @zpos: z-position of the plane.
+ * @type: type of the plane (primary, cursor or overlay).
+ * @pixel_formats: supported pixel formats.
+ * @num_pixel_formats: number of elements in 'pixel_formats'.
+ * @capabilities: supported features (see EXYNOS_DRM_PLANE_CAP_*)
+ */
+
+struct exynos_drm_plane_config {
+       unsigned int zpos;
+       enum drm_plane_type type;
+       const uint32_t *pixel_formats;
+       unsigned int num_pixel_formats;
+       unsigned int capabilities;
+};
+
 /*
  * Exynos drm crtc ops
  *
index 12b03b3..bc09bba 100644 (file)
@@ -1458,66 +1458,6 @@ static const struct mipi_dsi_host_ops exynos_dsi_ops = {
        .transfer = exynos_dsi_host_transfer,
 };
 
-static int exynos_dsi_poweron(struct exynos_dsi *dsi)
-{
-       struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-       int ret, i;
-
-       ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
-       if (ret < 0) {
-               dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
-               return ret;
-       }
-
-       for (i = 0; i < driver_data->num_clks; i++) {
-               ret = clk_prepare_enable(dsi->clks[i]);
-               if (ret < 0)
-                       goto err_clk;
-       }
-
-       ret = phy_power_on(dsi->phy);
-       if (ret < 0) {
-               dev_err(dsi->dev, "cannot enable phy %d\n", ret);
-               goto err_clk;
-       }
-
-       return 0;
-
-err_clk:
-       while (--i > -1)
-               clk_disable_unprepare(dsi->clks[i]);
-       regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
-
-       return ret;
-}
-
-static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
-{
-       struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-       int ret, i;
-
-       usleep_range(10000, 20000);
-
-       if (dsi->state & DSIM_STATE_INITIALIZED) {
-               dsi->state &= ~DSIM_STATE_INITIALIZED;
-
-               exynos_dsi_disable_clock(dsi);
-
-               exynos_dsi_disable_irq(dsi);
-       }
-
-       dsi->state &= ~DSIM_STATE_CMD_LPM;
-
-       phy_power_off(dsi->phy);
-
-       for (i = driver_data->num_clks - 1; i > -1; i--)
-               clk_disable_unprepare(dsi->clks[i]);
-
-       ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
-       if (ret < 0)
-               dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
-}
-
 static void exynos_dsi_enable(struct drm_encoder *encoder)
 {
        struct exynos_dsi *dsi = encoder_to_dsi(encoder);
@@ -1526,16 +1466,14 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
        if (dsi->state & DSIM_STATE_ENABLED)
                return;
 
-       ret = exynos_dsi_poweron(dsi);
-       if (ret < 0)
-               return;
+       pm_runtime_get_sync(dsi->dev);
 
        dsi->state |= DSIM_STATE_ENABLED;
 
        ret = drm_panel_prepare(dsi->panel);
        if (ret < 0) {
                dsi->state &= ~DSIM_STATE_ENABLED;
-               exynos_dsi_poweroff(dsi);
+               pm_runtime_put_sync(dsi->dev);
                return;
        }
 
@@ -1547,7 +1485,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
                dsi->state &= ~DSIM_STATE_ENABLED;
                exynos_dsi_set_display_enable(dsi, false);
                drm_panel_unprepare(dsi->panel);
-               exynos_dsi_poweroff(dsi);
+               pm_runtime_put_sync(dsi->dev);
                return;
        }
 
@@ -1569,7 +1507,7 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
 
        dsi->state &= ~DSIM_STATE_ENABLED;
 
-       exynos_dsi_poweroff(dsi);
+       pm_runtime_put_sync(dsi->dev);
 }
 
 static enum drm_connector_status
@@ -1797,13 +1735,13 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
 
        ep = of_graph_get_next_endpoint(node, NULL);
        if (!ep) {
-               ret = -ENXIO;
+               ret = -EINVAL;
                goto end;
        }
 
        dsi->bridge_node = of_graph_get_remote_port_parent(ep);
        if (!dsi->bridge_node) {
-               ret = -ENXIO;
+               ret = -EINVAL;
                goto end;
        }
 end:
@@ -1831,7 +1769,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
        DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
        drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
 
@@ -1954,22 +1892,99 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, &dsi->encoder);
 
+       pm_runtime_enable(dev);
+
        return component_add(dev, &exynos_dsi_component_ops);
 }
 
 static int exynos_dsi_remove(struct platform_device *pdev)
 {
+       pm_runtime_disable(&pdev->dev);
+
        component_del(&pdev->dev, &exynos_dsi_component_ops);
 
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int exynos_dsi_suspend(struct device *dev)
+{
+       struct drm_encoder *encoder = dev_get_drvdata(dev);
+       struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+       struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+       int ret, i;
+
+       usleep_range(10000, 20000);
+
+       if (dsi->state & DSIM_STATE_INITIALIZED) {
+               dsi->state &= ~DSIM_STATE_INITIALIZED;
+
+               exynos_dsi_disable_clock(dsi);
+
+               exynos_dsi_disable_irq(dsi);
+       }
+
+       dsi->state &= ~DSIM_STATE_CMD_LPM;
+
+       phy_power_off(dsi->phy);
+
+       for (i = driver_data->num_clks - 1; i > -1; i--)
+               clk_disable_unprepare(dsi->clks[i]);
+
+       ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+       if (ret < 0)
+               dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
+
+       return 0;
+}
+
+static int exynos_dsi_resume(struct device *dev)
+{
+       struct drm_encoder *encoder = dev_get_drvdata(dev);
+       struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+       struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+       int ret, i;
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+       if (ret < 0) {
+               dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
+               return ret;
+       }
+
+       for (i = 0; i < driver_data->num_clks; i++) {
+               ret = clk_prepare_enable(dsi->clks[i]);
+               if (ret < 0)
+                       goto err_clk;
+       }
+
+       ret = phy_power_on(dsi->phy);
+       if (ret < 0) {
+               dev_err(dsi->dev, "cannot enable phy %d\n", ret);
+               goto err_clk;
+       }
+
+       return 0;
+
+err_clk:
+       while (--i > -1)
+               clk_disable_unprepare(dsi->clks[i]);
+       regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+
+       return ret;
+}
+#endif
+
+static const struct dev_pm_ops exynos_dsi_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL)
+};
+
 struct platform_driver dsi_driver = {
        .probe = exynos_dsi_probe,
        .remove = exynos_dsi_remove,
        .driver = {
                   .name = "exynos-dsi",
                   .owner = THIS_MODULE,
+                  .pm = &exynos_dsi_pm_ops,
                   .of_match_table = exynos_dsi_of_match,
        },
 };
index 49b9bc3..f6bdb0d 100644 (file)
@@ -37,6 +37,7 @@
 struct exynos_drm_fb {
        struct drm_framebuffer  fb;
        struct exynos_drm_gem   *exynos_gem[MAX_FB_BUFFER];
+       dma_addr_t                      dma_addr[MAX_FB_BUFFER];
 };
 
 static int check_fb_gem_memory_type(struct drm_device *drm_dev,
@@ -135,6 +136,8 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
                        goto err;
 
                exynos_fb->exynos_gem[i] = exynos_gem[i];
+               exynos_fb->dma_addr[i] = exynos_gem[i]->dma_addr
+                                               + mode_cmd->offsets[i];
        }
 
        drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd);
@@ -189,21 +192,14 @@ err:
        return ERR_PTR(ret);
 }
 
-struct exynos_drm_gem *exynos_drm_fb_gem(struct drm_framebuffer *fb, int index)
+dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index)
 {
        struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
-       struct exynos_drm_gem *exynos_gem;
 
        if (index >= MAX_FB_BUFFER)
-               return NULL;
+               return DMA_ERROR_CODE;
 
-       exynos_gem = exynos_fb->exynos_gem[index];
-       if (!exynos_gem)
-               return NULL;
-
-       DRM_DEBUG_KMS("dma_addr: 0x%lx\n", (unsigned long)exynos_gem->dma_addr);
-
-       return exynos_gem;
+       return exynos_fb->dma_addr[index];
 }
 
 static void exynos_drm_output_poll_changed(struct drm_device *dev)
index a8a75ac..4aae9dd 100644 (file)
@@ -22,8 +22,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
                            struct exynos_drm_gem **exynos_gem,
                            int count);
 
-/* get gem object of a drm framebuffer */
-struct exynos_drm_gem *exynos_drm_fb_gem(struct drm_framebuffer *fb, int index);
+dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index);
 
 void exynos_drm_mode_config_init(struct drm_device *dev);
 
index bd75c15..2e22471 100644 (file)
@@ -29,6 +29,7 @@
 #include <drm/exynos_drm.h>
 
 #include "exynos_drm_drv.h"
+#include "exynos_drm_fb.h"
 #include "exynos_drm_fbdev.h"
 #include "exynos_drm_crtc.h"
 #include "exynos_drm_plane.h"
@@ -87,7 +88,6 @@
 
 /* FIMD has totally five hardware windows. */
 #define WINDOWS_NR     5
-#define CURSOR_WIN     4
 
 struct fimd_driver_data {
        unsigned int timing_base;
@@ -150,6 +150,7 @@ struct fimd_context {
        struct drm_device               *drm_dev;
        struct exynos_drm_crtc          *crtc;
        struct exynos_drm_plane         planes[WINDOWS_NR];
+       struct exynos_drm_plane_config  configs[WINDOWS_NR];
        struct clk                      *bus_clk;
        struct clk                      *lcd_clk;
        void __iomem                    *regs;
@@ -187,6 +188,14 @@ static const struct of_device_id fimd_driver_dt_match[] = {
 };
 MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
 
+static const enum drm_plane_type fimd_win_types[WINDOWS_NR] = {
+       DRM_PLANE_TYPE_PRIMARY,
+       DRM_PLANE_TYPE_OVERLAY,
+       DRM_PLANE_TYPE_OVERLAY,
+       DRM_PLANE_TYPE_OVERLAY,
+       DRM_PLANE_TYPE_CURSOR,
+};
+
 static const uint32_t fimd_formats[] = {
        DRM_FORMAT_C8,
        DRM_FORMAT_XRGB1555,
@@ -478,7 +487,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
 
 
 static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
-                               struct drm_framebuffer *fb)
+                               uint32_t pixel_format, int width)
 {
        unsigned long val;
 
@@ -489,11 +498,11 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
         * So the request format is ARGB8888 then change it to XRGB8888.
         */
        if (ctx->driver_data->has_limited_fmt && !win) {
-               if (fb->pixel_format == DRM_FORMAT_ARGB8888)
-                       fb->pixel_format = DRM_FORMAT_XRGB8888;
+               if (pixel_format == DRM_FORMAT_ARGB8888)
+                       pixel_format = DRM_FORMAT_XRGB8888;
        }
 
-       switch (fb->pixel_format) {
+       switch (pixel_format) {
        case DRM_FORMAT_C8:
                val |= WINCON0_BPPMODE_8BPP_PALETTE;
                val |= WINCONx_BURSTLEN_8WORD;
@@ -529,17 +538,15 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
                break;
        }
 
-       DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel);
-
        /*
-        * In case of exynos, setting dma-burst to 16Word causes permanent
-        * tearing for very small buffers, e.g. cursor buffer. Burst Mode
-        * switching which is based on plane size is not recommended as
-        * plane size varies alot towards the end of the screen and rapid
-        * movement causes unstable DMA which results into iommu crash/tear.
+        * Setting dma-burst to 16Word causes permanent tearing for very small
+        * buffers, e.g. cursor buffer. Burst Mode switching which based on
+        * plane size is not recommended as plane size varies alot towards the
+        * end of the screen and rapid movement causes unstable DMA, but it is
+        * still better to change dma-burst than displaying garbage.
         */
 
-       if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
+       if (width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
                val &= ~WINCONx_BURSTLEN_MASK;
                val |= WINCONx_BURSTLEN_4WORD;
        }
@@ -640,39 +647,41 @@ static void fimd_atomic_flush(struct exynos_drm_crtc *crtc,
 static void fimd_update_plane(struct exynos_drm_crtc *crtc,
                              struct exynos_drm_plane *plane)
 {
+       struct exynos_drm_plane_state *state =
+                               to_exynos_plane_state(plane->base.state);
        struct fimd_context *ctx = crtc->ctx;
-       struct drm_plane_state *state = plane->base.state;
+       struct drm_framebuffer *fb = state->base.fb;
        dma_addr_t dma_addr;
        unsigned long val, size, offset;
        unsigned int last_x, last_y, buf_offsize, line_size;
        unsigned int win = plane->zpos;
-       unsigned int bpp = state->fb->bits_per_pixel >> 3;
-       unsigned int pitch = state->fb->pitches[0];
+       unsigned int bpp = fb->bits_per_pixel >> 3;
+       unsigned int pitch = fb->pitches[0];
 
        if (ctx->suspended)
                return;
 
-       offset = plane->src_x * bpp;
-       offset += plane->src_y * pitch;
+       offset = state->src.x * bpp;
+       offset += state->src.y * pitch;
 
        /* buffer start address */
-       dma_addr = plane->dma_addr[0] + offset;
+       dma_addr = exynos_drm_fb_dma_addr(fb, 0) + offset;
        val = (unsigned long)dma_addr;
        writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
 
        /* buffer end address */
-       size = pitch * plane->crtc_h;
+       size = pitch * state->crtc.h;
        val = (unsigned long)(dma_addr + size);
        writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
 
        DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
                        (unsigned long)dma_addr, val, size);
        DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
-                       plane->crtc_w, plane->crtc_h);
+                       state->crtc.w, state->crtc.h);
 
        /* buffer size */
-       buf_offsize = pitch - (plane->crtc_w * bpp);
-       line_size = plane->crtc_w * bpp;
+       buf_offsize = pitch - (state->crtc.w * bpp);
+       line_size = state->crtc.w * bpp;
        val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |
                VIDW_BUF_SIZE_PAGEWIDTH(line_size) |
                VIDW_BUF_SIZE_OFFSET_E(buf_offsize) |
@@ -680,16 +689,16 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
        writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
 
        /* OSD position */
-       val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |
-               VIDOSDxA_TOPLEFT_Y(plane->crtc_y) |
-               VIDOSDxA_TOPLEFT_X_E(plane->crtc_x) |
-               VIDOSDxA_TOPLEFT_Y_E(plane->crtc_y);
+       val = VIDOSDxA_TOPLEFT_X(state->crtc.x) |
+               VIDOSDxA_TOPLEFT_Y(state->crtc.y) |
+               VIDOSDxA_TOPLEFT_X_E(state->crtc.x) |
+               VIDOSDxA_TOPLEFT_Y_E(state->crtc.y);
        writel(val, ctx->regs + VIDOSD_A(win));
 
-       last_x = plane->crtc_x + plane->crtc_w;
+       last_x = state->crtc.x + state->crtc.w;
        if (last_x)
                last_x--;
-       last_y = plane->crtc_y + plane->crtc_h;
+       last_y = state->crtc.y + state->crtc.h;
        if (last_y)
                last_y--;
 
@@ -699,20 +708,20 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
        writel(val, ctx->regs + VIDOSD_B(win));
 
        DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
-                       plane->crtc_x, plane->crtc_y, last_x, last_y);
+                       state->crtc.x, state->crtc.y, last_x, last_y);
 
        /* OSD size */
        if (win != 3 && win != 4) {
                u32 offset = VIDOSD_D(win);
                if (win == 0)
                        offset = VIDOSD_C(win);
-               val = plane->crtc_w * plane->crtc_h;
+               val = state->crtc.w * state->crtc.h;
                writel(val, ctx->regs + offset);
 
                DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
        }
 
-       fimd_win_set_pixfmt(ctx, win, state->fb);
+       fimd_win_set_pixfmt(ctx, win, fb->pixel_format, state->src.w);
 
        /* hardware window 0 doesn't support color key. */
        if (win != 0)
@@ -745,7 +754,6 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc,
 static void fimd_enable(struct exynos_drm_crtc *crtc)
 {
        struct fimd_context *ctx = crtc->ctx;
-       int ret;
 
        if (!ctx->suspended)
                return;
@@ -754,18 +762,6 @@ static void fimd_enable(struct exynos_drm_crtc *crtc)
 
        pm_runtime_get_sync(ctx->dev);
 
-       ret = clk_prepare_enable(ctx->bus_clk);
-       if (ret < 0) {
-               DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
-               return;
-       }
-
-       ret = clk_prepare_enable(ctx->lcd_clk);
-       if  (ret < 0) {
-               DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
-               return;
-       }
-
        /* if vblank was enabled status, enable it again. */
        if (test_and_clear_bit(0, &ctx->irq_flags))
                fimd_enable_vblank(ctx->crtc);
@@ -795,11 +791,7 @@ static void fimd_disable(struct exynos_drm_crtc *crtc)
 
        writel(0, ctx->regs + VIDCON0);
 
-       clk_disable_unprepare(ctx->lcd_clk);
-       clk_disable_unprepare(ctx->bus_clk);
-
        pm_runtime_put_sync(ctx->dev);
-
        ctx->suspended = true;
 }
 
@@ -941,18 +933,19 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
        struct drm_device *drm_dev = data;
        struct exynos_drm_private *priv = drm_dev->dev_private;
        struct exynos_drm_plane *exynos_plane;
-       enum drm_plane_type type;
-       unsigned int zpos;
+       unsigned int i;
        int ret;
 
        ctx->drm_dev = drm_dev;
        ctx->pipe = priv->pipe++;
 
-       for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
-               type = exynos_plane_get_type(zpos, CURSOR_WIN);
-               ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
-                                       1 << ctx->pipe, type, fimd_formats,
-                                       ARRAY_SIZE(fimd_formats), zpos);
+       for (i = 0; i < WINDOWS_NR; i++) {
+               ctx->configs[i].pixel_formats = fimd_formats;
+               ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_formats);
+               ctx->configs[i].zpos = i;
+               ctx->configs[i].type = fimd_win_types[i];
+               ret = exynos_plane_init(drm_dev, &ctx->planes[i],
+                                       1 << ctx->pipe, &ctx->configs[i]);
                if (ret)
                        return ret;
        }
@@ -1121,12 +1114,49 @@ static int fimd_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int exynos_fimd_suspend(struct device *dev)
+{
+       struct fimd_context *ctx = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(ctx->lcd_clk);
+       clk_disable_unprepare(ctx->bus_clk);
+
+       return 0;
+}
+
+static int exynos_fimd_resume(struct device *dev)
+{
+       struct fimd_context *ctx = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(ctx->bus_clk);
+       if (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(ctx->lcd_clk);
+       if  (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos_fimd_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos_fimd_suspend, exynos_fimd_resume, NULL)
+};
+
 struct platform_driver fimd_driver = {
        .probe          = fimd_probe,
        .remove         = fimd_remove,
        .driver         = {
                .name   = "exynos4-fb",
                .owner  = THIS_MODULE,
+               .pm     = &exynos_fimd_pm_ops,
                .of_match_table = fimd_driver_dt_match,
        },
 };
index 37ab8b2..9ca5047 100644 (file)
@@ -55,8 +55,6 @@ struct exynos_drm_gem {
        struct sg_table         *sgt;
 };
 
-struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
-
 /* destroy a buffer with gem object */
 void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem);
 
@@ -91,10 +89,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
                                        unsigned int gem_handle,
                                        struct drm_file *filp);
 
-/* map user space allocated by malloc to pages. */
-int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,
-                                     struct drm_file *file_priv);
-
 /* get buffer information to memory region allocated by gem. */
 int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
                                      struct drm_file *file_priv);
@@ -123,28 +117,6 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
 /* set vm_flags and we can change the vm attribute to other one at here. */
 int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 
-static inline int vma_is_io(struct vm_area_struct *vma)
-{
-       return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
-}
-
-/* get a copy of a virtual memory region. */
-struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma);
-
-/* release a userspace virtual memory area. */
-void exynos_gem_put_vma(struct vm_area_struct *vma);
-
-/* get pages from user space. */
-int exynos_gem_get_pages_from_userptr(unsigned long start,
-                                               unsigned int npages,
-                                               struct page **pages,
-                                               struct vm_area_struct *vma);
-
-/* drop the reference to pages. */
-void exynos_gem_put_pages_to_userptr(struct page **pages,
-                                       unsigned int npages,
-                                       struct vm_area_struct *vma);
-
 /* map sgt with dma region. */
 int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
                                struct sg_table *sgt,
index 11b87d2..7aecd23 100644 (file)
@@ -15,7 +15,8 @@
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/pm_runtime.h>
-#include <plat/map-base.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 
 #include <drm/drmP.h>
 #include <drm/exynos_drm.h>
@@ -126,6 +127,7 @@ struct gsc_capability {
  * @ippdrv: prepare initialization using ippdrv.
  * @regs_res: register resources.
  * @regs: memory mapped io registers.
+ * @sysreg: handle to SYSREG block regmap.
  * @lock: locking of operations.
  * @gsc_clk: gsc gate clock.
  * @sc: scaler infomations.
@@ -138,6 +140,7 @@ struct gsc_context {
        struct exynos_drm_ippdrv        ippdrv;
        struct resource *regs_res;
        void __iomem    *regs;
+       struct regmap   *sysreg;
        struct mutex    lock;
        struct clk      *gsc_clk;
        struct gsc_scaler       sc;
@@ -437,9 +440,12 @@ static int gsc_sw_reset(struct gsc_context *ctx)
 
 static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable)
 {
-       u32 gscblk_cfg;
+       unsigned int gscblk_cfg;
 
-       gscblk_cfg = readl(SYSREG_GSCBLK_CFG1);
+       if (!ctx->sysreg)
+               return;
+
+       regmap_read(ctx->sysreg, SYSREG_GSCBLK_CFG1, &gscblk_cfg);
 
        if (enable)
                gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) |
@@ -448,7 +454,7 @@ static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable)
        else
                gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id);
 
-       writel(gscblk_cfg, SYSREG_GSCBLK_CFG1);
+       regmap_write(ctx->sysreg, SYSREG_GSCBLK_CFG1, gscblk_cfg);
 }
 
 static void gsc_handle_irq(struct gsc_context *ctx, bool enable,
@@ -1215,10 +1221,10 @@ static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable)
        DRM_DEBUG_KMS("enable[%d]\n", enable);
 
        if (enable) {
-               clk_enable(ctx->gsc_clk);
+               clk_prepare_enable(ctx->gsc_clk);
                ctx->suspended = false;
        } else {
-               clk_disable(ctx->gsc_clk);
+               clk_disable_unprepare(ctx->gsc_clk);
                ctx->suspended = true;
        }
 
@@ -1663,6 +1669,15 @@ static int gsc_probe(struct platform_device *pdev)
        if (!ctx)
                return -ENOMEM;
 
+       if (dev->of_node) {
+               ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                                       "samsung,sysreg");
+               if (IS_ERR(ctx->sysreg)) {
+                       dev_warn(dev, "failed to get system register.\n");
+                       ctx->sysreg = NULL;
+               }
+       }
+
        /* clock control */
        ctx->gsc_clk = devm_clk_get(dev, "gscl");
        if (IS_ERR(ctx->gsc_clk)) {
@@ -1713,7 +1728,6 @@ static int gsc_probe(struct platform_device *pdev)
        mutex_init(&ctx->lock);
        platform_set_drvdata(pdev, ctx);
 
-       pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
 
        ret = exynos_drm_ippdrv_register(ippdrv);
@@ -1797,6 +1811,12 @@ static const struct dev_pm_ops gsc_pm_ops = {
        SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL)
 };
 
+static const struct of_device_id exynos_drm_gsc_of_match[] = {
+       { .compatible = "samsung,exynos5-gsc" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_drm_gsc_of_match);
+
 struct platform_driver gsc_driver = {
        .probe          = gsc_probe,
        .remove         = gsc_remove,
@@ -1804,6 +1824,7 @@ struct platform_driver gsc_driver = {
                .name   = "exynos-drm-gsc",
                .owner  = THIS_MODULE,
                .pm     = &gsc_pm_ops,
+               .of_match_table = of_match_ptr(exynos_drm_gsc_of_match),
        },
 };
 
index 1793117..e668fcd 100644 (file)
@@ -56,93 +56,170 @@ static int exynos_plane_get_size(int start, unsigned length, unsigned last)
        return size;
 }
 
-static void exynos_plane_mode_set(struct drm_plane *plane,
-                                 struct drm_crtc *crtc,
-                                 struct drm_framebuffer *fb,
-                                 int crtc_x, int crtc_y,
-                                 unsigned int crtc_w, unsigned int crtc_h,
-                                 uint32_t src_x, uint32_t src_y,
-                                 uint32_t src_w, uint32_t src_h)
+static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
+
 {
-       struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
+       struct drm_plane_state *state = &exynos_state->base;
+       struct drm_crtc *crtc = exynos_state->base.crtc;
        struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+       int crtc_x, crtc_y;
+       unsigned int crtc_w, crtc_h;
+       unsigned int src_x, src_y;
+       unsigned int src_w, src_h;
        unsigned int actual_w;
        unsigned int actual_h;
 
+       /*
+        * The original src/dest coordinates are stored in exynos_state->base,
+        * but we want to keep another copy internal to our driver that we can
+        * clip/modify ourselves.
+        */
+
+       crtc_x = state->crtc_x;
+       crtc_y = state->crtc_y;
+       crtc_w = state->crtc_w;
+       crtc_h = state->crtc_h;
+
+       src_x = state->src_x >> 16;
+       src_y = state->src_y >> 16;
+       src_w = state->src_w >> 16;
+       src_h = state->src_h >> 16;
+
+       /* set ratio */
+       exynos_state->h_ratio = (src_w << 16) / crtc_w;
+       exynos_state->v_ratio = (src_h << 16) / crtc_h;
+
+       /* clip to visible area */
        actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay);
        actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay);
 
        if (crtc_x < 0) {
                if (actual_w)
-                       src_x -= crtc_x;
+                       src_x += ((-crtc_x) * exynos_state->h_ratio) >> 16;
                crtc_x = 0;
        }
 
        if (crtc_y < 0) {
                if (actual_h)
-                       src_y -= crtc_y;
+                       src_y += ((-crtc_y) * exynos_state->v_ratio) >> 16;
                crtc_y = 0;
        }
 
-       /* set ratio */
-       exynos_plane->h_ratio = (src_w << 16) / crtc_w;
-       exynos_plane->v_ratio = (src_h << 16) / crtc_h;
-
        /* set drm framebuffer data. */
-       exynos_plane->src_x = src_x;
-       exynos_plane->src_y = src_y;
-       exynos_plane->src_w = (actual_w * exynos_plane->h_ratio) >> 16;
-       exynos_plane->src_h = (actual_h * exynos_plane->v_ratio) >> 16;
+       exynos_state->src.x = src_x;
+       exynos_state->src.y = src_y;
+       exynos_state->src.w = (actual_w * exynos_state->h_ratio) >> 16;
+       exynos_state->src.h = (actual_h * exynos_state->v_ratio) >> 16;
 
        /* set plane range to be displayed. */
-       exynos_plane->crtc_x = crtc_x;
-       exynos_plane->crtc_y = crtc_y;
-       exynos_plane->crtc_w = actual_w;
-       exynos_plane->crtc_h = actual_h;
+       exynos_state->crtc.x = crtc_x;
+       exynos_state->crtc.y = crtc_y;
+       exynos_state->crtc.w = actual_w;
+       exynos_state->crtc.h = actual_h;
 
        DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
-                       exynos_plane->crtc_x, exynos_plane->crtc_y,
-                       exynos_plane->crtc_w, exynos_plane->crtc_h);
+                       exynos_state->crtc.x, exynos_state->crtc.y,
+                       exynos_state->crtc.w, exynos_state->crtc.h);
+}
+
+static void exynos_drm_plane_reset(struct drm_plane *plane)
+{
+       struct exynos_drm_plane_state *exynos_state;
+
+       if (plane->state) {
+               exynos_state = to_exynos_plane_state(plane->state);
+               if (exynos_state->base.fb)
+                       drm_framebuffer_unreference(exynos_state->base.fb);
+               kfree(exynos_state);
+               plane->state = NULL;
+       }
+
+       exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
+       if (exynos_state) {
+               plane->state = &exynos_state->base;
+               plane->state->plane = plane;
+       }
+}
+
+static struct drm_plane_state *
+exynos_drm_plane_duplicate_state(struct drm_plane *plane)
+{
+       struct exynos_drm_plane_state *exynos_state;
+       struct exynos_drm_plane_state *copy;
+
+       exynos_state = to_exynos_plane_state(plane->state);
+       copy = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
+       if (!copy)
+               return NULL;
+
+       __drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
+       return &copy->base;
+}
 
-       plane->crtc = crtc;
+static void exynos_drm_plane_destroy_state(struct drm_plane *plane,
+                                          struct drm_plane_state *old_state)
+{
+       struct exynos_drm_plane_state *old_exynos_state =
+                                       to_exynos_plane_state(old_state);
+       __drm_atomic_helper_plane_destroy_state(plane, old_state);
+       kfree(old_exynos_state);
 }
 
 static struct drm_plane_funcs exynos_plane_funcs = {
        .update_plane   = drm_atomic_helper_update_plane,
        .disable_plane  = drm_atomic_helper_disable_plane,
        .destroy        = drm_plane_cleanup,
-       .reset = drm_atomic_helper_plane_reset,
-       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+       .reset          = exynos_drm_plane_reset,
+       .atomic_duplicate_state = exynos_drm_plane_duplicate_state,
+       .atomic_destroy_state = exynos_drm_plane_destroy_state,
 };
 
+static int
+exynos_drm_plane_check_size(const struct exynos_drm_plane_config *config,
+                           struct exynos_drm_plane_state *state)
+{
+       bool width_ok = false, height_ok = false;
+
+       if (config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE)
+               return 0;
+
+       if (state->src.w == state->crtc.w)
+               width_ok = true;
+
+       if (state->src.h == state->crtc.h)
+               height_ok = true;
+
+       if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) &&
+           state->h_ratio == (1 << 15))
+               width_ok = true;
+
+       if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) &&
+           state->v_ratio == (1 << 15))
+               height_ok = true;
+
+       if (width_ok & height_ok)
+               return 0;
+
+       DRM_DEBUG_KMS("scaling mode is not supported");
+       return -ENOTSUPP;
+}
+
 static int exynos_plane_atomic_check(struct drm_plane *plane,
                                     struct drm_plane_state *state)
 {
        struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
-       int nr;
-       int i;
+       struct exynos_drm_plane_state *exynos_state =
+                                               to_exynos_plane_state(state);
+       int ret = 0;
 
-       if (!state->fb)
+       if (!state->crtc || !state->fb)
                return 0;
 
-       nr = drm_format_num_planes(state->fb->pixel_format);
-       for (i = 0; i < nr; i++) {
-               struct exynos_drm_gem *exynos_gem =
-                                       exynos_drm_fb_gem(state->fb, i);
-               if (!exynos_gem) {
-                       DRM_DEBUG_KMS("gem object is null\n");
-                       return -EFAULT;
-               }
-
-               exynos_plane->dma_addr[i] = exynos_gem->dma_addr +
-                                           state->fb->offsets[i];
-
-               DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
-                               i, (unsigned long)exynos_plane->dma_addr[i]);
-       }
+       /* translate state into exynos_state */
+       exynos_plane_mode_set(exynos_state);
 
-       return 0;
+       ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state);
+       return ret;
 }
 
 static void exynos_plane_atomic_update(struct drm_plane *plane,
@@ -155,12 +232,7 @@ static void exynos_plane_atomic_update(struct drm_plane *plane,
        if (!state->crtc)
                return;
 
-       exynos_plane_mode_set(plane, state->crtc, state->fb,
-                             state->crtc_x, state->crtc_y,
-                             state->crtc_w, state->crtc_h,
-                             state->src_x >> 16, state->src_y >> 16,
-                             state->src_w >> 16, state->src_h >> 16);
-
+       plane->crtc = state->crtc;
        exynos_plane->pending_fb = state->fb;
 
        if (exynos_crtc->ops->update_plane)
@@ -177,8 +249,7 @@ static void exynos_plane_atomic_disable(struct drm_plane *plane,
                return;
 
        if (exynos_crtc->ops->disable_plane)
-               exynos_crtc->ops->disable_plane(exynos_crtc,
-                                               exynos_plane);
+               exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane);
 }
 
 static const struct drm_plane_helper_funcs plane_helper_funcs = {
@@ -207,28 +278,19 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
        drm_object_attach_property(&plane->base, prop, zpos);
 }
 
-enum drm_plane_type exynos_plane_get_type(unsigned int zpos,
-                                         unsigned int cursor_win)
-{
-               if (zpos == DEFAULT_WIN)
-                       return DRM_PLANE_TYPE_PRIMARY;
-               else if (zpos == cursor_win)
-                       return DRM_PLANE_TYPE_CURSOR;
-               else
-                       return DRM_PLANE_TYPE_OVERLAY;
-}
-
 int exynos_plane_init(struct drm_device *dev,
                      struct exynos_drm_plane *exynos_plane,
-                     unsigned long possible_crtcs, enum drm_plane_type type,
-                     const uint32_t *formats, unsigned int fcount,
-                     unsigned int zpos)
+                     unsigned long possible_crtcs,
+                     const struct exynos_drm_plane_config *config)
 {
        int err;
 
-       err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs,
-                                      &exynos_plane_funcs, formats, fcount,
-                                      type);
+       err = drm_universal_plane_init(dev, &exynos_plane->base,
+                                      possible_crtcs,
+                                      &exynos_plane_funcs,
+                                      config->pixel_formats,
+                                      config->num_pixel_formats,
+                                      config->type, NULL);
        if (err) {
                DRM_ERROR("failed to initialize plane\n");
                return err;
@@ -236,10 +298,12 @@ int exynos_plane_init(struct drm_device *dev,
 
        drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs);
 
-       exynos_plane->zpos = zpos;
+       exynos_plane->zpos = config->zpos;
+       exynos_plane->config = config;
 
-       if (type == DRM_PLANE_TYPE_OVERLAY)
-               exynos_plane_attach_zpos_property(&exynos_plane->base, zpos);
+       if (config->type == DRM_PLANE_TYPE_OVERLAY)
+               exynos_plane_attach_zpos_property(&exynos_plane->base,
+                                                 config->zpos);
 
        return 0;
 }
index abb641e..0dd0965 100644 (file)
@@ -9,10 +9,7 @@
  *
  */
 
-enum drm_plane_type exynos_plane_get_type(unsigned int zpos,
-                                         unsigned int cursor_win);
 int exynos_plane_init(struct drm_device *dev,
                      struct exynos_drm_plane *exynos_plane,
-                     unsigned long possible_crtcs, enum drm_plane_type type,
-                     const uint32_t *formats, unsigned int fcount,
-                     unsigned int zpos);
+                     unsigned long possible_crtcs,
+                     const struct exynos_drm_plane_config *config);
index 2f5c118..bea0f78 100644 (file)
@@ -790,10 +790,10 @@ static int rotator_remove(struct platform_device *pdev)
 static int rotator_clk_crtl(struct rot_context *rot, bool enable)
 {
        if (enable) {
-               clk_enable(rot->clock);
+               clk_prepare_enable(rot->clock);
                rot->suspended = false;
        } else {
-               clk_disable(rot->clock);
+               clk_disable_unprepare(rot->clock);
                rot->suspended = true;
        }
 
index 669362c..319aa31 100644 (file)
 
 #include "exynos_drm_drv.h"
 #include "exynos_drm_crtc.h"
+#include "exynos_drm_fb.h"
 #include "exynos_drm_plane.h"
 #include "exynos_drm_vidi.h"
 
 /* vidi has totally three virtual windows. */
 #define WINDOWS_NR             3
-#define CURSOR_WIN             2
 
 #define ctx_from_connector(c)  container_of(c, struct vidi_context, \
                                        connector)
@@ -89,6 +89,12 @@ static const uint32_t formats[] = {
        DRM_FORMAT_NV12,
 };
 
+static const enum drm_plane_type vidi_win_types[WINDOWS_NR] = {
+       DRM_PLANE_TYPE_PRIMARY,
+       DRM_PLANE_TYPE_OVERLAY,
+       DRM_PLANE_TYPE_CURSOR,
+};
+
 static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
 {
        struct vidi_context *ctx = crtc->ctx;
@@ -125,12 +131,15 @@ static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)
 static void vidi_update_plane(struct exynos_drm_crtc *crtc,
                              struct exynos_drm_plane *plane)
 {
+       struct drm_plane_state *state = plane->base.state;
        struct vidi_context *ctx = crtc->ctx;
+       dma_addr_t addr;
 
        if (ctx->suspended)
                return;
 
-       DRM_DEBUG_KMS("dma_addr = %pad\n", plane->dma_addr);
+       addr = exynos_drm_fb_dma_addr(state->fb, 0);
+       DRM_DEBUG_KMS("dma_addr = %pad\n", &addr);
 
        if (ctx->vblank_on)
                schedule_work(&ctx->work);
@@ -439,17 +448,21 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
        struct drm_device *drm_dev = data;
        struct drm_encoder *encoder = &ctx->encoder;
        struct exynos_drm_plane *exynos_plane;
-       enum drm_plane_type type;
-       unsigned int zpos;
+       struct exynos_drm_plane_config plane_config = { 0 };
+       unsigned int i;
        int pipe, ret;
 
        vidi_ctx_initialize(ctx, drm_dev);
 
-       for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
-               type = exynos_plane_get_type(zpos, CURSOR_WIN);
-               ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
-                                       1 << ctx->pipe, type, formats,
-                                       ARRAY_SIZE(formats), zpos);
+       plane_config.pixel_formats = formats;
+       plane_config.num_pixel_formats = ARRAY_SIZE(formats);
+
+       for (i = 0; i < WINDOWS_NR; i++) {
+               plane_config.zpos = i;
+               plane_config.type = vidi_win_types[i];
+
+               ret = exynos_plane_init(drm_dev, &ctx->planes[i],
+                                       1 << ctx->pipe, &plane_config);
                if (ret)
                        return ret;
        }
@@ -473,7 +486,7 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
        DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
        drm_encoder_init(drm_dev, encoder, &exynos_vidi_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        drm_encoder_helper_add(encoder, &exynos_vidi_encoder_helper_funcs);
 
index 57b6755..7d5ca6c 100644 (file)
@@ -113,7 +113,7 @@ struct hdmi_context {
        void __iomem                    *regs_hdmiphy;
        struct i2c_client               *hdmiphy_port;
        struct i2c_adapter              *ddc_adpt;
-       struct gpio_desc                *hpd_gpio;
+       struct gpio_desc                *hpd_gpio;
        int                             irq;
        struct regmap                   *pmureg;
        struct clk                      *hdmi;
@@ -1588,8 +1588,6 @@ static void hdmi_enable(struct drm_encoder *encoder)
        if (hdata->powered)
                return;
 
-       hdata->powered = true;
-
        pm_runtime_get_sync(hdata->dev);
 
        if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
@@ -1599,10 +1597,9 @@ static void hdmi_enable(struct drm_encoder *encoder)
        regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
                        PMU_HDMI_PHY_ENABLE_BIT, 1);
 
-       clk_prepare_enable(hdata->hdmi);
-       clk_prepare_enable(hdata->sclk_hdmi);
-
        hdmi_conf_apply(hdata);
+
+       hdata->powered = true;
 }
 
 static void hdmi_disable(struct drm_encoder *encoder)
@@ -1633,9 +1630,6 @@ static void hdmi_disable(struct drm_encoder *encoder)
 
        cancel_delayed_work(&hdata->hotplug_work);
 
-       clk_disable_unprepare(hdata->sclk_hdmi);
-       clk_disable_unprepare(hdata->hdmi);
-
        /* reset pmu hdmiphy control bit to disable hdmiphy */
        regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
                        PMU_HDMI_PHY_ENABLE_BIT, 0);
@@ -1793,7 +1787,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
        DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
        drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
 
@@ -1978,12 +1972,49 @@ static int hdmi_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int exynos_hdmi_suspend(struct device *dev)
+{
+       struct hdmi_context *hdata = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(hdata->sclk_hdmi);
+       clk_disable_unprepare(hdata->hdmi);
+
+       return 0;
+}
+
+static int exynos_hdmi_resume(struct device *dev)
+{
+       struct hdmi_context *hdata = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(hdata->hdmi);
+       if (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
+               return ret;
+       }
+       ret = clk_prepare_enable(hdata->sclk_hdmi);
+       if (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the sclk_mixer clk [%d]\n",
+                         ret);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos_hdmi_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
+};
+
 struct platform_driver hdmi_driver = {
        .probe          = hdmi_probe,
        .remove         = hdmi_remove,
        .driver         = {
                .name   = "exynos-hdmi",
                .owner  = THIS_MODULE,
+               .pm     = &exynos_hdmi_pm_ops,
                .of_match_table = hdmi_match_types,
        },
 };
index d09f8f9..dfb35e2 100644 (file)
 
 #include "exynos_drm_drv.h"
 #include "exynos_drm_crtc.h"
+#include "exynos_drm_fb.h"
 #include "exynos_drm_plane.h"
 #include "exynos_drm_iommu.h"
 
 #define MIXER_WIN_NR           3
 #define VP_DEFAULT_WIN         2
-#define CURSOR_WIN             1
 
 /* The pixelformats that are natively supported by the mixer. */
 #define MXR_FORMAT_RGB565      4
@@ -111,6 +111,28 @@ struct mixer_drv_data {
        bool                                    has_sclk;
 };
 
+static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
+       {
+               .zpos = 0,
+               .type = DRM_PLANE_TYPE_PRIMARY,
+               .pixel_formats = mixer_formats,
+               .num_pixel_formats = ARRAY_SIZE(mixer_formats),
+               .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
+       }, {
+               .zpos = 1,
+               .type = DRM_PLANE_TYPE_CURSOR,
+               .pixel_formats = mixer_formats,
+               .num_pixel_formats = ARRAY_SIZE(mixer_formats),
+               .capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
+       }, {
+               .zpos = 2,
+               .type = DRM_PLANE_TYPE_OVERLAY,
+               .pixel_formats = vp_formats,
+               .num_pixel_formats = ARRAY_SIZE(vp_formats),
+               .capabilities = EXYNOS_DRM_PLANE_CAP_SCALE,
+       },
+};
+
 static const u8 filter_y_horiz_tap8[] = {
        0,      -1,     -1,     -1,     -1,     -1,     -1,     -1,
        -1,     -1,     -1,     -1,     -1,     0,      0,      0,
@@ -399,10 +421,11 @@ static void mixer_stop(struct mixer_context *ctx)
 static void vp_video_buffer(struct mixer_context *ctx,
                            struct exynos_drm_plane *plane)
 {
+       struct exynos_drm_plane_state *state =
+                               to_exynos_plane_state(plane->base.state);
+       struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
        struct mixer_resources *res = &ctx->mixer_res;
-       struct drm_plane_state *state = plane->base.state;
-       struct drm_framebuffer *fb = state->fb;
-       struct drm_display_mode *mode = &state->crtc->mode;
+       struct drm_framebuffer *fb = state->base.fb;
        unsigned long flags;
        dma_addr_t luma_addr[2], chroma_addr[2];
        bool tiled_mode = false;
@@ -422,8 +445,8 @@ static void vp_video_buffer(struct mixer_context *ctx,
                return;
        }
 
-       luma_addr[0] = plane->dma_addr[0];
-       chroma_addr[0] = plane->dma_addr[1];
+       luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
+       chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
 
        if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
                ctx->interlace = true;
@@ -459,24 +482,24 @@ static void vp_video_buffer(struct mixer_context *ctx,
        vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
                VP_IMG_VSIZE(fb->height / 2));
 
-       vp_reg_write(res, VP_SRC_WIDTH, plane->src_w);
-       vp_reg_write(res, VP_SRC_HEIGHT, plane->src_h);
+       vp_reg_write(res, VP_SRC_WIDTH, state->src.w);
+       vp_reg_write(res, VP_SRC_HEIGHT, state->src.h);
        vp_reg_write(res, VP_SRC_H_POSITION,
-                       VP_SRC_H_POSITION_VAL(plane->src_x));
-       vp_reg_write(res, VP_SRC_V_POSITION, plane->src_y);
+                       VP_SRC_H_POSITION_VAL(state->src.x));
+       vp_reg_write(res, VP_SRC_V_POSITION, state->src.y);
 
-       vp_reg_write(res, VP_DST_WIDTH, plane->crtc_w);
-       vp_reg_write(res, VP_DST_H_POSITION, plane->crtc_x);
+       vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
+       vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
        if (ctx->interlace) {
-               vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h / 2);
-               vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y / 2);
+               vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
+               vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
        } else {
-               vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h);
-               vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y);
+               vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h);
+               vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y);
        }
 
-       vp_reg_write(res, VP_H_RATIO, plane->h_ratio);
-       vp_reg_write(res, VP_V_RATIO, plane->v_ratio);
+       vp_reg_write(res, VP_H_RATIO, state->h_ratio);
+       vp_reg_write(res, VP_V_RATIO, state->v_ratio);
 
        vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
 
@@ -505,37 +528,14 @@ static void mixer_layer_update(struct mixer_context *ctx)
        mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
 }
 
-static int mixer_setup_scale(const struct exynos_drm_plane *plane,
-               unsigned int *x_ratio, unsigned int *y_ratio)
-{
-       if (plane->crtc_w != plane->src_w) {
-               if (plane->crtc_w == 2 * plane->src_w)
-                       *x_ratio = 1;
-               else
-                       goto fail;
-       }
-
-       if (plane->crtc_h != plane->src_h) {
-               if (plane->crtc_h == 2 * plane->src_h)
-                       *y_ratio = 1;
-               else
-                       goto fail;
-       }
-
-       return 0;
-
-fail:
-       DRM_DEBUG_KMS("only 2x width/height scaling of plane supported\n");
-       return -ENOTSUPP;
-}
-
 static void mixer_graph_buffer(struct mixer_context *ctx,
                               struct exynos_drm_plane *plane)
 {
+       struct exynos_drm_plane_state *state =
+                               to_exynos_plane_state(plane->base.state);
+       struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
        struct mixer_resources *res = &ctx->mixer_res;
-       struct drm_plane_state *state = plane->base.state;
-       struct drm_framebuffer *fb = state->fb;
-       struct drm_display_mode *mode = &state->crtc->mode;
+       struct drm_framebuffer *fb = state->base.fb;
        unsigned long flags;
        unsigned int win = plane->zpos;
        unsigned int x_ratio = 0, y_ratio = 0;
@@ -567,17 +567,17 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
                return;
        }
 
-       /* check if mixer supports requested scaling setup */
-       if (mixer_setup_scale(plane, &x_ratio, &y_ratio))
-               return;
+       /* ratio is already checked by common plane code */
+       x_ratio = state->h_ratio == (1 << 15);
+       y_ratio = state->v_ratio == (1 << 15);
 
-       dst_x_offset = plane->crtc_x;
-       dst_y_offset = plane->crtc_y;
+       dst_x_offset = state->crtc.x;
+       dst_y_offset = state->crtc.y;
 
        /* converting dma address base and source offset */
-       dma_addr = plane->dma_addr[0]
-               + (plane->src_x * fb->bits_per_pixel >> 3)
-               + (plane->src_y * fb->pitches[0]);
+       dma_addr = exynos_drm_fb_dma_addr(fb, 0)
+               + (state->src.x * fb->bits_per_pixel >> 3)
+               + (state->src.y * fb->pitches[0]);
        src_x_offset = 0;
        src_y_offset = 0;
 
@@ -605,8 +605,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
                mixer_reg_write(res, MXR_RESOLUTION, val);
        }
 
-       val  = MXR_GRP_WH_WIDTH(plane->src_w);
-       val |= MXR_GRP_WH_HEIGHT(plane->src_h);
+       val  = MXR_GRP_WH_WIDTH(state->src.w);
+       val |= MXR_GRP_WH_HEIGHT(state->src.h);
        val |= MXR_GRP_WH_H_SCALE(x_ratio);
        val |= MXR_GRP_WH_V_SCALE(y_ratio);
        mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
@@ -1020,43 +1020,12 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
 {
        struct mixer_context *ctx = crtc->ctx;
        struct mixer_resources *res = &ctx->mixer_res;
-       int ret;
 
        if (test_bit(MXR_BIT_POWERED, &ctx->flags))
                return;
 
        pm_runtime_get_sync(ctx->dev);
 
-       ret = clk_prepare_enable(res->mixer);
-       if (ret < 0) {
-               DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
-               return;
-       }
-       ret = clk_prepare_enable(res->hdmi);
-       if (ret < 0) {
-               DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
-               return;
-       }
-       if (ctx->vp_enabled) {
-               ret = clk_prepare_enable(res->vp);
-               if (ret < 0) {
-                       DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
-                                 ret);
-                       return;
-               }
-               if (ctx->has_sclk) {
-                       ret = clk_prepare_enable(res->sclk_mixer);
-                       if (ret < 0) {
-                               DRM_ERROR("Failed to prepare_enable the " \
-                                          "sclk_mixer clk [%d]\n",
-                                         ret);
-                               return;
-                       }
-               }
-       }
-
-       set_bit(MXR_BIT_POWERED, &ctx->flags);
-
        mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
 
        if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
@@ -1064,12 +1033,13 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
                mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
        }
        mixer_win_reset(ctx);
+
+       set_bit(MXR_BIT_POWERED, &ctx->flags);
 }
 
 static void mixer_disable(struct exynos_drm_crtc *crtc)
 {
        struct mixer_context *ctx = crtc->ctx;
-       struct mixer_resources *res = &ctx->mixer_res;
        int i;
 
        if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
@@ -1081,17 +1051,9 @@ static void mixer_disable(struct exynos_drm_crtc *crtc)
        for (i = 0; i < MIXER_WIN_NR; i++)
                mixer_disable_plane(crtc, &ctx->planes[i]);
 
-       clear_bit(MXR_BIT_POWERED, &ctx->flags);
+       pm_runtime_put(ctx->dev);
 
-       clk_disable_unprepare(res->hdmi);
-       clk_disable_unprepare(res->mixer);
-       if (ctx->vp_enabled) {
-               clk_disable_unprepare(res->vp);
-               if (ctx->has_sclk)
-                       clk_disable_unprepare(res->sclk_mixer);
-       }
-
-       pm_runtime_put_sync(ctx->dev);
+       clear_bit(MXR_BIT_POWERED, &ctx->flags);
 }
 
 /* Only valid for Mixer version 16.0.33.0 */
@@ -1187,30 +1149,19 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
        struct mixer_context *ctx = dev_get_drvdata(dev);
        struct drm_device *drm_dev = data;
        struct exynos_drm_plane *exynos_plane;
-       unsigned int zpos;
+       unsigned int i;
        int ret;
 
        ret = mixer_initialize(ctx, drm_dev);
        if (ret)
                return ret;
 
-       for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) {
-               enum drm_plane_type type;
-               const uint32_t *formats;
-               unsigned int fcount;
-
-               if (zpos < VP_DEFAULT_WIN) {
-                       formats = mixer_formats;
-                       fcount = ARRAY_SIZE(mixer_formats);
-               } else {
-                       formats = vp_formats;
-                       fcount = ARRAY_SIZE(vp_formats);
-               }
+       for (i = 0; i < MIXER_WIN_NR; i++) {
+               if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
+                       continue;
 
-               type = exynos_plane_get_type(zpos, CURSOR_WIN);
-               ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
-                                       1 << ctx->pipe, type, formats, fcount,
-                                       zpos);
+               ret = exynos_plane_init(drm_dev, &ctx->planes[i],
+                                       1 << ctx->pipe, &plane_configs[i]);
                if (ret)
                        return ret;
        }
@@ -1293,10 +1244,70 @@ static int mixer_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int exynos_mixer_suspend(struct device *dev)
+{
+       struct mixer_context *ctx = dev_get_drvdata(dev);
+       struct mixer_resources *res = &ctx->mixer_res;
+
+       clk_disable_unprepare(res->hdmi);
+       clk_disable_unprepare(res->mixer);
+       if (ctx->vp_enabled) {
+               clk_disable_unprepare(res->vp);
+               if (ctx->has_sclk)
+                       clk_disable_unprepare(res->sclk_mixer);
+       }
+
+       return 0;
+}
+
+static int exynos_mixer_resume(struct device *dev)
+{
+       struct mixer_context *ctx = dev_get_drvdata(dev);
+       struct mixer_resources *res = &ctx->mixer_res;
+       int ret;
+
+       ret = clk_prepare_enable(res->mixer);
+       if (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
+               return ret;
+       }
+       ret = clk_prepare_enable(res->hdmi);
+       if (ret < 0) {
+               DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
+               return ret;
+       }
+       if (ctx->vp_enabled) {
+               ret = clk_prepare_enable(res->vp);
+               if (ret < 0) {
+                       DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
+                                 ret);
+                       return ret;
+               }
+               if (ctx->has_sclk) {
+                       ret = clk_prepare_enable(res->sclk_mixer);
+                       if (ret < 0) {
+                               DRM_ERROR("Failed to prepare_enable the " \
+                                          "sclk_mixer clk [%d]\n",
+                                         ret);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos_mixer_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
+};
+
 struct platform_driver mixer_driver = {
        .driver = {
                .name = "exynos-mixer",
                .owner = THIS_MODULE,
+               .pm = &exynos_mixer_pm_ops,
                .of_match_table = mixer_match_types,
        },
        .probe = mixer_probe,
index 9ad5927..4704a99 100644 (file)
 #define GSC_CLK_GATE_MODE_SNOOP_CNT(x) ((x) << 0)
 
 /* SYSCON. GSCBLK_CFG */
-#define SYSREG_GSCBLK_CFG1             (S3C_VA_SYS + 0x0224)
+#define SYSREG_GSCBLK_CFG1             0x0224
 #define GSC_BLK_DISP1WB_DEST(x)                (x << 10)
 #define GSC_BLK_SW_RESET_WB_DEST(x)    (1 << (18 + x))
 #define GSC_BLK_PXLASYNC_LO_MASK_WB(x) (0 << (14 + x))
 #define GSC_BLK_GSCL_WB_IN_SRC_SEL(x)  (1 << (2 * x))
-#define SYSREG_GSCBLK_CFG2             (S3C_VA_SYS + 0x2000)
+#define SYSREG_GSCBLK_CFG2             0x2000
 #define PXLASYNC_LO_MASK_CAMIF_GSCL(x) (1 << (x))
 
 #endif /* EXYNOS_REGS_GSC_H_ */
index 82a3d31..d8ab8f0 100644 (file)
@@ -175,7 +175,7 @@ int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev)
 
        primary = fsl_dcu_drm_primary_create_plane(fsl_dev->drm);
        ret = drm_crtc_init_with_planes(fsl_dev->drm, crtc, primary, NULL,
-                                       &fsl_dcu_drm_crtc_funcs);
+                                       &fsl_dcu_drm_crtc_funcs, NULL);
        if (ret < 0)
                return ret;
 
index 51daaea..4b13cf9 100644 (file)
@@ -249,7 +249,7 @@ struct drm_plane *fsl_dcu_drm_primary_create_plane(struct drm_device *dev)
                                       &fsl_dcu_drm_plane_funcs,
                                       fsl_dcu_drm_plane_formats,
                                       ARRAY_SIZE(fsl_dcu_drm_plane_formats),
-                                      DRM_PLANE_TYPE_PRIMARY);
+                                      DRM_PLANE_TYPE_PRIMARY, NULL);
        if (ret) {
                kfree(primary);
                primary = NULL;
index fe8ab5d..8780deb 100644 (file)
@@ -57,7 +57,7 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
 
        encoder->possible_crtcs = 1;
        ret = drm_encoder_init(fsl_dev->drm, encoder, &encoder_funcs,
-                              DRM_MODE_ENCODER_LVDS);
+                              DRM_MODE_ENCODER_LVDS, NULL);
        if (ret < 0)
                return ret;
 
index 3531f90..8745971 100644 (file)
@@ -619,6 +619,8 @@ const struct psb_ops cdv_chip_ops = {
        .init_pm = cdv_init_pm,
        .save_regs = cdv_save_display_registers,
        .restore_regs = cdv_restore_display_registers,
+       .save_crtc = gma_crtc_save,
+       .restore_crtc = gma_crtc_restore,
        .power_down = cdv_power_down,
        .power_up = cdv_power_up,
        .update_wm = cdv_update_wm,
index 248c33a..d0717a8 100644 (file)
@@ -273,7 +273,7 @@ void cdv_intel_crt_init(struct drm_device *dev,
 
        encoder = &gma_encoder->base;
        drm_encoder_init(dev, encoder,
-               &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC);
+               &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC, NULL);
 
        gma_connector_attach_encoder(gma_connector, gma_encoder);
 
index 7d47b3d..6126546 100644 (file)
@@ -983,8 +983,6 @@ const struct drm_crtc_helper_funcs cdv_intel_helper_funcs = {
 };
 
 const struct drm_crtc_funcs cdv_intel_crtc_funcs = {
-       .save = gma_crtc_save,
-       .restore = gma_crtc_restore,
        .cursor_set = gma_crtc_cursor_set,
        .cursor_move = gma_crtc_cursor_move,
        .gamma_set = gma_crtc_gamma_set,
index 17cea40..7bb1f1a 100644 (file)
@@ -2020,7 +2020,8 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
        encoder = &gma_encoder->base;
 
        drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, type);
-       drm_encoder_init(dev, encoder, &cdv_intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS);
+       drm_encoder_init(dev, encoder, &cdv_intel_dp_enc_funcs,
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        gma_connector_attach_encoder(gma_connector, gma_encoder);
 
index 6b1d334..ddf2d77 100644 (file)
@@ -270,8 +270,6 @@ static const struct drm_connector_helper_funcs
 
 static const struct drm_connector_funcs cdv_hdmi_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
-       .save = cdv_hdmi_save,
-       .restore = cdv_hdmi_restore,
        .detect = cdv_hdmi_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = cdv_hdmi_set_property,
@@ -306,13 +304,16 @@ void cdv_hdmi_init(struct drm_device *dev,
 
        connector = &gma_connector->base;
        connector->polled = DRM_CONNECTOR_POLL_HPD;
+       gma_connector->save = cdv_hdmi_save;
+       gma_connector->restore = cdv_hdmi_restore;
+
        encoder = &gma_encoder->base;
        drm_connector_init(dev, connector,
                           &cdv_hdmi_connector_funcs,
                           DRM_MODE_CONNECTOR_DVID);
 
        drm_encoder_init(dev, encoder, &psb_intel_lvds_enc_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        gma_connector_attach_encoder(gma_connector, gma_encoder);
        gma_encoder->type = INTEL_OUTPUT_HDMI;
index 211069b..813ef23 100644 (file)
@@ -530,8 +530,6 @@ static const struct drm_connector_helper_funcs
 
 static const struct drm_connector_funcs cdv_intel_lvds_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
-       .save = cdv_intel_lvds_save,
-       .restore = cdv_intel_lvds_restore,
        .detect = cdv_intel_lvds_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = cdv_intel_lvds_set_property,
@@ -643,6 +641,8 @@ void cdv_intel_lvds_init(struct drm_device *dev,
        gma_encoder->dev_priv = lvds_priv;
 
        connector = &gma_connector->base;
+       gma_connector->save = cdv_intel_lvds_save;
+       gma_connector->restore = cdv_intel_lvds_restore;
        encoder = &gma_encoder->base;
 
 
@@ -652,7 +652,7 @@ void cdv_intel_lvds_init(struct drm_device *dev,
 
        drm_encoder_init(dev, encoder,
                         &cdv_intel_lvds_enc_funcs,
-                        DRM_MODE_ENCODER_LVDS);
+                        DRM_MODE_ENCODER_LVDS, NULL);
 
 
        gma_connector_attach_encoder(gma_connector, gma_encoder);
index 265ad0d..e2ab858 100644 (file)
@@ -546,6 +546,8 @@ const struct psb_ops mdfld_chip_ops = {
 
        .save_regs = mdfld_save_registers,
        .restore_regs = mdfld_restore_registers,
+       .save_crtc = gma_crtc_save,
+       .restore_crtc = gma_crtc_restore,
        .power_down = mdfld_power_down,
        .power_up = mdfld_power_up,
 };
index d4813e0..1a1acd3 100644 (file)
@@ -994,7 +994,7 @@ struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev,
        drm_encoder_init(dev,
                        encoder,
                        p_funcs->encoder_funcs,
-                       DRM_MODE_ENCODER_LVDS);
+                       DRM_MODE_ENCODER_LVDS, NULL);
        drm_encoder_helper_add(encoder,
                                p_funcs->encoder_helper_funcs);
 
index 89f705c..d758f4c 100644 (file)
@@ -405,8 +405,6 @@ static struct drm_encoder *mdfld_dsi_connector_best_encoder(
 /*DSI connector funcs*/
 static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
        .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms,
-       .save = mdfld_dsi_connector_save,
-       .restore = mdfld_dsi_connector_restore,
        .detect = mdfld_dsi_connector_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = mdfld_dsi_connector_set_property,
@@ -563,6 +561,9 @@ void mdfld_dsi_output_init(struct drm_device *dev,
 
 
        connector = &dsi_connector->base.base;
+       dsi_connector->base.save = mdfld_dsi_connector_save;
+       dsi_connector->base.restore = mdfld_dsi_connector_restore;
+
        drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
                                                DRM_MODE_CONNECTOR_LVDS);
        drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
index 368a03a..ba30b43 100644 (file)
@@ -568,6 +568,8 @@ const struct psb_ops oaktrail_chip_ops = {
 
        .save_regs = oaktrail_save_display_registers,
        .restore_regs = oaktrail_restore_display_registers,
+       .save_crtc = gma_crtc_save,
+       .restore_crtc = gma_crtc_restore,
        .power_down = oaktrail_power_down,
        .power_up = oaktrail_power_up,
 
index 2310d87..2d18499 100644 (file)
@@ -654,7 +654,7 @@ void oaktrail_hdmi_init(struct drm_device *dev,
 
        drm_encoder_init(dev, encoder,
                         &oaktrail_hdmi_enc_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        gma_connector_attach_encoder(gma_connector, gma_encoder);
 
index 83bbc27..f7038f1 100644 (file)
@@ -323,7 +323,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
                           DRM_MODE_CONNECTOR_LVDS);
 
        drm_encoder_init(dev, encoder, &psb_intel_lvds_enc_funcs,
-                        DRM_MODE_ENCODER_LVDS);
+                        DRM_MODE_ENCODER_LVDS, NULL);
 
        gma_connector_attach_encoder(gma_connector, gma_encoder);
        gma_encoder->type = INTEL_OUTPUT_LVDS;
index 07df7d4..dc0f852 100644 (file)
@@ -181,7 +181,7 @@ static int psb_save_display_registers(struct drm_device *dev)
 {
        struct drm_psb_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
-       struct drm_connector *connector;
+       struct gma_connector *connector;
        struct psb_state *regs = &dev_priv->regs.psb;
 
        /* Display arbitration control + watermarks */
@@ -198,12 +198,12 @@ static int psb_save_display_registers(struct drm_device *dev)
        drm_modeset_lock_all(dev);
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                if (drm_helper_crtc_in_use(crtc))
-                       crtc->funcs->save(crtc);
+                       dev_priv->ops->save_crtc(crtc);
        }
 
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
-               if (connector->funcs->save)
-                       connector->funcs->save(connector);
+       list_for_each_entry(connector, &dev->mode_config.connector_list, base.head)
+               if (connector->save)
+                       connector->save(&connector->base);
 
        drm_modeset_unlock_all(dev);
        return 0;
@@ -219,7 +219,7 @@ static int psb_restore_display_registers(struct drm_device *dev)
 {
        struct drm_psb_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
-       struct drm_connector *connector;
+       struct gma_connector *connector;
        struct psb_state *regs = &dev_priv->regs.psb;
 
        /* Display arbitration + watermarks */
@@ -238,11 +238,11 @@ static int psb_restore_display_registers(struct drm_device *dev)
        drm_modeset_lock_all(dev);
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
                if (drm_helper_crtc_in_use(crtc))
-                       crtc->funcs->restore(crtc);
+                       dev_priv->ops->restore_crtc(crtc);
 
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
-               if (connector->funcs->restore)
-                       connector->funcs->restore(connector);
+       list_for_each_entry(connector, &dev->mode_config.connector_list, base.head)
+               if (connector->restore)
+                       connector->restore(&connector->base);
 
        drm_modeset_unlock_all(dev);
        return 0;
@@ -354,6 +354,8 @@ const struct psb_ops psb_chip_ops = {
        .init_pm = psb_init_pm,
        .save_regs = psb_save_display_registers,
        .restore_regs = psb_restore_display_registers,
+       .save_crtc = gma_crtc_save,
+       .restore_crtc = gma_crtc_restore,
        .power_down = psb_power_down,
        .power_up = psb_power_up,
 };
index 3bd2c72..b743727 100644 (file)
@@ -653,6 +653,8 @@ struct psb_ops {
        void (*init_pm)(struct drm_device *dev);
        int (*save_regs)(struct drm_device *dev);
        int (*restore_regs)(struct drm_device *dev);
+       void (*save_crtc)(struct drm_crtc *crtc);
+       void (*restore_crtc)(struct drm_crtc *crtc);
        int (*power_up)(struct drm_device *dev);
        int (*power_down)(struct drm_device *dev);
        void (*update_wm)(struct drm_device *dev, struct drm_crtc *crtc);
index 6659da8..dcdbc37 100644 (file)
@@ -439,8 +439,6 @@ const struct drm_crtc_helper_funcs psb_intel_helper_funcs = {
 };
 
 const struct drm_crtc_funcs psb_intel_crtc_funcs = {
-       .save = gma_crtc_save,
-       .restore = gma_crtc_restore,
        .cursor_set = gma_crtc_cursor_set,
        .cursor_move = gma_crtc_cursor_move,
        .gamma_set = gma_crtc_gamma_set,
index 860dd21..2a3b7c6 100644 (file)
@@ -140,6 +140,9 @@ struct gma_encoder {
 struct gma_connector {
        struct drm_connector base;
        struct gma_encoder *encoder;
+
+       void (*save)(struct drm_connector *connector);
+       void (*restore)(struct drm_connector *connector);
 };
 
 struct psb_intel_crtc_state {
index ce0645d..b1b9331 100644 (file)
@@ -653,8 +653,6 @@ const struct drm_connector_helper_funcs
 
 const struct drm_connector_funcs psb_intel_lvds_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
-       .save = psb_intel_lvds_save,
-       .restore = psb_intel_lvds_restore,
        .detect = psb_intel_lvds_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = psb_intel_lvds_set_property,
@@ -715,6 +713,9 @@ void psb_intel_lvds_init(struct drm_device *dev,
        gma_encoder->dev_priv = lvds_priv;
 
        connector = &gma_connector->base;
+       gma_connector->save = psb_intel_lvds_save;
+       gma_connector->restore = psb_intel_lvds_restore;
+
        encoder = &gma_encoder->base;
        drm_connector_init(dev, connector,
                           &psb_intel_lvds_connector_funcs,
@@ -722,7 +723,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
 
        drm_encoder_init(dev, encoder,
                         &psb_intel_lvds_enc_funcs,
-                        DRM_MODE_ENCODER_LVDS);
+                        DRM_MODE_ENCODER_LVDS, NULL);
 
        gma_connector_attach_encoder(gma_connector, gma_encoder);
        gma_encoder->type = INTEL_OUTPUT_LVDS;
index 58529ce..e787d37 100644 (file)
@@ -1837,8 +1837,6 @@ static const struct drm_encoder_helper_funcs psb_intel_sdvo_helper_funcs = {
 
 static const struct drm_connector_funcs psb_intel_sdvo_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
-       .save = psb_intel_sdvo_save,
-       .restore = psb_intel_sdvo_restore,
        .detect = psb_intel_sdvo_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = psb_intel_sdvo_set_property,
@@ -2021,6 +2019,9 @@ psb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector,
        connector->base.base.doublescan_allowed = 0;
        connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
 
+       connector->base.save = psb_intel_sdvo_save;
+       connector->base.restore = psb_intel_sdvo_restore;
+
        gma_connector_attach_encoder(&connector->base, &encoder->base);
        drm_connector_register(&connector->base.base);
 }
@@ -2525,7 +2526,8 @@ bool psb_intel_sdvo_init(struct drm_device *dev, int sdvo_reg)
        /* encoder type will be decided later */
        gma_encoder = &psb_intel_sdvo->base;
        gma_encoder->type = INTEL_OUTPUT_SDVO;
-       drm_encoder_init(dev, &gma_encoder->base, &psb_intel_sdvo_enc_funcs, 0);
+       drm_encoder_init(dev, &gma_encoder->base, &psb_intel_sdvo_enc_funcs,
+                        0, NULL);
 
        /* Read the regs to test if we can talk to the device */
        for (i = 0; i < 0x40; i++) {
index 896b6aa..a46248f 100644 (file)
@@ -855,18 +855,6 @@ static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
        priv->dpms = mode;
 }
 
-static void
-tda998x_encoder_save(struct drm_encoder *encoder)
-{
-       DBG("");
-}
-
-static void
-tda998x_encoder_restore(struct drm_encoder *encoder)
-{
-       DBG("");
-}
-
 static bool
 tda998x_encoder_mode_fixup(struct drm_encoder *encoder,
                          const struct drm_display_mode *mode,
@@ -1351,8 +1339,6 @@ static void tda998x_encoder_commit(struct drm_encoder *encoder)
 
 static const struct drm_encoder_helper_funcs tda998x_encoder_helper_funcs = {
        .dpms = tda998x_encoder_dpms,
-       .save = tda998x_encoder_save,
-       .restore = tda998x_encoder_restore,
        .mode_fixup = tda998x_encoder_mode_fixup,
        .prepare = tda998x_encoder_prepare,
        .commit = tda998x_encoder_commit,
@@ -1437,7 +1423,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 
        drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs);
        ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs,
-                              DRM_MODE_ENCODER_TMDS);
+                              DRM_MODE_ENCODER_TMDS, NULL);
        if (ret)
                goto err_encoder;
 
index 411a9c6..a8721fc 100644 (file)
@@ -1639,7 +1639,7 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
        intel_runtime_pm_get(dev_priv);
        mutex_lock(&dev_priv->fbc.lock);
 
-       if (intel_fbc_enabled(dev_priv))
+       if (intel_fbc_is_active(dev_priv))
                seq_puts(m, "FBC enabled\n");
        else
                seq_printf(m, "FBC disabled: %s\n",
@@ -1869,33 +1869,29 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
 {
        struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
-       struct intel_fbdev *ifbdev = NULL;
-       struct intel_framebuffer *fb;
+       struct intel_framebuffer *fbdev_fb = NULL;
        struct drm_framebuffer *drm_fb;
 
 #ifdef CONFIG_DRM_FBDEV_EMULATION
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       ifbdev = dev_priv->fbdev;
-       if (ifbdev) {
-               fb = to_intel_framebuffer(ifbdev->helper.fb);
-
-               seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ",
-                          fb->base.width,
-                          fb->base.height,
-                          fb->base.depth,
-                          fb->base.bits_per_pixel,
-                          fb->base.modifier[0],
-                          atomic_read(&fb->base.refcount.refcount));
-               describe_obj(m, fb->obj);
-               seq_putc(m, '\n');
-       }
+       if (to_i915(dev)->fbdev) {
+               fbdev_fb = to_intel_framebuffer(to_i915(dev)->fbdev->helper.fb);
+
+               seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ",
+                         fbdev_fb->base.width,
+                         fbdev_fb->base.height,
+                         fbdev_fb->base.depth,
+                         fbdev_fb->base.bits_per_pixel,
+                         fbdev_fb->base.modifier[0],
+                         atomic_read(&fbdev_fb->base.refcount.refcount));
+               describe_obj(m, fbdev_fb->obj);
+               seq_putc(m, '\n');
+       }
 #endif
 
        mutex_lock(&dev->mode_config.fb_lock);
        drm_for_each_fb(drm_fb, dev) {
-               fb = to_intel_framebuffer(drm_fb);
-               if (ifbdev && &fb->base == ifbdev->helper.fb)
+               struct intel_framebuffer *fb = to_intel_framebuffer(drm_fb);
+               if (fb == fbdev_fb)
                        continue;
 
                seq_printf(m, "user size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ",
@@ -2473,15 +2469,15 @@ static int i915_guc_info(struct seq_file *m, void *data)
        if (!HAS_GUC_SCHED(dev_priv->dev))
                return 0;
 
+       if (mutex_lock_interruptible(&dev->struct_mutex))
+               return 0;
+
        /* Take a local copy of the GuC data, so we can dump it at leisure */
-       spin_lock(&dev_priv->guc.host2guc_lock);
        guc = dev_priv->guc;
-       if (guc.execbuf_client) {
-               spin_lock(&guc.execbuf_client->wq_lock);
+       if (guc.execbuf_client)
                client = *guc.execbuf_client;
-               spin_unlock(&guc.execbuf_client->wq_lock);
-       }
-       spin_unlock(&dev_priv->guc.host2guc_lock);
+
+       mutex_unlock(&dev->struct_mutex);
 
        seq_printf(m, "GuC total action count: %llu\n", guc.action_count);
        seq_printf(m, "GuC action failure count: %u\n", guc.action_fail);
@@ -2582,8 +2578,11 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
                }
        seq_puts(m, "\n");
 
-       /* CHV PSR has no kind of performance counter */
-       if (HAS_DDI(dev)) {
+       /*
+        * VLV/CHV PSR has no kind of performance counter
+        * SKL+ Perf counter is reset to 0 everytime DC state is entered
+        */
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
                psrperf = I915_READ(EDP_PSR_PERF_CNT) &
                        EDP_PSR_PERF_CNT_MASK;
 
@@ -2685,71 +2684,6 @@ static int i915_runtime_pm_status(struct seq_file *m, void *unused)
        return 0;
 }
 
-static const char *power_domain_str(enum intel_display_power_domain domain)
-{
-       switch (domain) {
-       case POWER_DOMAIN_PIPE_A:
-               return "PIPE_A";
-       case POWER_DOMAIN_PIPE_B:
-               return "PIPE_B";
-       case POWER_DOMAIN_PIPE_C:
-               return "PIPE_C";
-       case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
-               return "PIPE_A_PANEL_FITTER";
-       case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
-               return "PIPE_B_PANEL_FITTER";
-       case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
-               return "PIPE_C_PANEL_FITTER";
-       case POWER_DOMAIN_TRANSCODER_A:
-               return "TRANSCODER_A";
-       case POWER_DOMAIN_TRANSCODER_B:
-               return "TRANSCODER_B";
-       case POWER_DOMAIN_TRANSCODER_C:
-               return "TRANSCODER_C";
-       case POWER_DOMAIN_TRANSCODER_EDP:
-               return "TRANSCODER_EDP";
-       case POWER_DOMAIN_PORT_DDI_A_LANES:
-               return "PORT_DDI_A_LANES";
-       case POWER_DOMAIN_PORT_DDI_B_LANES:
-               return "PORT_DDI_B_LANES";
-       case POWER_DOMAIN_PORT_DDI_C_LANES:
-               return "PORT_DDI_C_LANES";
-       case POWER_DOMAIN_PORT_DDI_D_LANES:
-               return "PORT_DDI_D_LANES";
-       case POWER_DOMAIN_PORT_DDI_E_LANES:
-               return "PORT_DDI_E_LANES";
-       case POWER_DOMAIN_PORT_DSI:
-               return "PORT_DSI";
-       case POWER_DOMAIN_PORT_CRT:
-               return "PORT_CRT";
-       case POWER_DOMAIN_PORT_OTHER:
-               return "PORT_OTHER";
-       case POWER_DOMAIN_VGA:
-               return "VGA";
-       case POWER_DOMAIN_AUDIO:
-               return "AUDIO";
-       case POWER_DOMAIN_PLLS:
-               return "PLLS";
-       case POWER_DOMAIN_AUX_A:
-               return "AUX_A";
-       case POWER_DOMAIN_AUX_B:
-               return "AUX_B";
-       case POWER_DOMAIN_AUX_C:
-               return "AUX_C";
-       case POWER_DOMAIN_AUX_D:
-               return "AUX_D";
-       case POWER_DOMAIN_GMBUS:
-               return "GMBUS";
-       case POWER_DOMAIN_MODESET:
-               return "MODESET";
-       case POWER_DOMAIN_INIT:
-               return "INIT";
-       default:
-               MISSING_CASE(domain);
-               return "?";
-       }
-}
-
 static int i915_power_domain_info(struct seq_file *m, void *unused)
 {
        struct drm_info_node *node = m->private;
@@ -2775,7 +2709,7 @@ static int i915_power_domain_info(struct seq_file *m, void *unused)
                                continue;
 
                        seq_printf(m, "  %-23s %d\n",
-                                power_domain_str(power_domain),
+                                intel_display_power_domain_str(power_domain),
                                 power_domains->domain_use_count[power_domain]);
                }
        }
index 6344dfb..e6935f1 100644 (file)
@@ -228,121 +228,83 @@ static const struct intel_device_info intel_sandybridge_m_info = {
        .need_gfx_hws = 1, .has_hotplug = 1, \
        .has_fbc = 1, \
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \
-       .has_llc = 1
+       .has_llc = 1, \
+       GEN_DEFAULT_PIPEOFFSETS, \
+       IVB_CURSOR_OFFSETS
 
 static const struct intel_device_info intel_ivybridge_d_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ivybridge_m_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
        .is_mobile = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ivybridge_q_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
        .num_pipes = 0, /* legal, last one wins */
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
+#define VLV_FEATURES  \
+       .gen = 7, .num_pipes = 2, \
+       .need_gfx_hws = 1, .has_hotplug = 1, \
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \
+       .display_mmio_offset = VLV_DISPLAY_BASE, \
+       GEN_DEFAULT_PIPEOFFSETS, \
+       CURSOR_OFFSETS
+
 static const struct intel_device_info intel_valleyview_m_info = {
-       GEN7_FEATURES,
-       .is_mobile = 1,
-       .num_pipes = 2,
+       VLV_FEATURES,
        .is_valleyview = 1,
-       .display_mmio_offset = VLV_DISPLAY_BASE,
-       .has_fbc = 0, /* legal, last one wins */
-       .has_llc = 0, /* legal, last one wins */
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       .is_mobile = 1,
 };
 
 static const struct intel_device_info intel_valleyview_d_info = {
-       GEN7_FEATURES,
-       .num_pipes = 2,
+       VLV_FEATURES,
        .is_valleyview = 1,
-       .display_mmio_offset = VLV_DISPLAY_BASE,
-       .has_fbc = 0, /* legal, last one wins */
-       .has_llc = 0, /* legal, last one wins */
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
 };
 
+#define HSW_FEATURES  \
+       GEN7_FEATURES, \
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, \
+       .has_ddi = 1, \
+       .has_fpga_dbg = 1
+
 static const struct intel_device_info intel_haswell_d_info = {
-       GEN7_FEATURES,
+       HSW_FEATURES,
        .is_haswell = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_haswell_m_info = {
-       GEN7_FEATURES,
+       HSW_FEATURES,
        .is_haswell = 1,
        .is_mobile = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_broadwell_d_info = {
-       .gen = 8, .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-       .has_llc = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .has_fbc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
+       HSW_FEATURES,
+       .gen = 8,
 };
 
 static const struct intel_device_info intel_broadwell_m_info = {
-       .gen = 8, .is_mobile = 1, .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-       .has_llc = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .has_fbc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
+       HSW_FEATURES,
+       .gen = 8, .is_mobile = 1,
 };
 
 static const struct intel_device_info intel_broadwell_gt3d_info = {
-       .gen = 8, .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
+       HSW_FEATURES,
+       .gen = 8,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
-       .has_llc = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .has_fbc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_broadwell_gt3m_info = {
-       .gen = 8, .is_mobile = 1, .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
+       HSW_FEATURES,
+       .gen = 8, .is_mobile = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
-       .has_llc = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .has_fbc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_cherryview_info = {
@@ -356,29 +318,16 @@ static const struct intel_device_info intel_cherryview_info = {
 };
 
 static const struct intel_device_info intel_skylake_info = {
+       HSW_FEATURES,
        .is_skylake = 1,
-       .gen = 9, .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-       .has_llc = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .has_fbc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
+       .gen = 9,
 };
 
 static const struct intel_device_info intel_skylake_gt3_info = {
+       HSW_FEATURES,
        .is_skylake = 1,
-       .gen = 9, .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
+       .gen = 9,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
-       .has_llc = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .has_fbc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_broxton_info = {
@@ -396,33 +345,18 @@ static const struct intel_device_info intel_broxton_info = {
 };
 
 static const struct intel_device_info intel_kabylake_info = {
+       HSW_FEATURES,
        .is_preliminary = 1,
        .is_kabylake = 1,
        .gen = 9,
-       .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-       .has_llc = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .has_fbc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_kabylake_gt3_info = {
+       HSW_FEATURES,
        .is_preliminary = 1,
        .is_kabylake = 1,
        .gen = 9,
-       .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
-       .has_llc = 1,
-       .has_ddi = 1,
-       .has_fpga_dbg = 1,
-       .has_fbc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       IVB_CURSOR_OFFSETS,
 };
 
 /*
@@ -465,6 +399,7 @@ static const struct pci_device_id pciidlist[] = {
        INTEL_SKL_GT1_IDS(&intel_skylake_info),
        INTEL_SKL_GT2_IDS(&intel_skylake_info),
        INTEL_SKL_GT3_IDS(&intel_skylake_gt3_info),
+       INTEL_SKL_GT4_IDS(&intel_skylake_gt3_info),
        INTEL_BXT_IDS(&intel_broxton_info),
        INTEL_KBL_GT1_IDS(&intel_kabylake_info),
        INTEL_KBL_GT2_IDS(&intel_kabylake_info),
@@ -565,7 +500,8 @@ void intel_detect_pch(struct drm_device *dev)
                                DRM_DEBUG_KMS("Found SunrisePoint LP PCH\n");
                                WARN_ON(!IS_SKYLAKE(dev) &&
                                        !IS_KABYLAKE(dev));
-                       } else if (id == INTEL_PCH_P2X_DEVICE_ID_TYPE) {
+                       } else if ((id == INTEL_PCH_P2X_DEVICE_ID_TYPE) ||
+                                  (id == INTEL_PCH_QEMU_DEVICE_ID_TYPE)) {
                                dev_priv->pch_type = intel_virt_detect_pch(dev);
                        } else
                                continue;
@@ -624,6 +560,14 @@ static int vlv_resume_prepare(struct drm_i915_private *dev_priv,
                              bool rpm_resume);
 static int bxt_resume_prepare(struct drm_i915_private *dev_priv);
 
+static bool suspend_to_idle(struct drm_i915_private *dev_priv)
+{
+#if IS_ENABLED(CONFIG_ACPI_SLEEP)
+       if (acpi_target_system_state() < ACPI_STATE_S3)
+               return true;
+#endif
+       return false;
+}
 
 static int i915_drm_suspend(struct drm_device *dev)
 {
@@ -676,11 +620,7 @@ static int i915_drm_suspend(struct drm_device *dev)
 
        i915_save_state(dev);
 
-       opregion_target_state = PCI_D3cold;
-#if IS_ENABLED(CONFIG_ACPI_SLEEP)
-       if (acpi_target_system_state() < ACPI_STATE_S3)
-               opregion_target_state = PCI_D1;
-#endif
+       opregion_target_state = suspend_to_idle(dev_priv) ? PCI_D1 : PCI_D3cold;
        intel_opregion_notify_adapter(dev, opregion_target_state);
 
        intel_uncore_forcewake_reset(dev, false);
@@ -701,15 +641,26 @@ static int i915_drm_suspend(struct drm_device *dev)
 static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
 {
        struct drm_i915_private *dev_priv = drm_dev->dev_private;
+       bool fw_csr;
        int ret;
 
-       intel_power_domains_suspend(dev_priv);
+       fw_csr = suspend_to_idle(dev_priv) && dev_priv->csr.dmc_payload;
+       /*
+        * In case of firmware assisted context save/restore don't manually
+        * deinit the power domains. This also means the CSR/DMC firmware will
+        * stay active, it will power down any HW resources as required and
+        * also enable deeper system power states that would be blocked if the
+        * firmware was inactive.
+        */
+       if (!fw_csr)
+               intel_power_domains_suspend(dev_priv);
 
        ret = intel_suspend_complete(dev_priv);
 
        if (ret) {
                DRM_ERROR("Suspend complete failed: %d\n", ret);
-               intel_power_domains_init_hw(dev_priv, true);
+               if (!fw_csr)
+                       intel_power_domains_init_hw(dev_priv, true);
 
                return ret;
        }
@@ -730,6 +681,8 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
        if (!(hibernation && INTEL_INFO(dev_priv)->gen < 6))
                pci_set_power_state(drm_dev->pdev, PCI_D3hot);
 
+       dev_priv->suspended_to_idle = suspend_to_idle(dev_priv);
+
        return 0;
 }
 
@@ -842,8 +795,10 @@ static int i915_drm_resume_early(struct drm_device *dev)
         * FIXME: This should be solved with a special hdmi sink device or
         * similar so that power domains can be employed.
         */
-       if (pci_enable_device(dev->pdev))
-               return -EIO;
+       if (pci_enable_device(dev->pdev)) {
+               ret = -EIO;
+               goto out;
+       }
 
        pci_set_master(dev->pdev);
 
@@ -861,7 +816,12 @@ static int i915_drm_resume_early(struct drm_device *dev)
                hsw_disable_pc8(dev_priv);
 
        intel_uncore_sanitize(dev);
-       intel_power_domains_init_hw(dev_priv, true);
+
+       if (!(dev_priv->suspended_to_idle && dev_priv->csr.dmc_payload))
+               intel_power_domains_init_hw(dev_priv, true);
+
+out:
+       dev_priv->suspended_to_idle = false;
 
        return ret;
 }
index 15c6dc0..f1a8a53 100644 (file)
@@ -57,7 +57,7 @@
 
 #define DRIVER_NAME            "i915"
 #define DRIVER_DESC            "Intel Graphics"
-#define DRIVER_DATE            "20151120"
+#define DRIVER_DATE            "20151204"
 
 #undef WARN_ON
 /* Many gcc seem to no see through this and fall over :( */
@@ -902,7 +902,6 @@ struct i915_fbc {
        /* This is always the inner lock when overlapping with struct_mutex and
         * it's the outer lock when overlapping with stolen_lock. */
        struct mutex lock;
-       unsigned long uncompressed_size;
        unsigned threshold;
        unsigned int fb_id;
        unsigned int possible_framebuffer_bits;
@@ -915,21 +914,21 @@ struct i915_fbc {
 
        bool false_color;
 
-       /* Tracks whether the HW is actually enabled, not whether the feature is
-        * possible. */
        bool enabled;
+       bool active;
 
        struct intel_fbc_work {
-               struct delayed_work work;
-               struct intel_crtc *crtc;
+               bool scheduled;
+               struct work_struct work;
                struct drm_framebuffer *fb;
-       } *fbc_work;
+               unsigned long enable_jiffies;
+       } work;
 
        const char *no_fbc_reason;
 
-       bool (*fbc_enabled)(struct drm_i915_private *dev_priv);
-       void (*enable_fbc)(struct intel_crtc *crtc);
-       void (*disable_fbc)(struct drm_i915_private *dev_priv);
+       bool (*is_active)(struct drm_i915_private *dev_priv);
+       void (*activate)(struct intel_crtc *crtc);
+       void (*deactivate)(struct drm_i915_private *dev_priv);
 };
 
 /**
@@ -1885,6 +1884,7 @@ struct drm_i915_private {
        u32 chv_phy_control;
 
        u32 suspend_count;
+       bool suspended_to_idle;
        struct i915_suspend_saved_registers regfile;
        struct vlv_s0ix_state vlv_s0ix_state;
 
@@ -2608,11 +2608,13 @@ struct drm_i915_cmd_table {
 #define INTEL_PCH_SPT_DEVICE_ID_TYPE           0xA100
 #define INTEL_PCH_SPT_LP_DEVICE_ID_TYPE                0x9D00
 #define INTEL_PCH_P2X_DEVICE_ID_TYPE           0x7100
+#define INTEL_PCH_QEMU_DEVICE_ID_TYPE          0x2900 /* qemu q35 has 2918 */
 
 #define INTEL_PCH_TYPE(dev) (__I915__(dev)->pch_type)
 #define HAS_PCH_SPT(dev) (INTEL_PCH_TYPE(dev) == PCH_SPT)
 #define HAS_PCH_LPT(dev) (INTEL_PCH_TYPE(dev) == PCH_LPT)
 #define HAS_PCH_LPT_LP(dev) (__I915__(dev)->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE)
+#define HAS_PCH_LPT_H(dev) (__I915__(dev)->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE)
 #define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT)
 #define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX)
 #define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP)
@@ -2749,17 +2751,47 @@ void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv);
 void i915_hotplug_interrupt_update(struct drm_i915_private *dev_priv,
                                   uint32_t mask,
                                   uint32_t bits);
-void
-ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask);
-void
-ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask);
+void ilk_update_display_irq(struct drm_i915_private *dev_priv,
+                           uint32_t interrupt_mask,
+                           uint32_t enabled_irq_mask);
+static inline void
+ilk_enable_display_irq(struct drm_i915_private *dev_priv, uint32_t bits)
+{
+       ilk_update_display_irq(dev_priv, bits, bits);
+}
+static inline void
+ilk_disable_display_irq(struct drm_i915_private *dev_priv, uint32_t bits)
+{
+       ilk_update_display_irq(dev_priv, bits, 0);
+}
+void bdw_update_pipe_irq(struct drm_i915_private *dev_priv,
+                        enum pipe pipe,
+                        uint32_t interrupt_mask,
+                        uint32_t enabled_irq_mask);
+static inline void bdw_enable_pipe_irq(struct drm_i915_private *dev_priv,
+                                      enum pipe pipe, uint32_t bits)
+{
+       bdw_update_pipe_irq(dev_priv, pipe, bits, bits);
+}
+static inline void bdw_disable_pipe_irq(struct drm_i915_private *dev_priv,
+                                       enum pipe pipe, uint32_t bits)
+{
+       bdw_update_pipe_irq(dev_priv, pipe, bits, 0);
+}
 void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
                                  uint32_t interrupt_mask,
                                  uint32_t enabled_irq_mask);
-#define ibx_enable_display_interrupt(dev_priv, bits) \
-       ibx_display_interrupt_update((dev_priv), (bits), (bits))
-#define ibx_disable_display_interrupt(dev_priv, bits) \
-       ibx_display_interrupt_update((dev_priv), (bits), 0)
+static inline void
+ibx_enable_display_interrupt(struct drm_i915_private *dev_priv, uint32_t bits)
+{
+       ibx_display_interrupt_update(dev_priv, bits, bits);
+}
+static inline void
+ibx_disable_display_interrupt(struct drm_i915_private *dev_priv, uint32_t bits)
+{
+       ibx_display_interrupt_update(dev_priv, bits, 0);
+}
+
 
 /* i915_gem.c */
 int i915_gem_create_ioctl(struct drm_device *dev, void *data,
index a6997a8..b7d7cec 100644 (file)
@@ -2949,6 +2949,10 @@ i915_gem_idle_work_handler(struct work_struct *work)
                if (!list_empty(&ring->request_list))
                        return;
 
+       /* we probably should sync with hangcheck here, using cancel_work_sync.
+        * Also locking seems to be fubar here, ring->request_list is protected
+        * by dev->struct_mutex. */
+
        intel_mark_idle(dev);
 
        if (mutex_trylock(&dev->struct_mutex)) {
@@ -3073,7 +3077,7 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
                if (ret == 0)
                        ret = __i915_wait_request(req[i], reset_counter, true,
                                                  args->timeout_ns > 0 ? &args->timeout_ns : NULL,
-                                                 file->driver_priv);
+                                                 to_rps_client(file));
                i915_gem_request_unreference__unlocked(req[i]);
        }
        return ret;
index 4b94004..43761c5 100644 (file)
@@ -141,8 +141,6 @@ static void i915_gem_context_clean(struct intel_context *ctx)
        if (!ppgtt)
                return;
 
-       WARN_ON(!list_empty(&ppgtt->base.active_list));
-
        list_for_each_entry_safe(vma, next, &ppgtt->base.inactive_list,
                                 mm_list) {
                if (WARN_ON(__i915_vma_unbind_no_wait(vma)))
index ed9f100..0d23785 100644 (file)
@@ -86,7 +86,6 @@ static int host2guc_action(struct intel_guc *guc, u32 *data, u32 len)
                return -EINVAL;
 
        intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
-       spin_lock(&dev_priv->guc.host2guc_lock);
 
        dev_priv->guc.action_count += 1;
        dev_priv->guc.action_cmd = data[0];
@@ -119,7 +118,6 @@ static int host2guc_action(struct intel_guc *guc, u32 *data, u32 len)
        }
        dev_priv->guc.action_status = status;
 
-       spin_unlock(&dev_priv->guc.host2guc_lock);
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
 
        return ret;
@@ -292,16 +290,12 @@ static uint32_t select_doorbell_cacheline(struct intel_guc *guc)
        const uint32_t cacheline_size = cache_line_size();
        uint32_t offset;
 
-       spin_lock(&guc->host2guc_lock);
-
        /* Doorbell uses a single cache line within a page */
        offset = offset_in_page(guc->db_cacheline);
 
        /* Moving to next cache line to reduce contention */
        guc->db_cacheline += cacheline_size;
 
-       spin_unlock(&guc->host2guc_lock);
-
        DRM_DEBUG_DRIVER("selected doorbell cacheline 0x%x, next 0x%x, linesize %u\n",
                        offset, guc->db_cacheline, cacheline_size);
 
@@ -322,13 +316,11 @@ static uint16_t assign_doorbell(struct intel_guc *guc, uint32_t priority)
        const uint16_t end = start + half;
        uint16_t id;
 
-       spin_lock(&guc->host2guc_lock);
        id = find_next_zero_bit(guc->doorbell_bitmap, end, start);
        if (id == end)
                id = GUC_INVALID_DOORBELL_ID;
        else
                bitmap_set(guc->doorbell_bitmap, id, 1);
-       spin_unlock(&guc->host2guc_lock);
 
        DRM_DEBUG_DRIVER("assigned %s priority doorbell id 0x%x\n",
                        hi_pri ? "high" : "normal", id);
@@ -338,9 +330,7 @@ static uint16_t assign_doorbell(struct intel_guc *guc, uint32_t priority)
 
 static void release_doorbell(struct intel_guc *guc, uint16_t id)
 {
-       spin_lock(&guc->host2guc_lock);
        bitmap_clear(guc->doorbell_bitmap, id, 1);
-       spin_unlock(&guc->host2guc_lock);
 }
 
 /*
@@ -487,16 +477,13 @@ static int guc_get_workqueue_space(struct i915_guc_client *gc, u32 *offset)
        struct guc_process_desc *desc;
        void *base;
        u32 size = sizeof(struct guc_wq_item);
-       int ret = 0, timeout_counter = 200;
+       int ret = -ETIMEDOUT, timeout_counter = 200;
 
        base = kmap_atomic(i915_gem_object_get_page(gc->client_obj, 0));
        desc = base + gc->proc_desc_offset;
 
        while (timeout_counter-- > 0) {
-               ret = wait_for_atomic(CIRC_SPACE(gc->wq_tail, desc->head,
-                               gc->wq_size) >= size, 1);
-
-               if (!ret) {
+               if (CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size) >= size) {
                        *offset = gc->wq_tail;
 
                        /* advance the tail for next workqueue item */
@@ -505,7 +492,11 @@ static int guc_get_workqueue_space(struct i915_guc_client *gc, u32 *offset)
 
                        /* this will break the loop */
                        timeout_counter = 0;
+                       ret = 0;
                }
+
+               if (timeout_counter)
+                       usleep_range(1000, 2000);
        };
 
        kunmap_atomic(base);
@@ -597,15 +588,12 @@ int i915_guc_submit(struct i915_guc_client *client,
 {
        struct intel_guc *guc = client->guc;
        enum intel_ring_id ring_id = rq->ring->id;
-       unsigned long flags;
        int q_ret, b_ret;
 
        /* Need this because of the deferred pin ctx and ring */
        /* Shall we move this right after ring is pinned? */
        lr_context_update(rq);
 
-       spin_lock_irqsave(&client->wq_lock, flags);
-
        q_ret = guc_add_workqueue_item(client, rq);
        if (q_ret == 0)
                b_ret = guc_ring_doorbell(client);
@@ -620,12 +608,8 @@ int i915_guc_submit(struct i915_guc_client *client,
        } else {
                client->retcode = 0;
        }
-       spin_unlock_irqrestore(&client->wq_lock, flags);
-
-       spin_lock(&guc->host2guc_lock);
        guc->submissions[ring_id] += 1;
        guc->last_seqno[ring_id] = rq->seqno;
-       spin_unlock(&guc->host2guc_lock);
 
        return q_ret;
 }
@@ -677,7 +661,7 @@ static struct drm_i915_gem_object *gem_allocate_guc_obj(struct drm_device *dev,
 /**
  * gem_release_guc_obj() - Release gem object allocated for GuC usage
  * @obj:       gem obj to be released
 */
+ */
 static void gem_release_guc_obj(struct drm_i915_gem_object *obj)
 {
        if (!obj)
@@ -768,7 +752,6 @@ static struct i915_guc_client *guc_client_alloc(struct drm_device *dev,
        client->client_obj = obj;
        client->wq_offset = GUC_DB_SIZE;
        client->wq_size = GUC_WQ_SIZE;
-       spin_lock_init(&client->wq_lock);
 
        client->doorbell_offset = select_doorbell_cacheline(guc);
 
@@ -871,8 +854,6 @@ int i915_guc_submission_init(struct drm_device *dev)
        if (!guc->ctx_pool_obj)
                return -ENOMEM;
 
-       spin_lock_init(&dev_priv->guc.host2guc_lock);
-
        ida_init(&guc->ctx_ids);
 
        guc_create_log(guc);
index c8ba949..e88d692 100644 (file)
@@ -215,9 +215,9 @@ void i915_hotplug_interrupt_update(struct drm_i915_private *dev_priv,
  * @interrupt_mask: mask of interrupt bits to update
  * @enabled_irq_mask: mask of interrupt bits to enable
  */
-static void ilk_update_display_irq(struct drm_i915_private *dev_priv,
-                                  uint32_t interrupt_mask,
-                                  uint32_t enabled_irq_mask)
+void ilk_update_display_irq(struct drm_i915_private *dev_priv,
+                           uint32_t interrupt_mask,
+                           uint32_t enabled_irq_mask)
 {
        uint32_t new_val;
 
@@ -239,18 +239,6 @@ static void ilk_update_display_irq(struct drm_i915_private *dev_priv,
        }
 }
 
-void
-ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
-{
-       ilk_update_display_irq(dev_priv, mask, mask);
-}
-
-void
-ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
-{
-       ilk_update_display_irq(dev_priv, mask, 0);
-}
-
 /**
  * ilk_update_gt_irq - update GTIMR
  * @dev_priv: driver private
@@ -300,11 +288,11 @@ static i915_reg_t gen6_pm_ier(struct drm_i915_private *dev_priv)
 }
 
 /**
 * snb_update_pm_irq - update GEN6_PMIMR
 * @dev_priv: driver private
 * @interrupt_mask: mask of interrupt bits to update
 * @enabled_irq_mask: mask of interrupt bits to enable
 */
+ * snb_update_pm_irq - update GEN6_PMIMR
+ * @dev_priv: driver private
+ * @interrupt_mask: mask of interrupt bits to update
+ * @enabled_irq_mask: mask of interrupt bits to enable
+ */
 static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
                              uint32_t interrupt_mask,
                              uint32_t enabled_irq_mask)
@@ -418,11 +406,11 @@ void gen6_disable_rps_interrupts(struct drm_device *dev)
 }
 
 /**
 * bdw_update_port_irq - update DE port interrupt
 * @dev_priv: driver private
 * @interrupt_mask: mask of interrupt bits to update
 * @enabled_irq_mask: mask of interrupt bits to enable
 */
+ * bdw_update_port_irq - update DE port interrupt
+ * @dev_priv: driver private
+ * @interrupt_mask: mask of interrupt bits to update
+ * @enabled_irq_mask: mask of interrupt bits to enable
+ */
 static void bdw_update_port_irq(struct drm_i915_private *dev_priv,
                                uint32_t interrupt_mask,
                                uint32_t enabled_irq_mask)
@@ -449,6 +437,38 @@ static void bdw_update_port_irq(struct drm_i915_private *dev_priv,
        }
 }
 
+/**
+ * bdw_update_pipe_irq - update DE pipe interrupt
+ * @dev_priv: driver private
+ * @pipe: pipe whose interrupt to update
+ * @interrupt_mask: mask of interrupt bits to update
+ * @enabled_irq_mask: mask of interrupt bits to enable
+ */
+void bdw_update_pipe_irq(struct drm_i915_private *dev_priv,
+                        enum pipe pipe,
+                        uint32_t interrupt_mask,
+                        uint32_t enabled_irq_mask)
+{
+       uint32_t new_val;
+
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       WARN_ON(enabled_irq_mask & ~interrupt_mask);
+
+       if (WARN_ON(!intel_irqs_enabled(dev_priv)))
+               return;
+
+       new_val = dev_priv->de_irq_mask[pipe];
+       new_val &= ~interrupt_mask;
+       new_val |= (~enabled_irq_mask & interrupt_mask);
+
+       if (new_val != dev_priv->de_irq_mask[pipe]) {
+               dev_priv->de_irq_mask[pipe] = new_val;
+               I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
+               POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+       }
+}
+
 /**
  * ibx_display_interrupt_update - update SDEIMR
  * @dev_priv: driver private
@@ -1824,8 +1844,24 @@ static void ibx_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger,
        struct drm_i915_private *dev_priv = to_i915(dev);
        u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
 
+       /*
+        * Somehow the PCH doesn't seem to really ack the interrupt to the CPU
+        * unless we touch the hotplug register, even if hotplug_trigger is
+        * zero. Not acking leads to "The master control interrupt lied (SDE)!"
+        * errors.
+        */
        dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
+       if (!hotplug_trigger) {
+               u32 mask = PORTA_HOTPLUG_STATUS_MASK |
+                       PORTD_HOTPLUG_STATUS_MASK |
+                       PORTC_HOTPLUG_STATUS_MASK |
+                       PORTB_HOTPLUG_STATUS_MASK;
+               dig_hotplug_reg &= ~mask;
+       }
+
        I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
+       if (!hotplug_trigger)
+               return;
 
        intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
                           dig_hotplug_reg, hpd,
@@ -1840,8 +1876,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
        int pipe;
        u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK;
 
-       if (hotplug_trigger)
-               ibx_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx);
+       ibx_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx);
 
        if (pch_iir & SDE_AUDIO_POWER_MASK) {
                int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >>
@@ -1934,8 +1969,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
        int pipe;
        u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT;
 
-       if (hotplug_trigger)
-               ibx_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt);
+       ibx_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt);
 
        if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) {
                int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
@@ -2351,13 +2385,9 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
                                spt_irq_handler(dev, pch_iir);
                        else
                                cpt_irq_handler(dev, pch_iir);
-               } else {
-                       /*
-                        * Like on previous PCH there seems to be something
-                        * fishy going on with forwarding PCH interrupts.
-                        */
-                       DRM_DEBUG_DRIVER("The master control interrupt lied (SDE)!\n");
-               }
+               } else
+                       DRM_ERROR("The master control interrupt lied (SDE)!\n");
+
        }
 
        I915_WRITE_FW(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
@@ -2645,7 +2675,7 @@ static int ironlake_enable_vblank(struct drm_device *dev, unsigned int pipe)
                                                     DE_PIPE_VBLANK(pipe);
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       ironlake_enable_display_irq(dev_priv, bit);
+       ilk_enable_display_irq(dev_priv, bit);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
        return 0;
@@ -2670,10 +2700,9 @@ static int gen8_enable_vblank(struct drm_device *dev, unsigned int pipe)
        unsigned long irqflags;
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_VBLANK;
-       I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
-       POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+       bdw_enable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
        return 0;
 }
 
@@ -2700,7 +2729,7 @@ static void ironlake_disable_vblank(struct drm_device *dev, unsigned int pipe)
                                                     DE_PIPE_VBLANK(pipe);
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       ironlake_disable_display_irq(dev_priv, bit);
+       ilk_disable_display_irq(dev_priv, bit);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
@@ -2721,9 +2750,7 @@ static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe)
        unsigned long irqflags;
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_VBLANK;
-       I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
-       POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+       bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
@@ -3452,7 +3479,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
                 * setup is guaranteed to run in single-threaded context. But we
                 * need it to make the assert_spin_locked happy. */
                spin_lock_irq(&dev_priv->irq_lock);
-               ironlake_enable_display_irq(dev_priv, DE_PCU_EVENT);
+               ilk_enable_display_irq(dev_priv, DE_PCU_EVENT);
                spin_unlock_irq(&dev_priv->irq_lock);
        }
 
index 1a12d44..206b213 100644 (file)
@@ -2972,6 +2972,13 @@ enum skl_disp_power_wells {
 #define OGAMC1                 _MMIO(0x30020)
 #define OGAMC0                 _MMIO(0x30024)
 
+/*
+ * GEN9 clock gating regs
+ */
+#define GEN9_CLKGATE_DIS_0             _MMIO(0x46530)
+#define   PWM2_GATING_DIS              (1 << 14)
+#define   PWM1_GATING_DIS              (1 << 13)
+
 /*
  * Display engine regs
  */
@@ -7549,6 +7556,7 @@ enum skl_disp_power_wells {
 #define SFUSE_STRAP                    _MMIO(0xc2014)
 #define  SFUSE_STRAP_FUSE_LOCK         (1<<13)
 #define  SFUSE_STRAP_DISPLAY_DISABLED  (1<<7)
+#define  SFUSE_STRAP_CRT_DISABLED      (1<<6)
 #define  SFUSE_STRAP_DDIB_DETECTED     (1<<2)
 #define  SFUSE_STRAP_DDIC_DETECTED     (1<<1)
 #define  SFUSE_STRAP_DDID_DETECTED     (1<<0)
@@ -7706,7 +7714,7 @@ enum skl_disp_power_wells {
 #define BXT_DSI_PLL_RATIO_MAX          0x7D
 #define BXT_DSI_PLL_RATIO_MIN          0x22
 #define BXT_DSI_PLL_RATIO_MASK         0xFF
-#define BXT_REF_CLOCK_KHZ              19500
+#define BXT_REF_CLOCK_KHZ              19200
 
 #define BXT_DSI_PLL_ENABLE             _MMIO(0x46080)
 #define  BXT_DSI_PLL_DO_ENABLE         (1 << 31)
index ce82f9c..070470f 100644 (file)
@@ -356,7 +356,10 @@ parse_general_features(struct drm_i915_private *dev_priv,
        general = find_section(bdb, BDB_GENERAL_FEATURES);
        if (general) {
                dev_priv->vbt.int_tv_support = general->int_tv_support;
-               dev_priv->vbt.int_crt_support = general->int_crt_support;
+               /* int_crt_support can't be trusted on earlier platforms */
+               if (bdb->version >= 155 &&
+                   (HAS_DDI(dev_priv) || IS_VALLEYVIEW(dev_priv)))
+                       dev_priv->vbt.int_crt_support = general->int_crt_support;
                dev_priv->vbt.lvds_use_ssc = general->enable_ssc;
                dev_priv->vbt.lvds_ssc_freq =
                        intel_bios_ssc_frequency(dev, general->ssc_freq);
index 27b3e61..9285fc1 100644 (file)
@@ -777,11 +777,37 @@ void intel_crt_init(struct drm_device *dev)
        struct intel_crt *crt;
        struct intel_connector *intel_connector;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       i915_reg_t adpa_reg;
+       u32 adpa;
 
        /* Skip machines without VGA that falsely report hotplug events */
        if (dmi_check_system(intel_no_crt))
                return;
 
+       if (HAS_PCH_SPLIT(dev))
+               adpa_reg = PCH_ADPA;
+       else if (IS_VALLEYVIEW(dev))
+               adpa_reg = VLV_ADPA;
+       else
+               adpa_reg = ADPA;
+
+       adpa = I915_READ(adpa_reg);
+       if ((adpa & ADPA_DAC_ENABLE) == 0) {
+               /*
+                * On some machines (some IVB at least) CRT can be
+                * fused off, but there's no known fuse bit to
+                * indicate that. On these machine the ADPA register
+                * works normally, except the DAC enable bit won't
+                * take. So the only way to tell is attempt to enable
+                * it and see what happens.
+                */
+               I915_WRITE(adpa_reg, adpa | ADPA_DAC_ENABLE |
+                          ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
+               if ((I915_READ(adpa_reg) & ADPA_DAC_ENABLE) == 0)
+                       return;
+               I915_WRITE(adpa_reg, adpa);
+       }
+
        crt = kzalloc(sizeof(struct intel_crt), GFP_KERNEL);
        if (!crt)
                return;
@@ -798,7 +824,7 @@ void intel_crt_init(struct drm_device *dev)
                           &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
 
        drm_encoder_init(dev, &crt->base.base, &intel_crt_enc_funcs,
-                        DRM_MODE_ENCODER_DAC);
+                        DRM_MODE_ENCODER_DAC, NULL);
 
        intel_connector_attach_encoder(intel_connector, &crt->base);
 
@@ -815,12 +841,7 @@ void intel_crt_init(struct drm_device *dev)
                connector->interlace_allowed = 1;
        connector->doublescan_allowed = 0;
 
-       if (HAS_PCH_SPLIT(dev))
-               crt->adpa_reg = PCH_ADPA;
-       else if (IS_VALLEYVIEW(dev))
-               crt->adpa_reg = VLV_ADPA;
-       else
-               crt->adpa_reg = ADPA;
+       crt->adpa_reg = adpa_reg;
 
        crt->base.compute_config = intel_crt_compute_config;
        if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev)) {
index 76ce7c2..4afb310 100644 (file)
@@ -3151,7 +3151,7 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
                pipe_config->has_hdmi_sink = true;
                intel_hdmi = enc_to_intel_hdmi(&encoder->base);
 
-               if (intel_hdmi->infoframe_enabled(&encoder->base))
+               if (intel_hdmi->infoframe_enabled(&encoder->base, pipe_config))
                        pipe_config->has_infoframe = true;
                break;
        case TRANS_DDI_MODE_SELECT_DVI:
@@ -3284,7 +3284,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
        encoder = &intel_encoder->base;
 
        drm_encoder_init(dev, encoder, &intel_ddi_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        intel_encoder->compute_config = intel_ddi_compute_config;
        intel_encoder->enable = intel_enable_ddi;
index 696f754..bda6b9c 100644 (file)
@@ -44,6 +44,8 @@
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_rect.h>
 #include <linux/dma_remapping.h>
+#include <linux/reservation.h>
+#include <linux/dma-buf.h>
 
 /* Primary plane formats for gen <= 3 */
 static const uint32_t i8xx_primary_formats[] = {
@@ -2130,7 +2132,7 @@ static void intel_enable_pipe(struct intel_crtc *crtc)
         * need the check.
         */
        if (HAS_GMCH_DISPLAY(dev_priv->dev))
-               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI))
+               if (crtc->config->has_dsi_encoder)
                        assert_dsi_pll_enabled(dev_priv);
                else
                        assert_pll_enabled(dev_priv, pipe);
@@ -3174,8 +3176,8 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (dev_priv->fbc.disable_fbc)
-               dev_priv->fbc.disable_fbc(dev_priv);
+       if (dev_priv->fbc.deactivate)
+               dev_priv->fbc.deactivate(dev_priv);
 
        dev_priv->display.update_primary_plane(crtc, fb, x, y);
 
@@ -4137,6 +4139,12 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
        I915_WRITE(FDI_RX_TUSIZE1(pipe),
                   I915_READ(PIPE_DATA_M1(pipe)) & TU_SIZE_MASK);
 
+       /*
+        * Sometimes spurious CPU pipe underruns happen during FDI
+        * training, at least with VGA+HDMI cloning. Suppress them.
+        */
+       intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
+
        /* For PCH output, training FDI link */
        dev_priv->display.fdi_link_train(crtc);
 
@@ -4170,6 +4178,8 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
 
        intel_fdi_normal_train(crtc);
 
+       intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
+
        /* For PCH DP, enable TRANS_DP_CTL */
        if (HAS_PCH_CPT(dev) && intel_crtc->config->has_dp_encoder) {
                const struct drm_display_mode *adjusted_mode =
@@ -4628,7 +4638,7 @@ static void intel_crtc_load_lut(struct drm_crtc *crtc)
                return;
 
        if (HAS_GMCH_DISPLAY(dev_priv->dev)) {
-               if (intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DSI))
+               if (intel_crtc->config->has_dsi_encoder)
                        assert_dsi_pll_enabled(dev_priv);
                else
                        assert_pll_enabled(dev_priv, pipe);
@@ -4784,7 +4794,6 @@ static void intel_post_plane_update(struct intel_crtc *crtc)
 {
        struct intel_crtc_atomic_commit *atomic = &crtc->atomic;
        struct drm_device *dev = crtc->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
 
        if (atomic->wait_vblank)
                intel_wait_for_vblank(dev, crtc->pipe);
@@ -4798,7 +4807,7 @@ static void intel_post_plane_update(struct intel_crtc *crtc)
                intel_update_watermarks(&crtc->base);
 
        if (atomic->update_fbc)
-               intel_fbc_update(dev_priv);
+               intel_fbc_update(crtc);
 
        if (atomic->post_enable_primary)
                intel_post_enable_primary(&crtc->base);
@@ -4813,7 +4822,7 @@ static void intel_pre_plane_update(struct intel_crtc *crtc)
        struct intel_crtc_atomic_commit *atomic = &crtc->atomic;
 
        if (atomic->disable_fbc)
-               intel_fbc_disable_crtc(crtc);
+               intel_fbc_deactivate(crtc);
 
        if (crtc->atomic.disable_ips)
                hsw_disable_ips(crtc);
@@ -4921,6 +4930,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        if (intel_crtc->config->has_pch_encoder)
                intel_wait_for_vblank(dev, pipe);
        intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true);
+
+       intel_fbc_enable(intel_crtc);
 }
 
 /* IPS only exists on ULT machines and is tied to pipe A. */
@@ -4938,7 +4949,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        int pipe = intel_crtc->pipe, hsw_workaround_pipe;
        struct intel_crtc_state *pipe_config =
                to_intel_crtc_state(crtc->state);
-       bool is_dsi = intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DSI);
 
        if (WARN_ON(intel_crtc->active))
                return;
@@ -4971,10 +4981,12 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
 
        intel_crtc->active = true;
 
-       intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
+       if (intel_crtc->config->has_pch_encoder)
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
+       else
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
+
        for_each_encoder_on_crtc(dev, crtc, encoder) {
-               if (encoder->pre_pll_enable)
-                       encoder->pre_pll_enable(encoder);
                if (encoder->pre_enable)
                        encoder->pre_enable(encoder);
        }
@@ -4982,7 +4994,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        if (intel_crtc->config->has_pch_encoder)
                dev_priv->display.fdi_link_train(crtc);
 
-       if (!is_dsi)
+       if (!intel_crtc->config->has_dsi_encoder)
                intel_ddi_enable_pipe_clock(intel_crtc);
 
        if (INTEL_INFO(dev)->gen >= 9)
@@ -4997,7 +5009,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        intel_crtc_load_lut(crtc);
 
        intel_ddi_set_pipe_settings(crtc);
-       if (!is_dsi)
+       if (!intel_crtc->config->has_dsi_encoder)
                intel_ddi_enable_transcoder_func(crtc);
 
        intel_update_watermarks(crtc);
@@ -5006,7 +5018,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        if (intel_crtc->config->has_pch_encoder)
                lpt_pch_enable(crtc);
 
-       if (intel_crtc->config->dp_encoder_is_mst && !is_dsi)
+       if (intel_crtc->config->dp_encoder_is_mst)
                intel_ddi_set_vc_payload_alloc(crtc, true);
 
        assert_vblank_disabled(crtc);
@@ -5017,9 +5029,13 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
                intel_opregion_notify_encoder(encoder, true);
        }
 
-       if (intel_crtc->config->has_pch_encoder)
+       if (intel_crtc->config->has_pch_encoder) {
+               intel_wait_for_vblank(dev, pipe);
+               intel_wait_for_vblank(dev, pipe);
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
                intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A,
                                                      true);
+       }
 
        /* If we change the relative order between pipe/planes enabling, we need
         * to change the workaround. */
@@ -5028,6 +5044,8 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
                intel_wait_for_vblank(dev, hsw_workaround_pipe);
                intel_wait_for_vblank(dev, hsw_workaround_pipe);
        }
+
+       intel_fbc_enable(intel_crtc);
 }
 
 static void ironlake_pfit_disable(struct intel_crtc *crtc, bool force)
@@ -5062,12 +5080,22 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        drm_crtc_vblank_off(crtc);
        assert_vblank_disabled(crtc);
 
+       /*
+        * Sometimes spurious CPU pipe underruns happen when the
+        * pipe is already disabled, but FDI RX/TX is still enabled.
+        * Happens at least with VGA+HDMI cloning. Suppress them.
+        */
+       if (intel_crtc->config->has_pch_encoder)
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
+
        intel_disable_pipe(intel_crtc);
 
        ironlake_pfit_disable(intel_crtc, false);
 
-       if (intel_crtc->config->has_pch_encoder)
+       if (intel_crtc->config->has_pch_encoder) {
                ironlake_fdi_disable(crtc);
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
+       }
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->post_disable)
@@ -5098,6 +5126,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        }
 
        intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true);
+
+       intel_fbc_disable_crtc(intel_crtc);
 }
 
 static void haswell_crtc_disable(struct drm_crtc *crtc)
@@ -5107,7 +5137,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *encoder;
        enum transcoder cpu_transcoder = intel_crtc->config->cpu_transcoder;
-       bool is_dsi = intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DSI);
 
        if (intel_crtc->config->has_pch_encoder)
                intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A,
@@ -5126,7 +5155,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        if (intel_crtc->config->dp_encoder_is_mst)
                intel_ddi_set_vc_payload_alloc(crtc, false);
 
-       if (!is_dsi)
+       if (!intel_crtc->config->has_dsi_encoder)
                intel_ddi_disable_transcoder_func(dev_priv, cpu_transcoder);
 
        if (INTEL_INFO(dev)->gen >= 9)
@@ -5134,7 +5163,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        else
                ironlake_pfit_disable(intel_crtc, false);
 
-       if (!is_dsi)
+       if (!intel_crtc->config->has_dsi_encoder)
                intel_ddi_disable_pipe_clock(intel_crtc);
 
        if (intel_crtc->config->has_pch_encoder) {
@@ -5149,6 +5178,8 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        if (intel_crtc->config->has_pch_encoder)
                intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A,
                                                      true);
+
+       intel_fbc_disable_crtc(intel_crtc);
 }
 
 static void i9xx_pfit_enable(struct intel_crtc *crtc)
@@ -5214,10 +5245,6 @@ static enum intel_display_power_domain port_to_aux_power_domain(enum port port)
        }
 }
 
-#define for_each_power_domain(domain, mask)                            \
-       for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++)     \
-               if ((1 << (domain)) & (mask))
-
 enum intel_display_power_domain
 intel_display_port_power_domain(struct intel_encoder *intel_encoder)
 {
@@ -6140,13 +6167,10 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
-       bool is_dsi;
 
        if (WARN_ON(intel_crtc->active))
                return;
 
-       is_dsi = intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DSI);
-
        if (intel_crtc->config->has_dp_encoder)
                intel_dp_set_m_n(intel_crtc, M1_N1);
 
@@ -6169,7 +6193,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
                if (encoder->pre_pll_enable)
                        encoder->pre_pll_enable(encoder);
 
-       if (!is_dsi) {
+       if (!intel_crtc->config->has_dsi_encoder) {
                if (IS_CHERRYVIEW(dev)) {
                        chv_prepare_pll(intel_crtc, intel_crtc->config);
                        chv_enable_pll(intel_crtc, intel_crtc->config);
@@ -6248,6 +6272,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                encoder->enable(encoder);
+
+       intel_fbc_enable(intel_crtc);
 }
 
 static void i9xx_pfit_disable(struct intel_crtc *crtc)
@@ -6295,7 +6321,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
                if (encoder->post_disable)
                        encoder->post_disable(encoder);
 
-       if (!intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DSI)) {
+       if (!intel_crtc->config->has_dsi_encoder) {
                if (IS_CHERRYVIEW(dev))
                        chv_disable_pll(dev_priv, pipe);
                else if (IS_VALLEYVIEW(dev))
@@ -6310,6 +6336,8 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
 
        if (!IS_GEN2(dev))
                intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
+
+       intel_fbc_disable_crtc(intel_crtc);
 }
 
 static void intel_crtc_disable_noatomic(struct drm_crtc *crtc)
@@ -7908,8 +7936,6 @@ static int i9xx_crtc_compute_clock(struct intel_crtc *crtc,
        int refclk, num_connectors = 0;
        intel_clock_t clock;
        bool ok;
-       bool is_dsi = false;
-       struct intel_encoder *encoder;
        const intel_limit_t *limit;
        struct drm_atomic_state *state = crtc_state->base.state;
        struct drm_connector *connector;
@@ -7919,26 +7945,14 @@ static int i9xx_crtc_compute_clock(struct intel_crtc *crtc,
        memset(&crtc_state->dpll_hw_state, 0,
               sizeof(crtc_state->dpll_hw_state));
 
-       for_each_connector_in_state(state, connector, connector_state, i) {
-               if (connector_state->crtc != &crtc->base)
-                       continue;
-
-               encoder = to_intel_encoder(connector_state->best_encoder);
-
-               switch (encoder->type) {
-               case INTEL_OUTPUT_DSI:
-                       is_dsi = true;
-                       break;
-               default:
-                       break;
-               }
+       if (crtc_state->has_dsi_encoder)
+               return 0;
 
-               num_connectors++;
+       for_each_connector_in_state(state, connector, connector_state, i) {
+               if (connector_state->crtc == &crtc->base)
+                       num_connectors++;
        }
 
-       if (is_dsi)
-               return 0;
-
        if (!crtc_state->clock_set) {
                refclk = i9xx_get_refclk(crtc_state, num_connectors);
 
@@ -8931,7 +8945,7 @@ static int ironlake_crtc_compute_clock(struct intel_crtc *crtc,
        memset(&crtc_state->dpll_hw_state, 0,
               sizeof(crtc_state->dpll_hw_state));
 
-       is_lvds = intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS);
+       is_lvds = intel_pipe_will_have_type(crtc_state, INTEL_OUTPUT_LVDS);
 
        WARN(!(HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)),
             "Unexpected PCH type %d\n", INTEL_PCH_TYPE(dev));
@@ -9705,14 +9719,10 @@ static int broadwell_modeset_calc_cdclk(struct drm_atomic_state *state)
        else
                cdclk = 337500;
 
-       /*
-        * FIXME move the cdclk caclulation to
-        * compute_config() so we can fail gracegully.
-        */
        if (cdclk > dev_priv->max_cdclk_freq) {
-               DRM_ERROR("requested cdclk (%d kHz) exceeds max (%d kHz)\n",
-                         cdclk, dev_priv->max_cdclk_freq);
-               cdclk = dev_priv->max_cdclk_freq;
+               DRM_DEBUG_KMS("requested cdclk (%d kHz) exceeds max (%d kHz)\n",
+                             cdclk, dev_priv->max_cdclk_freq);
+               return -EINVAL;
        }
 
        to_intel_atomic_state(state)->cdclk = cdclk;
@@ -9807,6 +9817,7 @@ static void haswell_get_ddi_pll(struct drm_i915_private *dev_priv,
                break;
        case PORT_CLK_SEL_SPLL:
                pipe_config->shared_dpll = DPLL_ID_SPLL;
+               break;
        }
 }
 
@@ -11191,6 +11202,10 @@ static bool use_mmio_flip(struct intel_engine_cs *ring,
                return true;
        else if (i915.enable_execlists)
                return true;
+       else if (obj->base.dma_buf &&
+                !reservation_object_test_signaled_rcu(obj->base.dma_buf->resv,
+                                                      false))
+               return true;
        else
                return ring != i915_gem_request_get_ring(obj->last_write_req);
 }
@@ -11305,6 +11320,9 @@ static void intel_mmio_flip_work_func(struct work_struct *work)
 {
        struct intel_mmio_flip *mmio_flip =
                container_of(work, struct intel_mmio_flip, work);
+       struct intel_framebuffer *intel_fb =
+               to_intel_framebuffer(mmio_flip->crtc->base.primary->fb);
+       struct drm_i915_gem_object *obj = intel_fb->obj;
 
        if (mmio_flip->req) {
                WARN_ON(__i915_wait_request(mmio_flip->req,
@@ -11314,6 +11332,12 @@ static void intel_mmio_flip_work_func(struct work_struct *work)
                i915_gem_request_unreference__unlocked(mmio_flip->req);
        }
 
+       /* For framebuffer backed by dmabuf, wait for fence */
+       if (obj->base.dma_buf)
+               WARN_ON(reservation_object_wait_timeout_rcu(obj->base.dma_buf->resv,
+                                                           false, false,
+                                                           MAX_SCHEDULE_TIMEOUT) < 0);
+
        intel_do_mmio_flip(mmio_flip);
        kfree(mmio_flip);
 }
@@ -11584,7 +11608,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
                          to_intel_plane(primary)->frontbuffer_bit);
        mutex_unlock(&dev->struct_mutex);
 
-       intel_fbc_disable_crtc(intel_crtc);
+       intel_fbc_deactivate(intel_crtc);
        intel_frontbuffer_flip_prepare(dev,
                                       to_intel_plane(primary)->frontbuffer_bit);
 
@@ -12587,6 +12611,8 @@ intel_pipe_config_compare(struct drm_device *dev,
        } else
                PIPE_CONF_CHECK_M_N_ALT(dp_m_n, dp_m2_n2);
 
+       PIPE_CONF_CHECK_I(has_dsi_encoder);
+
        PIPE_CONF_CHECK_I(base.adjusted_mode.crtc_hdisplay);
        PIPE_CONF_CHECK_I(base.adjusted_mode.crtc_htotal);
        PIPE_CONF_CHECK_I(base.adjusted_mode.crtc_hblank_start);
@@ -13383,6 +13409,13 @@ static int intel_atomic_commit(struct drm_device *dev,
                        dev_priv->display.crtc_disable(crtc);
                        intel_crtc->active = false;
                        intel_disable_shared_dpll(intel_crtc);
+
+                       /*
+                        * Underruns don't always raise
+                        * interrupts, so check manually.
+                        */
+                       intel_check_cpu_fifo_underruns(dev_priv);
+                       intel_check_pch_fifo_underruns(dev_priv);
                }
        }
 
@@ -13652,6 +13685,17 @@ intel_prepare_plane_fb(struct drm_plane *plane,
                        return ret;
        }
 
+       /* For framebuffer backed by dmabuf, wait for fence */
+       if (obj && obj->base.dma_buf) {
+               ret = reservation_object_wait_timeout_rcu(obj->base.dma_buf->resv,
+                                                         false, true,
+                                                         MAX_SCHEDULE_TIMEOUT);
+               if (ret == -ERESTARTSYS)
+                       return ret;
+
+               WARN_ON(ret < 0);
+       }
+
        if (!obj) {
                ret = 0;
        } else if (plane->type == DRM_PLANE_TYPE_CURSOR &&
@@ -13904,7 +13948,7 @@ static struct drm_plane *intel_primary_plane_create(struct drm_device *dev,
        drm_universal_plane_init(dev, &primary->base, 0,
                                 &intel_plane_funcs,
                                 intel_primary_formats, num_formats,
-                                DRM_PLANE_TYPE_PRIMARY);
+                                DRM_PLANE_TYPE_PRIMARY, NULL);
 
        if (INTEL_INFO(dev)->gen >= 4)
                intel_create_rotation_property(dev, primary);
@@ -14043,7 +14087,7 @@ static struct drm_plane *intel_cursor_plane_create(struct drm_device *dev,
                                 &intel_plane_funcs,
                                 intel_cursor_formats,
                                 ARRAY_SIZE(intel_cursor_formats),
-                                DRM_PLANE_TYPE_CURSOR);
+                                DRM_PLANE_TYPE_CURSOR, NULL);
 
        if (INTEL_INFO(dev)->gen >= 4) {
                if (!dev->mode_config.rotation_property)
@@ -14120,7 +14164,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
                goto fail;
 
        ret = drm_crtc_init_with_planes(dev, &intel_crtc->base, primary,
-                                       cursor, &intel_crtc_funcs);
+                                       cursor, &intel_crtc_funcs, NULL);
        if (ret)
                goto fail;
 
@@ -14246,7 +14290,14 @@ static bool intel_crt_present(struct drm_device *dev)
        if (IS_CHERRYVIEW(dev))
                return false;
 
-       if (IS_VALLEYVIEW(dev) && !dev_priv->vbt.int_crt_support)
+       if (HAS_PCH_LPT_H(dev) && I915_READ(SFUSE_STRAP) & SFUSE_STRAP_CRT_DISABLED)
+               return false;
+
+       /* DDI E can't be used if DDI A requires 4 lanes */
+       if (HAS_DDI(dev) && I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES)
+               return false;
+
+       if (!dev_priv->vbt.int_crt_support)
                return false;
 
        return true;
@@ -14789,9 +14840,6 @@ static void intel_init_display(struct drm_device *dev)
        else if (IS_I945GM(dev) || IS_845G(dev))
                dev_priv->display.get_display_clock_speed =
                        i9xx_misc_get_display_clock_speed;
-       else if (IS_PINEVIEW(dev))
-               dev_priv->display.get_display_clock_speed =
-                       pnv_get_display_clock_speed;
        else if (IS_I915GM(dev))
                dev_priv->display.get_display_clock_speed =
                        i915gm_get_display_clock_speed;
index e1ceff7..0f0573a 100644 (file)
@@ -681,7 +681,7 @@ static uint32_t i9xx_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
         * The clock divider is based off the hrawclk, and would like to run at
         * 2MHz.  So, take the hrawclk value and divide by 2 and use that
         */
-       return index ? 0 : intel_hrawclk(dev) / 2;
+       return index ? 0 : DIV_ROUND_CLOSEST(intel_hrawclk(dev), 2);
 }
 
 static uint32_t ilk_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
@@ -694,10 +694,10 @@ static uint32_t ilk_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
                return 0;
 
        if (intel_dig_port->port == PORT_A) {
-               return DIV_ROUND_UP(dev_priv->cdclk_freq, 2000);
+               return DIV_ROUND_CLOSEST(dev_priv->cdclk_freq, 2000);
 
        } else {
-               return DIV_ROUND_UP(intel_pch_rawclk(dev), 2);
+               return DIV_ROUND_CLOSEST(intel_pch_rawclk(dev), 2);
        }
 }
 
@@ -711,7 +711,7 @@ static uint32_t hsw_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
                if (index)
                        return 0;
                return DIV_ROUND_CLOSEST(dev_priv->cdclk_freq, 2000);
-       } else if (dev_priv->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE) {
+       } else if (HAS_PCH_LPT_H(dev_priv)) {
                /* Workaround for non-ULT HSW */
                switch (index) {
                case 0: return 63;
@@ -719,7 +719,7 @@ static uint32_t hsw_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
                default: return 0;
                }
        } else  {
-               return index ? 0 : DIV_ROUND_UP(intel_pch_rawclk(dev), 2);
+               return index ? 0 : DIV_ROUND_CLOSEST(intel_pch_rawclk(dev), 2);
        }
 }
 
@@ -2697,6 +2697,15 @@ static void intel_enable_dp(struct intel_encoder *encoder)
        if (IS_VALLEYVIEW(dev))
                vlv_init_panel_power_sequencer(intel_dp);
 
+       /*
+        * We get an occasional spurious underrun between the port
+        * enable and vdd enable, when enabling port A eDP.
+        *
+        * FIXME: Not sure if this applies to (PCH) port D eDP as well
+        */
+       if (port == PORT_A)
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
+
        intel_dp_enable_port(intel_dp);
 
        if (port == PORT_A && IS_GEN5(dev_priv)) {
@@ -2714,6 +2723,9 @@ static void intel_enable_dp(struct intel_encoder *encoder)
        edp_panel_on(intel_dp);
        edp_panel_vdd_off(intel_dp, true);
 
+       if (port == PORT_A)
+               intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
+
        pps_unlock(intel_dp);
 
        if (IS_VALLEYVIEW(dev)) {
@@ -5977,7 +5989,7 @@ intel_dp_init(struct drm_device *dev,
        encoder = &intel_encoder->base;
 
        drm_encoder_init(dev, &intel_encoder->base, &intel_dp_enc_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        intel_encoder->compute_config = intel_dp_compute_config;
        intel_encoder->disable = intel_disable_dp;
index 8c4e7df..e8d369d 100644 (file)
@@ -536,7 +536,7 @@ intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum
        intel_mst->primary = intel_dig_port;
 
        drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
-                        DRM_MODE_ENCODER_DPMST);
+                        DRM_MODE_ENCODER_DPMST, NULL);
 
        intel_encoder->type = INTEL_OUTPUT_DP_MST;
        intel_encoder->crtc_mask = 0x7;
index ab5c147..50f83d2 100644 (file)
@@ -393,6 +393,9 @@ struct intel_crtc_state {
         * accordingly. */
        bool has_dp_encoder;
 
+       /* DSI has special cases */
+       bool has_dsi_encoder;
+
        /* Whether we should send NULL infoframes. Required for audio. */
        bool has_hdmi_sink;
 
@@ -710,7 +713,8 @@ struct intel_hdmi {
        void (*set_infoframes)(struct drm_encoder *encoder,
                               bool enable,
                               const struct drm_display_mode *adjusted_mode);
-       bool (*infoframe_enabled)(struct drm_encoder *encoder);
+       bool (*infoframe_enabled)(struct drm_encoder *encoder,
+                                 const struct intel_crtc_state *pipe_config);
 };
 
 struct intel_dp_mst_encoder;
@@ -1316,9 +1320,11 @@ static inline void intel_fbdev_restore_mode(struct drm_device *dev)
 #endif
 
 /* intel_fbc.c */
-bool intel_fbc_enabled(struct drm_i915_private *dev_priv);
-void intel_fbc_update(struct drm_i915_private *dev_priv);
+bool intel_fbc_is_active(struct drm_i915_private *dev_priv);
+void intel_fbc_deactivate(struct intel_crtc *crtc);
+void intel_fbc_update(struct intel_crtc *crtc);
 void intel_fbc_init(struct drm_i915_private *dev_priv);
+void intel_fbc_enable(struct intel_crtc *crtc);
 void intel_fbc_disable(struct drm_i915_private *dev_priv);
 void intel_fbc_disable_crtc(struct intel_crtc *crtc);
 void intel_fbc_invalidate(struct drm_i915_private *dev_priv,
@@ -1410,6 +1416,8 @@ void intel_power_domains_suspend(struct drm_i915_private *dev_priv);
 void skl_pw1_misc_io_init(struct drm_i915_private *dev_priv);
 void skl_pw1_misc_io_fini(struct drm_i915_private *dev_priv);
 void intel_runtime_pm_enable(struct drm_i915_private *dev_priv);
+const char *
+intel_display_power_domain_str(enum intel_display_power_domain domain);
 
 bool intel_display_power_is_enabled(struct drm_i915_private *dev_priv,
                                    enum intel_display_power_domain domain);
index efb5a27..fff9a66 100644 (file)
@@ -266,16 +266,18 @@ static inline bool is_cmd_mode(struct intel_dsi *intel_dsi)
 }
 
 static bool intel_dsi_compute_config(struct intel_encoder *encoder,
-                                    struct intel_crtc_state *config)
+                                    struct intel_crtc_state *pipe_config)
 {
        struct intel_dsi *intel_dsi = container_of(encoder, struct intel_dsi,
                                                   base);
        struct intel_connector *intel_connector = intel_dsi->attached_connector;
        struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
-       struct drm_display_mode *adjusted_mode = &config->base.adjusted_mode;
+       struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
 
        DRM_DEBUG_KMS("\n");
 
+       pipe_config->has_dsi_encoder = true;
+
        if (fixed_mode)
                intel_fixed_panel_mode(fixed_mode, adjusted_mode);
 
@@ -462,6 +464,8 @@ static void intel_dsi_enable(struct intel_encoder *encoder)
        intel_panel_enable_backlight(intel_dsi->attached_connector);
 }
 
+static void intel_dsi_prepare(struct intel_encoder *intel_encoder);
+
 static void intel_dsi_pre_enable(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
@@ -474,6 +478,9 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
 
        DRM_DEBUG_KMS("\n");
 
+       intel_dsi_prepare(encoder);
+       intel_enable_dsi_pll(encoder);
+
        /* Panel Enable over CRC PMIC */
        if (intel_dsi->gpio_panel)
                gpiod_set_value_cansleep(intel_dsi->gpio_panel, 1);
@@ -699,6 +706,8 @@ static void intel_dsi_get_config(struct intel_encoder *encoder,
        u32 pclk = 0;
        DRM_DEBUG_KMS("\n");
 
+       pipe_config->has_dsi_encoder = true;
+
        /*
         * DPLL_MD is not used in case of DSI, reading will get some default value
         * set dpll_md = 0
@@ -1026,15 +1035,6 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder)
        }
 }
 
-static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder)
-{
-       DRM_DEBUG_KMS("\n");
-
-       intel_dsi_prepare(encoder);
-       intel_enable_dsi_pll(encoder);
-
-}
-
 static enum drm_connector_status
 intel_dsi_detect(struct drm_connector *connector, bool force)
 {
@@ -1152,11 +1152,10 @@ void intel_dsi_init(struct drm_device *dev)
 
        connector = &intel_connector->base;
 
-       drm_encoder_init(dev, encoder, &intel_dsi_funcs, DRM_MODE_ENCODER_DSI);
+       drm_encoder_init(dev, encoder, &intel_dsi_funcs, DRM_MODE_ENCODER_DSI,
+                        NULL);
 
-       /* XXX: very likely not all of these are needed */
        intel_encoder->compute_config = intel_dsi_compute_config;
-       intel_encoder->pre_pll_enable = intel_dsi_pre_pll_enable;
        intel_encoder->pre_enable = intel_dsi_pre_enable;
        intel_encoder->enable = intel_dsi_enable_nop;
        intel_encoder->disable = intel_dsi_pre_disable;
index 7161deb..286baec 100644 (file)
@@ -429,7 +429,7 @@ void intel_dvo_init(struct drm_device *dev)
 
        intel_encoder = &intel_dvo->base;
        drm_encoder_init(dev, &intel_encoder->base,
-                        &intel_dvo_enc_funcs, encoder_type);
+                        &intel_dvo_enc_funcs, encoder_type, NULL);
 
        intel_encoder->disable = intel_disable_dvo;
        intel_encoder->enable = intel_enable_dvo;
index 11fc528..a1988a4 100644 (file)
@@ -43,7 +43,7 @@
 
 static inline bool fbc_supported(struct drm_i915_private *dev_priv)
 {
-       return dev_priv->fbc.enable_fbc != NULL;
+       return dev_priv->fbc.activate != NULL;
 }
 
 static inline bool fbc_on_pipe_a_only(struct drm_i915_private *dev_priv)
@@ -51,6 +51,11 @@ static inline bool fbc_on_pipe_a_only(struct drm_i915_private *dev_priv)
        return IS_HASWELL(dev_priv) || INTEL_INFO(dev_priv)->gen >= 8;
 }
 
+static inline bool fbc_on_plane_a_only(struct drm_i915_private *dev_priv)
+{
+       return INTEL_INFO(dev_priv)->gen < 4;
+}
+
 /*
  * In some platforms where the CRTC's x:0/y:0 coordinates doesn't match the
  * frontbuffer's x:0/y:0 coordinates we lie to the hardware about the plane's
@@ -64,11 +69,51 @@ static unsigned int get_crtc_fence_y_offset(struct intel_crtc *crtc)
        return crtc->base.y - crtc->adjusted_y;
 }
 
-static void i8xx_fbc_disable(struct drm_i915_private *dev_priv)
+/*
+ * For SKL+, the plane source size used by the hardware is based on the value we
+ * write to the PLANE_SIZE register. For BDW-, the hardware looks at the value
+ * we wrote to PIPESRC.
+ */
+static void intel_fbc_get_plane_source_size(struct intel_crtc *crtc,
+                                           int *width, int *height)
+{
+       struct intel_plane_state *plane_state =
+                       to_intel_plane_state(crtc->base.primary->state);
+       int w, h;
+
+       if (intel_rotation_90_or_270(plane_state->base.rotation)) {
+               w = drm_rect_height(&plane_state->src) >> 16;
+               h = drm_rect_width(&plane_state->src) >> 16;
+       } else {
+               w = drm_rect_width(&plane_state->src) >> 16;
+               h = drm_rect_height(&plane_state->src) >> 16;
+       }
+
+       if (width)
+               *width = w;
+       if (height)
+               *height = h;
+}
+
+static int intel_fbc_calculate_cfb_size(struct intel_crtc *crtc,
+                                       struct drm_framebuffer *fb)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       int lines;
+
+       intel_fbc_get_plane_source_size(crtc, NULL, &lines);
+       if (INTEL_INFO(dev_priv)->gen >= 7)
+               lines = min(lines, 2048);
+
+       /* Hardware needs the full buffer stride, not just the active area. */
+       return lines * fb->pitches[0];
+}
+
+static void i8xx_fbc_deactivate(struct drm_i915_private *dev_priv)
 {
        u32 fbc_ctl;
 
-       dev_priv->fbc.enabled = false;
+       dev_priv->fbc.active = false;
 
        /* Disable compression */
        fbc_ctl = I915_READ(FBC_CONTROL);
@@ -83,11 +128,9 @@ static void i8xx_fbc_disable(struct drm_i915_private *dev_priv)
                DRM_DEBUG_KMS("FBC idle timed out\n");
                return;
        }
-
-       DRM_DEBUG_KMS("disabled FBC\n");
 }
 
-static void i8xx_fbc_enable(struct intel_crtc *crtc)
+static void i8xx_fbc_activate(struct intel_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
        struct drm_framebuffer *fb = crtc->base.primary->fb;
@@ -96,10 +139,10 @@ static void i8xx_fbc_enable(struct intel_crtc *crtc)
        int i;
        u32 fbc_ctl;
 
-       dev_priv->fbc.enabled = true;
+       dev_priv->fbc.active = true;
 
        /* Note: fbc.threshold == 1 for i8xx */
-       cfb_pitch = dev_priv->fbc.uncompressed_size / FBC_LL_SIZE;
+       cfb_pitch = intel_fbc_calculate_cfb_size(crtc, fb) / FBC_LL_SIZE;
        if (fb->pitches[0] < cfb_pitch)
                cfb_pitch = fb->pitches[0];
 
@@ -132,24 +175,21 @@ static void i8xx_fbc_enable(struct intel_crtc *crtc)
        fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
        fbc_ctl |= obj->fence_reg;
        I915_WRITE(FBC_CONTROL, fbc_ctl);
-
-       DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %c\n",
-                     cfb_pitch, crtc->base.y, plane_name(crtc->plane));
 }
 
-static bool i8xx_fbc_enabled(struct drm_i915_private *dev_priv)
+static bool i8xx_fbc_is_active(struct drm_i915_private *dev_priv)
 {
        return I915_READ(FBC_CONTROL) & FBC_CTL_EN;
 }
 
-static void g4x_fbc_enable(struct intel_crtc *crtc)
+static void g4x_fbc_activate(struct intel_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
        struct drm_framebuffer *fb = crtc->base.primary->fb;
        struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        u32 dpfc_ctl;
 
-       dev_priv->fbc.enabled = true;
+       dev_priv->fbc.active = true;
 
        dpfc_ctl = DPFC_CTL_PLANE(crtc->plane) | DPFC_SR_EN;
        if (drm_format_plane_cpp(fb->pixel_format, 0) == 2)
@@ -162,27 +202,23 @@ static void g4x_fbc_enable(struct intel_crtc *crtc)
 
        /* enable it... */
        I915_WRITE(DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
-
-       DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(crtc->plane));
 }
 
-static void g4x_fbc_disable(struct drm_i915_private *dev_priv)
+static void g4x_fbc_deactivate(struct drm_i915_private *dev_priv)
 {
        u32 dpfc_ctl;
 
-       dev_priv->fbc.enabled = false;
+       dev_priv->fbc.active = false;
 
        /* Disable compression */
        dpfc_ctl = I915_READ(DPFC_CONTROL);
        if (dpfc_ctl & DPFC_CTL_EN) {
                dpfc_ctl &= ~DPFC_CTL_EN;
                I915_WRITE(DPFC_CONTROL, dpfc_ctl);
-
-               DRM_DEBUG_KMS("disabled FBC\n");
        }
 }
 
-static bool g4x_fbc_enabled(struct drm_i915_private *dev_priv)
+static bool g4x_fbc_is_active(struct drm_i915_private *dev_priv)
 {
        return I915_READ(DPFC_CONTROL) & DPFC_CTL_EN;
 }
@@ -194,7 +230,7 @@ static void intel_fbc_recompress(struct drm_i915_private *dev_priv)
        POSTING_READ(MSG_FBC_REND_STATE);
 }
 
-static void ilk_fbc_enable(struct intel_crtc *crtc)
+static void ilk_fbc_activate(struct intel_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
        struct drm_framebuffer *fb = crtc->base.primary->fb;
@@ -203,7 +239,7 @@ static void ilk_fbc_enable(struct intel_crtc *crtc)
        int threshold = dev_priv->fbc.threshold;
        unsigned int y_offset;
 
-       dev_priv->fbc.enabled = true;
+       dev_priv->fbc.active = true;
 
        dpfc_ctl = DPFC_CTL_PLANE(crtc->plane);
        if (drm_format_plane_cpp(fb->pixel_format, 0) == 2)
@@ -238,32 +274,28 @@ static void ilk_fbc_enable(struct intel_crtc *crtc)
        }
 
        intel_fbc_recompress(dev_priv);
-
-       DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(crtc->plane));
 }
 
-static void ilk_fbc_disable(struct drm_i915_private *dev_priv)
+static void ilk_fbc_deactivate(struct drm_i915_private *dev_priv)
 {
        u32 dpfc_ctl;
 
-       dev_priv->fbc.enabled = false;
+       dev_priv->fbc.active = false;
 
        /* Disable compression */
        dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
        if (dpfc_ctl & DPFC_CTL_EN) {
                dpfc_ctl &= ~DPFC_CTL_EN;
                I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
-
-               DRM_DEBUG_KMS("disabled FBC\n");
        }
 }
 
-static bool ilk_fbc_enabled(struct drm_i915_private *dev_priv)
+static bool ilk_fbc_is_active(struct drm_i915_private *dev_priv)
 {
        return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN;
 }
 
-static void gen7_fbc_enable(struct intel_crtc *crtc)
+static void gen7_fbc_activate(struct intel_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
        struct drm_framebuffer *fb = crtc->base.primary->fb;
@@ -271,7 +303,7 @@ static void gen7_fbc_enable(struct intel_crtc *crtc)
        u32 dpfc_ctl;
        int threshold = dev_priv->fbc.threshold;
 
-       dev_priv->fbc.enabled = true;
+       dev_priv->fbc.active = true;
 
        dpfc_ctl = 0;
        if (IS_IVYBRIDGE(dev_priv))
@@ -317,153 +349,119 @@ static void gen7_fbc_enable(struct intel_crtc *crtc)
        I915_WRITE(DPFC_CPU_FENCE_OFFSET, get_crtc_fence_y_offset(crtc));
 
        intel_fbc_recompress(dev_priv);
-
-       DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(crtc->plane));
 }
 
 /**
- * intel_fbc_enabled - Is FBC enabled?
+ * intel_fbc_is_active - Is FBC active?
  * @dev_priv: i915 device instance
  *
  * This function is used to verify the current state of FBC.
  * FIXME: This should be tracked in the plane config eventually
  *        instead of queried at runtime for most callers.
  */
-bool intel_fbc_enabled(struct drm_i915_private *dev_priv)
+bool intel_fbc_is_active(struct drm_i915_private *dev_priv)
 {
-       return dev_priv->fbc.enabled;
+       return dev_priv->fbc.active;
 }
 
-static void intel_fbc_enable(struct intel_crtc *crtc,
-                            const struct drm_framebuffer *fb)
+static void intel_fbc_activate(const struct drm_framebuffer *fb)
 {
-       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct drm_i915_private *dev_priv = fb->dev->dev_private;
+       struct intel_crtc *crtc = dev_priv->fbc.crtc;
 
-       dev_priv->fbc.enable_fbc(crtc);
+       dev_priv->fbc.activate(crtc);
 
-       dev_priv->fbc.crtc = crtc;
        dev_priv->fbc.fb_id = fb->base.id;
        dev_priv->fbc.y = crtc->base.y;
 }
 
 static void intel_fbc_work_fn(struct work_struct *__work)
 {
-       struct intel_fbc_work *work =
-               container_of(to_delayed_work(__work),
-                            struct intel_fbc_work, work);
-       struct drm_i915_private *dev_priv = work->crtc->base.dev->dev_private;
-       struct drm_framebuffer *crtc_fb = work->crtc->base.primary->fb;
+       struct drm_i915_private *dev_priv =
+               container_of(__work, struct drm_i915_private, fbc.work.work);
+       struct intel_fbc_work *work = &dev_priv->fbc.work;
+       struct intel_crtc *crtc = dev_priv->fbc.crtc;
+       int delay_ms = 50;
+
+retry:
+       /* Delay the actual enabling to let pageflipping cease and the
+        * display to settle before starting the compression. Note that
+        * this delay also serves a second purpose: it allows for a
+        * vblank to pass after disabling the FBC before we attempt
+        * to modify the control registers.
+        *
+        * A more complicated solution would involve tracking vblanks
+        * following the termination of the page-flipping sequence
+        * and indeed performing the enable as a co-routine and not
+        * waiting synchronously upon the vblank.
+        *
+        * WaFbcWaitForVBlankBeforeEnable:ilk,snb
+        */
+       wait_remaining_ms_from_jiffies(work->enable_jiffies, delay_ms);
 
        mutex_lock(&dev_priv->fbc.lock);
-       if (work == dev_priv->fbc.fbc_work) {
-               /* Double check that we haven't switched fb without cancelling
-                * the prior work.
-                */
-               if (crtc_fb == work->fb)
-                       intel_fbc_enable(work->crtc, work->fb);
 
-               dev_priv->fbc.fbc_work = NULL;
+       /* Were we cancelled? */
+       if (!work->scheduled)
+               goto out;
+
+       /* Were we delayed again while this function was sleeping? */
+       if (time_after(work->enable_jiffies + msecs_to_jiffies(delay_ms),
+                      jiffies)) {
+               mutex_unlock(&dev_priv->fbc.lock);
+               goto retry;
        }
-       mutex_unlock(&dev_priv->fbc.lock);
 
-       kfree(work);
+       if (crtc->base.primary->fb == work->fb)
+               intel_fbc_activate(work->fb);
+
+       work->scheduled = false;
+
+out:
+       mutex_unlock(&dev_priv->fbc.lock);
 }
 
 static void intel_fbc_cancel_work(struct drm_i915_private *dev_priv)
 {
        WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock));
-
-       if (dev_priv->fbc.fbc_work == NULL)
-               return;
-
-       /* Synchronisation is provided by struct_mutex and checking of
-        * dev_priv->fbc.fbc_work, so we can perform the cancellation
-        * entirely asynchronously.
-        */
-       if (cancel_delayed_work(&dev_priv->fbc.fbc_work->work))
-               /* tasklet was killed before being run, clean up */
-               kfree(dev_priv->fbc.fbc_work);
-
-       /* Mark the work as no longer wanted so that if it does
-        * wake-up (because the work was already running and waiting
-        * for our mutex), it will discover that is no longer
-        * necessary to run.
-        */
-       dev_priv->fbc.fbc_work = NULL;
+       dev_priv->fbc.work.scheduled = false;
 }
 
-static void intel_fbc_schedule_enable(struct intel_crtc *crtc)
+static void intel_fbc_schedule_activation(struct intel_crtc *crtc)
 {
-       struct intel_fbc_work *work;
        struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct intel_fbc_work *work = &dev_priv->fbc.work;
 
        WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock));
 
-       intel_fbc_cancel_work(dev_priv);
-
-       work = kzalloc(sizeof(*work), GFP_KERNEL);
-       if (work == NULL) {
-               DRM_ERROR("Failed to allocate FBC work structure\n");
-               intel_fbc_enable(crtc, crtc->base.primary->fb);
-               return;
-       }
-
-       work->crtc = crtc;
+       /* It is useless to call intel_fbc_cancel_work() in this function since
+        * we're not releasing fbc.lock, so it won't have an opportunity to grab
+        * it to discover that it was cancelled. So we just update the expected
+        * jiffy count. */
        work->fb = crtc->base.primary->fb;
-       INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
-
-       dev_priv->fbc.fbc_work = work;
+       work->scheduled = true;
+       work->enable_jiffies = jiffies;
 
-       /* Delay the actual enabling to let pageflipping cease and the
-        * display to settle before starting the compression. Note that
-        * this delay also serves a second purpose: it allows for a
-        * vblank to pass after disabling the FBC before we attempt
-        * to modify the control registers.
-        *
-        * A more complicated solution would involve tracking vblanks
-        * following the termination of the page-flipping sequence
-        * and indeed performing the enable as a co-routine and not
-        * waiting synchronously upon the vblank.
-        *
-        * WaFbcWaitForVBlankBeforeEnable:ilk,snb
-        */
-       schedule_delayed_work(&work->work, msecs_to_jiffies(50));
+       schedule_work(&work->work);
 }
 
-static void __intel_fbc_disable(struct drm_i915_private *dev_priv)
+static void __intel_fbc_deactivate(struct drm_i915_private *dev_priv)
 {
        WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock));
 
        intel_fbc_cancel_work(dev_priv);
 
-       if (dev_priv->fbc.enabled)
-               dev_priv->fbc.disable_fbc(dev_priv);
-       dev_priv->fbc.crtc = NULL;
-}
-
-/**
- * intel_fbc_disable - disable FBC
- * @dev_priv: i915 device instance
- *
- * This function disables FBC.
- */
-void intel_fbc_disable(struct drm_i915_private *dev_priv)
-{
-       if (!fbc_supported(dev_priv))
-               return;
-
-       mutex_lock(&dev_priv->fbc.lock);
-       __intel_fbc_disable(dev_priv);
-       mutex_unlock(&dev_priv->fbc.lock);
+       if (dev_priv->fbc.active)
+               dev_priv->fbc.deactivate(dev_priv);
 }
 
 /*
- * intel_fbc_disable_crtc - disable FBC if it's associated with crtc
+ * intel_fbc_deactivate - deactivate FBC if it's associated with crtc
  * @crtc: the CRTC
  *
- * This function disables FBC if it's associated with the provided CRTC.
+ * This function deactivates FBC if it's associated with the provided CRTC.
  */
-void intel_fbc_disable_crtc(struct intel_crtc *crtc)
+void intel_fbc_deactivate(struct intel_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
 
@@ -472,7 +470,7 @@ void intel_fbc_disable_crtc(struct intel_crtc *crtc)
 
        mutex_lock(&dev_priv->fbc.lock);
        if (dev_priv->fbc.crtc == crtc)
-               __intel_fbc_disable(dev_priv);
+               __intel_fbc_deactivate(dev_priv);
        mutex_unlock(&dev_priv->fbc.lock);
 }
 
@@ -486,38 +484,28 @@ static void set_no_fbc_reason(struct drm_i915_private *dev_priv,
        DRM_DEBUG_KMS("Disabling FBC: %s\n", reason);
 }
 
-static bool crtc_is_valid(struct intel_crtc *crtc)
+static bool crtc_can_fbc(struct intel_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
 
        if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe != PIPE_A)
                return false;
 
-       if (!intel_crtc_active(&crtc->base))
-               return false;
-
-       if (!to_intel_plane_state(crtc->base.primary->state)->visible)
+       if (fbc_on_plane_a_only(dev_priv) && crtc->plane != PLANE_A)
                return false;
 
        return true;
 }
 
-static struct drm_crtc *intel_fbc_find_crtc(struct drm_i915_private *dev_priv)
+static bool crtc_is_valid(struct intel_crtc *crtc)
 {
-       struct drm_crtc *crtc = NULL, *tmp_crtc;
-       enum pipe pipe;
-
-       for_each_pipe(dev_priv, pipe) {
-               tmp_crtc = dev_priv->pipe_to_crtc_mapping[pipe];
-
-               if (crtc_is_valid(to_intel_crtc(tmp_crtc)))
-                       crtc = tmp_crtc;
-       }
+       if (!intel_crtc_active(&crtc->base))
+               return false;
 
-       if (!crtc)
-               return NULL;
+       if (!to_intel_plane_state(crtc->base.primary->state)->visible)
+               return false;
 
-       return crtc;
+       return true;
 }
 
 static bool multiple_pipes_ok(struct drm_i915_private *dev_priv)
@@ -590,11 +578,17 @@ again:
        }
 }
 
-static int intel_fbc_alloc_cfb(struct drm_i915_private *dev_priv, int size,
-                              int fb_cpp)
+static int intel_fbc_alloc_cfb(struct intel_crtc *crtc)
 {
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct drm_framebuffer *fb = crtc->base.primary->state->fb;
        struct drm_mm_node *uninitialized_var(compressed_llb);
-       int ret;
+       int size, fb_cpp, ret;
+
+       WARN_ON(drm_mm_node_allocated(&dev_priv->fbc.compressed_fb));
+
+       size = intel_fbc_calculate_cfb_size(crtc, fb);
+       fb_cpp = drm_format_plane_cpp(fb->pixel_format, 0);
 
        ret = find_compression_threshold(dev_priv, &dev_priv->fbc.compressed_fb,
                                         size, fb_cpp);
@@ -629,8 +623,6 @@ static int intel_fbc_alloc_cfb(struct drm_i915_private *dev_priv, int size,
                           dev_priv->mm.stolen_base + compressed_llb->start);
        }
 
-       dev_priv->fbc.uncompressed_size = size;
-
        DRM_DEBUG_KMS("reserved %llu bytes of contiguous stolen space for FBC, threshold: %d\n",
                      dev_priv->fbc.compressed_fb.size,
                      dev_priv->fbc.threshold);
@@ -647,18 +639,15 @@ err_llb:
 
 static void __intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv)
 {
-       if (dev_priv->fbc.uncompressed_size == 0)
-               return;
-
-       i915_gem_stolen_remove_node(dev_priv, &dev_priv->fbc.compressed_fb);
+       if (drm_mm_node_allocated(&dev_priv->fbc.compressed_fb))
+               i915_gem_stolen_remove_node(dev_priv,
+                                           &dev_priv->fbc.compressed_fb);
 
        if (dev_priv->fbc.compressed_llb) {
                i915_gem_stolen_remove_node(dev_priv,
                                            dev_priv->fbc.compressed_llb);
                kfree(dev_priv->fbc.compressed_llb);
        }
-
-       dev_priv->fbc.uncompressed_size = 0;
 }
 
 void intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv)
@@ -671,64 +660,6 @@ void intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv)
        mutex_unlock(&dev_priv->fbc.lock);
 }
 
-/*
- * For SKL+, the plane source size used by the hardware is based on the value we
- * write to the PLANE_SIZE register. For BDW-, the hardware looks at the value
- * we wrote to PIPESRC.
- */
-static void intel_fbc_get_plane_source_size(struct intel_crtc *crtc,
-                                           int *width, int *height)
-{
-       struct intel_plane_state *plane_state =
-                       to_intel_plane_state(crtc->base.primary->state);
-       int w, h;
-
-       if (intel_rotation_90_or_270(plane_state->base.rotation)) {
-               w = drm_rect_height(&plane_state->src) >> 16;
-               h = drm_rect_width(&plane_state->src) >> 16;
-       } else {
-               w = drm_rect_width(&plane_state->src) >> 16;
-               h = drm_rect_height(&plane_state->src) >> 16;
-       }
-
-       if (width)
-               *width = w;
-       if (height)
-               *height = h;
-}
-
-static int intel_fbc_calculate_cfb_size(struct intel_crtc *crtc)
-{
-       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
-       struct drm_framebuffer *fb = crtc->base.primary->fb;
-       int lines;
-
-       intel_fbc_get_plane_source_size(crtc, NULL, &lines);
-       if (INTEL_INFO(dev_priv)->gen >= 7)
-               lines = min(lines, 2048);
-
-       /* Hardware needs the full buffer stride, not just the active area. */
-       return lines * fb->pitches[0];
-}
-
-static int intel_fbc_setup_cfb(struct intel_crtc *crtc)
-{
-       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
-       struct drm_framebuffer *fb = crtc->base.primary->fb;
-       int size, cpp;
-
-       size = intel_fbc_calculate_cfb_size(crtc);
-       cpp = drm_format_plane_cpp(fb->pixel_format, 0);
-
-       if (size <= dev_priv->fbc.uncompressed_size)
-               return 0;
-
-       /* Release any current block */
-       __intel_fbc_cleanup_cfb(dev_priv);
-
-       return intel_fbc_alloc_cfb(dev_priv, size, cpp);
-}
-
 static bool stride_is_valid(struct drm_i915_private *dev_priv,
                            unsigned int stride)
 {
@@ -803,47 +734,34 @@ static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc)
 }
 
 /**
- * __intel_fbc_update - enable/disable FBC as needed, unlocked
- * @dev_priv: i915 device instance
+ * __intel_fbc_update - activate/deactivate FBC as needed, unlocked
+ * @crtc: the CRTC that triggered the update
  *
- * This function completely reevaluates the status of FBC, then enables,
- * disables or maintains it on the same state.
+ * This function completely reevaluates the status of FBC, then activates,
+ * deactivates or maintains it on the same state.
  */
-static void __intel_fbc_update(struct drm_i915_private *dev_priv)
+static void __intel_fbc_update(struct intel_crtc *crtc)
 {
-       struct drm_crtc *drm_crtc = NULL;
-       struct intel_crtc *crtc;
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
        struct drm_framebuffer *fb;
        struct drm_i915_gem_object *obj;
        const struct drm_display_mode *adjusted_mode;
 
        WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock));
 
-       if (intel_vgpu_active(dev_priv->dev))
-               i915.enable_fbc = 0;
-
-       if (i915.enable_fbc < 0) {
-               set_no_fbc_reason(dev_priv, "disabled per chip default");
+       if (!multiple_pipes_ok(dev_priv)) {
+               set_no_fbc_reason(dev_priv, "more than one pipe active");
                goto out_disable;
        }
 
-       if (!i915.enable_fbc) {
-               set_no_fbc_reason(dev_priv, "disabled per module param");
-               goto out_disable;
-       }
+       if (!dev_priv->fbc.enabled || dev_priv->fbc.crtc != crtc)
+               return;
 
-       drm_crtc = intel_fbc_find_crtc(dev_priv);
-       if (!drm_crtc) {
+       if (!crtc_is_valid(crtc)) {
                set_no_fbc_reason(dev_priv, "no output");
                goto out_disable;
        }
 
-       if (!multiple_pipes_ok(dev_priv)) {
-               set_no_fbc_reason(dev_priv, "more than one pipe active");
-               goto out_disable;
-       }
-
-       crtc = to_intel_crtc(drm_crtc);
        fb = crtc->base.primary->fb;
        obj = intel_fb_obj(fb);
        adjusted_mode = &crtc->config->base.adjusted_mode;
@@ -859,12 +777,6 @@ static void __intel_fbc_update(struct drm_i915_private *dev_priv)
                goto out_disable;
        }
 
-       if ((INTEL_INFO(dev_priv)->gen < 4 || HAS_DDI(dev_priv)) &&
-           crtc->plane != PLANE_A) {
-               set_no_fbc_reason(dev_priv, "FBC unsupported on plane");
-               goto out_disable;
-       }
-
        /* The use of a CPU fence is mandatory in order to detect writes
         * by the CPU to the scanout and trigger updates to the FBC.
         */
@@ -897,8 +809,19 @@ static void __intel_fbc_update(struct drm_i915_private *dev_priv)
                goto out_disable;
        }
 
-       if (intel_fbc_setup_cfb(crtc)) {
-               set_no_fbc_reason(dev_priv, "not enough stolen memory");
+       /* It is possible for the required CFB size change without a
+        * crtc->disable + crtc->enable since it is possible to change the
+        * stride without triggering a full modeset. Since we try to
+        * over-allocate the CFB, there's a chance we may keep FBC enabled even
+        * if this happens, but if we exceed the current CFB size we'll have to
+        * disable FBC. Notice that it would be possible to disable FBC, wait
+        * for a frame, free the stolen node, then try to reenable FBC in case
+        * we didn't get any invalidate/deactivate calls, but this would require
+        * a lot of tracking just for a specific case. If we conclude it's an
+        * important case, we can implement it later. */
+       if (intel_fbc_calculate_cfb_size(crtc, fb) >
+           dev_priv->fbc.compressed_fb.size * dev_priv->fbc.threshold) {
+               set_no_fbc_reason(dev_priv, "CFB requirements changed");
                goto out_disable;
        }
 
@@ -909,10 +832,11 @@ static void __intel_fbc_update(struct drm_i915_private *dev_priv)
         */
        if (dev_priv->fbc.crtc == crtc &&
            dev_priv->fbc.fb_id == fb->base.id &&
-           dev_priv->fbc.y == crtc->base.y)
+           dev_priv->fbc.y == crtc->base.y &&
+           dev_priv->fbc.active)
                return;
 
-       if (intel_fbc_enabled(dev_priv)) {
+       if (intel_fbc_is_active(dev_priv)) {
                /* We update FBC along two paths, after changing fb/crtc
                 * configuration (modeswitching) and after page-flipping
                 * finishes. For the latter, we know that not only did
@@ -936,36 +860,37 @@ static void __intel_fbc_update(struct drm_i915_private *dev_priv)
                 * disabling paths we do need to wait for a vblank at
                 * some point. And we wait before enabling FBC anyway.
                 */
-               DRM_DEBUG_KMS("disabling active FBC for update\n");
-               __intel_fbc_disable(dev_priv);
+               DRM_DEBUG_KMS("deactivating FBC for update\n");
+               __intel_fbc_deactivate(dev_priv);
        }
 
-       intel_fbc_schedule_enable(crtc);
+       intel_fbc_schedule_activation(crtc);
        dev_priv->fbc.no_fbc_reason = "FBC enabled (not necessarily active)";
        return;
 
 out_disable:
        /* Multiple disables should be harmless */
-       if (intel_fbc_enabled(dev_priv)) {
-               DRM_DEBUG_KMS("unsupported config, disabling FBC\n");
-               __intel_fbc_disable(dev_priv);
+       if (intel_fbc_is_active(dev_priv)) {
+               DRM_DEBUG_KMS("unsupported config, deactivating FBC\n");
+               __intel_fbc_deactivate(dev_priv);
        }
-       __intel_fbc_cleanup_cfb(dev_priv);
 }
 
 /*
- * intel_fbc_update - enable/disable FBC as needed
- * @dev_priv: i915 device instance
+ * intel_fbc_update - activate/deactivate FBC as needed
+ * @crtc: the CRTC that triggered the update
  *
- * This function reevaluates the overall state and enables or disables FBC.
+ * This function reevaluates the overall state and activates or deactivates FBC.
  */
-void intel_fbc_update(struct drm_i915_private *dev_priv)
+void intel_fbc_update(struct intel_crtc *crtc)
 {
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+
        if (!fbc_supported(dev_priv))
                return;
 
        mutex_lock(&dev_priv->fbc.lock);
-       __intel_fbc_update(dev_priv);
+       __intel_fbc_update(crtc);
        mutex_unlock(&dev_priv->fbc.lock);
 }
 
@@ -985,16 +910,13 @@ void intel_fbc_invalidate(struct drm_i915_private *dev_priv,
 
        if (dev_priv->fbc.enabled)
                fbc_bits = INTEL_FRONTBUFFER_PRIMARY(dev_priv->fbc.crtc->pipe);
-       else if (dev_priv->fbc.fbc_work)
-               fbc_bits = INTEL_FRONTBUFFER_PRIMARY(
-                                       dev_priv->fbc.fbc_work->crtc->pipe);
        else
                fbc_bits = dev_priv->fbc.possible_framebuffer_bits;
 
        dev_priv->fbc.busy_bits |= (fbc_bits & frontbuffer_bits);
 
        if (dev_priv->fbc.busy_bits)
-               __intel_fbc_disable(dev_priv);
+               __intel_fbc_deactivate(dev_priv);
 
        mutex_unlock(&dev_priv->fbc.lock);
 }
@@ -1012,11 +934,136 @@ void intel_fbc_flush(struct drm_i915_private *dev_priv,
 
        dev_priv->fbc.busy_bits &= ~frontbuffer_bits;
 
-       if (!dev_priv->fbc.busy_bits) {
+       if (!dev_priv->fbc.busy_bits && dev_priv->fbc.enabled) {
+               if (origin != ORIGIN_FLIP && dev_priv->fbc.active) {
+                       intel_fbc_recompress(dev_priv);
+               } else {
+                       __intel_fbc_deactivate(dev_priv);
+                       __intel_fbc_update(dev_priv->fbc.crtc);
+               }
+       }
+
+       mutex_unlock(&dev_priv->fbc.lock);
+}
+
+/**
+ * intel_fbc_enable: tries to enable FBC on the CRTC
+ * @crtc: the CRTC
+ *
+ * This function checks if it's possible to enable FBC on the following CRTC,
+ * then enables it. Notice that it doesn't activate FBC.
+ */
+void intel_fbc_enable(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+
+       if (!fbc_supported(dev_priv))
+               return;
+
+       mutex_lock(&dev_priv->fbc.lock);
+
+       if (dev_priv->fbc.enabled) {
+               WARN_ON(dev_priv->fbc.crtc == crtc);
+               goto out;
+       }
+
+       WARN_ON(dev_priv->fbc.active);
+       WARN_ON(dev_priv->fbc.crtc != NULL);
+
+       if (intel_vgpu_active(dev_priv->dev)) {
+               set_no_fbc_reason(dev_priv, "VGPU is active");
+               goto out;
+       }
+
+       if (i915.enable_fbc < 0) {
+               set_no_fbc_reason(dev_priv, "disabled per chip default");
+               goto out;
+       }
+
+       if (!i915.enable_fbc) {
+               set_no_fbc_reason(dev_priv, "disabled per module param");
+               goto out;
+       }
+
+       if (!crtc_can_fbc(crtc)) {
+               set_no_fbc_reason(dev_priv, "no enabled pipes can have FBC");
+               goto out;
+       }
+
+       if (intel_fbc_alloc_cfb(crtc)) {
+               set_no_fbc_reason(dev_priv, "not enough stolen memory");
+               goto out;
+       }
+
+       DRM_DEBUG_KMS("Enabling FBC on pipe %c\n", pipe_name(crtc->pipe));
+       dev_priv->fbc.no_fbc_reason = "FBC enabled but not active yet\n";
+
+       dev_priv->fbc.enabled = true;
+       dev_priv->fbc.crtc = crtc;
+out:
+       mutex_unlock(&dev_priv->fbc.lock);
+}
+
+/**
+ * __intel_fbc_disable - disable FBC
+ * @dev_priv: i915 device instance
+ *
+ * This is the low level function that actually disables FBC. Callers should
+ * grab the FBC lock.
+ */
+static void __intel_fbc_disable(struct drm_i915_private *dev_priv)
+{
+       struct intel_crtc *crtc = dev_priv->fbc.crtc;
+
+       WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock));
+       WARN_ON(!dev_priv->fbc.enabled);
+       WARN_ON(dev_priv->fbc.active);
+       assert_pipe_disabled(dev_priv, crtc->pipe);
+
+       DRM_DEBUG_KMS("Disabling FBC on pipe %c\n", pipe_name(crtc->pipe));
+
+       __intel_fbc_cleanup_cfb(dev_priv);
+
+       dev_priv->fbc.enabled = false;
+       dev_priv->fbc.crtc = NULL;
+}
+
+/**
+ * intel_fbc_disable_crtc - disable FBC if it's associated with crtc
+ * @crtc: the CRTC
+ *
+ * This function disables FBC if it's associated with the provided CRTC.
+ */
+void intel_fbc_disable_crtc(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+
+       if (!fbc_supported(dev_priv))
+               return;
+
+       mutex_lock(&dev_priv->fbc.lock);
+       if (dev_priv->fbc.crtc == crtc) {
+               WARN_ON(!dev_priv->fbc.enabled);
+               WARN_ON(dev_priv->fbc.active);
                __intel_fbc_disable(dev_priv);
-               __intel_fbc_update(dev_priv);
        }
+       mutex_unlock(&dev_priv->fbc.lock);
+}
 
+/**
+ * intel_fbc_disable - globally disable FBC
+ * @dev_priv: i915 device instance
+ *
+ * This function disables FBC regardless of which CRTC is associated with it.
+ */
+void intel_fbc_disable(struct drm_i915_private *dev_priv)
+{
+       if (!fbc_supported(dev_priv))
+               return;
+
+       mutex_lock(&dev_priv->fbc.lock);
+       if (dev_priv->fbc.enabled)
+               __intel_fbc_disable(dev_priv);
        mutex_unlock(&dev_priv->fbc.lock);
 }
 
@@ -1030,8 +1077,11 @@ void intel_fbc_init(struct drm_i915_private *dev_priv)
 {
        enum pipe pipe;
 
+       INIT_WORK(&dev_priv->fbc.work.work, intel_fbc_work_fn);
        mutex_init(&dev_priv->fbc.lock);
        dev_priv->fbc.enabled = false;
+       dev_priv->fbc.active = false;
+       dev_priv->fbc.work.scheduled = false;
 
        if (!HAS_FBC(dev_priv)) {
                dev_priv->fbc.no_fbc_reason = "unsupported by this chipset";
@@ -1047,29 +1097,29 @@ void intel_fbc_init(struct drm_i915_private *dev_priv)
        }
 
        if (INTEL_INFO(dev_priv)->gen >= 7) {
-               dev_priv->fbc.fbc_enabled = ilk_fbc_enabled;
-               dev_priv->fbc.enable_fbc = gen7_fbc_enable;
-               dev_priv->fbc.disable_fbc = ilk_fbc_disable;
+               dev_priv->fbc.is_active = ilk_fbc_is_active;
+               dev_priv->fbc.activate = gen7_fbc_activate;
+               dev_priv->fbc.deactivate = ilk_fbc_deactivate;
        } else if (INTEL_INFO(dev_priv)->gen >= 5) {
-               dev_priv->fbc.fbc_enabled = ilk_fbc_enabled;
-               dev_priv->fbc.enable_fbc = ilk_fbc_enable;
-               dev_priv->fbc.disable_fbc = ilk_fbc_disable;
+               dev_priv->fbc.is_active = ilk_fbc_is_active;
+               dev_priv->fbc.activate = ilk_fbc_activate;
+               dev_priv->fbc.deactivate = ilk_fbc_deactivate;
        } else if (IS_GM45(dev_priv)) {
-               dev_priv->fbc.fbc_enabled = g4x_fbc_enabled;
-               dev_priv->fbc.enable_fbc = g4x_fbc_enable;
-               dev_priv->fbc.disable_fbc = g4x_fbc_disable;
+               dev_priv->fbc.is_active = g4x_fbc_is_active;
+               dev_priv->fbc.activate = g4x_fbc_activate;
+               dev_priv->fbc.deactivate = g4x_fbc_deactivate;
        } else {
-               dev_priv->fbc.fbc_enabled = i8xx_fbc_enabled;
-               dev_priv->fbc.enable_fbc = i8xx_fbc_enable;
-               dev_priv->fbc.disable_fbc = i8xx_fbc_disable;
+               dev_priv->fbc.is_active = i8xx_fbc_is_active;
+               dev_priv->fbc.activate = i8xx_fbc_activate;
+               dev_priv->fbc.deactivate = i8xx_fbc_deactivate;
 
                /* This value was pulled out of someone's hat */
                I915_WRITE(FBC_CONTROL, 500 << FBC_CTL_INTERVAL_SHIFT);
        }
 
        /* We still don't have any sort of hardware state readout for FBC, so
-        * disable it in case the BIOS enabled it to make sure software matches
-        * the hardware state. */
-       if (dev_priv->fbc.fbc_enabled(dev_priv))
-               dev_priv->fbc.disable_fbc(dev_priv);
+        * deactivate it in case the BIOS activated it to make sure software
+        * matches the hardware state. */
+       if (dev_priv->fbc.is_active(dev_priv))
+               dev_priv->fbc.deactivate(dev_priv);
 }
index 7ae182d..bda5266 100644 (file)
@@ -128,9 +128,9 @@ static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
                                          DE_PIPEB_FIFO_UNDERRUN;
 
        if (enable)
-               ironlake_enable_display_irq(dev_priv, bit);
+               ilk_enable_display_irq(dev_priv, bit);
        else
-               ironlake_disable_display_irq(dev_priv, bit);
+               ilk_disable_display_irq(dev_priv, bit);
 }
 
 static void ivybridge_check_fifo_underruns(struct intel_crtc *crtc)
@@ -161,9 +161,9 @@ static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
                if (!ivb_can_enable_err_int(dev))
                        return;
 
-               ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
+               ilk_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
        } else {
-               ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+               ilk_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
 
                if (old &&
                    I915_READ(GEN7_ERR_INT) & ERR_INT_FIFO_UNDERRUN(pipe)) {
@@ -178,14 +178,10 @@ static void broadwell_set_fifo_underrun_reporting(struct drm_device *dev,
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       assert_spin_locked(&dev_priv->irq_lock);
-
        if (enable)
-               dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_FIFO_UNDERRUN;
+               bdw_enable_pipe_irq(dev_priv, pipe, GEN8_PIPE_FIFO_UNDERRUN);
        else
-               dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_FIFO_UNDERRUN;
-       I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
-       POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+               bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_FIFO_UNDERRUN);
 }
 
 static void ibx_set_fifo_underrun_reporting(struct drm_device *dev,
index 5ba5866..8229522 100644 (file)
@@ -42,8 +42,6 @@ struct i915_guc_client {
 
        uint32_t wq_offset;
        uint32_t wq_size;
-
-       spinlock_t wq_lock;             /* Protects all data below      */
        uint32_t wq_tail;
 
        /* GuC submission statistics & status */
@@ -95,8 +93,6 @@ struct intel_guc {
 
        struct i915_guc_client *execbuf_client;
 
-       spinlock_t host2guc_lock;       /* Protects all data below      */
-
        DECLARE_BITMAP(doorbell_bitmap, GUC_MAX_DOORBELLS);
        uint32_t db_cacheline;          /* Cyclic counter mod pagesize  */
 
index bdd462e..00d065f 100644 (file)
@@ -169,10 +169,10 @@ static void g4x_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(VIDEO_DIP_CTL);
 }
 
-static bool g4x_infoframe_enabled(struct drm_encoder *encoder)
+static bool g4x_infoframe_enabled(struct drm_encoder *encoder,
+                                 const struct intel_crtc_state *pipe_config)
 {
-       struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = to_i915(encoder->dev);
        struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
        u32 val = I915_READ(VIDEO_DIP_CTL);
 
@@ -225,13 +225,13 @@ static void ibx_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(reg);
 }
 
-static bool ibx_infoframe_enabled(struct drm_encoder *encoder)
+static bool ibx_infoframe_enabled(struct drm_encoder *encoder,
+                                 const struct intel_crtc_state *pipe_config)
 {
-       struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct drm_i915_private *dev_priv = to_i915(encoder->dev);
        struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
-       i915_reg_t reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       enum pipe pipe = to_intel_crtc(pipe_config->base.crtc)->pipe;
+       i915_reg_t reg = TVIDEO_DIP_CTL(pipe);
        u32 val = I915_READ(reg);
 
        if ((val & VIDEO_DIP_ENABLE) == 0)
@@ -287,12 +287,12 @@ static void cpt_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(reg);
 }
 
-static bool cpt_infoframe_enabled(struct drm_encoder *encoder)
+static bool cpt_infoframe_enabled(struct drm_encoder *encoder,
+                                 const struct intel_crtc_state *pipe_config)
 {
-       struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
-       u32 val = I915_READ(TVIDEO_DIP_CTL(intel_crtc->pipe));
+       struct drm_i915_private *dev_priv = to_i915(encoder->dev);
+       enum pipe pipe = to_intel_crtc(pipe_config->base.crtc)->pipe;
+       u32 val = I915_READ(TVIDEO_DIP_CTL(pipe));
 
        if ((val & VIDEO_DIP_ENABLE) == 0)
                return false;
@@ -341,13 +341,13 @@ static void vlv_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(reg);
 }
 
-static bool vlv_infoframe_enabled(struct drm_encoder *encoder)
+static bool vlv_infoframe_enabled(struct drm_encoder *encoder,
+                                 const struct intel_crtc_state *pipe_config)
 {
-       struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct drm_i915_private *dev_priv = to_i915(encoder->dev);
        struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
-       u32 val = I915_READ(VLV_TVIDEO_DIP_CTL(intel_crtc->pipe));
+       enum pipe pipe = to_intel_crtc(pipe_config->base.crtc)->pipe;
+       u32 val = I915_READ(VLV_TVIDEO_DIP_CTL(pipe));
 
        if ((val & VIDEO_DIP_ENABLE) == 0)
                return false;
@@ -398,12 +398,11 @@ static void hsw_write_infoframe(struct drm_encoder *encoder,
        POSTING_READ(ctl_reg);
 }
 
-static bool hsw_infoframe_enabled(struct drm_encoder *encoder)
+static bool hsw_infoframe_enabled(struct drm_encoder *encoder,
+                                 const struct intel_crtc_state *pipe_config)
 {
-       struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
-       u32 val = I915_READ(HSW_TVIDEO_DIP_CTL(intel_crtc->config->cpu_transcoder));
+       struct drm_i915_private *dev_priv = to_i915(encoder->dev);
+       u32 val = I915_READ(HSW_TVIDEO_DIP_CTL(pipe_config->cpu_transcoder));
 
        return val & (VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_AVI_HSW |
                      VIDEO_DIP_ENABLE_GCP_HSW | VIDEO_DIP_ENABLE_VS_HSW |
@@ -927,7 +926,7 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
        if (tmp & HDMI_MODE_SELECT_HDMI)
                pipe_config->has_hdmi_sink = true;
 
-       if (intel_hdmi->infoframe_enabled(&encoder->base))
+       if (intel_hdmi->infoframe_enabled(&encoder->base, pipe_config))
                pipe_config->has_infoframe = true;
 
        if (tmp & SDVO_AUDIO_ENABLE)
@@ -2165,7 +2164,7 @@ void intel_hdmi_init(struct drm_device *dev,
        intel_encoder = &intel_dig_port->base;
 
        drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        intel_encoder->compute_config = intel_hdmi_compute_config;
        if (HAS_PCH_SPLIT(dev)) {
index 1110c83..e26e22a 100644 (file)
@@ -472,9 +472,7 @@ gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
 }
 
 static int
-gmbus_xfer(struct i2c_adapter *adapter,
-          struct i2c_msg *msgs,
-          int num)
+do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
 {
        struct intel_gmbus *bus = container_of(adapter,
                                               struct intel_gmbus,
@@ -483,14 +481,6 @@ gmbus_xfer(struct i2c_adapter *adapter,
        int i = 0, inc, try = 0;
        int ret = 0;
 
-       intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
-       mutex_lock(&dev_priv->gmbus_mutex);
-
-       if (bus->force_bit) {
-               ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
-               goto out;
-       }
-
 retry:
        I915_WRITE(GMBUS0, bus->reg0);
 
@@ -505,17 +495,13 @@ retry:
                        ret = gmbus_xfer_write(dev_priv, &msgs[i]);
                }
 
+               if (!ret)
+                       ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
+                                                  GMBUS_HW_WAIT_EN);
                if (ret == -ETIMEDOUT)
                        goto timeout;
-               if (ret == -ENXIO)
-                       goto clear_err;
-
-               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
-                                          GMBUS_HW_WAIT_EN);
-               if (ret == -ENXIO)
+               else if (ret)
                        goto clear_err;
-               if (ret)
-                       goto timeout;
        }
 
        /* Generate a STOP condition on the bus. Note that gmbus can't generata
@@ -589,13 +575,34 @@ timeout:
                 bus->adapter.name, bus->reg0 & 0xff);
        I915_WRITE(GMBUS0, 0);
 
-       /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
+       /*
+        * Hardware may not support GMBUS over these pins? Try GPIO bitbanging
+        * instead. Use EAGAIN to have i2c core retry.
+        */
        bus->force_bit = 1;
-       ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
+       ret = -EAGAIN;
 
 out:
-       mutex_unlock(&dev_priv->gmbus_mutex);
+       return ret;
+}
+
+static int
+gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
+{
+       struct intel_gmbus *bus = container_of(adapter, struct intel_gmbus,
+                                              adapter);
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       int ret;
 
+       intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+       mutex_lock(&dev_priv->gmbus_mutex);
+
+       if (bus->force_bit)
+               ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
+       else
+               ret = do_gmbus_xfer(adapter, msgs, num);
+
+       mutex_unlock(&dev_priv->gmbus_mutex);
        intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
 
        return ret;
index 61f1145..0da0240 100644 (file)
@@ -1025,7 +1025,7 @@ void intel_lvds_init(struct drm_device *dev)
                           DRM_MODE_CONNECTOR_LVDS);
 
        drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
-                        DRM_MODE_ENCODER_LVDS);
+                        DRM_MODE_ENCODER_LVDS, NULL);
 
        intel_encoder->enable = intel_enable_lvds;
        intel_encoder->pre_enable = intel_pre_enable_lvds;
index a24df35..ae808b6 100644 (file)
@@ -1263,6 +1263,14 @@ static void intel_backlight_device_unregister(struct intel_connector *connector)
 }
 #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
 
+/*
+ * BXT: PWM clock frequency = 19.2 MHz.
+ */
+static u32 bxt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
+{
+       return KHz(19200) / pwm_freq_hz;
+}
+
 /*
  * SPT: This value represents the period of the PWM stream in clock periods
  * multiplied by 16 (default increment) or 128 (alternate increment selected in
@@ -1300,7 +1308,7 @@ static u32 lpt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
        else
                mul = 128;
 
-       if (dev_priv->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE)
+       if (HAS_PCH_LPT_H(dev_priv))
                clock = MHz(135); /* LPT:H */
        else
                clock = MHz(24); /* LPT:LP */
@@ -1335,22 +1343,28 @@ static u32 i9xx_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
        int clock;
 
        if (IS_PINEVIEW(dev))
-               clock = intel_hrawclk(dev);
+               clock = MHz(intel_hrawclk(dev));
        else
-               clock = 1000 * dev_priv->display.get_display_clock_speed(dev);
+               clock = 1000 * dev_priv->cdclk_freq;
 
        return clock / (pwm_freq_hz * 32);
 }
 
 /*
  * Gen4: This value represents the period of the PWM stream in display core
- * clocks multiplied by 128.
+ * clocks ([DevCTG] HRAW clocks) multiplied by 128.
+ *
  */
 static u32 i965_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
 {
        struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int clock = 1000 * dev_priv->display.get_display_clock_speed(dev);
+       int clock;
+
+       if (IS_G4X(dev_priv))
+               clock = MHz(intel_hrawclk(dev));
+       else
+               clock = 1000 * dev_priv->cdclk_freq;
 
        return clock / (pwm_freq_hz * 128);
 }
@@ -1385,14 +1399,18 @@ static u32 get_backlight_max_vbt(struct intel_connector *connector)
        u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
        u32 pwm;
 
-       if (!pwm_freq_hz) {
-               DRM_DEBUG_KMS("backlight frequency not specified in VBT\n");
+       if (!panel->backlight.hz_to_pwm) {
+               DRM_DEBUG_KMS("backlight frequency conversion not supported\n");
                return 0;
        }
 
-       if (!panel->backlight.hz_to_pwm) {
-               DRM_DEBUG_KMS("backlight frequency setting from VBT currently not supported on this platform\n");
-               return 0;
+       if (pwm_freq_hz) {
+               DRM_DEBUG_KMS("VBT defined backlight frequency %u Hz\n",
+                             pwm_freq_hz);
+       } else {
+               pwm_freq_hz = 200;
+               DRM_DEBUG_KMS("default backlight frequency %u Hz\n",
+                             pwm_freq_hz);
        }
 
        pwm = panel->backlight.hz_to_pwm(connector, pwm_freq_hz);
@@ -1401,8 +1419,6 @@ static u32 get_backlight_max_vbt(struct intel_connector *connector)
                return 0;
        }
 
-       DRM_DEBUG_KMS("backlight frequency %u Hz from VBT\n", pwm_freq_hz);
-
        return pwm;
 }
 
@@ -1750,6 +1766,7 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel)
                panel->backlight.disable = bxt_disable_backlight;
                panel->backlight.set = bxt_set_backlight;
                panel->backlight.get = bxt_get_backlight;
+               panel->backlight.hz_to_pwm = bxt_hz_to_pwm;
        } else if (HAS_PCH_LPT(dev) || HAS_PCH_SPT(dev)) {
                panel->backlight.setup = lpt_setup_backlight;
                panel->backlight.enable = lpt_enable_backlight;
index 96f45d7..ee05ce8 100644 (file)
@@ -66,6 +66,14 @@ static void bxt_init_clock_gating(struct drm_device *dev)
         */
        I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
                   GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ);
+
+       /*
+        * Wa: Backlight PWM may stop in the asserted state, causing backlight
+        * to stay fully on.
+        */
+       if (IS_BXT_REVID(dev_priv, BXT_REVID_B0, REVID_FOREVER))
+               I915_WRITE(GEN9_CLKGATE_DIS_0, I915_READ(GEN9_CLKGATE_DIS_0) |
+                          PWM1_GATING_DIS | PWM2_GATING_DIS);
 }
 
 static void i915_pineview_get_mem_freq(struct drm_device *dev)
@@ -2422,7 +2430,7 @@ static void ilk_wm_merge(struct drm_device *dev,
         * enabled sometime later.
         */
        if (IS_GEN5(dev) && !merged->fbc_wm_enabled &&
-           intel_fbc_enabled(dev_priv)) {
+           intel_fbc_is_active(dev_priv)) {
                for (level = 2; level <= max_level; level++) {
                        struct intel_wm_level *wm = &merged->wm[level];
 
index bc5ea2a..b6609e6 100644 (file)
@@ -191,9 +191,6 @@ static void hsw_psr_enable_sink(struct intel_dp *intel_dp)
 
        aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, 0);
 
-       drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
-                          DP_PSR_ENABLE & ~DP_PSR_MAIN_LINK_ACTIVE);
-
        /* Enable AUX frame sync at sink */
        if (dev_priv->psr.aux_frame_sync)
                drm_dp_dpcd_writeb(&intel_dp->aux,
@@ -414,9 +411,14 @@ void intel_psr_enable(struct intel_dp *intel_dp)
                                skl_psr_setup_su_vsc(intel_dp);
                }
 
-               /* Avoid continuous PSR exit by masking memup and hpd */
+               /*
+                * Per Spec: Avoid continuous PSR exit by masking MEMUP and HPD.
+                * Also mask LPSP to avoid dependency on other drivers that
+                * might block runtime_pm besides preventing other hw tracking
+                * issues now we can rely on frontbuffer tracking.
+                */
                I915_WRITE(EDP_PSR_DEBUG_CTL, EDP_PSR_DEBUG_MASK_MEMUP |
-                          EDP_PSR_DEBUG_MASK_HPD);
+                          EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
 
                /* Enable PSR on the panel */
                hsw_psr_enable_sink(intel_dp);
@@ -522,11 +524,15 @@ void intel_psr_disable(struct intel_dp *intel_dp)
                return;
        }
 
+       /* Disable PSR on Source */
        if (HAS_DDI(dev))
                hsw_psr_disable(intel_dp);
        else
                vlv_psr_disable(intel_dp);
 
+       /* Disable PSR on Sink */
+       drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, 0);
+
        dev_priv->psr.enabled = NULL;
        mutex_unlock(&dev_priv->psr.lock);
 
@@ -737,25 +743,9 @@ void intel_psr_flush(struct drm_device *dev,
        frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe);
        dev_priv->psr.busy_frontbuffer_bits &= ~frontbuffer_bits;
 
-       if (HAS_DDI(dev)) {
-               /*
-                * By definition every flush should mean invalidate + flush,
-                * however on core platforms let's minimize the
-                * disable/re-enable so we can avoid the invalidate when flip
-                * originated the flush.
-                */
-               if (frontbuffer_bits && origin != ORIGIN_FLIP)
-                       intel_psr_exit(dev);
-       } else {
-               /*
-                * On Valleyview and Cherryview we don't use hardware tracking
-                * so any plane updates or cursor moves don't result in a PSR
-                * invalidating. Which means we need to manually fake this in
-                * software for all flushes.
-                */
-               if (frontbuffer_bits)
-                       intel_psr_exit(dev);
-       }
+       /* By definition flush = invalidate + flush */
+       if (frontbuffer_bits)
+               intel_psr_exit(dev);
 
        if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits)
                if (!work_busy(&dev_priv->psr.work.work))
index afca6c9..2c2151f 100644 (file)
 bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv,
                                    int power_well_id);
 
+const char *
+intel_display_power_domain_str(enum intel_display_power_domain domain)
+{
+       switch (domain) {
+       case POWER_DOMAIN_PIPE_A:
+               return "PIPE_A";
+       case POWER_DOMAIN_PIPE_B:
+               return "PIPE_B";
+       case POWER_DOMAIN_PIPE_C:
+               return "PIPE_C";
+       case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
+               return "PIPE_A_PANEL_FITTER";
+       case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
+               return "PIPE_B_PANEL_FITTER";
+       case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
+               return "PIPE_C_PANEL_FITTER";
+       case POWER_DOMAIN_TRANSCODER_A:
+               return "TRANSCODER_A";
+       case POWER_DOMAIN_TRANSCODER_B:
+               return "TRANSCODER_B";
+       case POWER_DOMAIN_TRANSCODER_C:
+               return "TRANSCODER_C";
+       case POWER_DOMAIN_TRANSCODER_EDP:
+               return "TRANSCODER_EDP";
+       case POWER_DOMAIN_PORT_DDI_A_LANES:
+               return "PORT_DDI_A_LANES";
+       case POWER_DOMAIN_PORT_DDI_B_LANES:
+               return "PORT_DDI_B_LANES";
+       case POWER_DOMAIN_PORT_DDI_C_LANES:
+               return "PORT_DDI_C_LANES";
+       case POWER_DOMAIN_PORT_DDI_D_LANES:
+               return "PORT_DDI_D_LANES";
+       case POWER_DOMAIN_PORT_DDI_E_LANES:
+               return "PORT_DDI_E_LANES";
+       case POWER_DOMAIN_PORT_DSI:
+               return "PORT_DSI";
+       case POWER_DOMAIN_PORT_CRT:
+               return "PORT_CRT";
+       case POWER_DOMAIN_PORT_OTHER:
+               return "PORT_OTHER";
+       case POWER_DOMAIN_VGA:
+               return "VGA";
+       case POWER_DOMAIN_AUDIO:
+               return "AUDIO";
+       case POWER_DOMAIN_PLLS:
+               return "PLLS";
+       case POWER_DOMAIN_AUX_A:
+               return "AUX_A";
+       case POWER_DOMAIN_AUX_B:
+               return "AUX_B";
+       case POWER_DOMAIN_AUX_C:
+               return "AUX_C";
+       case POWER_DOMAIN_AUX_D:
+               return "AUX_D";
+       case POWER_DOMAIN_GMBUS:
+               return "GMBUS";
+       case POWER_DOMAIN_INIT:
+               return "INIT";
+       case POWER_DOMAIN_MODESET:
+               return "MODESET";
+       default:
+               MISSING_CASE(domain);
+               return "?";
+       }
+}
+
 static void intel_power_well_enable(struct drm_i915_private *dev_priv,
                                    struct i915_power_well *power_well)
 {
@@ -1433,11 +1499,15 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,
 
        mutex_lock(&power_domains->lock);
 
-       WARN_ON(!power_domains->domain_use_count[domain]);
+       WARN(!power_domains->domain_use_count[domain],
+            "Use count on domain %s is already zero\n",
+            intel_display_power_domain_str(domain));
        power_domains->domain_use_count[domain]--;
 
        for_each_power_well_rev(i, power_well, BIT(domain), power_domains) {
-               WARN_ON(!power_well->count);
+               WARN(!power_well->count,
+                    "Use count on power well %s is already zero",
+                    power_well->name);
 
                if (!--power_well->count)
                        intel_power_well_disable(dev_priv, power_well);
@@ -1841,7 +1911,7 @@ sanitize_disable_power_well_option(const struct drm_i915_private *dev_priv,
        if (disable_power_well >= 0)
                return !!disable_power_well;
 
-       if (IS_SKYLAKE(dev_priv)) {
+       if (IS_BROXTON(dev_priv)) {
                DRM_DEBUG_KMS("Disabling display power well support\n");
                return 0;
        }
index 06679f1..2e1da06 100644 (file)
@@ -2978,7 +2978,8 @@ bool intel_sdvo_init(struct drm_device *dev,
        /* encoder type will be decided later */
        intel_encoder = &intel_sdvo->base;
        intel_encoder->type = INTEL_OUTPUT_SDVO;
-       drm_encoder_init(dev, &intel_encoder->base, &intel_sdvo_enc_funcs, 0);
+       drm_encoder_init(dev, &intel_encoder->base, &intel_sdvo_enc_funcs, 0,
+                        NULL);
 
        /* Read the regs to test if we can talk to the device */
        for (i = 0; i < 0x40; i++) {
index 2b96f33..dbf4213 100644 (file)
@@ -1123,7 +1123,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)
        ret = drm_universal_plane_init(dev, &intel_plane->base, possible_crtcs,
                                       &intel_plane_funcs,
                                       plane_formats, num_plane_formats,
-                                      DRM_PLANE_TYPE_OVERLAY);
+                                      DRM_PLANE_TYPE_OVERLAY, NULL);
        if (ret) {
                kfree(intel_plane);
                goto out;
index 6bea789..948cbff 100644 (file)
@@ -1645,7 +1645,7 @@ intel_tv_init(struct drm_device *dev)
                           DRM_MODE_CONNECTOR_SVIDEO);
 
        drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
-                        DRM_MODE_ENCODER_TVDAC);
+                        DRM_MODE_ENCODER_TVDAC, NULL);
 
        intel_encoder->compute_config = intel_tv_compute_config;
        intel_encoder->get_config = intel_tv_get_config;
index 98605ea..35fcf6b 100644 (file)
@@ -251,7 +251,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
 
        drm_encoder_helper_add(encoder, &dw_hdmi_imx_encoder_helper_funcs);
        drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
 }
index 882cf3d..09e20ea 100644 (file)
@@ -373,7 +373,7 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
                        imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs);
 
        drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
-                       imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs);
+                       imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs, NULL);
 
        return 0;
 
index abacc8f..c79a61b 100644 (file)
@@ -422,7 +422,7 @@ static int imx_ldb_register(struct drm_device *drm,
        drm_encoder_helper_add(&imx_ldb_ch->encoder,
                        &imx_ldb_encoder_helper_funcs);
        drm_encoder_init(drm, &imx_ldb_ch->encoder, &imx_ldb_encoder_funcs,
-                        DRM_MODE_ENCODER_LVDS);
+                        DRM_MODE_ENCODER_LVDS, NULL);
 
        drm_connector_helper_add(&imx_ldb_ch->connector,
                        &imx_ldb_connector_helper_funcs);
index f959714..e61a8fc 100644 (file)
@@ -508,7 +508,7 @@ static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve)
 
        drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs);
        drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs,
-                        encoder_type);
+                        encoder_type, NULL);
 
        drm_connector_helper_add(&tve->connector,
                        &imx_tve_connector_helper_funcs);
index e2ff410..591ba2f 100644 (file)
@@ -401,7 +401,8 @@ struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
 
        ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs,
                                       &ipu_plane_funcs, ipu_plane_formats,
-                                      ARRAY_SIZE(ipu_plane_formats), type);
+                                      ARRAY_SIZE(ipu_plane_formats), type,
+                                      NULL);
        if (ret) {
                DRM_ERROR("failed to initialize plane\n");
                kfree(ipu_plane);
index 2e9b9f1..fcbe4d2 100644 (file)
@@ -192,7 +192,7 @@ static int imx_pd_register(struct drm_device *drm,
 
        drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs);
        drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs,
-                        DRM_MODE_ENCODER_NONE);
+                        DRM_MODE_ENCODER_NONE, NULL);
 
        drm_connector_helper_add(&imxpd->connector,
                        &imx_pd_connector_helper_funcs);
index c99d3fe..3180212 100644 (file)
@@ -1538,7 +1538,7 @@ static struct drm_encoder *mga_encoder_init(struct drm_device *dev)
        encoder->possible_crtcs = 0x1;
 
        drm_encoder_init(dev, encoder, &mga_encoder_encoder_funcs,
-                        DRM_MODE_ENCODER_DAC);
+                        DRM_MODE_ENCODER_DAC, NULL);
        drm_encoder_helper_add(encoder, &mga_encoder_helper_funcs);
 
        return encoder;
index 84d3ec9..215495c 100644 (file)
@@ -54,3 +54,11 @@ config DRM_MSM_DSI_20NM_PHY
        default y
        help
          Choose this option if the 20nm DSI PHY is used on the platform.
+
+config DRM_MSM_DSI_28NM_8960_PHY
+       bool "Enable DSI 28nm 8960 PHY driver in MSM DRM"
+       depends on DRM_MSM_DSI
+       default y
+       help
+         Choose this option if the 28nm DSI PHY 8960 variant is used on the
+         platform.
index 1c90290..065ad41 100644 (file)
@@ -54,6 +54,7 @@ msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
 msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
 
 msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \
+                       mdp/mdp4/mdp4_dsi_encoder.o \
                        dsi/dsi_cfg.o \
                        dsi/dsi_host.o \
                        dsi/dsi_manager.o \
@@ -62,10 +63,12 @@ msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \
 
 msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/phy/dsi_phy_28nm.o
 msm-$(CONFIG_DRM_MSM_DSI_20NM_PHY) += dsi/phy/dsi_phy_20nm.o
+msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/phy/dsi_phy_28nm_8960.o
 
 ifeq ($(CONFIG_DRM_MSM_DSI_PLL),y)
 msm-y += dsi/pll/dsi_pll.o
 msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/pll/dsi_pll_28nm.o
+msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/pll/dsi_pll_28nm_8960.o
 endif
 
 obj-$(CONFIG_DRM_MSM)  += msm.o
index 1ea2df5..950d27d 100644 (file)
 
 #include "adreno_gpu.h"
 
-#if defined(DOWNSTREAM_CONFIG_MSM_BUS_SCALING) && !defined(CONFIG_OF)
-#  include <mach/kgsl.h>
-#endif
-
 #define ANY_ID 0xff
 
 bool hang_debug = false;
@@ -168,7 +164,6 @@ static void set_gpu_pdev(struct drm_device *dev,
 static int adreno_bind(struct device *dev, struct device *master, void *data)
 {
        static struct adreno_platform_config config = {};
-#ifdef CONFIG_OF
        struct device_node *child, *node = dev->of_node;
        u32 val;
        int ret;
@@ -205,53 +200,6 @@ static int adreno_bind(struct device *dev, struct device *master, void *data)
                return -ENXIO;
        }
 
-#else
-       struct kgsl_device_platform_data *pdata = dev->platform_data;
-       uint32_t version = socinfo_get_version();
-       if (cpu_is_apq8064ab()) {
-               config.fast_rate = 450000000;
-               config.slow_rate = 27000000;
-               config.bus_freq  = 4;
-               config.rev = ADRENO_REV(3, 2, 1, 0);
-       } else if (cpu_is_apq8064()) {
-               config.fast_rate = 400000000;
-               config.slow_rate = 27000000;
-               config.bus_freq  = 4;
-
-               if (SOCINFO_VERSION_MAJOR(version) == 2)
-                       config.rev = ADRENO_REV(3, 2, 0, 2);
-               else if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
-                               (SOCINFO_VERSION_MINOR(version) == 1))
-                       config.rev = ADRENO_REV(3, 2, 0, 1);
-               else
-                       config.rev = ADRENO_REV(3, 2, 0, 0);
-
-       } else if (cpu_is_msm8960ab()) {
-               config.fast_rate = 400000000;
-               config.slow_rate = 320000000;
-               config.bus_freq  = 4;
-
-               if (SOCINFO_VERSION_MINOR(version) == 0)
-                       config.rev = ADRENO_REV(3, 2, 1, 0);
-               else
-                       config.rev = ADRENO_REV(3, 2, 1, 1);
-
-       } else if (cpu_is_msm8930()) {
-               config.fast_rate = 400000000;
-               config.slow_rate = 27000000;
-               config.bus_freq  = 3;
-
-               if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
-                       (SOCINFO_VERSION_MINOR(version) == 2))
-                       config.rev = ADRENO_REV(3, 0, 5, 2);
-               else
-                       config.rev = ADRENO_REV(3, 0, 5, 0);
-
-       }
-#  ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING
-       config.bus_scale_table = pdata->bus_scale_table;
-#  endif
-#endif
        dev->platform_data = &config;
        set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev));
        return 0;
index 5f5a373..749fbb2 100644 (file)
@@ -31,10 +31,12 @@ enum msm_dsi_phy_type {
        MSM_DSI_PHY_28NM_HPM,
        MSM_DSI_PHY_28NM_LP,
        MSM_DSI_PHY_20NM,
+       MSM_DSI_PHY_28NM_8960,
        MSM_DSI_PHY_MAX
 };
 
 #define DSI_DEV_REGULATOR_MAX  8
+#define DSI_BUS_CLK_MAX                4
 
 /* Regulators for DSI devices */
 struct dsi_reg_entry {
@@ -89,7 +91,7 @@ int msm_dsi_manager_phy_enable(int id,
                u32 *clk_pre, u32 *clk_post);
 void msm_dsi_manager_phy_disable(int id);
 int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg);
-bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len);
+bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len);
 int msm_dsi_manager_register(struct msm_dsi *msm_dsi);
 void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi);
 
@@ -143,7 +145,7 @@ int msm_dsi_host_cmd_tx(struct mipi_dsi_host *host,
 int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host,
                                        const struct mipi_dsi_msg *msg);
 void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host,
-                                       u32 iova, u32 len);
+                                       u32 dma_base, u32 len);
 int msm_dsi_host_enable(struct mipi_dsi_host *host);
 int msm_dsi_host_disable(struct mipi_dsi_host *host);
 int msm_dsi_host_power_on(struct mipi_dsi_host *host);
index 5872d5e..2a827d8 100644 (file)
 
 #include "dsi_cfg.h"
 
-/* DSI v2 has not been supported by now */
-static const struct msm_dsi_config dsi_v2_cfg = {
+static const char * const dsi_v2_bus_clk_names[] = {
+       "core_mmss_clk", "iface_clk", "bus_clk",
+};
+
+static const struct msm_dsi_config apq8064_dsi_cfg = {
        .io_offset = 0,
+       .reg_cfg = {
+               .num = 3,
+               .regs = {
+                       {"vdda", 1200000, 1200000, 100000, 100},
+                       {"avdd", 3000000, 3000000, 110000, 100},
+                       {"vddio", 1800000, 1800000, 100000, 100},
+               },
+       },
+       .bus_clk_names = dsi_v2_bus_clk_names,
+       .num_bus_clks = ARRAY_SIZE(dsi_v2_bus_clk_names),
+};
+
+static const char * const dsi_6g_bus_clk_names[] = {
+       "mdp_core_clk", "iface_clk", "bus_clk", "core_mmss_clk",
 };
 
 static const struct msm_dsi_config msm8974_apq8084_dsi_cfg = {
@@ -29,6 +46,12 @@ static const struct msm_dsi_config msm8974_apq8084_dsi_cfg = {
                        {"vddio", 1800000, 1800000, 100000, 100},
                },
        },
+       .bus_clk_names = dsi_6g_bus_clk_names,
+       .num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names),
+};
+
+static const char * const dsi_8916_bus_clk_names[] = {
+       "mdp_core_clk", "iface_clk", "bus_clk",
 };
 
 static const struct msm_dsi_config msm8916_dsi_cfg = {
@@ -42,6 +65,8 @@ static const struct msm_dsi_config msm8916_dsi_cfg = {
                        {"vddio", 1800000, 1800000, 100000, 100},
                },
        },
+       .bus_clk_names = dsi_8916_bus_clk_names,
+       .num_bus_clks = ARRAY_SIZE(dsi_8916_bus_clk_names),
 };
 
 static const struct msm_dsi_config msm8994_dsi_cfg = {
@@ -57,11 +82,13 @@ static const struct msm_dsi_config msm8994_dsi_cfg = {
                        {"lab_reg", -1, -1, -1, -1},
                        {"ibb_reg", -1, -1, -1, -1},
                },
-       }
+       },
+       .bus_clk_names = dsi_6g_bus_clk_names,
+       .num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names),
 };
 
 static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = {
-       {MSM_DSI_VER_MAJOR_V2, U32_MAX, &dsi_v2_cfg},
+       {MSM_DSI_VER_MAJOR_V2, MSM_DSI_V2_VER_MINOR_8064, &apq8064_dsi_cfg},
        {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_0,
                                                &msm8974_apq8084_dsi_cfg},
        {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1,
index 4cf8872..a68c836 100644 (file)
 #define MSM_DSI_6G_VER_MINOR_V1_3      0x10030000
 #define MSM_DSI_6G_VER_MINOR_V1_3_1    0x10030001
 
+#define MSM_DSI_V2_VER_MINOR_8064      0x0
+
 #define DSI_6G_REG_SHIFT       4
 
 struct msm_dsi_config {
        u32 io_offset;
        struct dsi_reg_config reg_cfg;
+       const char * const *bus_clk_names;
+       const int num_bus_clks;
 };
 
 struct msm_dsi_cfg_handler {
index 4c49868..48f9967 100644 (file)
 #include <linux/of_graph.h>
 #include <linux/regulator/consumer.h>
 #include <linux/spinlock.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 #include <video/mipi_display.h>
 
 #include "dsi.h"
 #include "dsi.xml.h"
+#include "sfpb.xml.h"
 #include "dsi_cfg.h"
 
 static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor)
 {
        u32 ver;
-       u32 ver_6g;
 
        if (!major || !minor)
                return -EINVAL;
 
-       /* From DSI6G(v3), addition of a 6G_HW_VERSION register at offset 0
+       /*
+        * From DSI6G(v3), addition of a 6G_HW_VERSION register at offset 0
         * makes all other registers 4-byte shifted down.
+        *
+        * In order to identify between DSI6G(v3) and beyond, and DSIv2 and
+        * older, we read the DSI_VERSION register without any shift(offset
+        * 0x1f0). In the case of DSIv2, this hast to be a non-zero value. In
+        * the case of DSI6G, this has to be zero (the offset points to a
+        * scratch register which we never touch)
         */
-       ver_6g = msm_readl(base + REG_DSI_6G_HW_VERSION);
-       if (ver_6g == 0) {
-               ver = msm_readl(base + REG_DSI_VERSION);
+
+       ver = msm_readl(base + REG_DSI_VERSION);
+       if (ver) {
+               /* older dsi host, there is no register shift */
                ver = FIELD(ver, DSI_VERSION_MAJOR);
                if (ver <= MSM_DSI_VER_MAJOR_V2) {
                        /* old versions */
@@ -54,12 +64,17 @@ static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor)
                        return -EINVAL;
                }
        } else {
+               /*
+                * newer host, offset 0 has 6G_HW_VERSION, the rest of the
+                * registers are shifted down, read DSI_VERSION again with
+                * the shifted offset
+                */
                ver = msm_readl(base + DSI_6G_REG_SHIFT + REG_DSI_VERSION);
                ver = FIELD(ver, DSI_VERSION_MAJOR);
                if (ver == MSM_DSI_VER_MAJOR_6G) {
                        /* 6G version */
                        *major = ver;
-                       *minor = ver_6g;
+                       *minor = msm_readl(base + REG_DSI_6G_HW_VERSION);
                        return 0;
                } else {
                        return -EINVAL;
@@ -91,10 +106,9 @@ struct msm_dsi_host {
 
        void __iomem *ctrl_base;
        struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX];
-       struct clk *mdp_core_clk;
-       struct clk *ahb_clk;
-       struct clk *axi_clk;
-       struct clk *mmss_misc_ahb_clk;
+
+       struct clk *bus_clks[DSI_BUS_CLK_MAX];
+
        struct clk *byte_clk;
        struct clk *esc_clk;
        struct clk *pixel_clk;
@@ -102,6 +116,14 @@ struct msm_dsi_host {
        struct clk *pixel_clk_src;
 
        u32 byte_clk_rate;
+       u32 esc_clk_rate;
+
+       /* DSI v2 specific clocks */
+       struct clk *src_clk;
+       struct clk *esc_clk_src;
+       struct clk *dsi_clk_src;
+
+       u32 src_clk_rate;
 
        struct gpio_desc *disp_en_gpio;
        struct gpio_desc *te_gpio;
@@ -119,9 +141,19 @@ struct msm_dsi_host {
        struct work_struct err_work;
        struct workqueue_struct *workqueue;
 
+       /* DSI 6G TX buffer*/
        struct drm_gem_object *tx_gem_obj;
+
+       /* DSI v2 TX buffer */
+       void *tx_buf;
+       dma_addr_t tx_buf_paddr;
+
+       int tx_size;
+
        u8 *rx_buf;
 
+       struct regmap *sfpb;
+
        struct drm_display_mode *mode;
 
        /* connected device info */
@@ -165,21 +197,31 @@ static const struct msm_dsi_cfg_handler *dsi_get_config(
                                                struct msm_dsi_host *msm_host)
 {
        const struct msm_dsi_cfg_handler *cfg_hnd = NULL;
+       struct device *dev = &msm_host->pdev->dev;
        struct regulator *gdsc_reg;
+       struct clk *ahb_clk;
        int ret;
        u32 major = 0, minor = 0;
 
-       gdsc_reg = regulator_get(&msm_host->pdev->dev, "gdsc");
+       gdsc_reg = regulator_get(dev, "gdsc");
        if (IS_ERR(gdsc_reg)) {
                pr_err("%s: cannot get gdsc\n", __func__);
                goto exit;
        }
+
+       ahb_clk = clk_get(dev, "iface_clk");
+       if (IS_ERR(ahb_clk)) {
+               pr_err("%s: cannot get interface clock\n", __func__);
+               goto put_gdsc;
+       }
+
        ret = regulator_enable(gdsc_reg);
        if (ret) {
                pr_err("%s: unable to enable gdsc\n", __func__);
-               goto put_gdsc;
+               goto put_clk;
        }
-       ret = clk_prepare_enable(msm_host->ahb_clk);
+
+       ret = clk_prepare_enable(ahb_clk);
        if (ret) {
                pr_err("%s: unable to enable ahb_clk\n", __func__);
                goto disable_gdsc;
@@ -196,9 +238,11 @@ static const struct msm_dsi_cfg_handler *dsi_get_config(
        DBG("%s: Version %x:%x\n", __func__, major, minor);
 
 disable_clks:
-       clk_disable_unprepare(msm_host->ahb_clk);
+       clk_disable_unprepare(ahb_clk);
 disable_gdsc:
        regulator_disable(gdsc_reg);
+put_clk:
+       clk_put(ahb_clk);
 put_gdsc:
        regulator_put(gdsc_reg);
 exit:
@@ -295,40 +339,23 @@ static int dsi_regulator_init(struct msm_dsi_host *msm_host)
 static int dsi_clk_init(struct msm_dsi_host *msm_host)
 {
        struct device *dev = &msm_host->pdev->dev;
-       int ret = 0;
-
-       msm_host->mdp_core_clk = devm_clk_get(dev, "mdp_core_clk");
-       if (IS_ERR(msm_host->mdp_core_clk)) {
-               ret = PTR_ERR(msm_host->mdp_core_clk);
-               pr_err("%s: Unable to get mdp core clk. ret=%d\n",
-                       __func__, ret);
-               goto exit;
-       }
-
-       msm_host->ahb_clk = devm_clk_get(dev, "iface_clk");
-       if (IS_ERR(msm_host->ahb_clk)) {
-               ret = PTR_ERR(msm_host->ahb_clk);
-               pr_err("%s: Unable to get mdss ahb clk. ret=%d\n",
-                       __func__, ret);
-               goto exit;
-       }
-
-       msm_host->axi_clk = devm_clk_get(dev, "bus_clk");
-       if (IS_ERR(msm_host->axi_clk)) {
-               ret = PTR_ERR(msm_host->axi_clk);
-               pr_err("%s: Unable to get axi bus clk. ret=%d\n",
-                       __func__, ret);
-               goto exit;
-       }
-
-       msm_host->mmss_misc_ahb_clk = devm_clk_get(dev, "core_mmss_clk");
-       if (IS_ERR(msm_host->mmss_misc_ahb_clk)) {
-               ret = PTR_ERR(msm_host->mmss_misc_ahb_clk);
-               pr_err("%s: Unable to get mmss misc ahb clk. ret=%d\n",
-                       __func__, ret);
-               goto exit;
+       const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
+       const struct msm_dsi_config *cfg = cfg_hnd->cfg;
+       int i, ret = 0;
+
+       /* get bus clocks */
+       for (i = 0; i < cfg->num_bus_clks; i++) {
+               msm_host->bus_clks[i] = devm_clk_get(dev,
+                                               cfg->bus_clk_names[i]);
+               if (IS_ERR(msm_host->bus_clks[i])) {
+                       ret = PTR_ERR(msm_host->bus_clks[i]);
+                       pr_err("%s: Unable to get %s, ret = %d\n",
+                               __func__, cfg->bus_clk_names[i], ret);
+                       goto exit;
+               }
        }
 
+       /* get link and source clocks */
        msm_host->byte_clk = devm_clk_get(dev, "byte_clk");
        if (IS_ERR(msm_host->byte_clk)) {
                ret = PTR_ERR(msm_host->byte_clk);
@@ -356,80 +383,85 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host)
                goto exit;
        }
 
-       msm_host->byte_clk_src = devm_clk_get(dev, "byte_clk_src");
-       if (IS_ERR(msm_host->byte_clk_src)) {
-               ret = PTR_ERR(msm_host->byte_clk_src);
+       msm_host->byte_clk_src = clk_get_parent(msm_host->byte_clk);
+       if (!msm_host->byte_clk_src) {
+               ret = -ENODEV;
                pr_err("%s: can't find byte_clk_src. ret=%d\n", __func__, ret);
-               msm_host->byte_clk_src = NULL;
                goto exit;
        }
 
-       msm_host->pixel_clk_src = devm_clk_get(dev, "pixel_clk_src");
-       if (IS_ERR(msm_host->pixel_clk_src)) {
-               ret = PTR_ERR(msm_host->pixel_clk_src);
+       msm_host->pixel_clk_src = clk_get_parent(msm_host->pixel_clk);
+       if (!msm_host->pixel_clk_src) {
+               ret = -ENODEV;
                pr_err("%s: can't find pixel_clk_src. ret=%d\n", __func__, ret);
-               msm_host->pixel_clk_src = NULL;
                goto exit;
        }
 
+       if (cfg_hnd->major == MSM_DSI_VER_MAJOR_V2) {
+               msm_host->src_clk = devm_clk_get(dev, "src_clk");
+               if (IS_ERR(msm_host->src_clk)) {
+                       ret = PTR_ERR(msm_host->src_clk);
+                       pr_err("%s: can't find dsi_src_clk. ret=%d\n",
+                               __func__, ret);
+                       msm_host->src_clk = NULL;
+                       goto exit;
+               }
+
+               msm_host->esc_clk_src = clk_get_parent(msm_host->esc_clk);
+               if (!msm_host->esc_clk_src) {
+                       ret = -ENODEV;
+                       pr_err("%s: can't get esc_clk_src. ret=%d\n",
+                               __func__, ret);
+                       goto exit;
+               }
+
+               msm_host->dsi_clk_src = clk_get_parent(msm_host->src_clk);
+               if (!msm_host->dsi_clk_src) {
+                       ret = -ENODEV;
+                       pr_err("%s: can't get dsi_clk_src. ret=%d\n",
+                               __func__, ret);
+               }
+       }
 exit:
        return ret;
 }
 
 static int dsi_bus_clk_enable(struct msm_dsi_host *msm_host)
 {
-       int ret;
+       const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg;
+       int i, ret;
 
        DBG("id=%d", msm_host->id);
 
-       ret = clk_prepare_enable(msm_host->mdp_core_clk);
-       if (ret) {
-               pr_err("%s: failed to enable mdp_core_clock, %d\n",
-                                                        __func__, ret);
-               goto core_clk_err;
-       }
-
-       ret = clk_prepare_enable(msm_host->ahb_clk);
-       if (ret) {
-               pr_err("%s: failed to enable ahb clock, %d\n", __func__, ret);
-               goto ahb_clk_err;
-       }
-
-       ret = clk_prepare_enable(msm_host->axi_clk);
-       if (ret) {
-               pr_err("%s: failed to enable ahb clock, %d\n", __func__, ret);
-               goto axi_clk_err;
-       }
-
-       ret = clk_prepare_enable(msm_host->mmss_misc_ahb_clk);
-       if (ret) {
-               pr_err("%s: failed to enable mmss misc ahb clk, %d\n",
-                       __func__, ret);
-               goto misc_ahb_clk_err;
+       for (i = 0; i < cfg->num_bus_clks; i++) {
+               ret = clk_prepare_enable(msm_host->bus_clks[i]);
+               if (ret) {
+                       pr_err("%s: failed to enable bus clock %d ret %d\n",
+                               __func__, i, ret);
+                       goto err;
+               }
        }
 
        return 0;
+err:
+       for (; i > 0; i--)
+               clk_disable_unprepare(msm_host->bus_clks[i]);
 
-misc_ahb_clk_err:
-       clk_disable_unprepare(msm_host->axi_clk);
-axi_clk_err:
-       clk_disable_unprepare(msm_host->ahb_clk);
-ahb_clk_err:
-       clk_disable_unprepare(msm_host->mdp_core_clk);
-core_clk_err:
        return ret;
 }
 
 static void dsi_bus_clk_disable(struct msm_dsi_host *msm_host)
 {
+       const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg;
+       int i;
+
        DBG("");
-       clk_disable_unprepare(msm_host->mmss_misc_ahb_clk);
-       clk_disable_unprepare(msm_host->axi_clk);
-       clk_disable_unprepare(msm_host->ahb_clk);
-       clk_disable_unprepare(msm_host->mdp_core_clk);
+
+       for (i = cfg->num_bus_clks - 1; i >= 0; i--)
+               clk_disable_unprepare(msm_host->bus_clks[i]);
 }
 
-static int dsi_link_clk_enable(struct msm_dsi_host *msm_host)
+static int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
 {
        int ret;
 
@@ -476,11 +508,98 @@ error:
        return ret;
 }
 
-static void dsi_link_clk_disable(struct msm_dsi_host *msm_host)
+static int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host)
 {
+       int ret;
+
+       DBG("Set clk rates: pclk=%d, byteclk=%d, esc_clk=%d, dsi_src_clk=%d",
+               msm_host->mode->clock, msm_host->byte_clk_rate,
+               msm_host->esc_clk_rate, msm_host->src_clk_rate);
+
+       ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate);
+       if (ret) {
+               pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret);
+               goto error;
+       }
+
+       ret = clk_set_rate(msm_host->esc_clk, msm_host->esc_clk_rate);
+       if (ret) {
+               pr_err("%s: Failed to set rate esc clk, %d\n", __func__, ret);
+               goto error;
+       }
+
+       ret = clk_set_rate(msm_host->src_clk, msm_host->src_clk_rate);
+       if (ret) {
+               pr_err("%s: Failed to set rate src clk, %d\n", __func__, ret);
+               goto error;
+       }
+
+       ret = clk_set_rate(msm_host->pixel_clk, msm_host->mode->clock * 1000);
+       if (ret) {
+               pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret);
+               goto error;
+       }
+
+       ret = clk_prepare_enable(msm_host->byte_clk);
+       if (ret) {
+               pr_err("%s: Failed to enable dsi byte clk\n", __func__);
+               goto error;
+       }
+
+       ret = clk_prepare_enable(msm_host->esc_clk);
+       if (ret) {
+               pr_err("%s: Failed to enable dsi esc clk\n", __func__);
+               goto esc_clk_err;
+       }
+
+       ret = clk_prepare_enable(msm_host->src_clk);
+       if (ret) {
+               pr_err("%s: Failed to enable dsi src clk\n", __func__);
+               goto src_clk_err;
+       }
+
+       ret = clk_prepare_enable(msm_host->pixel_clk);
+       if (ret) {
+               pr_err("%s: Failed to enable dsi pixel clk\n", __func__);
+               goto pixel_clk_err;
+       }
+
+       return 0;
+
+pixel_clk_err:
+       clk_disable_unprepare(msm_host->src_clk);
+src_clk_err:
        clk_disable_unprepare(msm_host->esc_clk);
-       clk_disable_unprepare(msm_host->pixel_clk);
+esc_clk_err:
        clk_disable_unprepare(msm_host->byte_clk);
+error:
+       return ret;
+}
+
+static int dsi_link_clk_enable(struct msm_dsi_host *msm_host)
+{
+       const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
+
+       if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G)
+               return dsi_link_clk_enable_6g(msm_host);
+       else
+               return dsi_link_clk_enable_v2(msm_host);
+}
+
+static void dsi_link_clk_disable(struct msm_dsi_host *msm_host)
+{
+       const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
+
+       if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) {
+               clk_disable_unprepare(msm_host->esc_clk);
+               clk_disable_unprepare(msm_host->pixel_clk);
+               clk_disable_unprepare(msm_host->byte_clk);
+       } else {
+               clk_disable_unprepare(msm_host->pixel_clk);
+               clk_disable_unprepare(msm_host->src_clk);
+               clk_disable_unprepare(msm_host->esc_clk);
+               clk_disable_unprepare(msm_host->byte_clk);
+       }
 }
 
 static int dsi_clk_ctrl(struct msm_dsi_host *msm_host, bool enable)
@@ -515,6 +634,7 @@ unlock_ret:
 static int dsi_calc_clk_rate(struct msm_dsi_host *msm_host)
 {
        struct drm_display_mode *mode = msm_host->mode;
+       const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
        u8 lanes = msm_host->lanes;
        u32 bpp = dsi_get_bpp(msm_host->format);
        u32 pclk_rate;
@@ -534,6 +654,47 @@ static int dsi_calc_clk_rate(struct msm_dsi_host *msm_host)
 
        DBG("pclk=%d, bclk=%d", pclk_rate, msm_host->byte_clk_rate);
 
+       msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk);
+
+       if (cfg_hnd->major == MSM_DSI_VER_MAJOR_V2) {
+               unsigned int esc_mhz, esc_div;
+               unsigned long byte_mhz;
+
+               msm_host->src_clk_rate = (pclk_rate * bpp) / 8;
+
+               /*
+                * esc clock is byte clock followed by a 4 bit divider,
+                * we need to find an escape clock frequency within the
+                * mipi DSI spec range within the maximum divider limit
+                * We iterate here between an escape clock frequencey
+                * between 20 Mhz to 5 Mhz and pick up the first one
+                * that can be supported by our divider
+                */
+
+               byte_mhz = msm_host->byte_clk_rate / 1000000;
+
+               for (esc_mhz = 20; esc_mhz >= 5; esc_mhz--) {
+                       esc_div = DIV_ROUND_UP(byte_mhz, esc_mhz);
+
+                       /*
+                        * TODO: Ideally, we shouldn't know what sort of divider
+                        * is available in mmss_cc, we're just assuming that
+                        * it'll always be a 4 bit divider. Need to come up with
+                        * a better way here.
+                        */
+                       if (esc_div >= 1 && esc_div <= 16)
+                               break;
+               }
+
+               if (esc_mhz < 5)
+                       return -EINVAL;
+
+               msm_host->esc_clk_rate = msm_host->byte_clk_rate / esc_div;
+
+               DBG("esc=%d, src=%d", msm_host->esc_clk_rate,
+                       msm_host->src_clk_rate);
+       }
+
        return 0;
 }
 
@@ -835,29 +996,46 @@ static void dsi_wait4video_eng_busy(struct msm_dsi_host *msm_host)
 static int dsi_tx_buf_alloc(struct msm_dsi_host *msm_host, int size)
 {
        struct drm_device *dev = msm_host->dev;
+       const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
        int ret;
        u32 iova;
 
-       mutex_lock(&dev->struct_mutex);
-       msm_host->tx_gem_obj = msm_gem_new(dev, size, MSM_BO_UNCACHED);
-       if (IS_ERR(msm_host->tx_gem_obj)) {
-               ret = PTR_ERR(msm_host->tx_gem_obj);
-               pr_err("%s: failed to allocate gem, %d\n", __func__, ret);
-               msm_host->tx_gem_obj = NULL;
+       if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) {
+               mutex_lock(&dev->struct_mutex);
+               msm_host->tx_gem_obj = msm_gem_new(dev, size, MSM_BO_UNCACHED);
+               if (IS_ERR(msm_host->tx_gem_obj)) {
+                       ret = PTR_ERR(msm_host->tx_gem_obj);
+                       pr_err("%s: failed to allocate gem, %d\n",
+                               __func__, ret);
+                       msm_host->tx_gem_obj = NULL;
+                       mutex_unlock(&dev->struct_mutex);
+                       return ret;
+               }
+
+               ret = msm_gem_get_iova_locked(msm_host->tx_gem_obj, 0, &iova);
                mutex_unlock(&dev->struct_mutex);
-               return ret;
-       }
+               if (ret) {
+                       pr_err("%s: failed to get iova, %d\n", __func__, ret);
+                       return ret;
+               }
 
-       ret = msm_gem_get_iova_locked(msm_host->tx_gem_obj, 0, &iova);
-       if (ret) {
-               pr_err("%s: failed to get iova, %d\n", __func__, ret);
-               return ret;
-       }
-       mutex_unlock(&dev->struct_mutex);
+               if (iova & 0x07) {
+                       pr_err("%s: buf NOT 8 bytes aligned\n", __func__);
+                       return -EINVAL;
+               }
 
-       if (iova & 0x07) {
-               pr_err("%s: buf NOT 8 bytes aligned\n", __func__);
-               return -EINVAL;
+               msm_host->tx_size = msm_host->tx_gem_obj->size;
+       } else {
+               msm_host->tx_buf = dma_alloc_coherent(dev->dev, size,
+                                       &msm_host->tx_buf_paddr, GFP_KERNEL);
+               if (!msm_host->tx_buf) {
+                       ret = -ENOMEM;
+                       pr_err("%s: failed to allocate tx buf, %d\n",
+                               __func__, ret);
+                       return ret;
+               }
+
+               msm_host->tx_size = size;
        }
 
        return 0;
@@ -874,14 +1052,19 @@ static void dsi_tx_buf_free(struct msm_dsi_host *msm_host)
                msm_host->tx_gem_obj = NULL;
                mutex_unlock(&dev->struct_mutex);
        }
+
+       if (msm_host->tx_buf)
+               dma_free_coherent(dev->dev, msm_host->tx_size, msm_host->tx_buf,
+                       msm_host->tx_buf_paddr);
 }
 
 /*
  * prepare cmd buffer to be txed
  */
-static int dsi_cmd_dma_add(struct drm_gem_object *tx_gem,
-                       const struct mipi_dsi_msg *msg)
+static int dsi_cmd_dma_add(struct msm_dsi_host *msm_host,
+                          const struct mipi_dsi_msg *msg)
 {
+       const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
        struct mipi_dsi_packet packet;
        int len;
        int ret;
@@ -894,17 +1077,20 @@ static int dsi_cmd_dma_add(struct drm_gem_object *tx_gem,
        }
        len = (packet.size + 3) & (~0x3);
 
-       if (len > tx_gem->size) {
+       if (len > msm_host->tx_size) {
                pr_err("%s: packet size is too big\n", __func__);
                return -EINVAL;
        }
 
-       data = msm_gem_vaddr(tx_gem);
-
-       if (IS_ERR(data)) {
-               ret = PTR_ERR(data);
-               pr_err("%s: get vaddr failed, %d\n", __func__, ret);
-               return ret;
+       if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) {
+               data = msm_gem_vaddr(msm_host->tx_gem_obj);
+               if (IS_ERR(data)) {
+                       ret = PTR_ERR(data);
+                       pr_err("%s: get vaddr failed, %d\n", __func__, ret);
+                       return ret;
+               }
+       } else {
+               data = msm_host->tx_buf;
        }
 
        /* MSM specific command format in memory */
@@ -970,17 +1156,21 @@ static int dsi_long_read_resp(u8 *buf, const struct mipi_dsi_msg *msg)
        return msg->rx_len;
 }
 
-
 static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len)
 {
+       const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
        int ret;
-       u32 iova;
+       u32 dma_base;
        bool triggered;
 
-       ret = msm_gem_get_iova(msm_host->tx_gem_obj, 0, &iova);
-       if (ret) {
-               pr_err("%s: failed to get iova: %d\n", __func__, ret);
-               return ret;
+       if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) {
+               ret = msm_gem_get_iova(msm_host->tx_gem_obj, 0, &dma_base);
+               if (ret) {
+                       pr_err("%s: failed to get iova: %d\n", __func__, ret);
+                       return ret;
+               }
+       } else {
+               dma_base = msm_host->tx_buf_paddr;
        }
 
        reinit_completion(&msm_host->dma_comp);
@@ -988,7 +1178,7 @@ static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len)
        dsi_wait4video_eng_busy(msm_host);
 
        triggered = msm_dsi_manager_cmd_xfer_trigger(
-                                               msm_host->id, iova, len);
+                                               msm_host->id, dma_base, len);
        if (triggered) {
                ret = wait_for_completion_timeout(&msm_host->dma_comp,
                                        msecs_to_jiffies(200));
@@ -1060,7 +1250,7 @@ static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host,
        int bllp_len = msm_host->mode->hdisplay *
                        dsi_get_bpp(msm_host->format) / 8;
 
-       len = dsi_cmd_dma_add(msm_host->tx_gem_obj, msg);
+       len = dsi_cmd_dma_add(msm_host, msg);
        if (!len) {
                pr_err("%s: failed to add cmd type = 0x%x\n",
                        __func__,  msg->type);
@@ -1383,6 +1573,16 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host)
 
        msm_host->device_node = device_node;
 
+       if (of_property_read_bool(np, "syscon-sfpb")) {
+               msm_host->sfpb = syscon_regmap_lookup_by_phandle(np,
+                                       "syscon-sfpb");
+               if (IS_ERR(msm_host->sfpb)) {
+                       dev_err(dev, "%s: failed to get sfpb regmap\n",
+                               __func__);
+                       return PTR_ERR(msm_host->sfpb);
+               }
+       }
+
        return 0;
 }
 
@@ -1408,12 +1608,6 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi)
                goto fail;
        }
 
-       ret = dsi_clk_init(msm_host);
-       if (ret) {
-               pr_err("%s: unable to initialize dsi clks\n", __func__);
-               goto fail;
-       }
-
        msm_host->ctrl_base = msm_ioremap(pdev, "dsi_ctrl", "DSI CTRL");
        if (IS_ERR(msm_host->ctrl_base)) {
                pr_err("%s: unable to map Dsi ctrl base\n", __func__);
@@ -1437,6 +1631,12 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi)
                goto fail;
        }
 
+       ret = dsi_clk_init(msm_host);
+       if (ret) {
+               pr_err("%s: unable to initialize dsi clks\n", __func__);
+               goto fail;
+       }
+
        msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL);
        if (!msm_host->rx_buf) {
                pr_err("%s: alloc rx temp buf failed\n", __func__);
@@ -1750,11 +1950,12 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host,
        return ret;
 }
 
-void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 iova, u32 len)
+void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base,
+                                 u32 len)
 {
        struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
 
-       dsi_write(msm_host, REG_DSI_DMA_BASE, iova);
+       dsi_write(msm_host, REG_DSI_DMA_BASE, dma_base);
        dsi_write(msm_host, REG_DSI_DMA_LEN, len);
        dsi_write(msm_host, REG_DSI_TRIG_DMA, 1);
 
@@ -1766,6 +1967,7 @@ int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host,
        struct msm_dsi_pll *src_pll)
 {
        struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+       const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
        struct clk *byte_clk_provider, *pixel_clk_provider;
        int ret;
 
@@ -1791,6 +1993,22 @@ int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host,
                goto exit;
        }
 
+       if (cfg_hnd->major == MSM_DSI_VER_MAJOR_V2) {
+               ret = clk_set_parent(msm_host->dsi_clk_src, pixel_clk_provider);
+               if (ret) {
+                       pr_err("%s: can't set parent to dsi_clk_src. ret=%d\n",
+                               __func__, ret);
+                       goto exit;
+               }
+
+               ret = clk_set_parent(msm_host->esc_clk_src, byte_clk_provider);
+               if (ret) {
+                       pr_err("%s: can't set parent to esc_clk_src. ret=%d\n",
+                               __func__, ret);
+                       goto exit;
+               }
+       }
+
 exit:
        return ret;
 }
@@ -1828,6 +2046,20 @@ int msm_dsi_host_disable(struct mipi_dsi_host *host)
        return 0;
 }
 
+static void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable)
+{
+       enum sfpb_ahb_arb_master_port_en en;
+
+       if (!msm_host->sfpb)
+               return;
+
+       en = enable ? SFPB_MASTER_PORT_ENABLE : SFPB_MASTER_PORT_DISABLE;
+
+       regmap_update_bits(msm_host->sfpb, REG_SFPB_GPREG,
+                       SFPB_GPREG_MASTER_PORT_EN__MASK,
+                       SFPB_GPREG_MASTER_PORT_EN(en));
+}
+
 int msm_dsi_host_power_on(struct mipi_dsi_host *host)
 {
        struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
@@ -1840,6 +2072,8 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host)
                goto unlock_ret;
        }
 
+       msm_dsi_sfpb_config(msm_host, true);
+
        ret = dsi_calc_clk_rate(msm_host);
        if (ret) {
                pr_err("%s: unable to calc clk rate, %d\n", __func__, ret);
@@ -1862,7 +2096,7 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host)
        dsi_phy_sw_reset(msm_host);
        ret = msm_dsi_manager_phy_enable(msm_host->id,
                                        msm_host->byte_clk_rate * 8,
-                                       clk_get_rate(msm_host->esc_clk),
+                                       msm_host->esc_clk_rate,
                                        &clk_pre, &clk_post);
        dsi_bus_clk_disable(msm_host);
        if (ret) {
@@ -1927,6 +2161,8 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host)
 
        dsi_host_regulator_disable(msm_host);
 
+       msm_dsi_sfpb_config(msm_host, false);
+
        DBG("-");
 
        msm_host->power_on = false;
index 0455ff7..58ba7ec 100644 (file)
@@ -774,7 +774,7 @@ restore_host0:
        return ret;
 }
 
-bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len)
+bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len)
 {
        struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
        struct msm_dsi *msm_dsi0 = dsi_mgr_get_dsi(DSI_0);
@@ -784,9 +784,9 @@ bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len)
                return false;
 
        if (IS_SYNC_NEEDED() && msm_dsi0)
-               msm_dsi_host_cmd_xfer_commit(msm_dsi0->host, iova, len);
+               msm_dsi_host_cmd_xfer_commit(msm_dsi0->host, dma_base, len);
 
-       msm_dsi_host_cmd_xfer_commit(host, iova, len);
+       msm_dsi_host_cmd_xfer_commit(host, dma_base, len);
 
        return true;
 }
index f1f955f..91a95fb 100644 (file)
@@ -276,6 +276,10 @@ static const struct of_device_id dsi_phy_dt_match[] = {
 #ifdef CONFIG_DRM_MSM_DSI_20NM_PHY
        { .compatible = "qcom,dsi-phy-20nm",
          .data = &dsi_phy_20nm_cfgs },
+#endif
+#ifdef CONFIG_DRM_MSM_DSI_28NM_8960_PHY
+       { .compatible = "qcom,dsi-phy-28nm-8960",
+         .data = &dsi_phy_28nm_8960_cfgs },
 #endif
        {}
 };
index 0456b25..0d54ed0 100644 (file)
@@ -43,6 +43,7 @@ struct msm_dsi_phy_cfg {
 extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs;
 extern const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs;
 extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs;
+extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs;
 
 struct msm_dsi_dphy_timing {
        u32 clk_pre;
diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c
new file mode 100644 (file)
index 0000000..197b039
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "dsi_phy.h"
+#include "dsi.xml.h"
+
+static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy,
+               struct msm_dsi_dphy_timing *timing)
+{
+       void __iomem *base = phy->base;
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_0,
+               DSI_28nm_8960_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_1,
+               DSI_28nm_8960_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_2,
+               DSI_28nm_8960_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_3, 0x0);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_4,
+               DSI_28nm_8960_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_5,
+               DSI_28nm_8960_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_6,
+               DSI_28nm_8960_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_7,
+               DSI_28nm_8960_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_8,
+               DSI_28nm_8960_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_9,
+               DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) |
+               DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_10,
+               DSI_28nm_8960_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get));
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_11,
+               DSI_28nm_8960_PHY_TIMING_CTRL_11_TRIG3_CMD(0));
+}
+
+static void dsi_28nm_phy_regulator_init(struct msm_dsi_phy *phy)
+{
+       void __iomem *base = phy->reg_base;
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_0, 0x3);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_1, 1);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_2, 1);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_3, 0);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_4,
+               0x100);
+}
+
+static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy)
+{
+       void __iomem *base = phy->reg_base;
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_0, 0x3);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_1, 0xa);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_2, 0x4);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_3, 0x0);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_4, 0x20);
+}
+
+static void dsi_28nm_phy_calibration(struct msm_dsi_phy *phy)
+{
+       void __iomem *base = phy->reg_base;
+       u32 status;
+       int i = 5000;
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CAL_PWR_CFG,
+                       0x3);
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_CAL_SW_CFG_2, 0x0);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_1, 0x5a);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_3, 0x10);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_4, 0x1);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_0, 0x1);
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_TRIGGER, 0x1);
+       usleep_range(5000, 6000);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_TRIGGER, 0x0);
+
+       do {
+               status = dsi_phy_read(base +
+                               REG_DSI_28nm_8960_PHY_MISC_CAL_STATUS);
+
+               if (!(status & DSI_28nm_8960_PHY_MISC_CAL_STATUS_CAL_BUSY))
+                       break;
+
+               udelay(1);
+       } while (--i > 0);
+}
+
+static void dsi_28nm_phy_lane_config(struct msm_dsi_phy *phy)
+{
+       void __iomem *base = phy->base;
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LN_CFG_0(i), 0x80);
+               dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LN_CFG_1(i), 0x45);
+               dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LN_CFG_2(i), 0x00);
+               dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LN_TEST_DATAPATH(i),
+                       0x00);
+               dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LN_TEST_STR_0(i),
+                       0x01);
+               dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LN_TEST_STR_1(i),
+                       0x66);
+       }
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LNCK_CFG_0, 0x40);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LNCK_CFG_1, 0x67);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LNCK_CFG_2, 0x0);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LNCK_TEST_DATAPATH, 0x0);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LNCK_TEST_STR0, 0x1);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LNCK_TEST_STR1, 0x88);
+}
+
+static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id,
+               const unsigned long bit_rate, const unsigned long esc_rate)
+{
+       struct msm_dsi_dphy_timing *timing = &phy->timing;
+       void __iomem *base = phy->base;
+
+       DBG("");
+
+       if (msm_dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) {
+               dev_err(&phy->pdev->dev,
+                       "%s: D-PHY timing calculation failed\n", __func__);
+               return -EINVAL;
+       }
+
+       dsi_28nm_phy_regulator_init(phy);
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_LDO_CTRL, 0x04);
+
+       /* strength control */
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_STRENGTH_0, 0xff);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_STRENGTH_1, 0x00);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_STRENGTH_2, 0x06);
+
+       /* phy ctrl */
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_CTRL_0, 0x5f);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_CTRL_1, 0x00);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_CTRL_2, 0x00);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_CTRL_3, 0x10);
+
+       dsi_28nm_phy_regulator_ctrl(phy);
+
+       dsi_28nm_phy_calibration(phy);
+
+       dsi_28nm_phy_lane_config(phy);
+
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_BIST_CTRL_4, 0x0f);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_BIST_CTRL_1, 0x03);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_BIST_CTRL_0, 0x03);
+       dsi_phy_write(base + REG_DSI_28nm_8960_PHY_BIST_CTRL_4, 0x0);
+
+       dsi_28nm_dphy_set_timing(phy, timing);
+
+       return 0;
+}
+
+static void dsi_28nm_phy_disable(struct msm_dsi_phy *phy)
+{
+       dsi_phy_write(phy->base + REG_DSI_28nm_8960_PHY_CTRL_0, 0x0);
+
+       /*
+        * Wait for the registers writes to complete in order to
+        * ensure that the phy is completely disabled
+        */
+       wmb();
+}
+
+const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs = {
+       .type = MSM_DSI_PHY_28NM_8960,
+       .src_pll_truthtable = { {true, true}, {false, true} },
+       .reg_cfg = {
+               .num = 1,
+               .regs = {
+                       {"vddio", 1800000, 1800000, 100000, 100},
+               },
+       },
+       .ops = {
+               .enable = dsi_28nm_phy_enable,
+               .disable = dsi_28nm_phy_disable,
+       },
+};
index 5104fc9..5cd438f 100644 (file)
@@ -151,6 +151,9 @@ struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
        case MSM_DSI_PHY_28NM_LP:
                pll = msm_dsi_pll_28nm_init(pdev, type, id);
                break;
+       case MSM_DSI_PHY_28NM_8960:
+               pll = msm_dsi_pll_28nm_8960_init(pdev, id);
+               break;
        default:
                pll = ERR_PTR(-ENXIO);
                break;
index 063caa2..80b6038 100644 (file)
@@ -93,6 +93,16 @@ static inline struct msm_dsi_pll *msm_dsi_pll_28nm_init(
        return ERR_PTR(-ENODEV);
 }
 #endif
+#ifdef CONFIG_DRM_MSM_DSI_28NM_8960_PHY
+struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init(struct platform_device *pdev,
+                                              int id);
+#else
+struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init(struct platform_device *pdev,
+                                              int id)
+{
+       return ERR_PTR(-ENODEV);
+}
+#endif
 
 #endif /* __DSI_PLL_H__ */
 
diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm_8960.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm_8960.c
new file mode 100644 (file)
index 0000000..38c90e1
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+
+#include "dsi_pll.h"
+#include "dsi.xml.h"
+
+/*
+ * DSI PLL 28nm (8960/A family) - clock diagram (eg: DSI1):
+ *
+ *
+ *                        +------+
+ *  dsi1vco_clk ----o-----| DIV1 |---dsi1pllbit (not exposed as clock)
+ *  F * byte_clk    |     +------+
+ *                  | bit clock divider (F / 8)
+ *                  |
+ *                  |     +------+
+ *                  o-----| DIV2 |---dsi0pllbyte---o---> To byte RCG
+ *                  |     +------+                 | (sets parent rate)
+ *                  | byte clock divider (F)       |
+ *                  |                              |
+ *                  |                              o---> To esc RCG
+ *                  |                                (doesn't set parent rate)
+ *                  |
+ *                  |     +------+
+ *                  o-----| DIV3 |----dsi0pll------o---> To dsi RCG
+ *                        +------+                 | (sets parent rate)
+ *                  dsi clock divider (F * magic)  |
+ *                                                 |
+ *                                                 o---> To pixel rcg
+ *                                                  (doesn't set parent rate)
+ */
+
+#define POLL_MAX_READS         8000
+#define POLL_TIMEOUT_US                1
+
+#define NUM_PROVIDED_CLKS      2
+
+#define VCO_REF_CLK_RATE       27000000
+#define VCO_MIN_RATE           600000000
+#define VCO_MAX_RATE           1200000000
+
+#define DSI_BYTE_PLL_CLK       0
+#define DSI_PIXEL_PLL_CLK      1
+
+#define VCO_PREF_DIV_RATIO     27
+
+struct pll_28nm_cached_state {
+       unsigned long vco_rate;
+       u8 postdiv3;
+       u8 postdiv2;
+       u8 postdiv1;
+};
+
+struct clk_bytediv {
+       struct clk_hw hw;
+       void __iomem *reg;
+};
+
+struct dsi_pll_28nm {
+       struct msm_dsi_pll base;
+
+       int id;
+       struct platform_device *pdev;
+       void __iomem *mmio;
+
+       /* custom byte clock divider */
+       struct clk_bytediv *bytediv;
+
+       /* private clocks: */
+       struct clk *clks[NUM_DSI_CLOCKS_MAX];
+       u32 num_clks;
+
+       /* clock-provider: */
+       struct clk *provided_clks[NUM_PROVIDED_CLKS];
+       struct clk_onecell_data clk_data;
+
+       struct pll_28nm_cached_state cached_state;
+};
+
+#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, base)
+
+static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm,
+                                   int nb_tries, int timeout_us)
+{
+       bool pll_locked = false;
+       u32 val;
+
+       while (nb_tries--) {
+               val = pll_read(pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_RDY);
+               pll_locked = !!(val & DSI_28nm_8960_PHY_PLL_RDY_PLL_RDY);
+
+               if (pll_locked)
+                       break;
+
+               udelay(timeout_us);
+       }
+       DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
+
+       return pll_locked;
+}
+
+/*
+ * Clock Callbacks
+ */
+static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+                                    unsigned long parent_rate)
+{
+       struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+       void __iomem *base = pll_28nm->mmio;
+       u32 val, temp, fb_divider;
+
+       DBG("rate=%lu, parent's=%lu", rate, parent_rate);
+
+       temp = rate / 10;
+       val = VCO_REF_CLK_RATE / 10;
+       fb_divider = (temp * VCO_PREF_DIV_RATIO) / val;
+       fb_divider = fb_divider / 2 - 1;
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_1,
+                       fb_divider & 0xff);
+
+       val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2);
+
+       val |= (fb_divider >> 8) & 0x07;
+
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2,
+                       val);
+
+       val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
+
+       val |= (VCO_PREF_DIV_RATIO - 1) & 0x3f;
+
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3,
+                       val);
+
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_6,
+                       0xf);
+
+       val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
+       val |= 0x7 << 4;
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8,
+                       val);
+
+       return 0;
+}
+
+static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw)
+{
+       struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+
+       return pll_28nm_poll_for_ready(pll_28nm, POLL_MAX_READS,
+                                       POLL_TIMEOUT_US);
+}
+
+static unsigned long dsi_pll_28nm_clk_recalc_rate(struct clk_hw *hw,
+                                                 unsigned long parent_rate)
+{
+       struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+       void __iomem *base = pll_28nm->mmio;
+       unsigned long vco_rate;
+       u32 status, fb_divider, temp, ref_divider;
+
+       VERB("parent_rate=%lu", parent_rate);
+
+       status = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0);
+
+       if (status & DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE) {
+               fb_divider = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_1);
+               fb_divider &= 0xff;
+               temp = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2) & 0x07;
+               fb_divider = (temp << 8) | fb_divider;
+               fb_divider += 1;
+
+               ref_divider = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
+               ref_divider &= 0x3f;
+               ref_divider += 1;
+
+               /* multiply by 2 */
+               vco_rate = (parent_rate / ref_divider) * fb_divider * 2;
+       } else {
+               vco_rate = 0;
+       }
+
+       DBG("returning vco rate = %lu", vco_rate);
+
+       return vco_rate;
+}
+
+static const struct clk_ops clk_ops_dsi_pll_28nm_vco = {
+       .round_rate = msm_dsi_pll_helper_clk_round_rate,
+       .set_rate = dsi_pll_28nm_clk_set_rate,
+       .recalc_rate = dsi_pll_28nm_clk_recalc_rate,
+       .prepare = msm_dsi_pll_helper_clk_prepare,
+       .unprepare = msm_dsi_pll_helper_clk_unprepare,
+       .is_enabled = dsi_pll_28nm_clk_is_enabled,
+};
+
+/*
+ * Custom byte clock divier clk_ops
+ *
+ * This clock is the entry point to configuring the PLL. The user (dsi host)
+ * will set this clock's rate to the desired byte clock rate. The VCO lock
+ * frequency is a multiple of the byte clock rate. The multiplication factor
+ * (shown as F in the diagram above) is a function of the byte clock rate.
+ *
+ * This custom divider clock ensures that its parent (VCO) is set to the
+ * desired rate, and that the byte clock postdivider (POSTDIV2) is configured
+ * accordingly
+ */
+#define to_clk_bytediv(_hw) container_of(_hw, struct clk_bytediv, hw)
+
+static unsigned long clk_bytediv_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_bytediv *bytediv = to_clk_bytediv(hw);
+       unsigned int div;
+
+       div = pll_read(bytediv->reg) & 0xff;
+
+       return parent_rate / (div + 1);
+}
+
+/* find multiplication factor(wrt byte clock) at which the VCO should be set */
+static unsigned int get_vco_mul_factor(unsigned long byte_clk_rate)
+{
+       unsigned long bit_mhz;
+
+       /* convert to bit clock in Mhz */
+       bit_mhz = (byte_clk_rate * 8) / 1000000;
+
+       if (bit_mhz < 125)
+               return 64;
+       else if (bit_mhz < 250)
+               return 32;
+       else if (bit_mhz < 600)
+               return 16;
+       else
+               return 8;
+}
+
+static long clk_bytediv_round_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long *prate)
+{
+       unsigned long best_parent;
+       unsigned int factor;
+
+       factor = get_vco_mul_factor(rate);
+
+       best_parent = rate * factor;
+       *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
+
+       return *prate / factor;
+}
+
+static int clk_bytediv_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+{
+       struct clk_bytediv *bytediv = to_clk_bytediv(hw);
+       u32 val;
+       unsigned int factor;
+
+       factor = get_vco_mul_factor(rate);
+
+       val = pll_read(bytediv->reg);
+       val |= (factor - 1) & 0xff;
+       pll_write(bytediv->reg, val);
+
+       return 0;
+}
+
+/* Our special byte clock divider ops */
+static const struct clk_ops clk_bytediv_ops = {
+       .round_rate = clk_bytediv_round_rate,
+       .set_rate = clk_bytediv_set_rate,
+       .recalc_rate = clk_bytediv_recalc_rate,
+};
+
+/*
+ * PLL Callbacks
+ */
+static int dsi_pll_28nm_enable_seq(struct msm_dsi_pll *pll)
+{
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+       struct device *dev = &pll_28nm->pdev->dev;
+       void __iomem *base = pll_28nm->mmio;
+       bool locked;
+       unsigned int bit_div, byte_div;
+       int max_reads = 1000, timeout_us = 100;
+       u32 val;
+
+       DBG("id=%d", pll_28nm->id);
+
+       /*
+        * before enabling the PLL, configure the bit clock divider since we
+        * don't expose it as a clock to the outside world
+        * 1: read back the byte clock divider that should already be set
+        * 2: divide by 8 to get bit clock divider
+        * 3: write it to POSTDIV1
+        */
+       val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
+       byte_div = val + 1;
+       bit_div = byte_div / 8;
+
+       val = pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
+       val &= ~0xf;
+       val |= (bit_div - 1);
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8, val);
+
+       /* enable the PLL */
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0,
+                       DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE);
+
+       locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us);
+
+       if (unlikely(!locked))
+               dev_err(dev, "DSI PLL lock failed\n");
+       else
+               DBG("DSI PLL lock success");
+
+       return locked ? 0 : -EINVAL;
+}
+
+static void dsi_pll_28nm_disable_seq(struct msm_dsi_pll *pll)
+{
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+
+       DBG("id=%d", pll_28nm->id);
+       pll_write(pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_CTRL_0, 0x00);
+}
+
+static void dsi_pll_28nm_save_state(struct msm_dsi_pll *pll)
+{
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+       struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
+       void __iomem *base = pll_28nm->mmio;
+
+       cached_state->postdiv3 =
+                       pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_10);
+       cached_state->postdiv2 =
+                       pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
+       cached_state->postdiv1 =
+                       pll_read(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
+
+       cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw);
+}
+
+static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll)
+{
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+       struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
+       void __iomem *base = pll_28nm->mmio;
+       int ret;
+
+       ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw,
+                                       cached_state->vco_rate, 0);
+       if (ret) {
+               dev_err(&pll_28nm->pdev->dev,
+                       "restore vco rate failed. ret=%d\n", ret);
+               return ret;
+       }
+
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_10,
+                       cached_state->postdiv3);
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9,
+                       cached_state->postdiv2);
+       pll_write(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8,
+                       cached_state->postdiv1);
+
+       return 0;
+}
+
+static int dsi_pll_28nm_get_provider(struct msm_dsi_pll *pll,
+                               struct clk **byte_clk_provider,
+                               struct clk **pixel_clk_provider)
+{
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+
+       if (byte_clk_provider)
+               *byte_clk_provider = pll_28nm->provided_clks[DSI_BYTE_PLL_CLK];
+       if (pixel_clk_provider)
+               *pixel_clk_provider =
+                               pll_28nm->provided_clks[DSI_PIXEL_PLL_CLK];
+
+       return 0;
+}
+
+static void dsi_pll_28nm_destroy(struct msm_dsi_pll *pll)
+{
+       struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+
+       msm_dsi_pll_helper_unregister_clks(pll_28nm->pdev,
+                                       pll_28nm->clks, pll_28nm->num_clks);
+}
+
+static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm)
+{
+       char *clk_name, *parent_name, *vco_name;
+       struct clk_init_data vco_init = {
+               .parent_names = (const char *[]){ "pxo" },
+               .num_parents = 1,
+               .ops = &clk_ops_dsi_pll_28nm_vco,
+       };
+       struct device *dev = &pll_28nm->pdev->dev;
+       struct clk **clks = pll_28nm->clks;
+       struct clk **provided_clks = pll_28nm->provided_clks;
+       struct clk_bytediv *bytediv;
+       struct clk_init_data bytediv_init = { };
+       int ret, num = 0;
+
+       DBG("%d", pll_28nm->id);
+
+       bytediv = devm_kzalloc(dev, sizeof(*bytediv), GFP_KERNEL);
+       if (!bytediv)
+               return -ENOMEM;
+
+       vco_name = devm_kzalloc(dev, 32, GFP_KERNEL);
+       if (!vco_name)
+               return -ENOMEM;
+
+       parent_name = devm_kzalloc(dev, 32, GFP_KERNEL);
+       if (!parent_name)
+               return -ENOMEM;
+
+       clk_name = devm_kzalloc(dev, 32, GFP_KERNEL);
+       if (!clk_name)
+               return -ENOMEM;
+
+       pll_28nm->bytediv = bytediv;
+
+       snprintf(vco_name, 32, "dsi%dvco_clk", pll_28nm->id);
+       vco_init.name = vco_name;
+
+       pll_28nm->base.clk_hw.init = &vco_init;
+
+       clks[num++] = clk_register(dev, &pll_28nm->base.clk_hw);
+
+       /* prepare and register bytediv */
+       bytediv->hw.init = &bytediv_init;
+       bytediv->reg = pll_28nm->mmio + REG_DSI_28nm_8960_PHY_PLL_CTRL_9;
+
+       snprintf(parent_name, 32, "dsi%dvco_clk", pll_28nm->id);
+       snprintf(clk_name, 32, "dsi%dpllbyte", pll_28nm->id);
+
+       bytediv_init.name = clk_name;
+       bytediv_init.ops = &clk_bytediv_ops;
+       bytediv_init.flags = CLK_SET_RATE_PARENT;
+       bytediv_init.parent_names = (const char * const *) &parent_name;
+       bytediv_init.num_parents = 1;
+
+       /* DIV2 */
+       clks[num++] = provided_clks[DSI_BYTE_PLL_CLK] =
+                       clk_register(dev, &bytediv->hw);
+
+       snprintf(clk_name, 32, "dsi%dpll", pll_28nm->id);
+       /* DIV3 */
+       clks[num++] = provided_clks[DSI_PIXEL_PLL_CLK] =
+                       clk_register_divider(dev, clk_name,
+                               parent_name, 0, pll_28nm->mmio +
+                               REG_DSI_28nm_8960_PHY_PLL_CTRL_10,
+                               0, 8, 0, NULL);
+
+       pll_28nm->num_clks = num;
+
+       pll_28nm->clk_data.clk_num = NUM_PROVIDED_CLKS;
+       pll_28nm->clk_data.clks = provided_clks;
+
+       ret = of_clk_add_provider(dev->of_node,
+                       of_clk_src_onecell_get, &pll_28nm->clk_data);
+       if (ret) {
+               dev_err(dev, "failed to register clk provider: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init(struct platform_device *pdev,
+                                              int id)
+{
+       struct dsi_pll_28nm *pll_28nm;
+       struct msm_dsi_pll *pll;
+       int ret;
+
+       if (!pdev)
+               return ERR_PTR(-ENODEV);
+
+       pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL);
+       if (!pll_28nm)
+               return ERR_PTR(-ENOMEM);
+
+       pll_28nm->pdev = pdev;
+       pll_28nm->id = id + 1;
+
+       pll_28nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
+       if (IS_ERR_OR_NULL(pll_28nm->mmio)) {
+               dev_err(&pdev->dev, "%s: failed to map pll base\n", __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       pll = &pll_28nm->base;
+       pll->min_rate = VCO_MIN_RATE;
+       pll->max_rate = VCO_MAX_RATE;
+       pll->get_provider = dsi_pll_28nm_get_provider;
+       pll->destroy = dsi_pll_28nm_destroy;
+       pll->disable_seq = dsi_pll_28nm_disable_seq;
+       pll->save_state = dsi_pll_28nm_save_state;
+       pll->restore_state = dsi_pll_28nm_restore_state;
+
+       pll->en_seq_cnt = 1;
+       pll->enable_seqs[0] = dsi_pll_28nm_enable_seq;
+
+       ret = pll_28nm_register(pll_28nm);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register PLL: %d\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       return pll;
+}
index 1f4a95e..9a0989c 100644 (file)
@@ -17,6 +17,8 @@
  */
 
 #include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+
 #include "hdmi.h"
 
 void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
@@ -322,8 +324,6 @@ fail:
  * The hdmi device:
  */
 
-#include <linux/of_gpio.h>
-
 #define HDMI_CFG(item, entry) \
        .item ## _names = item ##_names_ ## entry, \
        .item ## _cnt   = ARRAY_SIZE(item ## _names_ ## entry)
@@ -388,17 +388,6 @@ static struct hdmi_platform_config hdmi_tx_8996_config = {
                .hpd_freq      = hpd_clk_freq_8x74,
 };
 
-static const struct of_device_id dt_match[] = {
-       { .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config },
-       { .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config },
-       { .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config },
-       { .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config },
-       { .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config },
-       { .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config },
-       {}
-};
-
-#ifdef CONFIG_OF
 static int get_gpio(struct device *dev, struct device_node *of_node, const char *name)
 {
        int gpio = of_get_named_gpio(of_node, name, 0);
@@ -413,7 +402,6 @@ static int get_gpio(struct device *dev, struct device_node *of_node, const char
        }
        return gpio;
 }
-#endif
 
 static int hdmi_bind(struct device *dev, struct device *master, void *data)
 {
@@ -421,16 +409,12 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
        struct msm_drm_private *priv = drm->dev_private;
        static struct hdmi_platform_config *hdmi_cfg;
        struct hdmi *hdmi;
-#ifdef CONFIG_OF
        struct device_node *of_node = dev->of_node;
-       const struct of_device_id *match;
 
-       match = of_match_node(dt_match, of_node);
-       if (match && match->data) {
-               hdmi_cfg = (struct hdmi_platform_config *)match->data;
-               DBG("hdmi phy: %s", match->compatible);
-       } else {
-               dev_err(dev, "unknown phy: %s\n", of_node->name);
+       hdmi_cfg = (struct hdmi_platform_config *)
+                       of_device_get_match_data(dev);
+       if (!hdmi_cfg) {
+               dev_err(dev, "unknown hdmi_cfg: %s\n", of_node->name);
                return -ENXIO;
        }
 
@@ -443,55 +427,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
        hdmi_cfg->mux_sel_gpio  = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-sel");
        hdmi_cfg->mux_lpm_gpio  = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-lpm");
 
-#else
-       static struct hdmi_platform_config config = {};
-       static const char *hpd_clk_names[] = {
-                       "core_clk", "master_iface_clk", "slave_iface_clk",
-       };
-       if (cpu_is_apq8064()) {
-               static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
-               config.phy_init      = hdmi_phy_8960_init;
-               config.hpd_reg_names = hpd_reg_names;
-               config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
-               config.hpd_clk_names = hpd_clk_names;
-               config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
-               config.ddc_clk_gpio  = 70;
-               config.ddc_data_gpio = 71;
-               config.hpd_gpio      = 72;
-               config.mux_en_gpio   = -1;
-               config.mux_sel_gpio  = -1;
-       } else if (cpu_is_msm8960() || cpu_is_msm8960ab()) {
-               static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
-               config.phy_init      = hdmi_phy_8960_init;
-               config.hpd_reg_names = hpd_reg_names;
-               config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
-               config.hpd_clk_names = hpd_clk_names;
-               config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
-               config.ddc_clk_gpio  = 100;
-               config.ddc_data_gpio = 101;
-               config.hpd_gpio      = 102;
-               config.mux_en_gpio   = -1;
-               config.mux_sel_gpio  = -1;
-       } else if (cpu_is_msm8x60()) {
-               static const char *hpd_reg_names[] = {
-                               "8901_hdmi_mvs", "8901_mpp0"
-               };
-               config.phy_init      = hdmi_phy_8x60_init;
-               config.hpd_reg_names = hpd_reg_names;
-               config.hpd_reg_cnt   = ARRAY_SIZE(hpd_reg_names);
-               config.hpd_clk_names = hpd_clk_names;
-               config.hpd_clk_cnt   = ARRAY_SIZE(hpd_clk_names);
-               config.ddc_clk_gpio  = 170;
-               config.ddc_data_gpio = 171;
-               config.hpd_gpio      = 172;
-               config.mux_en_gpio   = -1;
-               config.mux_sel_gpio  = -1;
-       }
-       config.mmio_name     = "hdmi_msm_hdmi_addr";
-       config.qfprom_mmio_name = "hdmi_msm_qfprom_addr";
-
-       hdmi_cfg = &config;
-#endif
        dev->platform_data = hdmi_cfg;
 
        hdmi = hdmi_init(to_platform_device(dev));
@@ -529,6 +464,16 @@ static int hdmi_dev_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id dt_match[] = {
+       { .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config },
+       { .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config },
+       { .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config },
+       { .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config },
+       { .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config },
+       { .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config },
+       {}
+};
+
 static struct platform_driver hdmi_driver = {
        .probe = hdmi_dev_probe,
        .remove = hdmi_dev_remove,
index 6ac9aa1..28df397 100644 (file)
@@ -678,7 +678,8 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
        drm_flip_work_init(&mdp4_crtc->unref_cursor_work,
                        "unref cursor", unref_cursor_worker);
 
-       drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs);
+       drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs,
+                                 NULL);
        drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
        plane->crtc = crtc;
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dsi_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dsi_encoder.c
new file mode 100644 (file)
index 0000000..2f57e94
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, Inforce Computing. All rights reserved.
+ *
+ * Author: Vinay Simha <vinaysimha@inforcecomputing.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mdp4_kms.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+struct mdp4_dsi_encoder {
+       struct drm_encoder base;
+       struct drm_panel *panel;
+       bool enabled;
+};
+#define to_mdp4_dsi_encoder(x) container_of(x, struct mdp4_dsi_encoder, base)
+
+static struct mdp4_kms *get_kms(struct drm_encoder *encoder)
+{
+       struct msm_drm_private *priv = encoder->dev->dev_private;
+       return to_mdp4_kms(to_mdp_kms(priv->kms));
+}
+
+static void mdp4_dsi_encoder_destroy(struct drm_encoder *encoder)
+{
+       struct mdp4_dsi_encoder *mdp4_dsi_encoder = to_mdp4_dsi_encoder(encoder);
+
+       drm_encoder_cleanup(encoder);
+       kfree(mdp4_dsi_encoder);
+}
+
+static const struct drm_encoder_funcs mdp4_dsi_encoder_funcs = {
+       .destroy = mdp4_dsi_encoder_destroy,
+};
+
+static bool mdp4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+                                       const struct drm_display_mode *mode,
+                                       struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static void mdp4_dsi_encoder_mode_set(struct drm_encoder *encoder,
+                                     struct drm_display_mode *mode,
+                                     struct drm_display_mode *adjusted_mode)
+{
+       struct mdp4_kms *mdp4_kms = get_kms(encoder);
+       uint32_t dsi_hsync_skew, vsync_period, vsync_len, ctrl_pol;
+       uint32_t display_v_start, display_v_end;
+       uint32_t hsync_start_x, hsync_end_x;
+
+       mode = adjusted_mode;
+
+       DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+                       mode->base.id, mode->name,
+                       mode->vrefresh, mode->clock,
+                       mode->hdisplay, mode->hsync_start,
+                       mode->hsync_end, mode->htotal,
+                       mode->vdisplay, mode->vsync_start,
+                       mode->vsync_end, mode->vtotal,
+                       mode->type, mode->flags);
+
+       ctrl_pol = 0;
+       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+               ctrl_pol |= MDP4_DSI_CTRL_POLARITY_HSYNC_LOW;
+       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+               ctrl_pol |= MDP4_DSI_CTRL_POLARITY_VSYNC_LOW;
+       /* probably need to get DATA_EN polarity from panel.. */
+
+       dsi_hsync_skew = 0;  /* get this from panel? */
+
+       hsync_start_x = (mode->htotal - mode->hsync_start);
+       hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1;
+
+       vsync_period = mode->vtotal * mode->htotal;
+       vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal;
+       display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dsi_hsync_skew;
+       display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dsi_hsync_skew - 1;
+
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_HSYNC_CTRL,
+                       MDP4_DSI_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) |
+                       MDP4_DSI_HSYNC_CTRL_PERIOD(mode->htotal));
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_VSYNC_PERIOD, vsync_period);
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_VSYNC_LEN, vsync_len);
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_DISPLAY_HCTRL,
+                       MDP4_DSI_DISPLAY_HCTRL_START(hsync_start_x) |
+                       MDP4_DSI_DISPLAY_HCTRL_END(hsync_end_x));
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_DISPLAY_VSTART, display_v_start);
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_DISPLAY_VEND, display_v_end);
+
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_CTRL_POLARITY, ctrl_pol);
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_UNDERFLOW_CLR,
+                       MDP4_DSI_UNDERFLOW_CLR_ENABLE_RECOVERY |
+                       MDP4_DSI_UNDERFLOW_CLR_COLOR(0xff));
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_ACTIVE_HCTL,
+                       MDP4_DSI_ACTIVE_HCTL_START(0) |
+                       MDP4_DSI_ACTIVE_HCTL_END(0));
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_HSYNC_SKEW, dsi_hsync_skew);
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_BORDER_CLR, 0);
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_ACTIVE_VSTART, 0);
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_ACTIVE_VEND, 0);
+}
+
+static void mdp4_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+       struct mdp4_dsi_encoder *mdp4_dsi_encoder = to_mdp4_dsi_encoder(encoder);
+       struct mdp4_kms *mdp4_kms = get_kms(encoder);
+
+       if (!mdp4_dsi_encoder->enabled)
+               return;
+
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0);
+
+       /*
+        * Wait for a vsync so we know the ENABLE=0 latched before
+        * the (connector) source of the vsync's gets disabled,
+        * otherwise we end up in a funny state if we re-enable
+        * before the disable latches, which results that some of
+        * the settings changes for the new modeset (like new
+        * scanout buffer) don't latch properly..
+        */
+       mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_PRIMARY_VSYNC);
+
+       mdp4_dsi_encoder->enabled = false;
+}
+
+static void mdp4_dsi_encoder_enable(struct drm_encoder *encoder)
+{
+       struct mdp4_dsi_encoder *mdp4_dsi_encoder = to_mdp4_dsi_encoder(encoder);
+       struct mdp4_kms *mdp4_kms = get_kms(encoder);
+
+       if (mdp4_dsi_encoder->enabled)
+               return;
+
+        mdp4_crtc_set_config(encoder->crtc,
+                       MDP4_DMA_CONFIG_PACK_ALIGN_MSB |
+                       MDP4_DMA_CONFIG_DEFLKR_EN |
+                       MDP4_DMA_CONFIG_DITHER_EN |
+                       MDP4_DMA_CONFIG_R_BPC(BPC8) |
+                       MDP4_DMA_CONFIG_G_BPC(BPC8) |
+                       MDP4_DMA_CONFIG_B_BPC(BPC8) |
+                       MDP4_DMA_CONFIG_PACK(0x21));
+
+       mdp4_crtc_set_intf(encoder->crtc, INTF_DSI_VIDEO, 0);
+
+       mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 1);
+
+       mdp4_dsi_encoder->enabled = true;
+}
+
+static const struct drm_encoder_helper_funcs mdp4_dsi_encoder_helper_funcs = {
+       .mode_fixup = mdp4_dsi_encoder_mode_fixup,
+       .mode_set = mdp4_dsi_encoder_mode_set,
+       .disable = mdp4_dsi_encoder_disable,
+       .enable = mdp4_dsi_encoder_enable,
+};
+
+/* initialize encoder */
+struct drm_encoder *mdp4_dsi_encoder_init(struct drm_device *dev)
+{
+       struct drm_encoder *encoder = NULL;
+       struct mdp4_dsi_encoder *mdp4_dsi_encoder;
+       int ret;
+
+       mdp4_dsi_encoder = kzalloc(sizeof(*mdp4_dsi_encoder), GFP_KERNEL);
+       if (!mdp4_dsi_encoder) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       encoder = &mdp4_dsi_encoder->base;
+
+       drm_encoder_init(dev, encoder, &mdp4_dsi_encoder_funcs,
+                        DRM_MODE_ENCODER_DSI, NULL);
+       drm_encoder_helper_add(encoder, &mdp4_dsi_encoder_helper_funcs);
+
+       return encoder;
+
+fail:
+       if (encoder)
+               mdp4_dsi_encoder_destroy(encoder);
+
+       return ERR_PTR(ret);
+}
index 89614c6..a21df54 100644 (file)
@@ -262,7 +262,7 @@ struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev)
        encoder = &mdp4_dtv_encoder->base;
 
        drm_encoder_init(dev, encoder, &mdp4_dtv_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
        drm_encoder_helper_add(encoder, &mdp4_dtv_encoder_helper_funcs);
 
        mdp4_dtv_encoder->src_clk = devm_clk_get(dev->dev, "src_clk");
index 5ed38cf..a521207 100644 (file)
@@ -29,7 +29,7 @@ void mdp4_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask,
 
 static void mdp4_irq_error_handler(struct mdp_irq *irq, uint32_t irqstatus)
 {
-       DRM_ERROR("errors: %08x\n", irqstatus);
+       DRM_ERROR_RATELIMITED("errors: %08x\n", irqstatus);
 }
 
 void mdp4_irq_preinstall(struct msm_kms *kms)
index 077f752..5a8e3d6 100644 (file)
@@ -169,7 +169,14 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
                struct drm_encoder *encoder)
 {
        /* if we had >1 encoder, we'd need something more clever: */
-       return mdp4_dtv_round_pixclk(encoder, rate);
+       switch (encoder->encoder_type) {
+       case DRM_MODE_ENCODER_TMDS:
+               return mdp4_dtv_round_pixclk(encoder, rate);
+       case DRM_MODE_ENCODER_LVDS:
+       case DRM_MODE_ENCODER_DSI:
+       default:
+               return rate;
+       }
 }
 
 static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)
@@ -240,19 +247,18 @@ int mdp4_enable(struct mdp4_kms *mdp4_kms)
        return 0;
 }
 
-#ifdef CONFIG_OF
-static struct drm_panel *detect_panel(struct drm_device *dev)
+static struct device_node *mdp4_detect_lcdc_panel(struct drm_device *dev)
 {
        struct device_node *endpoint, *panel_node;
        struct device_node *np = dev->dev->of_node;
-       struct drm_panel *panel = NULL;
 
        endpoint = of_graph_get_next_endpoint(np, NULL);
        if (!endpoint) {
-               dev_err(dev->dev, "no valid endpoint\n");
-               return ERR_PTR(-ENODEV);
+               DBG("no endpoint in MDP4 to fetch LVDS panel\n");
+               return NULL;
        }
 
+       /* don't proceed if we have an endpoint but no panel_node tied to it */
        panel_node = of_graph_get_remote_port_parent(endpoint);
        if (!panel_node) {
                dev_err(dev->dev, "no valid panel node\n");
@@ -262,132 +268,185 @@ static struct drm_panel *detect_panel(struct drm_device *dev)
 
        of_node_put(endpoint);
 
-       panel = of_drm_find_panel(panel_node);
-       if (!panel) {
-               of_node_put(panel_node);
-               return ERR_PTR(-EPROBE_DEFER);
-       }
-
-       return panel;
+       return panel_node;
 }
-#else
-static struct drm_panel *detect_panel(struct drm_device *dev)
-{
-       // ??? maybe use a module param to specify which panel is attached?
-}
-#endif
 
-static int modeset_init(struct mdp4_kms *mdp4_kms)
+static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms,
+                                 int intf_type)
 {
        struct drm_device *dev = mdp4_kms->dev;
        struct msm_drm_private *priv = dev->dev_private;
-       struct drm_plane *plane;
-       struct drm_crtc *crtc;
        struct drm_encoder *encoder;
        struct drm_connector *connector;
-       struct drm_panel *panel;
+       struct device_node *panel_node;
+       struct drm_encoder *dsi_encs[MSM_DSI_ENCODER_NUM];
+       int i, dsi_id;
        int ret;
 
-       /* construct non-private planes: */
-       plane = mdp4_plane_init(dev, VG1, false);
-       if (IS_ERR(plane)) {
-               dev_err(dev->dev, "failed to construct plane for VG1\n");
-               ret = PTR_ERR(plane);
-               goto fail;
-       }
-       priv->planes[priv->num_planes++] = plane;
+       switch (intf_type) {
+       case DRM_MODE_ENCODER_LVDS:
+               /*
+                * bail out early if:
+                * - there is no panel node (no need to initialize lcdc
+                *   encoder and lvds connector), or
+                * - panel node is a bad pointer
+                */
+               panel_node = mdp4_detect_lcdc_panel(dev);
+               if (IS_ERR_OR_NULL(panel_node))
+                       return PTR_ERR(panel_node);
+
+               encoder = mdp4_lcdc_encoder_init(dev, panel_node);
+               if (IS_ERR(encoder)) {
+                       dev_err(dev->dev, "failed to construct LCDC encoder\n");
+                       return PTR_ERR(encoder);
+               }
 
-       plane = mdp4_plane_init(dev, VG2, false);
-       if (IS_ERR(plane)) {
-               dev_err(dev->dev, "failed to construct plane for VG2\n");
-               ret = PTR_ERR(plane);
-               goto fail;
-       }
-       priv->planes[priv->num_planes++] = plane;
+               /* LCDC can be hooked to DMA_P (TODO: Add DMA_S later?) */
+               encoder->possible_crtcs = 1 << DMA_P;
 
-       /*
-        * Setup the LCDC/LVDS path: RGB2 -> DMA_P -> LCDC -> LVDS:
-        */
+               connector = mdp4_lvds_connector_init(dev, panel_node, encoder);
+               if (IS_ERR(connector)) {
+                       dev_err(dev->dev, "failed to initialize LVDS connector\n");
+                       return PTR_ERR(connector);
+               }
 
-       panel = detect_panel(dev);
-       if (IS_ERR(panel)) {
-               ret = PTR_ERR(panel);
-               dev_err(dev->dev, "failed to detect LVDS panel: %d\n", ret);
-               goto fail;
-       }
+               priv->encoders[priv->num_encoders++] = encoder;
+               priv->connectors[priv->num_connectors++] = connector;
 
-       plane = mdp4_plane_init(dev, RGB2, true);
-       if (IS_ERR(plane)) {
-               dev_err(dev->dev, "failed to construct plane for RGB2\n");
-               ret = PTR_ERR(plane);
-               goto fail;
-       }
+               break;
+       case DRM_MODE_ENCODER_TMDS:
+               encoder = mdp4_dtv_encoder_init(dev);
+               if (IS_ERR(encoder)) {
+                       dev_err(dev->dev, "failed to construct DTV encoder\n");
+                       return PTR_ERR(encoder);
+               }
 
-       crtc  = mdp4_crtc_init(dev, plane, priv->num_crtcs, 0, DMA_P);
-       if (IS_ERR(crtc)) {
-               dev_err(dev->dev, "failed to construct crtc for DMA_P\n");
-               ret = PTR_ERR(crtc);
-               goto fail;
-       }
+               /* DTV can be hooked to DMA_E: */
+               encoder->possible_crtcs = 1 << 1;
 
-       encoder = mdp4_lcdc_encoder_init(dev, panel);
-       if (IS_ERR(encoder)) {
-               dev_err(dev->dev, "failed to construct LCDC encoder\n");
-               ret = PTR_ERR(encoder);
-               goto fail;
-       }
+               if (priv->hdmi) {
+                       /* Construct bridge/connector for HDMI: */
+                       ret = hdmi_modeset_init(priv->hdmi, dev, encoder);
+                       if (ret) {
+                               dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret);
+                               return ret;
+                       }
+               }
 
-       /* LCDC can be hooked to DMA_P: */
-       encoder->possible_crtcs = 1 << priv->num_crtcs;
+               priv->encoders[priv->num_encoders++] = encoder;
 
-       priv->crtcs[priv->num_crtcs++] = crtc;
-       priv->encoders[priv->num_encoders++] = encoder;
+               break;
+       case DRM_MODE_ENCODER_DSI:
+               /* only DSI1 supported for now */
+               dsi_id = 0;
 
-       connector = mdp4_lvds_connector_init(dev, panel, encoder);
-       if (IS_ERR(connector)) {
-               ret = PTR_ERR(connector);
-               dev_err(dev->dev, "failed to initialize LVDS connector: %d\n", ret);
-               goto fail;
-       }
+               if (!priv->dsi[dsi_id])
+                       break;
 
-       priv->connectors[priv->num_connectors++] = connector;
+               for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) {
+                       dsi_encs[i] = mdp4_dsi_encoder_init(dev);
+                       if (IS_ERR(dsi_encs[i])) {
+                               ret = PTR_ERR(dsi_encs[i]);
+                               dev_err(dev->dev,
+                                       "failed to construct DSI encoder: %d\n",
+                                       ret);
+                               return ret;
+                       }
 
-       /*
-        * Setup DTV/HDMI path: RGB1 -> DMA_E -> DTV -> HDMI:
-        */
+                       /* TODO: Add DMA_S later? */
+                       dsi_encs[i]->possible_crtcs = 1 << DMA_P;
+                       priv->encoders[priv->num_encoders++] = dsi_encs[i];
+               }
 
-       plane = mdp4_plane_init(dev, RGB1, true);
-       if (IS_ERR(plane)) {
-               dev_err(dev->dev, "failed to construct plane for RGB1\n");
-               ret = PTR_ERR(plane);
-               goto fail;
-       }
+               ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, dsi_encs);
+               if (ret) {
+                       dev_err(dev->dev, "failed to initialize DSI: %d\n",
+                               ret);
+                       return ret;
+               }
 
-       crtc  = mdp4_crtc_init(dev, plane, priv->num_crtcs, 1, DMA_E);
-       if (IS_ERR(crtc)) {
-               dev_err(dev->dev, "failed to construct crtc for DMA_E\n");
-               ret = PTR_ERR(crtc);
-               goto fail;
+               break;
+       default:
+               dev_err(dev->dev, "Invalid or unsupported interface\n");
+               return -EINVAL;
        }
 
-       encoder = mdp4_dtv_encoder_init(dev);
-       if (IS_ERR(encoder)) {
-               dev_err(dev->dev, "failed to construct DTV encoder\n");
-               ret = PTR_ERR(encoder);
-               goto fail;
+       return 0;
+}
+
+static int modeset_init(struct mdp4_kms *mdp4_kms)
+{
+       struct drm_device *dev = mdp4_kms->dev;
+       struct msm_drm_private *priv = dev->dev_private;
+       struct drm_plane *plane;
+       struct drm_crtc *crtc;
+       int i, ret;
+       static const enum mdp4_pipe rgb_planes[] = {
+               RGB1, RGB2,
+       };
+       static const enum mdp4_pipe vg_planes[] = {
+               VG1, VG2,
+       };
+       static const enum mdp4_dma mdp4_crtcs[] = {
+               DMA_P, DMA_E,
+       };
+       static const char * const mdp4_crtc_names[] = {
+               "DMA_P", "DMA_E",
+       };
+       static const int mdp4_intfs[] = {
+               DRM_MODE_ENCODER_LVDS,
+               DRM_MODE_ENCODER_DSI,
+               DRM_MODE_ENCODER_TMDS,
+       };
+
+       /* construct non-private planes: */
+       for (i = 0; i < ARRAY_SIZE(vg_planes); i++) {
+               plane = mdp4_plane_init(dev, vg_planes[i], false);
+               if (IS_ERR(plane)) {
+                       dev_err(dev->dev,
+                               "failed to construct plane for VG%d\n", i + 1);
+                       ret = PTR_ERR(plane);
+                       goto fail;
+               }
+               priv->planes[priv->num_planes++] = plane;
        }
 
-       /* DTV can be hooked to DMA_E: */
-       encoder->possible_crtcs = 1 << priv->num_crtcs;
+       for (i = 0; i < ARRAY_SIZE(mdp4_crtcs); i++) {
+               plane = mdp4_plane_init(dev, rgb_planes[i], true);
+               if (IS_ERR(plane)) {
+                       dev_err(dev->dev,
+                               "failed to construct plane for RGB%d\n", i + 1);
+                       ret = PTR_ERR(plane);
+                       goto fail;
+               }
+
+               crtc  = mdp4_crtc_init(dev, plane, priv->num_crtcs, i,
+                               mdp4_crtcs[i]);
+               if (IS_ERR(crtc)) {
+                       dev_err(dev->dev, "failed to construct crtc for %s\n",
+                               mdp4_crtc_names[i]);
+                       ret = PTR_ERR(crtc);
+                       goto fail;
+               }
+
+               priv->crtcs[priv->num_crtcs++] = crtc;
+       }
 
-       priv->crtcs[priv->num_crtcs++] = crtc;
-       priv->encoders[priv->num_encoders++] = encoder;
+       /*
+        * we currently set up two relatively fixed paths:
+        *
+        * LCDC/LVDS path: RGB1 -> DMA_P -> LCDC -> LVDS
+        *                      or
+        * DSI path: RGB1 -> DMA_P -> DSI1 -> DSI Panel
+        *
+        * DTV/HDMI path: RGB2 -> DMA_E -> DTV -> HDMI
+        */
 
-       if (priv->hdmi) {
-               /* Construct bridge/connector for HDMI: */
-               ret = hdmi_modeset_init(priv->hdmi, dev, encoder);
+       for (i = 0; i < ARRAY_SIZE(mdp4_intfs); i++) {
+               ret = mdp4_modeset_init_intf(mdp4_kms, mdp4_intfs[i]);
                if (ret) {
-                       dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret);
+                       dev_err(dev->dev, "failed to initialize intf: %d, %d\n",
+                               i, ret);
                        goto fail;
                }
        }
@@ -558,17 +617,10 @@ fail:
 static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev)
 {
        static struct mdp4_platform_config config = {};
-#ifdef CONFIG_OF
-       /* TODO */
+
+       /* TODO: Chips that aren't apq8064 have a 200 Mhz max_clk */
        config.max_clk = 266667000;
        config.iommu = iommu_domain_alloc(&platform_bus_type);
-#else
-       if (cpu_is_apq8064())
-               config.max_clk = 266667000;
-       else
-               config.max_clk = 200000000;
-
-       config.iommu = msm_get_iommu_domain(DISPLAY_READ_DOMAIN);
-#endif
+
        return &config;
 }
index 8a7f6e1..d2c96ef 100644 (file)
@@ -157,7 +157,7 @@ static inline uint32_t mixercfg(uint32_t mixer_cfg, int mixer,
                        COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1);
                break;
        default:
-               WARN_ON("invalid pipe");
+               WARN(1, "invalid pipe");
                break;
        }
 
@@ -212,10 +212,19 @@ struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev);
 
 long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate);
 struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
-               struct drm_panel *panel);
+               struct device_node *panel_node);
 
 struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
-               struct drm_panel *panel, struct drm_encoder *encoder);
+               struct device_node *panel_node, struct drm_encoder *encoder);
+
+#ifdef CONFIG_DRM_MSM_DSI
+struct drm_encoder *mdp4_dsi_encoder_init(struct drm_device *dev);
+#else
+static inline struct drm_encoder *mdp4_dsi_encoder_init(struct drm_device *dev)
+{
+       return ERR_PTR(-ENODEV);
+}
+#endif
 
 #ifdef CONFIG_COMMON_CLK
 struct clk *mpd4_lvds_pll_init(struct drm_device *dev);
index 4cd6e72..cd63fed 100644 (file)
@@ -23,6 +23,7 @@
 
 struct mdp4_lcdc_encoder {
        struct drm_encoder base;
+       struct device_node *panel_node;
        struct drm_panel *panel;
        struct clk *lcdc_clk;
        unsigned long int pixclock;
@@ -338,7 +339,7 @@ static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder)
        struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
                        to_mdp4_lcdc_encoder(encoder);
        struct mdp4_kms *mdp4_kms = get_kms(encoder);
-       struct drm_panel *panel = mdp4_lcdc_encoder->panel;
+       struct drm_panel *panel;
        int i, ret;
 
        if (WARN_ON(!mdp4_lcdc_encoder->enabled))
@@ -346,6 +347,7 @@ static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder)
 
        mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);
 
+       panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node);
        if (panel) {
                drm_panel_disable(panel);
                drm_panel_unprepare(panel);
@@ -381,7 +383,7 @@ static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder)
                        to_mdp4_lcdc_encoder(encoder);
        unsigned long pc = mdp4_lcdc_encoder->pixclock;
        struct mdp4_kms *mdp4_kms = get_kms(encoder);
-       struct drm_panel *panel = mdp4_lcdc_encoder->panel;
+       struct drm_panel *panel;
        int i, ret;
 
        if (WARN_ON(mdp4_lcdc_encoder->enabled))
@@ -414,6 +416,7 @@ static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder)
        if (ret)
                dev_err(dev->dev, "failed to enable lcdc_clk: %d\n", ret);
 
+       panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node);
        if (panel) {
                drm_panel_prepare(panel);
                drm_panel_enable(panel);
@@ -442,7 +445,7 @@ long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate)
 
 /* initialize encoder */
 struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
-               struct drm_panel *panel)
+               struct device_node *panel_node)
 {
        struct drm_encoder *encoder = NULL;
        struct mdp4_lcdc_encoder *mdp4_lcdc_encoder;
@@ -455,12 +458,12 @@ struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
                goto fail;
        }
 
-       mdp4_lcdc_encoder->panel = panel;
+       mdp4_lcdc_encoder->panel_node = panel_node;
 
        encoder = &mdp4_lcdc_encoder->base;
 
        drm_encoder_init(dev, encoder, &mdp4_lcdc_encoder_funcs,
-                        DRM_MODE_ENCODER_LVDS);
+                        DRM_MODE_ENCODER_LVDS, NULL);
        drm_encoder_helper_add(encoder, &mdp4_lcdc_encoder_helper_funcs);
 
        /* TODO: do we need different pll in other cases? */
index 9211851..e73e174 100644 (file)
@@ -23,6 +23,7 @@
 struct mdp4_lvds_connector {
        struct drm_connector base;
        struct drm_encoder *encoder;
+       struct device_node *panel_node;
        struct drm_panel *panel;
 };
 #define to_mdp4_lvds_connector(x) container_of(x, struct mdp4_lvds_connector, base)
@@ -33,6 +34,10 @@ static enum drm_connector_status mdp4_lvds_connector_detect(
        struct mdp4_lvds_connector *mdp4_lvds_connector =
                        to_mdp4_lvds_connector(connector);
 
+       if (!mdp4_lvds_connector->panel)
+               mdp4_lvds_connector->panel =
+                       of_drm_find_panel(mdp4_lvds_connector->panel_node);
+
        return mdp4_lvds_connector->panel ?
                        connector_status_connected :
                        connector_status_disconnected;
@@ -42,10 +47,6 @@ static void mdp4_lvds_connector_destroy(struct drm_connector *connector)
 {
        struct mdp4_lvds_connector *mdp4_lvds_connector =
                        to_mdp4_lvds_connector(connector);
-       struct drm_panel *panel = mdp4_lvds_connector->panel;
-
-       if (panel)
-               drm_panel_detach(panel);
 
        drm_connector_unregister(connector);
        drm_connector_cleanup(connector);
@@ -60,9 +61,14 @@ static int mdp4_lvds_connector_get_modes(struct drm_connector *connector)
        struct drm_panel *panel = mdp4_lvds_connector->panel;
        int ret = 0;
 
-       if (panel)
+       if (panel) {
+               drm_panel_attach(panel, connector);
+
                ret = panel->funcs->get_modes(panel);
 
+               drm_panel_detach(panel);
+       }
+
        return ret;
 }
 
@@ -111,7 +117,7 @@ static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs
 
 /* initialize connector */
 struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
-               struct drm_panel *panel, struct drm_encoder *encoder)
+               struct device_node *panel_node, struct drm_encoder *encoder)
 {
        struct drm_connector *connector = NULL;
        struct mdp4_lvds_connector *mdp4_lvds_connector;
@@ -124,7 +130,7 @@ struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
        }
 
        mdp4_lvds_connector->encoder = encoder;
-       mdp4_lvds_connector->panel = panel;
+       mdp4_lvds_connector->panel_node = panel_node;
 
        connector = &mdp4_lvds_connector->base;
 
@@ -141,9 +147,6 @@ struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
 
        drm_mode_connector_attach_encoder(connector, encoder);
 
-       if (panel)
-               drm_panel_attach(panel, connector);
-
        return connector;
 
 fail:
index 30d57e7..9f96dfe 100644 (file)
@@ -397,7 +397,8 @@ struct drm_plane *mdp4_plane_init(struct drm_device *dev,
 
        type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
        ret = drm_universal_plane_init(dev, plane, 0xff, &mdp4_plane_funcs,
-                                mdp4_plane->formats, mdp4_plane->nformats, type);
+                                mdp4_plane->formats, mdp4_plane->nformats,
+                                type, NULL);
        if (ret)
                goto fail;
 
index bb1225a..57f73f0 100644 (file)
@@ -553,9 +553,7 @@ fail:
 static struct mdp5_cfg_platform *mdp5_get_config(struct platform_device *dev)
 {
        static struct mdp5_cfg_platform config = {};
-#ifdef CONFIG_OF
-       /* TODO */
-#endif
+
        config.iommu = iommu_domain_alloc(&platform_bus_type);
 
        return &config;
index 8e6c9b5..1aa21db 100644 (file)
@@ -326,7 +326,7 @@ struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
        mdp5_cmd_enc->ctl = ctl;
 
        drm_encoder_init(dev, encoder, &mdp5_cmd_encoder_funcs,
-                       DRM_MODE_ENCODER_DSI);
+                       DRM_MODE_ENCODER_DSI, NULL);
 
        drm_encoder_helper_add(encoder, &mdp5_cmd_encoder_helper_funcs);
 
index 7f9f4ac..20cee5c 100644 (file)
@@ -797,7 +797,8 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
        snprintf(mdp5_crtc->name, sizeof(mdp5_crtc->name), "%s:%d",
                        pipe2name(mdp5_plane_pipe(plane)), id);
 
-       drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs);
+       drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs,
+                                 NULL);
 
        drm_flip_work_init(&mdp5_crtc->unref_cursor_work,
                        "unref cursor", unref_cursor_worker);
index c9e32b0..0d737ca 100644 (file)
@@ -293,6 +293,24 @@ static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = {
        .enable = mdp5_encoder_enable,
 };
 
+int mdp5_encoder_get_linecount(struct drm_encoder *encoder)
+{
+       struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
+       struct mdp5_kms *mdp5_kms = get_kms(encoder);
+       int intf = mdp5_encoder->intf.num;
+
+       return mdp5_read(mdp5_kms, REG_MDP5_INTF_LINE_COUNT(intf));
+}
+
+u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder)
+{
+       struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
+       struct mdp5_kms *mdp5_kms = get_kms(encoder);
+       int intf = mdp5_encoder->intf.num;
+
+       return mdp5_read(mdp5_kms, REG_MDP5_INTF_FRAME_COUNT(intf));
+}
+
 int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
                                        struct drm_encoder *slave_encoder)
 {
@@ -354,7 +372,7 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
 
        spin_lock_init(&mdp5_encoder->intf_lock);
 
-       drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, enc_type);
+       drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, enc_type, NULL);
 
        drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs);
 
index b0d4b53..73bc3e3 100644 (file)
@@ -31,7 +31,7 @@ void mdp5_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask,
 
 static void mdp5_irq_error_handler(struct mdp_irq *irq, uint32_t irqstatus)
 {
-       DRM_ERROR("errors: %08x\n", irqstatus);
+       DRM_ERROR_RATELIMITED("errors: %08x\n", irqstatus);
 }
 
 void mdp5_irq_preinstall(struct msm_kms *kms)
index b532faa..e115318 100644 (file)
@@ -468,6 +468,127 @@ static int get_clk(struct platform_device *pdev, struct clk **clkp,
        return 0;
 }
 
+static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_encoder *encoder;
+
+       drm_for_each_encoder(encoder, dev)
+               if (encoder->crtc == crtc)
+                       return encoder;
+
+       return NULL;
+}
+
+static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
+                              unsigned int flags, int *vpos, int *hpos,
+                              ktime_t *stime, ktime_t *etime,
+                              const struct drm_display_mode *mode)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct drm_crtc *crtc;
+       struct drm_encoder *encoder;
+       int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
+       int ret = 0;
+
+       crtc = priv->crtcs[pipe];
+       if (!crtc) {
+               DRM_ERROR("Invalid crtc %d\n", pipe);
+               return 0;
+       }
+
+       encoder = get_encoder_from_crtc(crtc);
+       if (!encoder) {
+               DRM_ERROR("no encoder found for crtc %d\n", pipe);
+               return 0;
+       }
+
+       ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
+
+       vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
+       vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
+
+       /*
+        * the line counter is 1 at the start of the VSYNC pulse and VTOTAL at
+        * the end of VFP. Translate the porch values relative to the line
+        * counter positions.
+        */
+
+       vactive_start = vsw + vbp + 1;
+
+       vactive_end = vactive_start + mode->crtc_vdisplay;
+
+       /* last scan line before VSYNC */
+       vfp_end = mode->crtc_vtotal;
+
+       if (stime)
+               *stime = ktime_get();
+
+       line = mdp5_encoder_get_linecount(encoder);
+
+       if (line < vactive_start) {
+               line -= vactive_start;
+               ret |= DRM_SCANOUTPOS_IN_VBLANK;
+       } else if (line > vactive_end) {
+               line = line - vfp_end - vactive_start;
+               ret |= DRM_SCANOUTPOS_IN_VBLANK;
+       } else {
+               line -= vactive_start;
+       }
+
+       *vpos = line;
+       *hpos = 0;
+
+       if (etime)
+               *etime = ktime_get();
+
+       return ret;
+}
+
+static int mdp5_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
+                                    int *max_error,
+                                    struct timeval *vblank_time,
+                                    unsigned flags)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct drm_crtc *crtc;
+
+       if (pipe < 0 || pipe >= priv->num_crtcs) {
+               DRM_ERROR("Invalid crtc %d\n", pipe);
+               return -EINVAL;
+       }
+
+       crtc = priv->crtcs[pipe];
+       if (!crtc) {
+               DRM_ERROR("Invalid crtc %d\n", pipe);
+               return -EINVAL;
+       }
+
+       return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
+                                                    vblank_time, flags,
+                                                    &crtc->mode);
+}
+
+static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       struct drm_crtc *crtc;
+       struct drm_encoder *encoder;
+
+       if (pipe < 0 || pipe >= priv->num_crtcs)
+               return 0;
+
+       crtc = priv->crtcs[pipe];
+       if (!crtc)
+               return 0;
+
+       encoder = get_encoder_from_crtc(crtc);
+       if (!encoder)
+               return 0;
+
+       return mdp5_encoder_get_framecount(encoder);
+}
+
 struct msm_kms *mdp5_kms_init(struct drm_device *dev)
 {
        struct platform_device *pdev = dev->platformdev;
@@ -590,6 +711,8 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
                                !config->hw->intf.base[i])
                        continue;
                mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0);
+
+               mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(i), 0x3);
        }
        mdp5_disable(mdp5_kms);
        mdelay(16);
@@ -635,6 +758,12 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
        dev->mode_config.max_width = config->hw->lm.max_width;
        dev->mode_config.max_height = config->hw->lm.max_height;
 
+       dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp;
+       dev->driver->get_scanout_position = mdp5_get_scanoutpos;
+       dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
+       dev->max_vblank_count = 0xffffffff;
+       dev->vblank_disable_immediate = true;
+
        return kms;
 
 fail:
index 84f65d4..00730ba 100644 (file)
@@ -222,6 +222,8 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
                struct mdp5_interface *intf, struct mdp5_ctl *ctl);
 int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
                                        struct drm_encoder *slave_encoder);
+int mdp5_encoder_get_linecount(struct drm_encoder *encoder);
+u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder);
 
 #ifdef CONFIG_DRM_MSM_DSI
 struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
index 81cd490..432c098 100644 (file)
@@ -904,7 +904,7 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev,
        type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
        ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs,
                                 mdp5_plane->formats, mdp5_plane->nformats,
-                                type);
+                                type, NULL);
        if (ret)
                goto fail;
 
index b88ce51..9a30807 100644 (file)
@@ -237,20 +237,9 @@ static int msm_unload(struct drm_device *dev)
 
 static int get_mdp_ver(struct platform_device *pdev)
 {
-#ifdef CONFIG_OF
-       static const struct of_device_id match_types[] = { {
-               .compatible = "qcom,mdss_mdp",
-               .data   = (void *)5,
-       }, {
-               /* end node */
-       } };
        struct device *dev = &pdev->dev;
-       const struct of_device_id *match;
-       match = of_match_node(match_types, dev->of_node);
-       if (match)
-               return (int)(unsigned long)match->data;
-#endif
-       return 4;
+
+       return (int) (unsigned long) of_device_get_match_data(dev);
 }
 
 #include <linux/of_address.h>
@@ -258,10 +247,10 @@ static int get_mdp_ver(struct platform_device *pdev)
 static int msm_init_vram(struct drm_device *dev)
 {
        struct msm_drm_private *priv = dev->dev_private;
+       struct device_node *node;
        unsigned long size = 0;
        int ret = 0;
 
-#ifdef CONFIG_OF
        /* In the device-tree world, we could have a 'memory-region'
         * phandle, which gives us a link to our "vram".  Allocating
         * is all nicely abstracted behind the dma api, but we need
@@ -278,7 +267,6 @@ static int msm_init_vram(struct drm_device *dev)
         *     as corruption on screen before we have a chance to
         *     load and do initial modeset)
         */
-       struct device_node *node;
 
        node = of_parse_phandle(dev->dev->of_node, "memory-region", 0);
        if (node) {
@@ -288,14 +276,12 @@ static int msm_init_vram(struct drm_device *dev)
                        return ret;
                size = r.end - r.start;
                DRM_INFO("using VRAM carveout: %lx@%pa\n", size, &r.start);
-       } else
-#endif
 
-       /* if we have no IOMMU, then we need to use carveout allocator.
-        * Grab the entire CMA chunk carved out in early startup in
-        * mach-msm:
-        */
-       if (!iommu_present(&platform_bus_type)) {
+               /* if we have no IOMMU, then we need to use carveout allocator.
+                * Grab the entire CMA chunk carved out in early startup in
+                * mach-msm:
+                */
+       } else if (!iommu_present(&platform_bus_type)) {
                DRM_INFO("using %s VRAM carveout\n", vram);
                size = memparse(vram, NULL);
        }
@@ -1035,9 +1021,9 @@ static const struct dev_pm_ops msm_pm_ops = {
  * Componentized driver support:
  */
 
-#ifdef CONFIG_OF
-/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx
- * (or probably any other).. so probably some room for some helpers
+/*
+ * NOTE: duplication of the same code as exynos or imx (or probably any other).
+ * so probably some room for some helpers
  */
 static int compare_of(struct device *dev, void *data)
 {
@@ -1062,12 +1048,6 @@ static int add_components(struct device *dev, struct component_match **matchptr,
 
        return 0;
 }
-#else
-static int compare_dev(struct device *dev, void *data)
-{
-       return dev == data;
-}
-#endif
 
 static int msm_drm_bind(struct device *dev)
 {
@@ -1091,35 +1071,9 @@ static const struct component_master_ops msm_drm_ops = {
 static int msm_pdev_probe(struct platform_device *pdev)
 {
        struct component_match *match = NULL;
-#ifdef CONFIG_OF
+
        add_components(&pdev->dev, &match, "connectors");
        add_components(&pdev->dev, &match, "gpus");
-#else
-       /* For non-DT case, it kinda sucks.  We don't actually have a way
-        * to know whether or not we are waiting for certain devices (or if
-        * they are simply not present).  But for non-DT we only need to
-        * care about apq8064/apq8060/etc (all mdp4/a3xx):
-        */
-       static const char *devnames[] = {
-                       "hdmi_msm.0", "kgsl-3d0.0",
-       };
-       int i;
-
-       DBG("Adding components..");
-
-       for (i = 0; i < ARRAY_SIZE(devnames); i++) {
-               struct device *dev;
-
-               dev = bus_find_device_by_name(&platform_bus_type,
-                               NULL, devnames[i]);
-               if (!dev) {
-                       dev_info(&pdev->dev, "still waiting for %s\n", devnames[i]);
-                       return -EPROBE_DEFER;
-               }
-
-               component_match_add(&pdev->dev, &match, compare_dev, dev);
-       }
-#endif
 
        pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
        return component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
@@ -1138,8 +1092,10 @@ static const struct platform_device_id msm_id[] = {
 };
 
 static const struct of_device_id dt_match[] = {
-       { .compatible = "qcom,mdp" },      /* mdp4 */
-       { .compatible = "qcom,mdss_mdp" }, /* mdp5 */
+       { .compatible = "qcom,mdp4", .data = (void *) 4 },      /* mdp4 */
+       { .compatible = "qcom,mdp5", .data = (void *) 5 },      /* mdp5 */
+       /* to support downstream DT files */
+       { .compatible = "qcom,mdss_mdp", .data = (void *) 5 },  /* mdp5 */
        {}
 };
 MODULE_DEVICE_TABLE(of, dt_match);
index 9a713b7..c1e7bba 100644 (file)
 #include <linux/iommu.h>
 #include <linux/types.h>
 #include <linux/of_graph.h>
+#include <linux/of_device.h>
 #include <asm/sizes.h>
 
-#ifndef CONFIG_OF
-#include <mach/board.h>
-#include <mach/socinfo.h>
-#include <mach/iommu_domains.h>
-#endif
-
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
index 3f6ec07..d95af6e 100644 (file)
@@ -121,7 +121,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper,
                /* note: if fb creation failed, we can't rely on fb destroy
                 * to unref the bo:
                 */
-               drm_gem_object_unreference(fbdev->bo);
+               drm_gem_object_unreference_unlocked(fbdev->bo);
                ret = PTR_ERR(fb);
                goto fail;
        }
index 3d96b49..6f04397 100644 (file)
@@ -1081,8 +1081,6 @@ nouveau_crtc_set_config(struct drm_mode_set *set)
 }
 
 static const struct drm_crtc_funcs nv04_crtc_funcs = {
-       .save = nv_crtc_save,
-       .restore = nv_crtc_restore,
        .cursor_set = nv04_crtc_cursor_set,
        .cursor_move = nv04_crtc_cursor_move,
        .gamma_set = nv_crtc_gamma_set,
@@ -1123,6 +1121,9 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num)
        nv_crtc->index = crtc_num;
        nv_crtc->last_dpms = NV_DPMS_CLEARED;
 
+       nv_crtc->save = nv_crtc_save;
+       nv_crtc->restore = nv_crtc_restore;
+
        drm_crtc_init(dev, &nv_crtc->base, &nv04_crtc_funcs);
        drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs);
        drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
index 78cb033..b48eec3 100644 (file)
@@ -504,8 +504,6 @@ static void nv04_dac_destroy(struct drm_encoder *encoder)
 
 static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
        .dpms = nv04_dac_dpms,
-       .save = nv04_dac_save,
-       .restore = nv04_dac_restore,
        .mode_fixup = nv04_dac_mode_fixup,
        .prepare = nv04_dac_prepare,
        .commit = nv04_dac_commit,
@@ -515,8 +513,6 @@ static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
 
 static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
        .dpms = nv04_dac_dpms,
-       .save = nv04_dac_save,
-       .restore = nv04_dac_restore,
        .mode_fixup = nv04_dac_mode_fixup,
        .prepare = nv04_dac_prepare,
        .commit = nv04_dac_commit,
@@ -545,12 +541,16 @@ nv04_dac_create(struct drm_connector *connector, struct dcb_output *entry)
        nv_encoder->dcb = entry;
        nv_encoder->or = ffs(entry->or) - 1;
 
+       nv_encoder->enc_save = nv04_dac_save;
+       nv_encoder->enc_restore = nv04_dac_restore;
+
        if (nv_gf4_disp_arch(dev))
                helper = &nv17_dac_helper_funcs;
        else
                helper = &nv04_dac_helper_funcs;
 
-       drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC);
+       drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC,
+                        NULL);
        drm_encoder_helper_add(encoder, helper);
 
        encoder->possible_crtcs = entry->heads;
index 429ab5e..05bfd15 100644 (file)
@@ -652,8 +652,6 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder)
 
 static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
        .dpms = nv04_lvds_dpms,
-       .save = nv04_dfp_save,
-       .restore = nv04_dfp_restore,
        .mode_fixup = nv04_dfp_mode_fixup,
        .prepare = nv04_dfp_prepare,
        .commit = nv04_dfp_commit,
@@ -663,8 +661,6 @@ static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
 
 static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = {
        .dpms = nv04_tmds_dpms,
-       .save = nv04_dfp_save,
-       .restore = nv04_dfp_restore,
        .mode_fixup = nv04_dfp_mode_fixup,
        .prepare = nv04_dfp_prepare,
        .commit = nv04_dfp_commit,
@@ -701,12 +697,15 @@ nv04_dfp_create(struct drm_connector *connector, struct dcb_output *entry)
        if (!nv_encoder)
                return -ENOMEM;
 
+       nv_encoder->enc_save = nv04_dfp_save;
+       nv_encoder->enc_restore = nv04_dfp_restore;
+
        encoder = to_drm_encoder(nv_encoder);
 
        nv_encoder->dcb = entry;
        nv_encoder->or = ffs(entry->or) - 1;
 
-       drm_encoder_init(connector->dev, encoder, &nv04_dfp_funcs, type);
+       drm_encoder_init(connector->dev, encoder, &nv04_dfp_funcs, type, NULL);
        drm_encoder_helper_add(encoder, helper);
 
        encoder->possible_crtcs = entry->heads;
index 9e65008..b4a6bc4 100644 (file)
@@ -39,7 +39,8 @@ nv04_display_create(struct drm_device *dev)
        struct dcb_table *dcb = &drm->vbios.dcb;
        struct drm_connector *connector, *ct;
        struct drm_encoder *encoder;
-       struct drm_crtc *crtc;
+       struct nouveau_encoder *nv_encoder;
+       struct nouveau_crtc *crtc;
        struct nv04_display *disp;
        int i, ret;
 
@@ -107,14 +108,11 @@ nv04_display_create(struct drm_device *dev)
        }
 
        /* Save previous state */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               crtc->funcs->save(crtc);
-
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               const struct drm_encoder_helper_funcs *func = encoder->helper_private;
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head)
+               crtc->save(&crtc->base);
 
-               func->save(encoder);
-       }
+       list_for_each_entry(nv_encoder, &dev->mode_config.encoder_list, base.base.head)
+               nv_encoder->enc_save(&nv_encoder->base.base);
 
        nouveau_overlay_init(dev);
 
@@ -126,8 +124,9 @@ nv04_display_destroy(struct drm_device *dev)
 {
        struct nv04_display *disp = nv04_display(dev);
        struct nouveau_drm *drm = nouveau_drm(dev);
-       struct drm_encoder *encoder;
+       struct nouveau_encoder *encoder;
        struct drm_crtc *crtc;
+       struct nouveau_crtc *nv_crtc;
 
        /* Turn every CRTC off. */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -139,14 +138,11 @@ nv04_display_destroy(struct drm_device *dev)
        }
 
        /* Restore state */
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               const struct drm_encoder_helper_funcs *func = encoder->helper_private;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head)
+               encoder->enc_restore(&encoder->base.base);
 
-               func->restore(encoder);
-       }
-
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               crtc->funcs->restore(crtc);
+       list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head)
+               nv_crtc->restore(&nv_crtc->base);
 
        nouveau_hw_save_vga_fonts(dev, 0);
 
@@ -159,8 +155,8 @@ nv04_display_destroy(struct drm_device *dev)
 int
 nv04_display_init(struct drm_device *dev)
 {
-       struct drm_encoder *encoder;
-       struct drm_crtc *crtc;
+       struct nouveau_encoder *encoder;
+       struct nouveau_crtc *crtc;
 
        /* meh.. modeset apparently doesn't setup all the regs and depends
         * on pre-existing state, for now load the state of the card *before*
@@ -170,14 +166,11 @@ nv04_display_init(struct drm_device *dev)
         * save/restore "pre-load" state, but more general so we can save
         * on suspend too.
         */
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               const struct drm_encoder_helper_funcs *func = encoder->helper_private;
-
-               func->restore(encoder);
-       }
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head)
+               crtc->save(&crtc->base);
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               crtc->funcs->restore(crtc);
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head)
+               encoder->enc_save(&encoder->base.base);
 
        return 0;
 }
index 5345eb5..54e9fb9 100644 (file)
@@ -192,8 +192,6 @@ static const struct drm_encoder_funcs nv04_tv_funcs = {
 
 static const struct drm_encoder_helper_funcs nv04_tv_helper_funcs = {
        .dpms = nv04_tv_dpms,
-       .save = drm_i2c_encoder_save,
-       .restore = drm_i2c_encoder_restore,
        .mode_fixup = drm_i2c_encoder_mode_fixup,
        .prepare = nv04_tv_prepare,
        .commit = nv04_tv_commit,
@@ -225,9 +223,13 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry)
        /* Initialize the common members */
        encoder = to_drm_encoder(nv_encoder);
 
-       drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC);
+       drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC,
+                        NULL);
        drm_encoder_helper_add(encoder, &nv04_tv_helper_funcs);
 
+       nv_encoder->enc_save = drm_i2c_encoder_save;
+       nv_encoder->enc_restore = drm_i2c_encoder_restore;
+
        encoder->possible_crtcs = entry->heads;
        encoder->possible_clones = 0;
        nv_encoder->dcb = entry;
index b734195..d9644c0 100644 (file)
@@ -771,8 +771,6 @@ static void nv17_tv_destroy(struct drm_encoder *encoder)
 
 static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = {
        .dpms = nv17_tv_dpms,
-       .save = nv17_tv_save,
-       .restore = nv17_tv_restore,
        .mode_fixup = nv17_tv_mode_fixup,
        .prepare = nv17_tv_prepare,
        .commit = nv17_tv_commit,
@@ -816,10 +814,14 @@ nv17_tv_create(struct drm_connector *connector, struct dcb_output *entry)
        tv_enc->base.dcb = entry;
        tv_enc->base.or = ffs(entry->or) - 1;
 
-       drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC);
+       drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC,
+                        NULL);
        drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs);
        to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs;
 
+       tv_enc->base.enc_save = nv17_tv_save;
+       tv_enc->base.enc_restore = nv17_tv_restore;
+
        encoder->possible_crtcs = entry->heads;
        encoder->possible_clones = 0;
 
index 2e7cbe9..5dd1d01 100644 (file)
@@ -898,8 +898,6 @@ nouveau_connector_helper_funcs = {
 static const struct drm_connector_funcs
 nouveau_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
-       .save = NULL,
-       .restore = NULL,
        .detect = nouveau_connector_detect,
        .destroy = nouveau_connector_destroy,
        .fill_modes = drm_helper_probe_single_connector_modes,
@@ -910,8 +908,6 @@ nouveau_connector_funcs = {
 static const struct drm_connector_funcs
 nouveau_connector_funcs_lvds = {
        .dpms = drm_helper_connector_dpms,
-       .save = NULL,
-       .restore = NULL,
        .detect = nouveau_connector_detect_lvds,
        .destroy = nouveau_connector_destroy,
        .fill_modes = drm_helper_probe_single_connector_modes,
@@ -944,8 +940,6 @@ nouveau_connector_dp_dpms(struct drm_connector *connector, int mode)
 static const struct drm_connector_funcs
 nouveau_connector_funcs_dp = {
        .dpms = nouveau_connector_dp_dpms,
-       .save = NULL,
-       .restore = NULL,
        .detect = nouveau_connector_detect,
        .destroy = nouveau_connector_destroy,
        .fill_modes = drm_helper_probe_single_connector_modes,
index f19cb1c..863f10b 100644 (file)
@@ -73,6 +73,9 @@ struct nouveau_crtc {
        int (*set_dither)(struct nouveau_crtc *crtc, bool update);
        int (*set_scale)(struct nouveau_crtc *crtc, bool update);
        int (*set_color_vibrance)(struct nouveau_crtc *crtc, bool update);
+
+       void (*save)(struct drm_crtc *crtc);
+       void (*restore)(struct drm_crtc *crtc);
 };
 
 static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
index b37da95..c38a864 100644 (file)
@@ -63,6 +63,9 @@ struct nouveau_encoder {
                        u32 datarate;
                } dp;
        };
+
+       void (*enc_save)(struct drm_encoder *encoder);
+       void (*enc_restore)(struct drm_encoder *encoder);
 };
 
 struct nouveau_encoder *
index c053c50..44e1952 100644 (file)
@@ -28,6 +28,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_dp_helper.h>
+#include <drm/drm_fb_helper.h>
 
 #include <nvif/class.h>
 
@@ -1717,7 +1718,7 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe)
        encoder = to_drm_encoder(nv_encoder);
        encoder->possible_crtcs = dcbe->heads;
        encoder->possible_clones = 0;
-       drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type);
+       drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type, NULL);
        drm_encoder_helper_add(encoder, &nv50_dac_hfunc);
 
        drm_mode_connector_attach_encoder(connector, encoder);
@@ -2125,7 +2126,7 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
        encoder = to_drm_encoder(nv_encoder);
        encoder->possible_crtcs = dcbe->heads;
        encoder->possible_clones = 0;
-       drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type);
+       drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type, NULL);
        drm_encoder_helper_add(encoder, &nv50_sor_hfunc);
 
        drm_mode_connector_attach_encoder(connector, encoder);
@@ -2305,7 +2306,7 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
        encoder = to_drm_encoder(nv_encoder);
        encoder->possible_crtcs = dcbe->heads;
        encoder->possible_clones = 0;
-       drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type);
+       drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type, NULL);
        drm_encoder_helper_add(encoder, &nv50_pior_hfunc);
 
        drm_mode_connector_attach_encoder(connector, encoder);
index ad09590..2ed0754 100644 (file)
@@ -524,7 +524,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
        omap_crtc->mgr = omap_dss_get_overlay_manager(channel);
 
        ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
-                                       &omap_crtc_funcs);
+                                       &omap_crtc_funcs, NULL);
        if (ret < 0) {
                kfree(omap_crtc);
                return NULL;
index 7d9b32a..0c104ad 100644 (file)
@@ -178,7 +178,7 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev,
        encoder = &omap_encoder->base;
 
        drm_encoder_init(dev, encoder, &omap_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
        drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
 
        return encoder;
index 3054bda..d5ecabd 100644 (file)
@@ -366,7 +366,7 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
 
        ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1,
                                       &omap_plane_funcs, omap_plane->formats,
-                                      omap_plane->nformats, type);
+                                      omap_plane->nformats, type, NULL);
        if (ret < 0)
                goto error;
 
index 7d4704b..1500ab9 100644 (file)
@@ -31,6 +31,16 @@ config DRM_PANEL_LG_LG4573
          Say Y here if you want to enable support for LG4573 RGB panel.
          To compile this driver as a module, choose M here.
 
+config DRM_PANEL_PANASONIC_VVX10F034N00
+       tristate "Panasonic VVX10F034N00 1920x1200 video mode panel"
+       depends on OF
+       depends on DRM_MIPI_DSI
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+         Say Y here if you want to enable support for Panasonic VVX10F034N00
+         WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
+         Xperia Z2 tablets
+
 config DRM_PANEL_SAMSUNG_S6E8AA0
        tristate "Samsung S6E8AA0 DSI video mode panel"
        depends on OF
@@ -51,4 +61,13 @@ config DRM_PANEL_SHARP_LQ101R1SX01
          To compile this driver as a module, choose M here: the module
          will be called panel-sharp-lq101r1sx01.
 
+config DRM_PANEL_SHARP_LS043T1LE01
+       tristate "Sharp LS043T1LE01 qHD video mode panel"
+       depends on OF
+       depends on DRM_MIPI_DSI
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+         Say Y here if you want to enable support for Sharp LS043T1LE01 qHD
+         (540x960) DSI panel as found on the Qualcomm APQ8074 Dragonboard
+
 endmenu
index d0f016d..f277eed 100644 (file)
@@ -1,5 +1,7 @@
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
+obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
+obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
diff --git a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c
new file mode 100644 (file)
index 0000000..7f915f7
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2015 Red Hat
+ * Copyright (C) 2015 Sony Mobile Communications Inc.
+ * Author: Werner Johansson <werner.johansson@sonymobile.com>
+ *
+ * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+/*
+ * When power is turned off to this panel a minimum off time of 500ms has to be
+ * observed before powering back on as there's no external reset pin. Keep
+ * track of earliest wakeup time and delay subsequent prepare call accordingly
+ */
+#define MIN_POFF_MS (500)
+
+struct wuxga_nt_panel {
+       struct drm_panel base;
+       struct mipi_dsi_device *dsi;
+
+       struct backlight_device *backlight;
+       struct regulator *supply;
+
+       bool prepared;
+       bool enabled;
+
+       ktime_t earliest_wake;
+
+       const struct drm_display_mode *mode;
+};
+
+static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel)
+{
+       return container_of(panel, struct wuxga_nt_panel, base);
+}
+
+static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)
+{
+       struct mipi_dsi_device *dsi = wuxga_nt->dsi;
+       int ret;
+
+       ret = mipi_dsi_turn_on_peripheral(dsi);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int wuxga_nt_panel_disable(struct drm_panel *panel)
+{
+       struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
+
+       if (!wuxga_nt->enabled)
+               return 0;
+
+       mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
+
+       if (wuxga_nt->backlight) {
+               wuxga_nt->backlight->props.power = FB_BLANK_POWERDOWN;
+               wuxga_nt->backlight->props.state |= BL_CORE_FBBLANK;
+               backlight_update_status(wuxga_nt->backlight);
+       }
+
+       wuxga_nt->enabled = false;
+
+       return 0;
+}
+
+static int wuxga_nt_panel_unprepare(struct drm_panel *panel)
+{
+       struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
+
+       if (!wuxga_nt->prepared)
+               return 0;
+
+       regulator_disable(wuxga_nt->supply);
+       wuxga_nt->earliest_wake = ktime_add_ms(ktime_get_real(), MIN_POFF_MS);
+       wuxga_nt->prepared = false;
+
+       return 0;
+}
+
+static int wuxga_nt_panel_prepare(struct drm_panel *panel)
+{
+       struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
+       int ret;
+       s64 enablewait;
+
+       if (wuxga_nt->prepared)
+               return 0;
+
+       /*
+        * If the user re-enabled the panel before the required off-time then
+        * we need to wait the remaining period before re-enabling regulator
+        */
+       enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real());
+
+       /* Sanity check, this should never happen */
+       if (enablewait > MIN_POFF_MS)
+               enablewait = MIN_POFF_MS;
+
+       if (enablewait > 0)
+               msleep(enablewait);
+
+       ret = regulator_enable(wuxga_nt->supply);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * A minimum delay of 250ms is required after power-up until commands
+        * can be sent
+        */
+       msleep(250);
+
+       ret = wuxga_nt_panel_on(wuxga_nt);
+       if (ret < 0) {
+               dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+               goto poweroff;
+       }
+
+       wuxga_nt->prepared = true;
+
+       return 0;
+
+poweroff:
+       regulator_disable(wuxga_nt->supply);
+
+       return ret;
+}
+
+static int wuxga_nt_panel_enable(struct drm_panel *panel)
+{
+       struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
+
+       if (wuxga_nt->enabled)
+               return 0;
+
+       if (wuxga_nt->backlight) {
+               wuxga_nt->backlight->props.power = FB_BLANK_UNBLANK;
+               wuxga_nt->backlight->props.state &= ~BL_CORE_FBBLANK;
+               backlight_update_status(wuxga_nt->backlight);
+       }
+
+       wuxga_nt->enabled = true;
+
+       return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+       .clock = 164402,
+       .hdisplay = 1920,
+       .hsync_start = 1920 + 152,
+       .hsync_end = 1920 + 152 + 52,
+       .htotal = 1920 + 152 + 52 + 20,
+       .vdisplay = 1200,
+       .vsync_start = 1200 + 24,
+       .vsync_end = 1200 + 24 + 6,
+       .vtotal = 1200 + 24 + 6 + 48,
+       .vrefresh = 60,
+};
+
+static int wuxga_nt_panel_get_modes(struct drm_panel *panel)
+{
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_duplicate(panel->drm, &default_mode);
+       if (!mode) {
+               dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+                               default_mode.hdisplay, default_mode.vdisplay,
+                               default_mode.vrefresh);
+               return -ENOMEM;
+       }
+
+       drm_mode_set_name(mode);
+
+       drm_mode_probed_add(panel->connector, mode);
+
+       panel->connector->display_info.width_mm = 217;
+       panel->connector->display_info.height_mm = 136;
+
+       return 1;
+}
+
+static const struct drm_panel_funcs wuxga_nt_panel_funcs = {
+       .disable = wuxga_nt_panel_disable,
+       .unprepare = wuxga_nt_panel_unprepare,
+       .prepare = wuxga_nt_panel_prepare,
+       .enable = wuxga_nt_panel_enable,
+       .get_modes = wuxga_nt_panel_get_modes,
+};
+
+static const struct of_device_id wuxga_nt_of_match[] = {
+       { .compatible = "panasonic,vvx10f034n00", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
+
+static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)
+{
+       struct device *dev = &wuxga_nt->dsi->dev;
+       struct device_node *np;
+       int ret;
+
+       wuxga_nt->mode = &default_mode;
+
+       wuxga_nt->supply = devm_regulator_get(dev, "power");
+       if (IS_ERR(wuxga_nt->supply))
+               return PTR_ERR(wuxga_nt->supply);
+
+       np = of_parse_phandle(dev->of_node, "backlight", 0);
+       if (np) {
+               wuxga_nt->backlight = of_find_backlight_by_node(np);
+               of_node_put(np);
+
+               if (!wuxga_nt->backlight)
+                       return -EPROBE_DEFER;
+       }
+
+       drm_panel_init(&wuxga_nt->base);
+       wuxga_nt->base.funcs = &wuxga_nt_panel_funcs;
+       wuxga_nt->base.dev = &wuxga_nt->dsi->dev;
+
+       ret = drm_panel_add(&wuxga_nt->base);
+       if (ret < 0)
+               goto put_backlight;
+
+       return 0;
+
+put_backlight:
+       if (wuxga_nt->backlight)
+               put_device(&wuxga_nt->backlight->dev);
+
+       return ret;
+}
+
+static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt)
+{
+       if (wuxga_nt->base.dev)
+               drm_panel_remove(&wuxga_nt->base);
+
+       if (wuxga_nt->backlight)
+               put_device(&wuxga_nt->backlight->dev);
+}
+
+static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi)
+{
+       struct wuxga_nt_panel *wuxga_nt;
+       int ret;
+
+       dsi->lanes = 4;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
+                       MIPI_DSI_MODE_VIDEO_HSE |
+                       MIPI_DSI_CLOCK_NON_CONTINUOUS |
+                       MIPI_DSI_MODE_LPM;
+
+       wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL);
+       if (!wuxga_nt)
+               return -ENOMEM;
+
+       mipi_dsi_set_drvdata(dsi, wuxga_nt);
+
+       wuxga_nt->dsi = dsi;
+
+       ret = wuxga_nt_panel_add(wuxga_nt);
+       if (ret < 0)
+               return ret;
+
+       return mipi_dsi_attach(dsi);
+}
+
+static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi)
+{
+       struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
+       int ret;
+
+       ret = wuxga_nt_panel_disable(&wuxga_nt->base);
+       if (ret < 0)
+               dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+       ret = mipi_dsi_detach(dsi);
+       if (ret < 0)
+               dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
+
+       drm_panel_detach(&wuxga_nt->base);
+       wuxga_nt_panel_del(wuxga_nt);
+
+       return 0;
+}
+
+static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+       struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
+
+       wuxga_nt_panel_disable(&wuxga_nt->base);
+}
+
+static struct mipi_dsi_driver wuxga_nt_panel_driver = {
+       .driver = {
+               .name = "panel-panasonic-vvx10f034n00",
+               .of_match_table = wuxga_nt_of_match,
+       },
+       .probe = wuxga_nt_panel_probe,
+       .remove = wuxga_nt_panel_remove,
+       .shutdown = wuxga_nt_panel_shutdown,
+};
+module_mipi_dsi_driver(wuxga_nt_panel_driver);
+
+MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
+MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
new file mode 100644 (file)
index 0000000..3aeb0bd
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2015 Red Hat
+ * Copyright (C) 2015 Sony Mobile Communications Inc.
+ * Author: Werner Johansson <werner.johansson@sonymobile.com>
+ *
+ * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+struct sharp_nt_panel {
+       struct drm_panel base;
+       struct mipi_dsi_device *dsi;
+
+       struct backlight_device *backlight;
+       struct regulator *supply;
+       struct gpio_desc *reset_gpio;
+
+       bool prepared;
+       bool enabled;
+
+       const struct drm_display_mode *mode;
+};
+
+static inline struct sharp_nt_panel *to_sharp_nt_panel(struct drm_panel *panel)
+{
+       return container_of(panel, struct sharp_nt_panel, base);
+}
+
+static int sharp_nt_panel_init(struct sharp_nt_panel *sharp_nt)
+{
+       struct mipi_dsi_device *dsi = sharp_nt->dsi;
+       int ret;
+
+       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+       ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+       if (ret < 0)
+               return ret;
+
+       msleep(120);
+
+       /* Novatek two-lane operation */
+       ret = mipi_dsi_dcs_write(dsi, 0xae, (u8[]){ 0x03 }, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Set both MCU and RGB I/F to 24bpp */
+       ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
+                                       (MIPI_DCS_PIXEL_FMT_24BIT << 4));
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int sharp_nt_panel_on(struct sharp_nt_panel *sharp_nt)
+{
+       struct mipi_dsi_device *dsi = sharp_nt->dsi;
+       int ret;
+
+       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+       ret = mipi_dsi_dcs_set_display_on(dsi);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int sharp_nt_panel_off(struct sharp_nt_panel *sharp_nt)
+{
+       struct mipi_dsi_device *dsi = sharp_nt->dsi;
+       int ret;
+
+       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+       ret = mipi_dsi_dcs_set_display_off(dsi);
+       if (ret < 0)
+               return ret;
+
+       ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+
+static int sharp_nt_panel_disable(struct drm_panel *panel)
+{
+       struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
+
+       if (!sharp_nt->enabled)
+               return 0;
+
+       if (sharp_nt->backlight) {
+               sharp_nt->backlight->props.power = FB_BLANK_POWERDOWN;
+               backlight_update_status(sharp_nt->backlight);
+       }
+
+       sharp_nt->enabled = false;
+
+       return 0;
+}
+
+static int sharp_nt_panel_unprepare(struct drm_panel *panel)
+{
+       struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
+       int ret;
+
+       if (!sharp_nt->prepared)
+               return 0;
+
+       ret = sharp_nt_panel_off(sharp_nt);
+       if (ret < 0) {
+               dev_err(panel->dev, "failed to set panel off: %d\n", ret);
+               return ret;
+       }
+
+       regulator_disable(sharp_nt->supply);
+       if (sharp_nt->reset_gpio)
+               gpiod_set_value(sharp_nt->reset_gpio, 0);
+
+       sharp_nt->prepared = false;
+
+       return 0;
+}
+
+static int sharp_nt_panel_prepare(struct drm_panel *panel)
+{
+       struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
+       int ret;
+
+       if (sharp_nt->prepared)
+               return 0;
+
+       ret = regulator_enable(sharp_nt->supply);
+       if (ret < 0)
+               return ret;
+
+       msleep(20);
+
+       if (sharp_nt->reset_gpio) {
+               gpiod_set_value(sharp_nt->reset_gpio, 1);
+               msleep(1);
+               gpiod_set_value(sharp_nt->reset_gpio, 0);
+               msleep(1);
+               gpiod_set_value(sharp_nt->reset_gpio, 1);
+               msleep(10);
+       }
+
+       ret = sharp_nt_panel_init(sharp_nt);
+       if (ret < 0) {
+               dev_err(panel->dev, "failed to init panel: %d\n", ret);
+               goto poweroff;
+       }
+
+       ret = sharp_nt_panel_on(sharp_nt);
+       if (ret < 0) {
+               dev_err(panel->dev, "failed to set panel on: %d\n", ret);
+               goto poweroff;
+       }
+
+       sharp_nt->prepared = true;
+
+       return 0;
+
+poweroff:
+       regulator_disable(sharp_nt->supply);
+       if (sharp_nt->reset_gpio)
+               gpiod_set_value(sharp_nt->reset_gpio, 0);
+       return ret;
+}
+
+static int sharp_nt_panel_enable(struct drm_panel *panel)
+{
+       struct sharp_nt_panel *sharp_nt = to_sharp_nt_panel(panel);
+
+       if (sharp_nt->enabled)
+               return 0;
+
+       if (sharp_nt->backlight) {
+               sharp_nt->backlight->props.power = FB_BLANK_UNBLANK;
+               backlight_update_status(sharp_nt->backlight);
+       }
+
+       sharp_nt->enabled = true;
+
+       return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+       .clock = 41118,
+       .hdisplay = 540,
+       .hsync_start = 540 + 48,
+       .hsync_end = 540 + 48 + 80,
+       .htotal = 540 + 48 + 80 + 32,
+       .vdisplay = 960,
+       .vsync_start = 960 + 3,
+       .vsync_end = 960 + 3 + 15,
+       .vtotal = 960 + 3 + 15 + 1,
+       .vrefresh = 60,
+};
+
+static int sharp_nt_panel_get_modes(struct drm_panel *panel)
+{
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_duplicate(panel->drm, &default_mode);
+       if (!mode) {
+               dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+                               default_mode.hdisplay, default_mode.vdisplay,
+                               default_mode.vrefresh);
+               return -ENOMEM;
+       }
+
+       drm_mode_set_name(mode);
+
+       drm_mode_probed_add(panel->connector, mode);
+
+       panel->connector->display_info.width_mm = 54;
+       panel->connector->display_info.height_mm = 95;
+
+       return 1;
+}
+
+static const struct drm_panel_funcs sharp_nt_panel_funcs = {
+       .disable = sharp_nt_panel_disable,
+       .unprepare = sharp_nt_panel_unprepare,
+       .prepare = sharp_nt_panel_prepare,
+       .enable = sharp_nt_panel_enable,
+       .get_modes = sharp_nt_panel_get_modes,
+};
+
+static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)
+{
+       struct device *dev = &sharp_nt->dsi->dev;
+       struct device_node *np;
+       int ret;
+
+       sharp_nt->mode = &default_mode;
+
+       sharp_nt->supply = devm_regulator_get(dev, "avdd");
+       if (IS_ERR(sharp_nt->supply))
+               return PTR_ERR(sharp_nt->supply);
+
+       sharp_nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(sharp_nt->reset_gpio)) {
+               dev_err(dev, "cannot get reset-gpios %ld\n",
+                       PTR_ERR(sharp_nt->reset_gpio));
+               sharp_nt->reset_gpio = NULL;
+       } else {
+               gpiod_set_value(sharp_nt->reset_gpio, 0);
+       }
+
+       np = of_parse_phandle(dev->of_node, "backlight", 0);
+       if (np) {
+               sharp_nt->backlight = of_find_backlight_by_node(np);
+               of_node_put(np);
+
+               if (!sharp_nt->backlight)
+                       return -EPROBE_DEFER;
+       }
+
+       drm_panel_init(&sharp_nt->base);
+       sharp_nt->base.funcs = &sharp_nt_panel_funcs;
+       sharp_nt->base.dev = &sharp_nt->dsi->dev;
+
+       ret = drm_panel_add(&sharp_nt->base);
+       if (ret < 0)
+               goto put_backlight;
+
+       return 0;
+
+put_backlight:
+       if (sharp_nt->backlight)
+               put_device(&sharp_nt->backlight->dev);
+
+       return ret;
+}
+
+static void sharp_nt_panel_del(struct sharp_nt_panel *sharp_nt)
+{
+       if (sharp_nt->base.dev)
+               drm_panel_remove(&sharp_nt->base);
+
+       if (sharp_nt->backlight)
+               put_device(&sharp_nt->backlight->dev);
+}
+
+static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)
+{
+       struct sharp_nt_panel *sharp_nt;
+       int ret;
+
+       dsi->lanes = 2;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
+                       MIPI_DSI_MODE_VIDEO_HSE |
+                       MIPI_DSI_CLOCK_NON_CONTINUOUS |
+                       MIPI_DSI_MODE_EOT_PACKET;
+
+       sharp_nt = devm_kzalloc(&dsi->dev, sizeof(*sharp_nt), GFP_KERNEL);
+       if (!sharp_nt)
+               return -ENOMEM;
+
+       mipi_dsi_set_drvdata(dsi, sharp_nt);
+
+       sharp_nt->dsi = dsi;
+
+       ret = sharp_nt_panel_add(sharp_nt);
+       if (ret < 0)
+               return ret;
+
+       return mipi_dsi_attach(dsi);
+}
+
+static int sharp_nt_panel_remove(struct mipi_dsi_device *dsi)
+{
+       struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
+       int ret;
+
+       ret = sharp_nt_panel_disable(&sharp_nt->base);
+       if (ret < 0)
+               dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+       ret = mipi_dsi_detach(dsi);
+       if (ret < 0)
+               dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
+
+       drm_panel_detach(&sharp_nt->base);
+       sharp_nt_panel_del(sharp_nt);
+
+       return 0;
+}
+
+static void sharp_nt_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+       struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
+
+       sharp_nt_panel_disable(&sharp_nt->base);
+}
+
+static const struct of_device_id sharp_nt_of_match[] = {
+       { .compatible = "sharp,ls043t1le01-qhd", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sharp_nt_of_match);
+
+static struct mipi_dsi_driver sharp_nt_panel_driver = {
+       .driver = {
+               .name = "panel-sharp-ls043t1le01-qhd",
+               .of_match_table = sharp_nt_of_match,
+       },
+       .probe = sharp_nt_panel_probe,
+       .remove = sharp_nt_panel_remove,
+       .shutdown = sharp_nt_panel_shutdown,
+};
+module_mipi_dsi_driver(sharp_nt_panel_driver);
+
+MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
+MODULE_DESCRIPTION("Sharp LS043T1LE01 NT35565-based qHD (540x960) video mode panel driver");
+MODULE_LICENSE("GPL v2");
index f97b73e..f88a631 100644 (file)
@@ -44,6 +44,10 @@ struct panel_desc {
 
        unsigned int bpc;
 
+       /**
+        * @width: width (in millimeters) of the panel's active display area
+        * @height: height (in millimeters) of the panel's active display area
+        */
        struct {
                unsigned int width;
                unsigned int height;
@@ -832,6 +836,34 @@ static const struct panel_desc innolux_g121i1_l01 = {
        },
 };
 
+static const struct drm_display_mode innolux_g121x1_l03_mode = {
+       .clock = 65000,
+       .hdisplay = 1024,
+       .hsync_start = 1024 + 0,
+       .hsync_end = 1024 + 1,
+       .htotal = 1024 + 0 + 1 + 320,
+       .vdisplay = 768,
+       .vsync_start = 768 + 38,
+       .vsync_end = 768 + 38 + 1,
+       .vtotal = 768 + 38 + 1 + 0,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc innolux_g121x1_l03 = {
+       .modes = &innolux_g121x1_l03_mode,
+       .num_modes = 1,
+       .bpc = 6,
+       .size = {
+               .width = 246,
+               .height = 185,
+       },
+       .delay = {
+               .enable = 200,
+               .unprepare = 200,
+               .disable = 400,
+       },
+};
+
 static const struct drm_display_mode innolux_n116bge_mode = {
        .clock = 76420,
        .hdisplay = 1366,
@@ -902,6 +934,30 @@ static const struct panel_desc innolux_zj070na_01p = {
        },
 };
 
+static const struct display_timing kyo_tcg121xglp_timing = {
+       .pixelclock = { 52000000, 65000000, 71000000 },
+       .hactive = { 1024, 1024, 1024 },
+       .hfront_porch = { 2, 2, 2 },
+       .hback_porch = { 2, 2, 2 },
+       .hsync_len = { 86, 124, 244 },
+       .vactive = { 768, 768, 768 },
+       .vfront_porch = { 2, 2, 2 },
+       .vback_porch = { 2, 2, 2 },
+       .vsync_len = { 6, 34, 73 },
+       .flags = DISPLAY_FLAGS_DE_HIGH,
+};
+
+static const struct panel_desc kyo_tcg121xglp = {
+       .timings = &kyo_tcg121xglp_timing,
+       .num_timings = 1,
+       .bpc = 8,
+       .size = {
+               .width = 246,
+               .height = 184,
+       },
+       .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+};
+
 static const struct drm_display_mode lg_lb070wv8_mode = {
        .clock = 33246,
        .hdisplay = 800,
@@ -1027,6 +1083,30 @@ static const struct panel_desc ortustech_com43h4m85ulc = {
        .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
 };
 
+static const struct drm_display_mode qd43003c0_40_mode = {
+       .clock = 9000,
+       .hdisplay = 480,
+       .hsync_start = 480 + 8,
+       .hsync_end = 480 + 8 + 4,
+       .htotal = 480 + 8 + 4 + 39,
+       .vdisplay = 272,
+       .vsync_start = 272 + 4,
+       .vsync_end = 272 + 4 + 10,
+       .vtotal = 272 + 4 + 10 + 2,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc qd43003c0_40 = {
+       .modes = &qd43003c0_40_mode,
+       .num_modes = 1,
+       .bpc = 8,
+       .size = {
+               .width = 95,
+               .height = 53,
+       },
+       .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+};
+
 static const struct drm_display_mode samsung_ltn101nt05_mode = {
        .clock = 54030,
        .hdisplay = 1024,
@@ -1157,6 +1237,9 @@ static const struct of_device_id platform_of_match[] = {
        }, {
                .compatible ="innolux,g121i1-l01",
                .data = &innolux_g121i1_l01
+       }, {
+               .compatible = "innolux,g121x1-l03",
+               .data = &innolux_g121x1_l03,
        }, {
                .compatible = "innolux,n116bge",
                .data = &innolux_n116bge,
@@ -1166,6 +1249,9 @@ static const struct of_device_id platform_of_match[] = {
        }, {
                .compatible = "innolux,zj070na-01p",
                .data = &innolux_zj070na_01p,
+       }, {
+               .compatible = "kyo,tcg121xglp",
+               .data = &kyo_tcg121xglp,
        }, {
                .compatible = "lg,lb070wv8",
                .data = &lg_lb070wv8,
@@ -1181,6 +1267,9 @@ static const struct of_device_id platform_of_match[] = {
        }, {
                .compatible = "ortustech,com43h4m85ulc",
                .data = &ortustech_com43h4m85ulc,
+       }, {
+               .compatible = "qiaodian,qd43003c0-40",
+               .data = &qd43003c0_40,
        }, {
                .compatible = "samsung,ltn101nt05",
                .data = &samsung_ltn101nt05,
@@ -1263,6 +1352,36 @@ static const struct panel_desc_dsi auo_b080uan01 = {
        .lanes = 4,
 };
 
+static const struct drm_display_mode boe_tv080wum_nl0_mode = {
+       .clock = 160000,
+       .hdisplay = 1200,
+       .hsync_start = 1200 + 120,
+       .hsync_end = 1200 + 120 + 20,
+       .htotal = 1200 + 120 + 20 + 21,
+       .vdisplay = 1920,
+       .vsync_start = 1920 + 21,
+       .vsync_end = 1920 + 21 + 3,
+       .vtotal = 1920 + 21 + 3 + 18,
+       .vrefresh = 60,
+       .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc_dsi boe_tv080wum_nl0 = {
+       .desc = {
+               .modes = &boe_tv080wum_nl0_mode,
+               .num_modes = 1,
+               .size = {
+                       .width = 107,
+                       .height = 172,
+               },
+       },
+       .flags = MIPI_DSI_MODE_VIDEO |
+                MIPI_DSI_MODE_VIDEO_BURST |
+                MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
+       .format = MIPI_DSI_FMT_RGB888,
+       .lanes = 4,
+};
+
 static const struct drm_display_mode lg_ld070wx3_sl01_mode = {
        .clock = 71000,
        .hdisplay = 800,
@@ -1348,10 +1467,14 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
        .lanes = 4,
 };
 
+
 static const struct of_device_id dsi_of_match[] = {
        {
                .compatible = "auo,b080uan01",
                .data = &auo_b080uan01
+       }, {
+               .compatible = "boe,tv080wum-nl0",
+               .data = &boe_tv080wum_nl0
        }, {
                .compatible = "lg,ld070wx3-sl01",
                .data = &lg_ld070wx3_sl01
index cddba07..8627651 100644 (file)
@@ -876,16 +876,6 @@ static const struct drm_connector_helper_funcs qxl_connector_helper_funcs = {
        .best_encoder = qxl_best_encoder,
 };
 
-static void qxl_conn_save(struct drm_connector *connector)
-{
-       DRM_DEBUG("\n");
-}
-
-static void qxl_conn_restore(struct drm_connector *connector)
-{
-       DRM_DEBUG("\n");
-}
-
 static enum drm_connector_status qxl_conn_detect(
                        struct drm_connector *connector,
                        bool force)
@@ -932,10 +922,8 @@ static void qxl_conn_destroy(struct drm_connector *connector)
 
 static const struct drm_connector_funcs qxl_connector_funcs = {
        .dpms = drm_helper_connector_dpms,
-       .save = qxl_conn_save,
-       .restore = qxl_conn_restore,
        .detect = qxl_conn_detect,
-       .fill_modes = drm_helper_probe_single_connector_modes_nomerge,
+       .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = qxl_conn_set_property,
        .destroy = qxl_conn_destroy,
 };
@@ -980,7 +968,7 @@ static int qdev_output_init(struct drm_device *dev, int num_output)
                           &qxl_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
 
        drm_encoder_init(dev, &qxl_output->enc, &qxl_enc_funcs,
-                        DRM_MODE_ENCODER_VIRTUAL);
+                        DRM_MODE_ENCODER_VIRTUAL, NULL);
 
        /* we get HPD via client monitors config */
        connector->polled = DRM_CONNECTOR_POLL_HPD;
index dac78ad..801dd60 100644 (file)
@@ -25,6 +25,7 @@
  */
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/radeon_drm.h>
 #include <drm/drm_fixed.h>
 #include "radeon.h"
index bb29214..01b20e1 100644 (file)
@@ -2767,23 +2767,27 @@ radeon_add_atom_encoder(struct drm_device *dev,
        case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
                if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
                        radeon_encoder->rmx_type = RMX_FULL;
-                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
+                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                        radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder);
                } else {
-                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
+                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                        radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
                }
                drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_DAC1:
-               drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
+               drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                DRM_MODE_ENCODER_DAC, NULL);
                radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder);
                drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_DAC2:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
-               drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TVDAC);
+               drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                DRM_MODE_ENCODER_TVDAC, NULL);
                radeon_encoder->enc_priv = radeon_atombios_set_dac_info(radeon_encoder);
                drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs);
                break;
@@ -2797,13 +2801,16 @@ radeon_add_atom_encoder(struct drm_device *dev,
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
                if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
                        radeon_encoder->rmx_type = RMX_FULL;
-                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
+                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                        radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder);
                } else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) {
-                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
+                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                        DRM_MODE_ENCODER_DAC, NULL);
                        radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
                } else {
-                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
+                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                        radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
                }
                drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
@@ -2820,11 +2827,14 @@ radeon_add_atom_encoder(struct drm_device *dev,
                /* these are handled by the primary encoders */
                radeon_encoder->is_ext_encoder = true;
                if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
-                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
+                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                        DRM_MODE_ENCODER_LVDS, NULL);
                else if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT))
-                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_DAC);
+                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                        DRM_MODE_ENCODER_DAC, NULL);
                else
-                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
+                       drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs,
+                                        DRM_MODE_ENCODER_TMDS, NULL);
                drm_encoder_helper_add(encoder, &radeon_atom_ext_helper_funcs);
                break;
        }
index 744f5c4..94323f5 100644 (file)
@@ -641,7 +641,7 @@ radeon_dp_create_fake_mst_encoder(struct radeon_connector *connector)
        }
 
        drm_encoder_init(dev, &radeon_encoder->base, &radeon_dp_mst_enc_funcs,
-                        DRM_MODE_ENCODER_DPMST);
+                        DRM_MODE_ENCODER_DPMST, NULL);
        drm_encoder_helper_add(encoder, &radeon_mst_helper_funcs);
 
        mst_enc = radeon_encoder->enc_priv;
index 678b438..32b338f 100644 (file)
@@ -25,6 +25,7 @@
  */
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/radeon_drm.h>
 #include <drm/drm_fixed.h>
 #include "radeon.h"
index 30de433..88dc973 100644 (file)
@@ -1772,7 +1772,8 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_enum, uint32_
        switch (radeon_encoder->encoder_id) {
        case ENCODER_OBJECT_ID_INTERNAL_LVDS:
                encoder->possible_crtcs = 0x1;
-               drm_encoder_init(dev, encoder, &radeon_legacy_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS);
+               drm_encoder_init(dev, encoder, &radeon_legacy_lvds_enc_funcs,
+                                DRM_MODE_ENCODER_LVDS, NULL);
                drm_encoder_helper_add(encoder, &radeon_legacy_lvds_helper_funcs);
                if (rdev->is_atom_bios)
                        radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder);
@@ -1781,12 +1782,14 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_enum, uint32_
                radeon_encoder->rmx_type = RMX_FULL;
                break;
        case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
-               drm_encoder_init(dev, encoder, &radeon_legacy_tmds_int_enc_funcs, DRM_MODE_ENCODER_TMDS);
+               drm_encoder_init(dev, encoder, &radeon_legacy_tmds_int_enc_funcs,
+                                DRM_MODE_ENCODER_TMDS, NULL);
                drm_encoder_helper_add(encoder, &radeon_legacy_tmds_int_helper_funcs);
                radeon_encoder->enc_priv = radeon_legacy_get_tmds_info(radeon_encoder);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_DAC1:
-               drm_encoder_init(dev, encoder, &radeon_legacy_primary_dac_enc_funcs, DRM_MODE_ENCODER_DAC);
+               drm_encoder_init(dev, encoder, &radeon_legacy_primary_dac_enc_funcs,
+                                DRM_MODE_ENCODER_DAC, NULL);
                drm_encoder_helper_add(encoder, &radeon_legacy_primary_dac_helper_funcs);
                if (rdev->is_atom_bios)
                        radeon_encoder->enc_priv = radeon_atombios_get_primary_dac_info(radeon_encoder);
@@ -1794,7 +1797,8 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_enum, uint32_
                        radeon_encoder->enc_priv = radeon_combios_get_primary_dac_info(radeon_encoder);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_DAC2:
-               drm_encoder_init(dev, encoder, &radeon_legacy_tv_dac_enc_funcs, DRM_MODE_ENCODER_TVDAC);
+               drm_encoder_init(dev, encoder, &radeon_legacy_tv_dac_enc_funcs,
+                                DRM_MODE_ENCODER_TVDAC, NULL);
                drm_encoder_helper_add(encoder, &radeon_legacy_tv_dac_helper_funcs);
                if (rdev->is_atom_bios)
                        radeon_encoder->enc_priv = radeon_atombios_get_tv_dac_info(radeon_encoder);
@@ -1802,7 +1806,8 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_enum, uint32_
                        radeon_encoder->enc_priv = radeon_combios_get_tv_dac_info(radeon_encoder);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_DVO1:
-               drm_encoder_init(dev, encoder, &radeon_legacy_tmds_ext_enc_funcs, DRM_MODE_ENCODER_TMDS);
+               drm_encoder_init(dev, encoder, &radeon_legacy_tmds_ext_enc_funcs,
+                                DRM_MODE_ENCODER_TMDS, NULL);
                drm_encoder_helper_add(encoder, &radeon_legacy_tmds_ext_helper_funcs);
                if (!rdev->is_atom_bios)
                        radeon_encoder->enc_priv = radeon_legacy_get_ext_tmds_info(radeon_encoder);
index 48cb199..88a4b70 100644 (file)
@@ -613,7 +613,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
 
        ret = drm_crtc_init_with_planes(rcdu->ddev, crtc,
                                        &rgrp->planes[index % 2].plane,
-                                       NULL, &crtc_funcs);
+                                       NULL, &crtc_funcs, NULL);
        if (ret < 0)
                return ret;
 
index d0ae1e8..c087007 100644 (file)
@@ -173,7 +173,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
                        goto done;
        } else {
                ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
-                                      encoder_type);
+                                      encoder_type, NULL);
                if (ret < 0)
                        goto done;
 
index 81da841..11267de 100644 (file)
@@ -151,7 +151,7 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
                goto error;
 
        ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
-                              DRM_MODE_ENCODER_TMDS);
+                              DRM_MODE_ENCODER_TMDS, NULL);
        if (ret < 0)
                goto error;
 
index ffa5837..c3ed952 100644 (file)
@@ -410,7 +410,8 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp)
 
                ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
                                               &rcar_du_plane_funcs, formats,
-                                              ARRAY_SIZE(formats), type);
+                                              ARRAY_SIZE(formats), type,
+                                              NULL);
                if (ret < 0)
                        return ret;
 
index 80d6fc8..525b5a8 100644 (file)
@@ -295,7 +295,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 
        drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
        drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
 
        return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
 }
index 03c47ee..dd8e086 100644 (file)
@@ -1478,7 +1478,7 @@ static int vop_create_crtc(struct vop *vop)
                                               0, &vop_plane_funcs,
                                               win_data->phy->data_formats,
                                               win_data->phy->nformats,
-                                              win_data->type);
+                                              win_data->type, NULL);
                if (ret) {
                        DRM_ERROR("failed to initialize plane\n");
                        goto err_cleanup_planes;
@@ -1492,7 +1492,7 @@ static int vop_create_crtc(struct vop *vop)
        }
 
        ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
-                                       &vop_crtc_funcs);
+                                       &vop_crtc_funcs, NULL);
        if (ret)
                return ret;
 
@@ -1515,7 +1515,7 @@ static int vop_create_crtc(struct vop *vop)
                                               &vop_plane_funcs,
                                               win_data->phy->data_formats,
                                               win_data->phy->nformats,
-                                              win_data->type);
+                                              win_data->type, NULL);
                if (ret) {
                        DRM_ERROR("failed to initialize overlay plane\n");
                        goto err_cleanup_crtc;
index e9272b0..b80802f 100644 (file)
@@ -613,7 +613,7 @@ int shmob_drm_encoder_create(struct shmob_drm_device *sdev)
        encoder->possible_crtcs = 1;
 
        ret = drm_encoder_init(sdev->ddev, encoder, &encoder_funcs,
-                              DRM_MODE_ENCODER_LVDS);
+                              DRM_MODE_ENCODER_LVDS, NULL);
        if (ret < 0)
                return ret;
 
index 3ae09dc..de11c7c 100644 (file)
@@ -367,7 +367,7 @@ int sti_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer,
        int res;
 
        res = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
-                                       &sti_crtc_funcs);
+                                       &sti_crtc_funcs, NULL);
        if (res) {
                DRM_ERROR("Can't initialze CRTC\n");
                return -EINVAL;
index dd10321..8078631 100644 (file)
@@ -272,7 +272,7 @@ struct drm_plane *sti_cursor_create(struct drm_device *drm_dev,
                                       &sti_plane_helpers_funcs,
                                       cursor_supported_formats,
                                       ARRAY_SIZE(cursor_supported_formats),
-                                      DRM_PLANE_TYPE_CURSOR);
+                                      DRM_PLANE_TYPE_CURSOR, NULL);
        if (res) {
                DRM_ERROR("Failed to initialize universal plane\n");
                goto err_plane;
index c85dc7d..f9a1d92 100644 (file)
@@ -630,7 +630,7 @@ struct drm_plane *sti_gdp_create(struct drm_device *drm_dev,
                                       &sti_plane_helpers_funcs,
                                       gdp_supported_formats,
                                       ARRAY_SIZE(gdp_supported_formats),
-                                      type);
+                                      type, NULL);
        if (res) {
                DRM_ERROR("Failed to initialize universal plane\n");
                goto err;
index d735dac..49cce83 100644 (file)
@@ -543,8 +543,6 @@ static int sti_hda_connector_get_modes(struct drm_connector *connector)
                count++;
        }
 
-       drm_mode_sort(&connector->modes);
-
        return count;
 }
 
index ea0690b..43861b5 100644 (file)
@@ -973,7 +973,7 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev,
                                       &sti_plane_helpers_funcs,
                                       hqvdp_supported_formats,
                                       ARRAY_SIZE(hqvdp_supported_formats),
-                                      DRM_PLANE_TYPE_OVERLAY);
+                                      DRM_PLANE_TYPE_OVERLAY, NULL);
        if (res) {
                DRM_ERROR("Failed to initialize universal plane\n");
                return NULL;
index c8a4c5d..f2afcf5 100644 (file)
@@ -512,7 +512,8 @@ sti_tvout_create_dvo_encoder(struct drm_device *dev,
        drm_encoder->possible_clones = 1 << 0;
 
        drm_encoder_init(dev, drm_encoder,
-                        &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS);
+                        &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS,
+                        NULL);
 
        drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs);
 
@@ -564,7 +565,7 @@ static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,
        drm_encoder->possible_clones = 1 << 0;
 
        drm_encoder_init(dev, drm_encoder,
-                       &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC);
+                       &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL);
 
        drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs);
 
@@ -613,7 +614,7 @@ static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev,
        drm_encoder->possible_clones = 1 << 1;
 
        drm_encoder_init(dev, drm_encoder,
-                       &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS);
+                       &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL);
 
        drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs);
 
index ea174b5..dde6f20 100644 (file)
@@ -660,7 +660,8 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
 
        err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
                                       &tegra_primary_plane_funcs, formats,
-                                      num_formats, DRM_PLANE_TYPE_PRIMARY);
+                                      num_formats, DRM_PLANE_TYPE_PRIMARY,
+                                      NULL);
        if (err < 0) {
                kfree(plane);
                return ERR_PTR(err);
@@ -827,7 +828,8 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 
        err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
                                       &tegra_cursor_plane_funcs, formats,
-                                      num_formats, DRM_PLANE_TYPE_CURSOR);
+                                      num_formats, DRM_PLANE_TYPE_CURSOR,
+                                      NULL);
        if (err < 0) {
                kfree(plane);
                return ERR_PTR(err);
@@ -890,7 +892,8 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
 
        err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
                                       &tegra_overlay_plane_funcs, formats,
-                                      num_formats, DRM_PLANE_TYPE_OVERLAY);
+                                      num_formats, DRM_PLANE_TYPE_OVERLAY,
+                                      NULL);
        if (err < 0) {
                kfree(plane);
                return ERR_PTR(err);
@@ -1732,7 +1735,7 @@ static int tegra_dc_init(struct host1x_client *client)
        }
 
        err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
-                                       &tegra_crtc_funcs);
+                                       &tegra_crtc_funcs, NULL);
        if (err < 0)
                goto cleanup;
 
index f0a138e..50d46ae 100644 (file)
@@ -1023,7 +1023,7 @@ static int tegra_dsi_init(struct host1x_client *client)
 
                drm_encoder_init(drm, &dsi->output.encoder,
                                 &tegra_dsi_encoder_funcs,
-                                DRM_MODE_ENCODER_DSI);
+                                DRM_MODE_ENCODER_DSI, NULL);
                drm_encoder_helper_add(&dsi->output.encoder,
                                       &tegra_dsi_encoder_helper_funcs);
 
index 52b32cb..b7ef492 100644 (file)
@@ -1320,7 +1320,7 @@ static int tegra_hdmi_init(struct host1x_client *client)
        hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF;
 
        drm_encoder_init(drm, &hdmi->output.encoder, &tegra_hdmi_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
        drm_encoder_helper_add(&hdmi->output.encoder,
                               &tegra_hdmi_encoder_helper_funcs);
 
index bc9735b..e246334 100644 (file)
@@ -287,7 +287,7 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
        output->connector.dpms = DRM_MODE_DPMS_OFF;
 
        drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs,
-                        DRM_MODE_ENCODER_LVDS);
+                        DRM_MODE_ENCODER_LVDS, NULL);
        drm_encoder_helper_add(&output->encoder,
                               &tegra_rgb_encoder_helper_funcs);
 
index a5fddd3..757c6e8 100644 (file)
@@ -2163,7 +2163,7 @@ static int tegra_sor_init(struct host1x_client *client)
        sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
 
        drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs,
-                        encoder);
+                        encoder, NULL);
        drm_encoder_helper_add(&sor->output.encoder, helpers);
 
        drm_mode_connector_attach_encoder(&sor->output.connector,
index 0af8bed..4dda6e2 100644 (file)
@@ -128,7 +128,7 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev,
        encoder->possible_crtcs = 1;
 
        ret = drm_encoder_init(dev, encoder, &panel_encoder_funcs,
-                       DRM_MODE_ENCODER_LVDS);
+                       DRM_MODE_ENCODER_LVDS, NULL);
        if (ret < 0)
                goto fail;
 
index 354c47c..5052a8a 100644 (file)
@@ -138,7 +138,7 @@ static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev,
        encoder->possible_crtcs = 1;
 
        ret = drm_encoder_init(dev, encoder, &tfp410_encoder_funcs,
-                       DRM_MODE_ENCODER_TMDS);
+                       DRM_MODE_ENCODER_TMDS, NULL);
        if (ret < 0)
                goto fail;
 
index 4052c46..a181a64 100644 (file)
@@ -73,7 +73,8 @@ struct drm_encoder *udl_encoder_init(struct drm_device *dev)
        if (!encoder)
                return NULL;
 
-       drm_encoder_init(dev, encoder, &udl_enc_funcs, DRM_MODE_ENCODER_TMDS);
+       drm_encoder_init(dev, encoder, &udl_enc_funcs, DRM_MODE_ENCODER_TMDS,
+                        NULL);
        drm_encoder_helper_add(encoder, &udl_helper_funcs);
        encoder->possible_crtcs = 1;
        return encoder;
index 32b4f9c..4c6a99f 100644 (file)
@@ -8,10 +8,19 @@ vc4-y := \
        vc4_crtc.o \
        vc4_drv.o \
        vc4_kms.o \
+       vc4_gem.o \
        vc4_hdmi.o \
        vc4_hvs.o \
-       vc4_plane.o
+       vc4_irq.o \
+       vc4_plane.o \
+       vc4_render_cl.o \
+       vc4_trace_points.o \
+       vc4_v3d.o \
+       vc4_validate.o \
+       vc4_validate_shaders.o
 
 vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
 
 obj-$(CONFIG_DRM_VC4)  += vc4.o
+
+CFLAGS_vc4_trace_points.o := -I$(src)
index ab9f510..18dfe3e 100644 (file)
  * access to system memory with no MMU in between.  To support it, we
  * use the GEM CMA helper functions to allocate contiguous ranges of
  * physical memory for our BOs.
+ *
+ * Since the CMA allocator is very slow, we keep a cache of recently
+ * freed BOs around so that the kernel's allocation of objects for 3D
+ * rendering can return quickly.
  */
 
 #include "vc4_drv.h"
+#include "uapi/drm/vc4_drm.h"
+
+static void vc4_bo_stats_dump(struct vc4_dev *vc4)
+{
+       DRM_INFO("num bos allocated: %d\n",
+                vc4->bo_stats.num_allocated);
+       DRM_INFO("size bos allocated: %dkb\n",
+                vc4->bo_stats.size_allocated / 1024);
+       DRM_INFO("num bos used: %d\n",
+                vc4->bo_stats.num_allocated - vc4->bo_stats.num_cached);
+       DRM_INFO("size bos used: %dkb\n",
+                (vc4->bo_stats.size_allocated -
+                 vc4->bo_stats.size_cached) / 1024);
+       DRM_INFO("num bos cached: %d\n",
+                vc4->bo_stats.num_cached);
+       DRM_INFO("size bos cached: %dkb\n",
+                vc4->bo_stats.size_cached / 1024);
+}
+
+#ifdef CONFIG_DEBUG_FS
+int vc4_bo_stats_debugfs(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *)m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_bo_stats stats;
+
+       /* Take a snapshot of the current stats with the lock held. */
+       mutex_lock(&vc4->bo_lock);
+       stats = vc4->bo_stats;
+       mutex_unlock(&vc4->bo_lock);
+
+       seq_printf(m, "num bos allocated: %d\n",
+                  stats.num_allocated);
+       seq_printf(m, "size bos allocated: %dkb\n",
+                  stats.size_allocated / 1024);
+       seq_printf(m, "num bos used: %d\n",
+                  stats.num_allocated - stats.num_cached);
+       seq_printf(m, "size bos used: %dkb\n",
+                  (stats.size_allocated - stats.size_cached) / 1024);
+       seq_printf(m, "num bos cached: %d\n",
+                  stats.num_cached);
+       seq_printf(m, "size bos cached: %dkb\n",
+                  stats.size_cached / 1024);
+
+       return 0;
+}
+#endif
+
+static uint32_t bo_page_index(size_t size)
+{
+       return (size / PAGE_SIZE) - 1;
+}
+
+/* Must be called with bo_lock held. */
+static void vc4_bo_destroy(struct vc4_bo *bo)
+{
+       struct drm_gem_object *obj = &bo->base.base;
+       struct vc4_dev *vc4 = to_vc4_dev(obj->dev);
+
+       if (bo->validated_shader) {
+               kfree(bo->validated_shader->texture_samples);
+               kfree(bo->validated_shader);
+               bo->validated_shader = NULL;
+       }
+
+       vc4->bo_stats.num_allocated--;
+       vc4->bo_stats.size_allocated -= obj->size;
+       drm_gem_cma_free_object(obj);
+}
+
+/* Must be called with bo_lock held. */
+static void vc4_bo_remove_from_cache(struct vc4_bo *bo)
+{
+       struct drm_gem_object *obj = &bo->base.base;
+       struct vc4_dev *vc4 = to_vc4_dev(obj->dev);
+
+       vc4->bo_stats.num_cached--;
+       vc4->bo_stats.size_cached -= obj->size;
+
+       list_del(&bo->unref_head);
+       list_del(&bo->size_head);
+}
+
+static struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev,
+                                                    size_t size)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       uint32_t page_index = bo_page_index(size);
+
+       if (vc4->bo_cache.size_list_size <= page_index) {
+               uint32_t new_size = max(vc4->bo_cache.size_list_size * 2,
+                                       page_index + 1);
+               struct list_head *new_list;
+               uint32_t i;
+
+               new_list = kmalloc_array(new_size, sizeof(struct list_head),
+                                        GFP_KERNEL);
+               if (!new_list)
+                       return NULL;
+
+               /* Rebase the old cached BO lists to their new list
+                * head locations.
+                */
+               for (i = 0; i < vc4->bo_cache.size_list_size; i++) {
+                       struct list_head *old_list =
+                               &vc4->bo_cache.size_list[i];
+
+                       if (list_empty(old_list))
+                               INIT_LIST_HEAD(&new_list[i]);
+                       else
+                               list_replace(old_list, &new_list[i]);
+               }
+               /* And initialize the brand new BO list heads. */
+               for (i = vc4->bo_cache.size_list_size; i < new_size; i++)
+                       INIT_LIST_HEAD(&new_list[i]);
+
+               kfree(vc4->bo_cache.size_list);
+               vc4->bo_cache.size_list = new_list;
+               vc4->bo_cache.size_list_size = new_size;
+       }
+
+       return &vc4->bo_cache.size_list[page_index];
+}
+
+void vc4_bo_cache_purge(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       mutex_lock(&vc4->bo_lock);
+       while (!list_empty(&vc4->bo_cache.time_list)) {
+               struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list,
+                                                   struct vc4_bo, unref_head);
+               vc4_bo_remove_from_cache(bo);
+               vc4_bo_destroy(bo);
+       }
+       mutex_unlock(&vc4->bo_lock);
+}
+
+static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev,
+                                           uint32_t size)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       uint32_t page_index = bo_page_index(size);
+       struct vc4_bo *bo = NULL;
+
+       size = roundup(size, PAGE_SIZE);
+
+       mutex_lock(&vc4->bo_lock);
+       if (page_index >= vc4->bo_cache.size_list_size)
+               goto out;
 
-struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size)
+       if (list_empty(&vc4->bo_cache.size_list[page_index]))
+               goto out;
+
+       bo = list_first_entry(&vc4->bo_cache.size_list[page_index],
+                             struct vc4_bo, size_head);
+       vc4_bo_remove_from_cache(bo);
+       kref_init(&bo->base.base.refcount);
+
+out:
+       mutex_unlock(&vc4->bo_lock);
+       return bo;
+}
+
+/**
+ * vc4_gem_create_object - Implementation of driver->gem_create_object.
+ *
+ * This lets the CMA helpers allocate object structs for us, and keep
+ * our BO stats correct.
+ */
+struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
 {
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_bo *bo;
+
+       bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+       if (!bo)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_lock(&vc4->bo_lock);
+       vc4->bo_stats.num_allocated++;
+       vc4->bo_stats.size_allocated += size;
+       mutex_unlock(&vc4->bo_lock);
+
+       return &bo->base.base;
+}
+
+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
+                            bool from_cache)
+{
+       size_t size = roundup(unaligned_size, PAGE_SIZE);
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
        struct drm_gem_cma_object *cma_obj;
 
-       cma_obj = drm_gem_cma_create(dev, size);
-       if (IS_ERR(cma_obj))
+       if (size == 0)
                return NULL;
-       else
-               return to_vc4_bo(&cma_obj->base);
+
+       /* First, try to get a vc4_bo from the kernel BO cache. */
+       if (from_cache) {
+               struct vc4_bo *bo = vc4_bo_get_from_cache(dev, size);
+
+               if (bo)
+                       return bo;
+       }
+
+       cma_obj = drm_gem_cma_create(dev, size);
+       if (IS_ERR(cma_obj)) {
+               /*
+                * If we've run out of CMA memory, kill the cache of
+                * CMA allocations we've got laying around and try again.
+                */
+               vc4_bo_cache_purge(dev);
+
+               cma_obj = drm_gem_cma_create(dev, size);
+               if (IS_ERR(cma_obj)) {
+                       DRM_ERROR("Failed to allocate from CMA:\n");
+                       vc4_bo_stats_dump(vc4);
+                       return NULL;
+               }
+       }
+
+       return to_vc4_bo(&cma_obj->base);
 }
 
 int vc4_dumb_create(struct drm_file *file_priv,
@@ -41,7 +258,191 @@ int vc4_dumb_create(struct drm_file *file_priv,
        if (args->size < args->pitch * args->height)
                args->size = args->pitch * args->height;
 
-       bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE));
+       bo = vc4_bo_create(dev, args->size, false);
+       if (!bo)
+               return -ENOMEM;
+
+       ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
+       drm_gem_object_unreference_unlocked(&bo->base.base);
+
+       return ret;
+}
+
+/* Must be called with bo_lock held. */
+static void vc4_bo_cache_free_old(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long expire_time = jiffies - msecs_to_jiffies(1000);
+
+       while (!list_empty(&vc4->bo_cache.time_list)) {
+               struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list,
+                                                   struct vc4_bo, unref_head);
+               if (time_before(expire_time, bo->free_time)) {
+                       mod_timer(&vc4->bo_cache.time_timer,
+                                 round_jiffies_up(jiffies +
+                                                  msecs_to_jiffies(1000)));
+                       return;
+               }
+
+               vc4_bo_remove_from_cache(bo);
+               vc4_bo_destroy(bo);
+       }
+}
+
+/* Called on the last userspace/kernel unreference of the BO.  Returns
+ * it to the BO cache if possible, otherwise frees it.
+ *
+ * Note that this is called with the struct_mutex held.
+ */
+void vc4_free_object(struct drm_gem_object *gem_bo)
+{
+       struct drm_device *dev = gem_bo->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_bo *bo = to_vc4_bo(gem_bo);
+       struct list_head *cache_list;
+
+       mutex_lock(&vc4->bo_lock);
+       /* If the object references someone else's memory, we can't cache it.
+        */
+       if (gem_bo->import_attach) {
+               vc4_bo_destroy(bo);
+               goto out;
+       }
+
+       /* Don't cache if it was publicly named. */
+       if (gem_bo->name) {
+               vc4_bo_destroy(bo);
+               goto out;
+       }
+
+       cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size);
+       if (!cache_list) {
+               vc4_bo_destroy(bo);
+               goto out;
+       }
+
+       if (bo->validated_shader) {
+               kfree(bo->validated_shader->texture_samples);
+               kfree(bo->validated_shader);
+               bo->validated_shader = NULL;
+       }
+
+       bo->free_time = jiffies;
+       list_add(&bo->size_head, cache_list);
+       list_add(&bo->unref_head, &vc4->bo_cache.time_list);
+
+       vc4->bo_stats.num_cached++;
+       vc4->bo_stats.size_cached += gem_bo->size;
+
+       vc4_bo_cache_free_old(dev);
+
+out:
+       mutex_unlock(&vc4->bo_lock);
+}
+
+static void vc4_bo_cache_time_work(struct work_struct *work)
+{
+       struct vc4_dev *vc4 =
+               container_of(work, struct vc4_dev, bo_cache.time_work);
+       struct drm_device *dev = vc4->dev;
+
+       mutex_lock(&vc4->bo_lock);
+       vc4_bo_cache_free_old(dev);
+       mutex_unlock(&vc4->bo_lock);
+}
+
+static void vc4_bo_cache_time_timer(unsigned long data)
+{
+       struct drm_device *dev = (struct drm_device *)data;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       schedule_work(&vc4->bo_cache.time_work);
+}
+
+struct dma_buf *
+vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags)
+{
+       struct vc4_bo *bo = to_vc4_bo(obj);
+
+       if (bo->validated_shader) {
+               DRM_ERROR("Attempting to export shader BO\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       return drm_gem_prime_export(dev, obj, flags);
+}
+
+int vc4_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct drm_gem_object *gem_obj;
+       struct vc4_bo *bo;
+       int ret;
+
+       ret = drm_gem_mmap(filp, vma);
+       if (ret)
+               return ret;
+
+       gem_obj = vma->vm_private_data;
+       bo = to_vc4_bo(gem_obj);
+
+       if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) {
+               DRM_ERROR("mmaping of shader BOs for writing not allowed.\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
+        * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
+        * the whole buffer.
+        */
+       vma->vm_flags &= ~VM_PFNMAP;
+       vma->vm_pgoff = 0;
+
+       ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma,
+                                   bo->base.vaddr, bo->base.paddr,
+                                   vma->vm_end - vma->vm_start);
+       if (ret)
+               drm_gem_vm_close(vma);
+
+       return ret;
+}
+
+int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+       struct vc4_bo *bo = to_vc4_bo(obj);
+
+       if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) {
+               DRM_ERROR("mmaping of shader BOs for writing not allowed.\n");
+               return -EINVAL;
+       }
+
+       return drm_gem_cma_prime_mmap(obj, vma);
+}
+
+void *vc4_prime_vmap(struct drm_gem_object *obj)
+{
+       struct vc4_bo *bo = to_vc4_bo(obj);
+
+       if (bo->validated_shader) {
+               DRM_ERROR("mmaping of shader BOs not allowed.\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       return drm_gem_cma_prime_vmap(obj);
+}
+
+int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv)
+{
+       struct drm_vc4_create_bo *args = data;
+       struct vc4_bo *bo = NULL;
+       int ret;
+
+       /*
+        * We can't allocate from the BO cache, because the BOs don't
+        * get zeroed, and that might leak data between users.
+        */
+       bo = vc4_bo_create(dev, args->size, false);
        if (!bo)
                return -ENOMEM;
 
@@ -50,3 +451,107 @@ int vc4_dumb_create(struct drm_file *file_priv,
 
        return ret;
 }
+
+int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
+{
+       struct drm_vc4_mmap_bo *args = data;
+       struct drm_gem_object *gem_obj;
+
+       gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+       if (!gem_obj) {
+               DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
+               return -EINVAL;
+       }
+
+       /* The mmap offset was set up at BO allocation time. */
+       args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
+
+       drm_gem_object_unreference_unlocked(gem_obj);
+       return 0;
+}
+
+int
+vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data,
+                          struct drm_file *file_priv)
+{
+       struct drm_vc4_create_shader_bo *args = data;
+       struct vc4_bo *bo = NULL;
+       int ret;
+
+       if (args->size == 0)
+               return -EINVAL;
+
+       if (args->size % sizeof(u64) != 0)
+               return -EINVAL;
+
+       if (args->flags != 0) {
+               DRM_INFO("Unknown flags set: 0x%08x\n", args->flags);
+               return -EINVAL;
+       }
+
+       if (args->pad != 0) {
+               DRM_INFO("Pad set: 0x%08x\n", args->pad);
+               return -EINVAL;
+       }
+
+       bo = vc4_bo_create(dev, args->size, true);
+       if (!bo)
+               return -ENOMEM;
+
+       ret = copy_from_user(bo->base.vaddr,
+                            (void __user *)(uintptr_t)args->data,
+                            args->size);
+       if (ret != 0)
+               goto fail;
+       /* Clear the rest of the memory from allocating from the BO
+        * cache.
+        */
+       memset(bo->base.vaddr + args->size, 0,
+              bo->base.base.size - args->size);
+
+       bo->validated_shader = vc4_validate_shader(&bo->base);
+       if (!bo->validated_shader) {
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       /* We have to create the handle after validation, to avoid
+        * races for users to do doing things like mmap the shader BO.
+        */
+       ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
+
+ fail:
+       drm_gem_object_unreference_unlocked(&bo->base.base);
+
+       return ret;
+}
+
+void vc4_bo_cache_init(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       mutex_init(&vc4->bo_lock);
+
+       INIT_LIST_HEAD(&vc4->bo_cache.time_list);
+
+       INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work);
+       setup_timer(&vc4->bo_cache.time_timer,
+                   vc4_bo_cache_time_timer,
+                   (unsigned long)dev);
+}
+
+void vc4_bo_cache_destroy(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       del_timer(&vc4->bo_cache.time_timer);
+       cancel_work_sync(&vc4->bo_cache.time_work);
+
+       vc4_bo_cache_purge(dev);
+
+       if (vc4->bo_stats.num_allocated) {
+               DRM_ERROR("Destroying BO cache while BOs still allocated:\n");
+               vc4_bo_stats_dump(vc4);
+       }
+}
index 265064c..8d0d70e 100644 (file)
@@ -35,6 +35,7 @@
 #include "drm_atomic_helper.h"
 #include "drm_crtc_helper.h"
 #include "linux/clk.h"
+#include "drm_fb_cma_helper.h"
 #include "linux/component.h"
 #include "linux/of_device.h"
 #include "vc4_drv.h"
@@ -476,10 +477,106 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
        return ret;
 }
 
+struct vc4_async_flip_state {
+       struct drm_crtc *crtc;
+       struct drm_framebuffer *fb;
+       struct drm_pending_vblank_event *event;
+
+       struct vc4_seqno_cb cb;
+};
+
+/* Called when the V3D execution for the BO being flipped to is done, so that
+ * we can actually update the plane's address to point to it.
+ */
+static void
+vc4_async_page_flip_complete(struct vc4_seqno_cb *cb)
+{
+       struct vc4_async_flip_state *flip_state =
+               container_of(cb, struct vc4_async_flip_state, cb);
+       struct drm_crtc *crtc = flip_state->crtc;
+       struct drm_device *dev = crtc->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_plane *plane = crtc->primary;
+
+       vc4_plane_async_set_fb(plane, flip_state->fb);
+       if (flip_state->event) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&dev->event_lock, flags);
+               drm_crtc_send_vblank_event(crtc, flip_state->event);
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+
+       drm_framebuffer_unreference(flip_state->fb);
+       kfree(flip_state);
+
+       up(&vc4->async_modeset);
+}
+
+/* Implements async (non-vblank-synced) page flips.
+ *
+ * The page flip ioctl needs to return immediately, so we grab the
+ * modeset semaphore on the pipe, and queue the address update for
+ * when V3D is done with the BO being flipped to.
+ */
+static int vc4_async_page_flip(struct drm_crtc *crtc,
+                              struct drm_framebuffer *fb,
+                              struct drm_pending_vblank_event *event,
+                              uint32_t flags)
+{
+       struct drm_device *dev = crtc->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_plane *plane = crtc->primary;
+       int ret = 0;
+       struct vc4_async_flip_state *flip_state;
+       struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(fb, 0);
+       struct vc4_bo *bo = to_vc4_bo(&cma_bo->base);
+
+       flip_state = kzalloc(sizeof(*flip_state), GFP_KERNEL);
+       if (!flip_state)
+               return -ENOMEM;
+
+       drm_framebuffer_reference(fb);
+       flip_state->fb = fb;
+       flip_state->crtc = crtc;
+       flip_state->event = event;
+
+       /* Make sure all other async modesetes have landed. */
+       ret = down_interruptible(&vc4->async_modeset);
+       if (ret) {
+               kfree(flip_state);
+               return ret;
+       }
+
+       /* Immediately update the plane's legacy fb pointer, so that later
+        * modeset prep sees the state that will be present when the semaphore
+        * is released.
+        */
+       drm_atomic_set_fb_for_plane(plane->state, fb);
+       plane->fb = fb;
+
+       vc4_queue_seqno_cb(dev, &flip_state->cb, bo->seqno,
+                          vc4_async_page_flip_complete);
+
+       /* Driver takes ownership of state on successful async commit. */
+       return 0;
+}
+
+static int vc4_page_flip(struct drm_crtc *crtc,
+                        struct drm_framebuffer *fb,
+                        struct drm_pending_vblank_event *event,
+                        uint32_t flags)
+{
+       if (flags & DRM_MODE_PAGE_FLIP_ASYNC)
+               return vc4_async_page_flip(crtc, fb, event, flags);
+       else
+               return drm_atomic_helper_page_flip(crtc, fb, event, flags);
+}
+
 static const struct drm_crtc_funcs vc4_crtc_funcs = {
        .set_config = drm_atomic_helper_set_config,
        .destroy = vc4_crtc_destroy,
-       .page_flip = drm_atomic_helper_page_flip,
+       .page_flip = vc4_page_flip,
        .set_property = NULL,
        .cursor_set = NULL, /* handled by drm_mode_cursor_universal */
        .cursor_move = NULL, /* handled by drm_mode_cursor_universal */
@@ -606,7 +703,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
        }
 
        drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane,
-                                 &vc4_crtc_funcs);
+                                 &vc4_crtc_funcs, NULL);
        drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
        primary_plane->crtc = crtc;
        cursor_plane->crtc = crtc;
index 4297b0a..d76ad10 100644 (file)
 #include "vc4_regs.h"
 
 static const struct drm_info_list vc4_debugfs_list[] = {
+       {"bo_stats", vc4_bo_stats_debugfs, 0},
        {"hdmi_regs", vc4_hdmi_debugfs_regs, 0},
        {"hvs_regs", vc4_hvs_debugfs_regs, 0},
        {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
        {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1},
        {"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2},
+       {"v3d_ident", vc4_v3d_debugfs_ident, 0},
+       {"v3d_regs", vc4_v3d_debugfs_regs, 0},
 };
 
 #define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list)
index d5db9e0..cbcbbb8 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/platform_device.h>
 #include "drm_fb_cma_helper.h"
 
+#include "uapi/drm/vc4_drm.h"
 #include "vc4_drv.h"
 #include "vc4_regs.h"
 
@@ -63,7 +64,7 @@ static const struct file_operations vc4_drm_fops = {
        .open = drm_open,
        .release = drm_release,
        .unlocked_ioctl = drm_ioctl,
-       .mmap = drm_gem_cma_mmap,
+       .mmap = vc4_mmap,
        .poll = drm_poll,
        .read = drm_read,
 #ifdef CONFIG_COMPAT
@@ -73,16 +74,30 @@ static const struct file_operations vc4_drm_fops = {
 };
 
 static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
+       DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, 0),
+       DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, 0),
+       DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, 0),
+       DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0),
+       DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0),
+       DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0),
+       DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl,
+                         DRM_ROOT_ONLY),
 };
 
 static struct drm_driver vc4_drm_driver = {
        .driver_features = (DRIVER_MODESET |
                            DRIVER_ATOMIC |
                            DRIVER_GEM |
+                           DRIVER_HAVE_IRQ |
                            DRIVER_PRIME),
        .lastclose = vc4_lastclose,
        .preclose = vc4_drm_preclose,
 
+       .irq_handler = vc4_irq,
+       .irq_preinstall = vc4_irq_preinstall,
+       .irq_postinstall = vc4_irq_postinstall,
+       .irq_uninstall = vc4_irq_uninstall,
+
        .enable_vblank = vc4_enable_vblank,
        .disable_vblank = vc4_disable_vblank,
        .get_vblank_counter = drm_vblank_count,
@@ -92,18 +107,19 @@ static struct drm_driver vc4_drm_driver = {
        .debugfs_cleanup = vc4_debugfs_cleanup,
 #endif
 
-       .gem_free_object = drm_gem_cma_free_object,
+       .gem_create_object = vc4_create_object,
+       .gem_free_object = vc4_free_object,
        .gem_vm_ops = &drm_gem_cma_vm_ops,
 
        .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
        .gem_prime_import = drm_gem_prime_import,
-       .gem_prime_export = drm_gem_prime_export,
+       .gem_prime_export = vc4_prime_export,
        .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
        .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
-       .gem_prime_vmap = drm_gem_cma_prime_vmap,
+       .gem_prime_vmap = vc4_prime_vmap,
        .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
-       .gem_prime_mmap = drm_gem_cma_prime_mmap,
+       .gem_prime_mmap = vc4_prime_mmap,
 
        .dumb_create = vc4_dumb_create,
        .dumb_map_offset = drm_gem_cma_dumb_map_offset,
@@ -170,13 +186,17 @@ static int vc4_drm_bind(struct device *dev)
 
        drm_dev_set_unique(drm, dev_name(dev));
 
+       vc4_bo_cache_init(drm);
+
        drm_mode_config_init(drm);
        if (ret)
                goto unref;
 
+       vc4_gem_init(drm);
+
        ret = component_bind_all(dev, drm);
        if (ret)
-               goto unref;
+               goto gem_destroy;
 
        ret = drm_dev_register(drm, 0);
        if (ret < 0)
@@ -200,8 +220,11 @@ unregister:
        drm_dev_unregister(drm);
 unbind_all:
        component_unbind_all(dev, drm);
+gem_destroy:
+       vc4_gem_destroy(drm);
 unref:
        drm_dev_unref(drm);
+       vc4_bo_cache_destroy(drm);
        return ret;
 }
 
@@ -228,6 +251,7 @@ static struct platform_driver *const component_drivers[] = {
        &vc4_hdmi_driver,
        &vc4_crtc_driver,
        &vc4_hvs_driver,
+       &vc4_v3d_driver,
 };
 
 static int vc4_platform_drm_probe(struct platform_device *pdev)
index fd8319f..080865e 100644 (file)
@@ -15,8 +15,89 @@ struct vc4_dev {
        struct vc4_hdmi *hdmi;
        struct vc4_hvs *hvs;
        struct vc4_crtc *crtc[3];
+       struct vc4_v3d *v3d;
 
        struct drm_fbdev_cma *fbdev;
+
+       struct vc4_hang_state *hang_state;
+
+       /* The kernel-space BO cache.  Tracks buffers that have been
+        * unreferenced by all other users (refcounts of 0!) but not
+        * yet freed, so we can do cheap allocations.
+        */
+       struct vc4_bo_cache {
+               /* Array of list heads for entries in the BO cache,
+                * based on number of pages, so we can do O(1) lookups
+                * in the cache when allocating.
+                */
+               struct list_head *size_list;
+               uint32_t size_list_size;
+
+               /* List of all BOs in the cache, ordered by age, so we
+                * can do O(1) lookups when trying to free old
+                * buffers.
+                */
+               struct list_head time_list;
+               struct work_struct time_work;
+               struct timer_list time_timer;
+       } bo_cache;
+
+       struct vc4_bo_stats {
+               u32 num_allocated;
+               u32 size_allocated;
+               u32 num_cached;
+               u32 size_cached;
+       } bo_stats;
+
+       /* Protects bo_cache and the BO stats. */
+       struct mutex bo_lock;
+
+       /* Sequence number for the last job queued in job_list.
+        * Starts at 0 (no jobs emitted).
+        */
+       uint64_t emit_seqno;
+
+       /* Sequence number for the last completed job on the GPU.
+        * Starts at 0 (no jobs completed).
+        */
+       uint64_t finished_seqno;
+
+       /* List of all struct vc4_exec_info for jobs to be executed.
+        * The first job in the list is the one currently programmed
+        * into ct0ca/ct1ca for execution.
+        */
+       struct list_head job_list;
+       /* List of the finished vc4_exec_infos waiting to be freed by
+        * job_done_work.
+        */
+       struct list_head job_done_list;
+       /* Spinlock used to synchronize the job_list and seqno
+        * accesses between the IRQ handler and GEM ioctls.
+        */
+       spinlock_t job_lock;
+       wait_queue_head_t job_wait_queue;
+       struct work_struct job_done_work;
+
+       /* List of struct vc4_seqno_cb for callbacks to be made from a
+        * workqueue when the given seqno is passed.
+        */
+       struct list_head seqno_cb_list;
+
+       /* The binner overflow memory that's currently set up in
+        * BPOA/BPOS registers.  When overflow occurs and a new one is
+        * allocated, the previous one will be moved to
+        * vc4->current_exec's free list.
+        */
+       struct vc4_bo *overflow_mem;
+       struct work_struct overflow_mem_work;
+
+       struct {
+               uint32_t last_ct0ca, last_ct1ca;
+               struct timer_list timer;
+               struct work_struct reset_work;
+       } hangcheck;
+
+       struct semaphore async_modeset;
 };
 
 static inline struct vc4_dev *
@@ -27,6 +108,25 @@ to_vc4_dev(struct drm_device *dev)
 
 struct vc4_bo {
        struct drm_gem_cma_object base;
+
+       /* seqno of the last job to render to this BO. */
+       uint64_t seqno;
+
+       /* List entry for the BO's position in either
+        * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list
+        */
+       struct list_head unref_head;
+
+       /* Time in jiffies when the BO was put in vc4->bo_cache. */
+       unsigned long free_time;
+
+       /* List entry for the BO's position in vc4_dev->bo_cache.size_list */
+       struct list_head size_head;
+
+       /* Struct for shader validation state, if created by
+        * DRM_IOCTL_VC4_CREATE_SHADER_BO.
+        */
+       struct vc4_validated_shader_info *validated_shader;
 };
 
 static inline struct vc4_bo *
@@ -35,6 +135,17 @@ to_vc4_bo(struct drm_gem_object *bo)
        return (struct vc4_bo *)bo;
 }
 
+struct vc4_seqno_cb {
+       struct work_struct work;
+       uint64_t seqno;
+       void (*func)(struct vc4_seqno_cb *cb);
+};
+
+struct vc4_v3d {
+       struct platform_device *pdev;
+       void __iomem *regs;
+};
+
 struct vc4_hvs {
        struct platform_device *pdev;
        void __iomem *regs;
@@ -72,9 +183,142 @@ to_vc4_encoder(struct drm_encoder *encoder)
        return container_of(encoder, struct vc4_encoder, base);
 }
 
+#define V3D_READ(offset) readl(vc4->v3d->regs + offset)
+#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset)
 #define HVS_READ(offset) readl(vc4->hvs->regs + offset)
 #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset)
 
+struct vc4_exec_info {
+       /* Sequence number for this bin/render job. */
+       uint64_t seqno;
+
+       /* Kernel-space copy of the ioctl arguments */
+       struct drm_vc4_submit_cl *args;
+
+       /* This is the array of BOs that were looked up at the start of exec.
+        * Command validation will use indices into this array.
+        */
+       struct drm_gem_cma_object **bo;
+       uint32_t bo_count;
+
+       /* Pointers for our position in vc4->job_list */
+       struct list_head head;
+
+       /* List of other BOs used in the job that need to be released
+        * once the job is complete.
+        */
+       struct list_head unref_list;
+
+       /* Current unvalidated indices into @bo loaded by the non-hardware
+        * VC4_PACKET_GEM_HANDLES.
+        */
+       uint32_t bo_index[2];
+
+       /* This is the BO where we store the validated command lists, shader
+        * records, and uniforms.
+        */
+       struct drm_gem_cma_object *exec_bo;
+
+       /**
+        * This tracks the per-shader-record state (packet 64) that
+        * determines the length of the shader record and the offset
+        * it's expected to be found at.  It gets read in from the
+        * command lists.
+        */
+       struct vc4_shader_state {
+               uint32_t addr;
+               /* Maximum vertex index referenced by any primitive using this
+                * shader state.
+                */
+               uint32_t max_index;
+       } *shader_state;
+
+       /** How many shader states the user declared they were using. */
+       uint32_t shader_state_size;
+       /** How many shader state records the validator has seen. */
+       uint32_t shader_state_count;
+
+       bool found_tile_binning_mode_config_packet;
+       bool found_start_tile_binning_packet;
+       bool found_increment_semaphore_packet;
+       bool found_flush;
+       uint8_t bin_tiles_x, bin_tiles_y;
+       struct drm_gem_cma_object *tile_bo;
+       uint32_t tile_alloc_offset;
+
+       /**
+        * Computed addresses pointing into exec_bo where we start the
+        * bin thread (ct0) and render thread (ct1).
+        */
+       uint32_t ct0ca, ct0ea;
+       uint32_t ct1ca, ct1ea;
+
+       /* Pointer to the unvalidated bin CL (if present). */
+       void *bin_u;
+
+       /* Pointers to the shader recs.  These paddr gets incremented as CL
+        * packets are relocated in validate_gl_shader_state, and the vaddrs
+        * (u and v) get incremented and size decremented as the shader recs
+        * themselves are validated.
+        */
+       void *shader_rec_u;
+       void *shader_rec_v;
+       uint32_t shader_rec_p;
+       uint32_t shader_rec_size;
+
+       /* Pointers to the uniform data.  These pointers are incremented, and
+        * size decremented, as each batch of uniforms is uploaded.
+        */
+       void *uniforms_u;
+       void *uniforms_v;
+       uint32_t uniforms_p;
+       uint32_t uniforms_size;
+};
+
+static inline struct vc4_exec_info *
+vc4_first_job(struct vc4_dev *vc4)
+{
+       if (list_empty(&vc4->job_list))
+               return NULL;
+       return list_first_entry(&vc4->job_list, struct vc4_exec_info, head);
+}
+
+/**
+ * struct vc4_texture_sample_info - saves the offsets into the UBO for texture
+ * setup parameters.
+ *
+ * This will be used at draw time to relocate the reference to the texture
+ * contents in p0, and validate that the offset combined with
+ * width/height/stride/etc. from p1 and p2/p3 doesn't sample outside the BO.
+ * Note that the hardware treats unprovided config parameters as 0, so not all
+ * of them need to be set up for every texure sample, and we'll store ~0 as
+ * the offset to mark the unused ones.
+ *
+ * See the VC4 3D architecture guide page 41 ("Texture and Memory Lookup Unit
+ * Setup") for definitions of the texture parameters.
+ */
+struct vc4_texture_sample_info {
+       bool is_direct;
+       uint32_t p_offset[4];
+};
+
+/**
+ * struct vc4_validated_shader_info - information about validated shaders that
+ * needs to be used from command list validation.
+ *
+ * For a given shader, each time a shader state record references it, we need
+ * to verify that the shader doesn't read more uniforms than the shader state
+ * record's uniform BO pointer can provide, and we need to apply relocations
+ * and validate the shader state record's uniforms that define the texture
+ * samples.
+ */
+struct vc4_validated_shader_info {
+       uint32_t uniforms_size;
+       uint32_t uniforms_src_size;
+       uint32_t num_texture_samples;
+       struct vc4_texture_sample_info *texture_samples;
+};
+
 /**
  * _wait_for - magic (register) wait macro
  *
@@ -104,13 +348,29 @@ to_vc4_encoder(struct drm_encoder *encoder)
 #define wait_for(COND, MS) _wait_for(COND, MS, 1)
 
 /* vc4_bo.c */
+struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size);
 void vc4_free_object(struct drm_gem_object *gem_obj);
-struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size);
+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size,
+                            bool from_cache);
 int vc4_dumb_create(struct drm_file *file_priv,
                    struct drm_device *dev,
                    struct drm_mode_create_dumb *args);
 struct dma_buf *vc4_prime_export(struct drm_device *dev,
                                 struct drm_gem_object *obj, int flags);
+int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv);
+int vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data,
+                              struct drm_file *file_priv);
+int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv);
+int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
+                            struct drm_file *file_priv);
+int vc4_mmap(struct file *filp, struct vm_area_struct *vma);
+int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+void *vc4_prime_vmap(struct drm_gem_object *obj);
+void vc4_bo_cache_init(struct drm_device *dev);
+void vc4_bo_cache_destroy(struct drm_device *dev);
+int vc4_bo_stats_debugfs(struct seq_file *m, void *arg);
 
 /* vc4_crtc.c */
 extern struct platform_driver vc4_crtc_driver;
@@ -126,10 +386,34 @@ void vc4_debugfs_cleanup(struct drm_minor *minor);
 /* vc4_drv.c */
 void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index);
 
+/* vc4_gem.c */
+void vc4_gem_init(struct drm_device *dev);
+void vc4_gem_destroy(struct drm_device *dev);
+int vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv);
+int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data,
+                        struct drm_file *file_priv);
+int vc4_wait_bo_ioctl(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv);
+void vc4_submit_next_job(struct drm_device *dev);
+int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno,
+                      uint64_t timeout_ns, bool interruptible);
+void vc4_job_handle_completed(struct vc4_dev *vc4);
+int vc4_queue_seqno_cb(struct drm_device *dev,
+                      struct vc4_seqno_cb *cb, uint64_t seqno,
+                      void (*func)(struct vc4_seqno_cb *cb));
+
 /* vc4_hdmi.c */
 extern struct platform_driver vc4_hdmi_driver;
 int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused);
 
+/* vc4_irq.c */
+irqreturn_t vc4_irq(int irq, void *arg);
+void vc4_irq_preinstall(struct drm_device *dev);
+int vc4_irq_postinstall(struct drm_device *dev);
+void vc4_irq_uninstall(struct drm_device *dev);
+void vc4_irq_reset(struct drm_device *dev);
+
 /* vc4_hvs.c */
 extern struct platform_driver vc4_hvs_driver;
 void vc4_hvs_dump_state(struct drm_device *dev);
@@ -143,3 +427,35 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
                                 enum drm_plane_type type);
 u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
 u32 vc4_plane_dlist_size(struct drm_plane_state *state);
+void vc4_plane_async_set_fb(struct drm_plane *plane,
+                           struct drm_framebuffer *fb);
+
+/* vc4_v3d.c */
+extern struct platform_driver vc4_v3d_driver;
+int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
+int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
+int vc4_v3d_set_power(struct vc4_dev *vc4, bool on);
+
+/* vc4_validate.c */
+int
+vc4_validate_bin_cl(struct drm_device *dev,
+                   void *validated,
+                   void *unvalidated,
+                   struct vc4_exec_info *exec);
+
+int
+vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec);
+
+struct drm_gem_cma_object *vc4_use_bo(struct vc4_exec_info *exec,
+                                     uint32_t hindex);
+
+int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec);
+
+bool vc4_check_tex_size(struct vc4_exec_info *exec,
+                       struct drm_gem_cma_object *fbo,
+                       uint32_t offset, uint8_t tiling_format,
+                       uint32_t width, uint32_t height, uint8_t cpp);
+
+/* vc4_validate_shader.c */
+struct vc4_validated_shader_info *
+vc4_validate_shader(struct drm_gem_cma_object *shader_obj);
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
new file mode 100644 (file)
index 0000000..39f29e7
--- /dev/null
@@ -0,0 +1,867 @@
+/*
+ * Copyright © 2014 Broadcom
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+
+#include "uapi/drm/vc4_drm.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+#include "vc4_trace.h"
+
+static void
+vc4_queue_hangcheck(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       mod_timer(&vc4->hangcheck.timer,
+                 round_jiffies_up(jiffies + msecs_to_jiffies(100)));
+}
+
+struct vc4_hang_state {
+       struct drm_vc4_get_hang_state user_state;
+
+       u32 bo_count;
+       struct drm_gem_object **bo;
+};
+
+static void
+vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state)
+{
+       unsigned int i;
+
+       mutex_lock(&dev->struct_mutex);
+       for (i = 0; i < state->user_state.bo_count; i++)
+               drm_gem_object_unreference(state->bo[i]);
+       mutex_unlock(&dev->struct_mutex);
+
+       kfree(state);
+}
+
+int
+vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
+                        struct drm_file *file_priv)
+{
+       struct drm_vc4_get_hang_state *get_state = data;
+       struct drm_vc4_get_hang_state_bo *bo_state;
+       struct vc4_hang_state *kernel_state;
+       struct drm_vc4_get_hang_state *state;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long irqflags;
+       u32 i;
+       int ret;
+
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       kernel_state = vc4->hang_state;
+       if (!kernel_state) {
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+               return -ENOENT;
+       }
+       state = &kernel_state->user_state;
+
+       /* If the user's array isn't big enough, just return the
+        * required array size.
+        */
+       if (get_state->bo_count < state->bo_count) {
+               get_state->bo_count = state->bo_count;
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+               return 0;
+       }
+
+       vc4->hang_state = NULL;
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+       /* Save the user's BO pointer, so we don't stomp it with the memcpy. */
+       state->bo = get_state->bo;
+       memcpy(get_state, state, sizeof(*state));
+
+       bo_state = kcalloc(state->bo_count, sizeof(*bo_state), GFP_KERNEL);
+       if (!bo_state) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
+       for (i = 0; i < state->bo_count; i++) {
+               struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]);
+               u32 handle;
+
+               ret = drm_gem_handle_create(file_priv, kernel_state->bo[i],
+                                           &handle);
+
+               if (ret) {
+                       state->bo_count = i - 1;
+                       goto err;
+               }
+               bo_state[i].handle = handle;
+               bo_state[i].paddr = vc4_bo->base.paddr;
+               bo_state[i].size = vc4_bo->base.base.size;
+       }
+
+       ret = copy_to_user((void __user *)(uintptr_t)get_state->bo,
+                          bo_state,
+                          state->bo_count * sizeof(*bo_state));
+       kfree(bo_state);
+
+err_free:
+
+       vc4_free_hang_state(dev, kernel_state);
+
+err:
+       return ret;
+}
+
+static void
+vc4_save_hang_state(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_vc4_get_hang_state *state;
+       struct vc4_hang_state *kernel_state;
+       struct vc4_exec_info *exec;
+       struct vc4_bo *bo;
+       unsigned long irqflags;
+       unsigned int i, unref_list_count;
+
+       kernel_state = kcalloc(1, sizeof(*state), GFP_KERNEL);
+       if (!kernel_state)
+               return;
+
+       state = &kernel_state->user_state;
+
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       exec = vc4_first_job(vc4);
+       if (!exec) {
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+               return;
+       }
+
+       unref_list_count = 0;
+       list_for_each_entry(bo, &exec->unref_list, unref_head)
+               unref_list_count++;
+
+       state->bo_count = exec->bo_count + unref_list_count;
+       kernel_state->bo = kcalloc(state->bo_count, sizeof(*kernel_state->bo),
+                                  GFP_ATOMIC);
+       if (!kernel_state->bo) {
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+               return;
+       }
+
+       for (i = 0; i < exec->bo_count; i++) {
+               drm_gem_object_reference(&exec->bo[i]->base);
+               kernel_state->bo[i] = &exec->bo[i]->base;
+       }
+
+       list_for_each_entry(bo, &exec->unref_list, unref_head) {
+               drm_gem_object_reference(&bo->base.base);
+               kernel_state->bo[i] = &bo->base.base;
+               i++;
+       }
+
+       state->start_bin = exec->ct0ca;
+       state->start_render = exec->ct1ca;
+
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+       state->ct0ca = V3D_READ(V3D_CTNCA(0));
+       state->ct0ea = V3D_READ(V3D_CTNEA(0));
+
+       state->ct1ca = V3D_READ(V3D_CTNCA(1));
+       state->ct1ea = V3D_READ(V3D_CTNEA(1));
+
+       state->ct0cs = V3D_READ(V3D_CTNCS(0));
+       state->ct1cs = V3D_READ(V3D_CTNCS(1));
+
+       state->ct0ra0 = V3D_READ(V3D_CT00RA0);
+       state->ct1ra0 = V3D_READ(V3D_CT01RA0);
+
+       state->bpca = V3D_READ(V3D_BPCA);
+       state->bpcs = V3D_READ(V3D_BPCS);
+       state->bpoa = V3D_READ(V3D_BPOA);
+       state->bpos = V3D_READ(V3D_BPOS);
+
+       state->vpmbase = V3D_READ(V3D_VPMBASE);
+
+       state->dbge = V3D_READ(V3D_DBGE);
+       state->fdbgo = V3D_READ(V3D_FDBGO);
+       state->fdbgb = V3D_READ(V3D_FDBGB);
+       state->fdbgr = V3D_READ(V3D_FDBGR);
+       state->fdbgs = V3D_READ(V3D_FDBGS);
+       state->errstat = V3D_READ(V3D_ERRSTAT);
+
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       if (vc4->hang_state) {
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+               vc4_free_hang_state(dev, kernel_state);
+       } else {
+               vc4->hang_state = kernel_state;
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+       }
+}
+
+static void
+vc4_reset(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       DRM_INFO("Resetting GPU.\n");
+       vc4_v3d_set_power(vc4, false);
+       vc4_v3d_set_power(vc4, true);
+
+       vc4_irq_reset(dev);
+
+       /* Rearm the hangcheck -- another job might have been waiting
+        * for our hung one to get kicked off, and vc4_irq_reset()
+        * would have started it.
+        */
+       vc4_queue_hangcheck(dev);
+}
+
+static void
+vc4_reset_work(struct work_struct *work)
+{
+       struct vc4_dev *vc4 =
+               container_of(work, struct vc4_dev, hangcheck.reset_work);
+
+       vc4_save_hang_state(vc4->dev);
+
+       vc4_reset(vc4->dev);
+}
+
+static void
+vc4_hangcheck_elapsed(unsigned long data)
+{
+       struct drm_device *dev = (struct drm_device *)data;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       uint32_t ct0ca, ct1ca;
+
+       /* If idle, we can stop watching for hangs. */
+       if (list_empty(&vc4->job_list))
+               return;
+
+       ct0ca = V3D_READ(V3D_CTNCA(0));
+       ct1ca = V3D_READ(V3D_CTNCA(1));
+
+       /* If we've made any progress in execution, rearm the timer
+        * and wait.
+        */
+       if (ct0ca != vc4->hangcheck.last_ct0ca ||
+           ct1ca != vc4->hangcheck.last_ct1ca) {
+               vc4->hangcheck.last_ct0ca = ct0ca;
+               vc4->hangcheck.last_ct1ca = ct1ca;
+               vc4_queue_hangcheck(dev);
+               return;
+       }
+
+       /* We've gone too long with no progress, reset.  This has to
+        * be done from a work struct, since resetting can sleep and
+        * this timer hook isn't allowed to.
+        */
+       schedule_work(&vc4->hangcheck.reset_work);
+}
+
+static void
+submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       /* Set the current and end address of the control list.
+        * Writing the end register is what starts the job.
+        */
+       V3D_WRITE(V3D_CTNCA(thread), start);
+       V3D_WRITE(V3D_CTNEA(thread), end);
+}
+
+int
+vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns,
+                  bool interruptible)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       int ret = 0;
+       unsigned long timeout_expire;
+       DEFINE_WAIT(wait);
+
+       if (vc4->finished_seqno >= seqno)
+               return 0;
+
+       if (timeout_ns == 0)
+               return -ETIME;
+
+       timeout_expire = jiffies + nsecs_to_jiffies(timeout_ns);
+
+       trace_vc4_wait_for_seqno_begin(dev, seqno, timeout_ns);
+       for (;;) {
+               prepare_to_wait(&vc4->job_wait_queue, &wait,
+                               interruptible ? TASK_INTERRUPTIBLE :
+                               TASK_UNINTERRUPTIBLE);
+
+               if (interruptible && signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+
+               if (vc4->finished_seqno >= seqno)
+                       break;
+
+               if (timeout_ns != ~0ull) {
+                       if (time_after_eq(jiffies, timeout_expire)) {
+                               ret = -ETIME;
+                               break;
+                       }
+                       schedule_timeout(timeout_expire - jiffies);
+               } else {
+                       schedule();
+               }
+       }
+
+       finish_wait(&vc4->job_wait_queue, &wait);
+       trace_vc4_wait_for_seqno_end(dev, seqno);
+
+       if (ret && ret != -ERESTARTSYS) {
+               DRM_ERROR("timeout waiting for render thread idle\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void
+vc4_flush_caches(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       /* Flush the GPU L2 caches.  These caches sit on top of system
+        * L3 (the 128kb or so shared with the CPU), and are
+        * non-allocating in the L3.
+        */
+       V3D_WRITE(V3D_L2CACTL,
+                 V3D_L2CACTL_L2CCLR);
+
+       V3D_WRITE(V3D_SLCACTL,
+                 VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) |
+                 VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) |
+                 VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) |
+                 VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC));
+}
+
+/* Sets the registers for the next job to be actually be executed in
+ * the hardware.
+ *
+ * The job_lock should be held during this.
+ */
+void
+vc4_submit_next_job(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_exec_info *exec = vc4_first_job(vc4);
+
+       if (!exec)
+               return;
+
+       vc4_flush_caches(dev);
+
+       /* Disable the binner's pre-loaded overflow memory address */
+       V3D_WRITE(V3D_BPOA, 0);
+       V3D_WRITE(V3D_BPOS, 0);
+
+       if (exec->ct0ca != exec->ct0ea)
+               submit_cl(dev, 0, exec->ct0ca, exec->ct0ea);
+       submit_cl(dev, 1, exec->ct1ca, exec->ct1ea);
+}
+
+static void
+vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
+{
+       struct vc4_bo *bo;
+       unsigned i;
+
+       for (i = 0; i < exec->bo_count; i++) {
+               bo = to_vc4_bo(&exec->bo[i]->base);
+               bo->seqno = seqno;
+       }
+
+       list_for_each_entry(bo, &exec->unref_list, unref_head) {
+               bo->seqno = seqno;
+       }
+}
+
+/* Queues a struct vc4_exec_info for execution.  If no job is
+ * currently executing, then submits it.
+ *
+ * Unlike most GPUs, our hardware only handles one command list at a
+ * time.  To queue multiple jobs at once, we'd need to edit the
+ * previous command list to have a jump to the new one at the end, and
+ * then bump the end address.  That's a change for a later date,
+ * though.
+ */
+static void
+vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       uint64_t seqno;
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+
+       seqno = ++vc4->emit_seqno;
+       exec->seqno = seqno;
+       vc4_update_bo_seqnos(exec, seqno);
+
+       list_add_tail(&exec->head, &vc4->job_list);
+
+       /* If no job was executing, kick ours off.  Otherwise, it'll
+        * get started when the previous job's frame done interrupt
+        * occurs.
+        */
+       if (vc4_first_job(vc4) == exec) {
+               vc4_submit_next_job(dev);
+               vc4_queue_hangcheck(dev);
+       }
+
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+}
+
+/**
+ * Looks up a bunch of GEM handles for BOs and stores the array for
+ * use in the command validator that actually writes relocated
+ * addresses pointing to them.
+ */
+static int
+vc4_cl_lookup_bos(struct drm_device *dev,
+                 struct drm_file *file_priv,
+                 struct vc4_exec_info *exec)
+{
+       struct drm_vc4_submit_cl *args = exec->args;
+       uint32_t *handles;
+       int ret = 0;
+       int i;
+
+       exec->bo_count = args->bo_handle_count;
+
+       if (!exec->bo_count) {
+               /* See comment on bo_index for why we have to check
+                * this.
+                */
+               DRM_ERROR("Rendering requires BOs to validate\n");
+               return -EINVAL;
+       }
+
+       exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_cma_object *),
+                          GFP_KERNEL);
+       if (!exec->bo) {
+               DRM_ERROR("Failed to allocate validated BO pointers\n");
+               return -ENOMEM;
+       }
+
+       handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t));
+       if (!handles) {
+               DRM_ERROR("Failed to allocate incoming GEM handles\n");
+               goto fail;
+       }
+
+       ret = copy_from_user(handles,
+                            (void __user *)(uintptr_t)args->bo_handles,
+                            exec->bo_count * sizeof(uint32_t));
+       if (ret) {
+               DRM_ERROR("Failed to copy in GEM handles\n");
+               goto fail;
+       }
+
+       spin_lock(&file_priv->table_lock);
+       for (i = 0; i < exec->bo_count; i++) {
+               struct drm_gem_object *bo = idr_find(&file_priv->object_idr,
+                                                    handles[i]);
+               if (!bo) {
+                       DRM_ERROR("Failed to look up GEM BO %d: %d\n",
+                                 i, handles[i]);
+                       ret = -EINVAL;
+                       spin_unlock(&file_priv->table_lock);
+                       goto fail;
+               }
+               drm_gem_object_reference(bo);
+               exec->bo[i] = (struct drm_gem_cma_object *)bo;
+       }
+       spin_unlock(&file_priv->table_lock);
+
+fail:
+       kfree(handles);
+       return 0;
+}
+
+static int
+vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec)
+{
+       struct drm_vc4_submit_cl *args = exec->args;
+       void *temp = NULL;
+       void *bin;
+       int ret = 0;
+       uint32_t bin_offset = 0;
+       uint32_t shader_rec_offset = roundup(bin_offset + args->bin_cl_size,
+                                            16);
+       uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size;
+       uint32_t exec_size = uniforms_offset + args->uniforms_size;
+       uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) *
+                                         args->shader_rec_count);
+       struct vc4_bo *bo;
+
+       if (uniforms_offset < shader_rec_offset ||
+           exec_size < uniforms_offset ||
+           args->shader_rec_count >= (UINT_MAX /
+                                         sizeof(struct vc4_shader_state)) ||
+           temp_size < exec_size) {
+               DRM_ERROR("overflow in exec arguments\n");
+               goto fail;
+       }
+
+       /* Allocate space where we'll store the copied in user command lists
+        * and shader records.
+        *
+        * We don't just copy directly into the BOs because we need to
+        * read the contents back for validation, and I think the
+        * bo->vaddr is uncached access.
+        */
+       temp = kmalloc(temp_size, GFP_KERNEL);
+       if (!temp) {
+               DRM_ERROR("Failed to allocate storage for copying "
+                         "in bin/render CLs.\n");
+               ret = -ENOMEM;
+               goto fail;
+       }
+       bin = temp + bin_offset;
+       exec->shader_rec_u = temp + shader_rec_offset;
+       exec->uniforms_u = temp + uniforms_offset;
+       exec->shader_state = temp + exec_size;
+       exec->shader_state_size = args->shader_rec_count;
+
+       ret = copy_from_user(bin,
+                            (void __user *)(uintptr_t)args->bin_cl,
+                            args->bin_cl_size);
+       if (ret) {
+               DRM_ERROR("Failed to copy in bin cl\n");
+               goto fail;
+       }
+
+       ret = copy_from_user(exec->shader_rec_u,
+                            (void __user *)(uintptr_t)args->shader_rec,
+                            args->shader_rec_size);
+       if (ret) {
+               DRM_ERROR("Failed to copy in shader recs\n");
+               goto fail;
+       }
+
+       ret = copy_from_user(exec->uniforms_u,
+                            (void __user *)(uintptr_t)args->uniforms,
+                            args->uniforms_size);
+       if (ret) {
+               DRM_ERROR("Failed to copy in uniforms cl\n");
+               goto fail;
+       }
+
+       bo = vc4_bo_create(dev, exec_size, true);
+       if (!bo) {
+               DRM_ERROR("Couldn't allocate BO for binning\n");
+               ret = PTR_ERR(exec->exec_bo);
+               goto fail;
+       }
+       exec->exec_bo = &bo->base;
+
+       list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head,
+                     &exec->unref_list);
+
+       exec->ct0ca = exec->exec_bo->paddr + bin_offset;
+
+       exec->bin_u = bin;
+
+       exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset;
+       exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset;
+       exec->shader_rec_size = args->shader_rec_size;
+
+       exec->uniforms_v = exec->exec_bo->vaddr + uniforms_offset;
+       exec->uniforms_p = exec->exec_bo->paddr + uniforms_offset;
+       exec->uniforms_size = args->uniforms_size;
+
+       ret = vc4_validate_bin_cl(dev,
+                                 exec->exec_bo->vaddr + bin_offset,
+                                 bin,
+                                 exec);
+       if (ret)
+               goto fail;
+
+       ret = vc4_validate_shader_recs(dev, exec);
+
+fail:
+       kfree(temp);
+       return ret;
+}
+
+static void
+vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
+{
+       unsigned i;
+
+       /* Need the struct lock for drm_gem_object_unreference(). */
+       mutex_lock(&dev->struct_mutex);
+       if (exec->bo) {
+               for (i = 0; i < exec->bo_count; i++)
+                       drm_gem_object_unreference(&exec->bo[i]->base);
+               kfree(exec->bo);
+       }
+
+       while (!list_empty(&exec->unref_list)) {
+               struct vc4_bo *bo = list_first_entry(&exec->unref_list,
+                                                    struct vc4_bo, unref_head);
+               list_del(&bo->unref_head);
+               drm_gem_object_unreference(&bo->base.base);
+       }
+       mutex_unlock(&dev->struct_mutex);
+
+       kfree(exec);
+}
+
+void
+vc4_job_handle_completed(struct vc4_dev *vc4)
+{
+       unsigned long irqflags;
+       struct vc4_seqno_cb *cb, *cb_temp;
+
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       while (!list_empty(&vc4->job_done_list)) {
+               struct vc4_exec_info *exec =
+                       list_first_entry(&vc4->job_done_list,
+                                        struct vc4_exec_info, head);
+               list_del(&exec->head);
+
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+               vc4_complete_exec(vc4->dev, exec);
+               spin_lock_irqsave(&vc4->job_lock, irqflags);
+       }
+
+       list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) {
+               if (cb->seqno <= vc4->finished_seqno) {
+                       list_del_init(&cb->work.entry);
+                       schedule_work(&cb->work);
+               }
+       }
+
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+}
+
+static void vc4_seqno_cb_work(struct work_struct *work)
+{
+       struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work);
+
+       cb->func(cb);
+}
+
+int vc4_queue_seqno_cb(struct drm_device *dev,
+                      struct vc4_seqno_cb *cb, uint64_t seqno,
+                      void (*func)(struct vc4_seqno_cb *cb))
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       int ret = 0;
+       unsigned long irqflags;
+
+       cb->func = func;
+       INIT_WORK(&cb->work, vc4_seqno_cb_work);
+
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       if (seqno > vc4->finished_seqno) {
+               cb->seqno = seqno;
+               list_add_tail(&cb->work.entry, &vc4->seqno_cb_list);
+       } else {
+               schedule_work(&cb->work);
+       }
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+       return ret;
+}
+
+/* Scheduled when any job has been completed, this walks the list of
+ * jobs that had completed and unrefs their BOs and frees their exec
+ * structs.
+ */
+static void
+vc4_job_done_work(struct work_struct *work)
+{
+       struct vc4_dev *vc4 =
+               container_of(work, struct vc4_dev, job_done_work);
+
+       vc4_job_handle_completed(vc4);
+}
+
+static int
+vc4_wait_for_seqno_ioctl_helper(struct drm_device *dev,
+                               uint64_t seqno,
+                               uint64_t *timeout_ns)
+{
+       unsigned long start = jiffies;
+       int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns, true);
+
+       if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) {
+               uint64_t delta = jiffies_to_nsecs(jiffies - start);
+
+               if (*timeout_ns >= delta)
+                       *timeout_ns -= delta;
+       }
+
+       return ret;
+}
+
+int
+vc4_wait_seqno_ioctl(struct drm_device *dev, void *data,
+                    struct drm_file *file_priv)
+{
+       struct drm_vc4_wait_seqno *args = data;
+
+       return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno,
+                                              &args->timeout_ns);
+}
+
+int
+vc4_wait_bo_ioctl(struct drm_device *dev, void *data,
+                 struct drm_file *file_priv)
+{
+       int ret;
+       struct drm_vc4_wait_bo *args = data;
+       struct drm_gem_object *gem_obj;
+       struct vc4_bo *bo;
+
+       gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+       if (!gem_obj) {
+               DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
+               return -EINVAL;
+       }
+       bo = to_vc4_bo(gem_obj);
+
+       ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno,
+                                             &args->timeout_ns);
+
+       drm_gem_object_unreference_unlocked(gem_obj);
+       return ret;
+}
+
+/**
+ * Submits a command list to the VC4.
+ *
+ * This is what is called batchbuffer emitting on other hardware.
+ */
+int
+vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
+                   struct drm_file *file_priv)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_vc4_submit_cl *args = data;
+       struct vc4_exec_info *exec;
+       int ret;
+
+       if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
+               DRM_ERROR("Unknown flags: 0x%02x\n", args->flags);
+               return -EINVAL;
+       }
+
+       exec = kcalloc(1, sizeof(*exec), GFP_KERNEL);
+       if (!exec) {
+               DRM_ERROR("malloc failure on exec struct\n");
+               return -ENOMEM;
+       }
+
+       exec->args = args;
+       INIT_LIST_HEAD(&exec->unref_list);
+
+       ret = vc4_cl_lookup_bos(dev, file_priv, exec);
+       if (ret)
+               goto fail;
+
+       if (exec->args->bin_cl_size != 0) {
+               ret = vc4_get_bcl(dev, exec);
+               if (ret)
+                       goto fail;
+       } else {
+               exec->ct0ca = 0;
+               exec->ct0ea = 0;
+       }
+
+       ret = vc4_get_rcl(dev, exec);
+       if (ret)
+               goto fail;
+
+       /* Clear this out of the struct we'll be putting in the queue,
+        * since it's part of our stack.
+        */
+       exec->args = NULL;
+
+       vc4_queue_submit(dev, exec);
+
+       /* Return the seqno for our job. */
+       args->seqno = vc4->emit_seqno;
+
+       return 0;
+
+fail:
+       vc4_complete_exec(vc4->dev, exec);
+
+       return ret;
+}
+
+void
+vc4_gem_init(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       INIT_LIST_HEAD(&vc4->job_list);
+       INIT_LIST_HEAD(&vc4->job_done_list);
+       INIT_LIST_HEAD(&vc4->seqno_cb_list);
+       spin_lock_init(&vc4->job_lock);
+
+       INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work);
+       setup_timer(&vc4->hangcheck.timer,
+                   vc4_hangcheck_elapsed,
+                   (unsigned long)dev);
+
+       INIT_WORK(&vc4->job_done_work, vc4_job_done_work);
+}
+
+void
+vc4_gem_destroy(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       /* Waiting for exec to finish would need to be done before
+        * unregistering V3D.
+        */
+       WARN_ON(vc4->emit_seqno != vc4->finished_seqno);
+
+       /* V3D should already have disabled its interrupt and cleared
+        * the overflow allocation registers.  Now free the object.
+        */
+       if (vc4->overflow_mem) {
+               drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
+               vc4->overflow_mem = NULL;
+       }
+
+       vc4_bo_cache_destroy(dev);
+
+       if (vc4->hang_state)
+               vc4_free_hang_state(dev, vc4->hang_state);
+}
index da9a36d..c69c046 100644 (file)
@@ -519,7 +519,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
        WARN_ON_ONCE((HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE) == 0);
 
        drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+                        DRM_MODE_ENCODER_TMDS, NULL);
        drm_encoder_helper_add(hdmi->encoder, &vc4_hdmi_encoder_helper_funcs);
 
        hdmi->connector = vc4_hdmi_connector_init(drm, hdmi->encoder);
diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
new file mode 100644 (file)
index 0000000..b68060e
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright © 2014 Broadcom
+ *
+ * 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: Interrupt management for the V3D engine.
+ *
+ * We have an interrupt status register (V3D_INTCTL) which reports
+ * interrupts, and where writing 1 bits clears those interrupts.
+ * There are also a pair of interrupt registers
+ * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or
+ * disables that specific interrupt, and 0s written are ignored
+ * (reading either one returns the set of enabled interrupts).
+ *
+ * When we take a render frame interrupt, we need to wake the
+ * processes waiting for some frame to be done, and get the next frame
+ * submitted ASAP (so the hardware doesn't sit idle when there's work
+ * to do).
+ *
+ * When we take the binner out of memory interrupt, we need to
+ * allocate some new memory and pass it to the binner so that the
+ * current job can make progress.
+ */
+
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \
+                        V3D_INT_FRDONE)
+
+DECLARE_WAIT_QUEUE_HEAD(render_wait);
+
+static void
+vc4_overflow_mem_work(struct work_struct *work)
+{
+       struct vc4_dev *vc4 =
+               container_of(work, struct vc4_dev, overflow_mem_work);
+       struct drm_device *dev = vc4->dev;
+       struct vc4_bo *bo;
+
+       bo = vc4_bo_create(dev, 256 * 1024, true);
+       if (!bo) {
+               DRM_ERROR("Couldn't allocate binner overflow mem\n");
+               return;
+       }
+
+       /* If there's a job executing currently, then our previous
+        * overflow allocation is getting used in that job and we need
+        * to queue it to be released when the job is done.  But if no
+        * job is executing at all, then we can free the old overflow
+        * object direcctly.
+        *
+        * No lock necessary for this pointer since we're the only
+        * ones that update the pointer, and our workqueue won't
+        * reenter.
+        */
+       if (vc4->overflow_mem) {
+               struct vc4_exec_info *current_exec;
+               unsigned long irqflags;
+
+               spin_lock_irqsave(&vc4->job_lock, irqflags);
+               current_exec = vc4_first_job(vc4);
+               if (current_exec) {
+                       vc4->overflow_mem->seqno = vc4->finished_seqno + 1;
+                       list_add_tail(&vc4->overflow_mem->unref_head,
+                                     &current_exec->unref_list);
+                       vc4->overflow_mem = NULL;
+               }
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+       }
+
+       if (vc4->overflow_mem)
+               drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
+       vc4->overflow_mem = bo;
+
+       V3D_WRITE(V3D_BPOA, bo->base.paddr);
+       V3D_WRITE(V3D_BPOS, bo->base.base.size);
+       V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
+       V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
+}
+
+static void
+vc4_irq_finish_job(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_exec_info *exec = vc4_first_job(vc4);
+
+       if (!exec)
+               return;
+
+       vc4->finished_seqno++;
+       list_move_tail(&exec->head, &vc4->job_done_list);
+       vc4_submit_next_job(dev);
+
+       wake_up_all(&vc4->job_wait_queue);
+       schedule_work(&vc4->job_done_work);
+}
+
+irqreturn_t
+vc4_irq(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       uint32_t intctl;
+       irqreturn_t status = IRQ_NONE;
+
+       barrier();
+       intctl = V3D_READ(V3D_INTCTL);
+
+       /* Acknowledge the interrupts we're handling here. The render
+        * frame done interrupt will be cleared, while OUTOMEM will
+        * stay high until the underlying cause is cleared.
+        */
+       V3D_WRITE(V3D_INTCTL, intctl);
+
+       if (intctl & V3D_INT_OUTOMEM) {
+               /* Disable OUTOMEM until the work is done. */
+               V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM);
+               schedule_work(&vc4->overflow_mem_work);
+               status = IRQ_HANDLED;
+       }
+
+       if (intctl & V3D_INT_FRDONE) {
+               spin_lock(&vc4->job_lock);
+               vc4_irq_finish_job(dev);
+               spin_unlock(&vc4->job_lock);
+               status = IRQ_HANDLED;
+       }
+
+       return status;
+}
+
+void
+vc4_irq_preinstall(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       init_waitqueue_head(&vc4->job_wait_queue);
+       INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work);
+
+       /* Clear any pending interrupts someone might have left around
+        * for us.
+        */
+       V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
+}
+
+int
+vc4_irq_postinstall(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       /* Enable both the render done and out of memory interrupts. */
+       V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
+
+       return 0;
+}
+
+void
+vc4_irq_uninstall(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       /* Disable sending interrupts for our driver's IRQs. */
+       V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS);
+
+       /* Clear any pending interrupts we might have left. */
+       V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
+
+       cancel_work_sync(&vc4->overflow_mem_work);
+}
+
+/** Reinitializes interrupt registers when a GPU reset is performed. */
+void vc4_irq_reset(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long irqflags;
+
+       /* Acknowledge any stale IRQs. */
+       V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
+
+       /*
+        * Turn all our interrupts on.  Binner out of memory is the
+        * only one we expect to trigger at this point, since we've
+        * just come from poweron and haven't supplied any overflow
+        * memory yet.
+        */
+       V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
+
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       vc4_irq_finish_job(dev);
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+}
index 2e5597d..f95f2df 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "drm_crtc.h"
+#include "drm_atomic.h"
 #include "drm_atomic_helper.h"
 #include "drm_crtc_helper.h"
 #include "drm_plane_helper.h"
@@ -29,10 +30,152 @@ static void vc4_output_poll_changed(struct drm_device *dev)
                drm_fbdev_cma_hotplug_event(vc4->fbdev);
 }
 
+struct vc4_commit {
+       struct drm_device *dev;
+       struct drm_atomic_state *state;
+       struct vc4_seqno_cb cb;
+};
+
+static void
+vc4_atomic_complete_commit(struct vc4_commit *c)
+{
+       struct drm_atomic_state *state = c->state;
+       struct drm_device *dev = state->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       drm_atomic_helper_commit_modeset_disables(dev, state);
+
+       drm_atomic_helper_commit_planes(dev, state, false);
+
+       drm_atomic_helper_commit_modeset_enables(dev, state);
+
+       drm_atomic_helper_wait_for_vblanks(dev, state);
+
+       drm_atomic_helper_cleanup_planes(dev, state);
+
+       drm_atomic_state_free(state);
+
+       up(&vc4->async_modeset);
+
+       kfree(c);
+}
+
+static void
+vc4_atomic_complete_commit_seqno_cb(struct vc4_seqno_cb *cb)
+{
+       struct vc4_commit *c = container_of(cb, struct vc4_commit, cb);
+
+       vc4_atomic_complete_commit(c);
+}
+
+static struct vc4_commit *commit_init(struct drm_atomic_state *state)
+{
+       struct vc4_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
+
+       if (!c)
+               return NULL;
+       c->dev = state->dev;
+       c->state = state;
+
+       return c;
+}
+
+/**
+ * vc4_atomic_commit - commit validated state object
+ * @dev: DRM device
+ * @state: the driver state object
+ * @async: asynchronous commit
+ *
+ * This function commits a with drm_atomic_helper_check() pre-validated state
+ * object. This can still fail when e.g. the framebuffer reservation fails. For
+ * now this doesn't implement asynchronous commits.
+ *
+ * RETURNS
+ * Zero for success or -errno.
+ */
+static int vc4_atomic_commit(struct drm_device *dev,
+                            struct drm_atomic_state *state,
+                            bool async)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       int ret;
+       int i;
+       uint64_t wait_seqno = 0;
+       struct vc4_commit *c;
+
+       c = commit_init(state);
+       if (!c)
+               return -ENOMEM;
+
+       /* Make sure that any outstanding modesets have finished. */
+       ret = down_interruptible(&vc4->async_modeset);
+       if (ret) {
+               kfree(c);
+               return ret;
+       }
+
+       ret = drm_atomic_helper_prepare_planes(dev, state);
+       if (ret) {
+               kfree(c);
+               up(&vc4->async_modeset);
+               return ret;
+       }
+
+       for (i = 0; i < dev->mode_config.num_total_plane; i++) {
+               struct drm_plane *plane = state->planes[i];
+               struct drm_plane_state *new_state = state->plane_states[i];
+
+               if (!plane)
+                       continue;
+
+               if ((plane->state->fb != new_state->fb) && new_state->fb) {
+                       struct drm_gem_cma_object *cma_bo =
+                               drm_fb_cma_get_gem_obj(new_state->fb, 0);
+                       struct vc4_bo *bo = to_vc4_bo(&cma_bo->base);
+
+                       wait_seqno = max(bo->seqno, wait_seqno);
+               }
+       }
+
+       /*
+        * This is the point of no return - everything below never fails except
+        * when the hw goes bonghits. Which means we can commit the new state on
+        * the software side now.
+        */
+
+       drm_atomic_helper_swap_state(dev, state);
+
+       /*
+        * Everything below can be run asynchronously without the need to grab
+        * any modeset locks at all under one condition: It must be guaranteed
+        * that the asynchronous work has either been cancelled (if the driver
+        * supports it, which at least requires that the framebuffers get
+        * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
+        * before the new state gets committed on the software side with
+        * drm_atomic_helper_swap_state().
+        *
+        * This scheme allows new atomic state updates to be prepared and
+        * checked in parallel to the asynchronous completion of the previous
+        * update. Which is important since compositors need to figure out the
+        * composition of the next frame right after having submitted the
+        * current layout.
+        */
+
+       if (async) {
+               vc4_queue_seqno_cb(dev, &c->cb, wait_seqno,
+                                  vc4_atomic_complete_commit_seqno_cb);
+       } else {
+               vc4_wait_for_seqno(dev, wait_seqno, ~0ull, false);
+               vc4_atomic_complete_commit(c);
+       }
+
+       return 0;
+}
+
 static const struct drm_mode_config_funcs vc4_mode_funcs = {
        .output_poll_changed = vc4_output_poll_changed,
        .atomic_check = drm_atomic_helper_check,
-       .atomic_commit = drm_atomic_helper_commit,
+       .atomic_commit = vc4_atomic_commit,
        .fb_create = drm_fb_cma_create,
 };
 
@@ -41,6 +184,8 @@ int vc4_kms_load(struct drm_device *dev)
        struct vc4_dev *vc4 = to_vc4_dev(dev);
        int ret;
 
+       sema_init(&vc4->async_modeset, 1);
+
        ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
        if (ret < 0) {
                dev_err(dev->dev, "failed to initialize vblank\n");
@@ -51,6 +196,8 @@ int vc4_kms_load(struct drm_device *dev)
        dev->mode_config.max_height = 2048;
        dev->mode_config.funcs = &vc4_mode_funcs;
        dev->mode_config.preferred_depth = 24;
+       dev->mode_config.async_page_flip = true;
+
        dev->vblank_disable_allowed = true;
 
        drm_mode_config_reset(dev);
diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h
new file mode 100644 (file)
index 0000000..0f31cc0
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright © 2014 Broadcom
+ *
+ * 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.
+ */
+
+#ifndef VC4_PACKET_H
+#define VC4_PACKET_H
+
+#include "vc4_regs.h" /* for VC4_MASK, VC4_GET_FIELD, VC4_SET_FIELD */
+
+enum vc4_packet {
+       VC4_PACKET_HALT = 0,
+       VC4_PACKET_NOP = 1,
+
+       VC4_PACKET_FLUSH = 4,
+       VC4_PACKET_FLUSH_ALL = 5,
+       VC4_PACKET_START_TILE_BINNING = 6,
+       VC4_PACKET_INCREMENT_SEMAPHORE = 7,
+       VC4_PACKET_WAIT_ON_SEMAPHORE = 8,
+
+       VC4_PACKET_BRANCH = 16,
+       VC4_PACKET_BRANCH_TO_SUB_LIST = 17,
+
+       VC4_PACKET_STORE_MS_TILE_BUFFER = 24,
+       VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25,
+       VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26,
+       VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27,
+       VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28,
+       VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29,
+
+       VC4_PACKET_GL_INDEXED_PRIMITIVE = 32,
+       VC4_PACKET_GL_ARRAY_PRIMITIVE = 33,
+
+       VC4_PACKET_COMPRESSED_PRIMITIVE = 48,
+       VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49,
+
+       VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56,
+
+       VC4_PACKET_GL_SHADER_STATE = 64,
+       VC4_PACKET_NV_SHADER_STATE = 65,
+       VC4_PACKET_VG_SHADER_STATE = 66,
+
+       VC4_PACKET_CONFIGURATION_BITS = 96,
+       VC4_PACKET_FLAT_SHADE_FLAGS = 97,
+       VC4_PACKET_POINT_SIZE = 98,
+       VC4_PACKET_LINE_WIDTH = 99,
+       VC4_PACKET_RHT_X_BOUNDARY = 100,
+       VC4_PACKET_DEPTH_OFFSET = 101,
+       VC4_PACKET_CLIP_WINDOW = 102,
+       VC4_PACKET_VIEWPORT_OFFSET = 103,
+       VC4_PACKET_Z_CLIPPING = 104,
+       VC4_PACKET_CLIPPER_XY_SCALING = 105,
+       VC4_PACKET_CLIPPER_Z_SCALING = 106,
+
+       VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112,
+       VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113,
+       VC4_PACKET_CLEAR_COLORS = 114,
+       VC4_PACKET_TILE_COORDINATES = 115,
+
+       /* Not an actual hardware packet -- this is what we use to put
+        * references to GEM bos in the command stream, since we need the u32
+        * int the actual address packet in order to store the offset from the
+        * start of the BO.
+        */
+       VC4_PACKET_GEM_HANDLES = 254,
+} __attribute__ ((__packed__));
+
+#define VC4_PACKET_HALT_SIZE                                           1
+#define VC4_PACKET_NOP_SIZE                                            1
+#define VC4_PACKET_FLUSH_SIZE                                          1
+#define VC4_PACKET_FLUSH_ALL_SIZE                                      1
+#define VC4_PACKET_START_TILE_BINNING_SIZE                             1
+#define VC4_PACKET_INCREMENT_SEMAPHORE_SIZE                            1
+#define VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE                              1
+#define VC4_PACKET_BRANCH_SIZE                                         5
+#define VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE                             5
+#define VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE                           1
+#define VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF_SIZE                   1
+#define VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE                     5
+#define VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE                      5
+#define VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE                      7
+#define VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE                       7
+#define VC4_PACKET_GL_INDEXED_PRIMITIVE_SIZE                           14
+#define VC4_PACKET_GL_ARRAY_PRIMITIVE_SIZE                             10
+#define VC4_PACKET_COMPRESSED_PRIMITIVE_SIZE                           1
+#define VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE_SIZE                   1
+#define VC4_PACKET_PRIMITIVE_LIST_FORMAT_SIZE                          2
+#define VC4_PACKET_GL_SHADER_STATE_SIZE                                        5
+#define VC4_PACKET_NV_SHADER_STATE_SIZE                                        5
+#define VC4_PACKET_VG_SHADER_STATE_SIZE                                        5
+#define VC4_PACKET_CONFIGURATION_BITS_SIZE                             4
+#define VC4_PACKET_FLAT_SHADE_FLAGS_SIZE                               5
+#define VC4_PACKET_POINT_SIZE_SIZE                                     5
+#define VC4_PACKET_LINE_WIDTH_SIZE                                     5
+#define VC4_PACKET_RHT_X_BOUNDARY_SIZE                                 3
+#define VC4_PACKET_DEPTH_OFFSET_SIZE                                   5
+#define VC4_PACKET_CLIP_WINDOW_SIZE                                    9
+#define VC4_PACKET_VIEWPORT_OFFSET_SIZE                                        5
+#define VC4_PACKET_Z_CLIPPING_SIZE                                     9
+#define VC4_PACKET_CLIPPER_XY_SCALING_SIZE                             9
+#define VC4_PACKET_CLIPPER_Z_SCALING_SIZE                              9
+#define VC4_PACKET_TILE_BINNING_MODE_CONFIG_SIZE                       16
+#define VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE                     11
+#define VC4_PACKET_CLEAR_COLORS_SIZE                                   14
+#define VC4_PACKET_TILE_COORDINATES_SIZE                               3
+#define VC4_PACKET_GEM_HANDLES_SIZE                                    9
+
+/* Number of multisamples supported. */
+#define VC4_MAX_SAMPLES                                                        4
+/* Size of a full resolution color or Z tile buffer load/store. */
+#define VC4_TILE_BUFFER_SIZE                   (64 * 64 * 4)
+
+/** @{
+ * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and
+ * VC4_PACKET_TILE_RENDERING_MODE_CONFIG.
+*/
+#define VC4_TILING_FORMAT_LINEAR    0
+#define VC4_TILING_FORMAT_T         1
+#define VC4_TILING_FORMAT_LT        2
+/** @} */
+
+/** @{
+ *
+ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and
+ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER.
+ */
+#define VC4_LOADSTORE_FULL_RES_EOF                     BIT(3)
+#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL       BIT(2)
+#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS              BIT(1)
+#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR           BIT(0)
+
+/** @{
+ *
+ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and
+ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER.
+ */
+#define VC4_LOADSTORE_FULL_RES_EOF                     BIT(3)
+#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL       BIT(2)
+#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS              BIT(1)
+#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR           BIT(0)
+
+/** @{
+ *
+ * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and
+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address)
+ */
+
+#define VC4_LOADSTORE_TILE_BUFFER_EOF                  BIT(3)
+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK BIT(2)
+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS      BIT(1)
+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR   BIT(0)
+
+/** @} */
+
+/** @{
+ *
+ * byte 0-1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and
+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL
+ */
+#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR BIT(15)
+#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR     BIT(14)
+#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR  BIT(13)
+#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP         BIT(12)
+
+#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK      VC4_MASK(9, 8)
+#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT     8
+#define VC4_LOADSTORE_TILE_BUFFER_RGBA8888         0
+#define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER    1
+#define VC4_LOADSTORE_TILE_BUFFER_BGR565           2
+/** @} */
+
+/** @{
+ *
+ * byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and
+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL
+ */
+#define VC4_STORE_TILE_BUFFER_MODE_MASK            VC4_MASK(7, 6)
+#define VC4_STORE_TILE_BUFFER_MODE_SHIFT           6
+#define VC4_STORE_TILE_BUFFER_MODE_SAMPLE0         (0 << 6)
+#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4     (1 << 6)
+#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16    (2 << 6)
+
+/** The values of the field are VC4_TILING_FORMAT_* */
+#define VC4_LOADSTORE_TILE_BUFFER_TILING_MASK      VC4_MASK(5, 4)
+#define VC4_LOADSTORE_TILE_BUFFER_TILING_SHIFT     4
+
+#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK      VC4_MASK(2, 0)
+#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_SHIFT     0
+#define VC4_LOADSTORE_TILE_BUFFER_NONE             0
+#define VC4_LOADSTORE_TILE_BUFFER_COLOR            1
+#define VC4_LOADSTORE_TILE_BUFFER_ZS               2
+#define VC4_LOADSTORE_TILE_BUFFER_Z                3
+#define VC4_LOADSTORE_TILE_BUFFER_VG_MASK          4
+#define VC4_LOADSTORE_TILE_BUFFER_FULL             5
+/** @} */
+
+#define VC4_INDEX_BUFFER_U8                        (0 << 4)
+#define VC4_INDEX_BUFFER_U16                       (1 << 4)
+
+/* This flag is only present in NV shader state. */
+#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS         BIT(3)
+#define VC4_SHADER_FLAG_ENABLE_CLIPPING            BIT(2)
+#define VC4_SHADER_FLAG_VS_POINT_SIZE              BIT(1)
+#define VC4_SHADER_FLAG_FS_SINGLE_THREAD           BIT(0)
+
+/** @{ byte 2 of config bits. */
+#define VC4_CONFIG_BITS_EARLY_Z_UPDATE             BIT(1)
+#define VC4_CONFIG_BITS_EARLY_Z                    BIT(0)
+/** @} */
+
+/** @{ byte 1 of config bits. */
+#define VC4_CONFIG_BITS_Z_UPDATE                   BIT(7)
+/** same values in this 3-bit field as PIPE_FUNC_* */
+#define VC4_CONFIG_BITS_DEPTH_FUNC_SHIFT           4
+#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE        BIT(3)
+
+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_NONZERO    (0 << 1)
+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ODD        (1 << 1)
+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_OR         (2 << 1)
+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ZERO       (3 << 1)
+
+#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT       BIT(0)
+/** @} */
+
+/** @{ byte 0 of config bits. */
+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_NONE (0 << 6)
+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_4X   (1 << 6)
+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_16X  (2 << 6)
+
+#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES        BIT(4)
+#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET        BIT(3)
+#define VC4_CONFIG_BITS_CW_PRIMITIVES              BIT(2)
+#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK           BIT(1)
+#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT          BIT(0)
+/** @} */
+
+/** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */
+#define VC4_BIN_CONFIG_DB_NON_MS                   BIT(7)
+
+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK       VC4_MASK(6, 5)
+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_SHIFT      5
+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_32         0
+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_64         1
+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128        2
+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_256        3
+
+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK  VC4_MASK(4, 3)
+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_SHIFT 3
+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32    0
+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_64    1
+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128   2
+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256   3
+
+#define VC4_BIN_CONFIG_AUTO_INIT_TSDA              BIT(2)
+#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT           BIT(1)
+#define VC4_BIN_CONFIG_MS_MODE_4X                  BIT(0)
+/** @} */
+
+/** @{ bits in the last u16 of VC4_PACKET_TILE_RENDERING_MODE_CONFIG */
+#define VC4_RENDER_CONFIG_DB_NON_MS                BIT(12)
+#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE BIT(11)
+#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G      BIT(10)
+#define VC4_RENDER_CONFIG_COVERAGE_MODE            BIT(9)
+#define VC4_RENDER_CONFIG_ENABLE_VG_MASK           BIT(8)
+
+/** The values of the field are VC4_TILING_FORMAT_* */
+#define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK       VC4_MASK(7, 6)
+#define VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT      6
+
+#define VC4_RENDER_CONFIG_DECIMATE_MODE_1X         (0 << 4)
+#define VC4_RENDER_CONFIG_DECIMATE_MODE_4X         (1 << 4)
+#define VC4_RENDER_CONFIG_DECIMATE_MODE_16X        (2 << 4)
+
+#define VC4_RENDER_CONFIG_FORMAT_MASK              VC4_MASK(3, 2)
+#define VC4_RENDER_CONFIG_FORMAT_SHIFT             2
+#define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED   0
+#define VC4_RENDER_CONFIG_FORMAT_RGBA8888          1
+#define VC4_RENDER_CONFIG_FORMAT_BGR565            2
+
+#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT        BIT(1)
+#define VC4_RENDER_CONFIG_MS_MODE_4X               BIT(0)
+
+#define VC4_PRIMITIVE_LIST_FORMAT_16_INDEX         (1 << 4)
+#define VC4_PRIMITIVE_LIST_FORMAT_32_XY            (3 << 4)
+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_POINTS      (0 << 0)
+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_LINES       (1 << 0)
+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_TRIANGLES   (2 << 0)
+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_RHT         (3 << 0)
+
+enum vc4_texture_data_type {
+       VC4_TEXTURE_TYPE_RGBA8888 = 0,
+       VC4_TEXTURE_TYPE_RGBX8888 = 1,
+       VC4_TEXTURE_TYPE_RGBA4444 = 2,
+       VC4_TEXTURE_TYPE_RGBA5551 = 3,
+       VC4_TEXTURE_TYPE_RGB565 = 4,
+       VC4_TEXTURE_TYPE_LUMINANCE = 5,
+       VC4_TEXTURE_TYPE_ALPHA = 6,
+       VC4_TEXTURE_TYPE_LUMALPHA = 7,
+       VC4_TEXTURE_TYPE_ETC1 = 8,
+       VC4_TEXTURE_TYPE_S16F = 9,
+       VC4_TEXTURE_TYPE_S8 = 10,
+       VC4_TEXTURE_TYPE_S16 = 11,
+       VC4_TEXTURE_TYPE_BW1 = 12,
+       VC4_TEXTURE_TYPE_A4 = 13,
+       VC4_TEXTURE_TYPE_A1 = 14,
+       VC4_TEXTURE_TYPE_RGBA64 = 15,
+       VC4_TEXTURE_TYPE_RGBA32R = 16,
+       VC4_TEXTURE_TYPE_YUV422R = 17,
+};
+
+#define VC4_TEX_P0_OFFSET_MASK                     VC4_MASK(31, 12)
+#define VC4_TEX_P0_OFFSET_SHIFT                    12
+#define VC4_TEX_P0_CSWIZ_MASK                      VC4_MASK(11, 10)
+#define VC4_TEX_P0_CSWIZ_SHIFT                     10
+#define VC4_TEX_P0_CMMODE_MASK                     VC4_MASK(9, 9)
+#define VC4_TEX_P0_CMMODE_SHIFT                    9
+#define VC4_TEX_P0_FLIPY_MASK                      VC4_MASK(8, 8)
+#define VC4_TEX_P0_FLIPY_SHIFT                     8
+#define VC4_TEX_P0_TYPE_MASK                       VC4_MASK(7, 4)
+#define VC4_TEX_P0_TYPE_SHIFT                      4
+#define VC4_TEX_P0_MIPLVLS_MASK                    VC4_MASK(3, 0)
+#define VC4_TEX_P0_MIPLVLS_SHIFT                   0
+
+#define VC4_TEX_P1_TYPE4_MASK                      VC4_MASK(31, 31)
+#define VC4_TEX_P1_TYPE4_SHIFT                     31
+#define VC4_TEX_P1_HEIGHT_MASK                     VC4_MASK(30, 20)
+#define VC4_TEX_P1_HEIGHT_SHIFT                    20
+#define VC4_TEX_P1_ETCFLIP_MASK                    VC4_MASK(19, 19)
+#define VC4_TEX_P1_ETCFLIP_SHIFT                   19
+#define VC4_TEX_P1_WIDTH_MASK                      VC4_MASK(18, 8)
+#define VC4_TEX_P1_WIDTH_SHIFT                     8
+
+#define VC4_TEX_P1_MAGFILT_MASK                    VC4_MASK(7, 7)
+#define VC4_TEX_P1_MAGFILT_SHIFT                   7
+# define VC4_TEX_P1_MAGFILT_LINEAR                 0
+# define VC4_TEX_P1_MAGFILT_NEAREST                1
+
+#define VC4_TEX_P1_MINFILT_MASK                    VC4_MASK(6, 4)
+#define VC4_TEX_P1_MINFILT_SHIFT                   4
+# define VC4_TEX_P1_MINFILT_LINEAR                 0
+# define VC4_TEX_P1_MINFILT_NEAREST                1
+# define VC4_TEX_P1_MINFILT_NEAR_MIP_NEAR          2
+# define VC4_TEX_P1_MINFILT_NEAR_MIP_LIN           3
+# define VC4_TEX_P1_MINFILT_LIN_MIP_NEAR           4
+# define VC4_TEX_P1_MINFILT_LIN_MIP_LIN            5
+
+#define VC4_TEX_P1_WRAP_T_MASK                     VC4_MASK(3, 2)
+#define VC4_TEX_P1_WRAP_T_SHIFT                    2
+#define VC4_TEX_P1_WRAP_S_MASK                     VC4_MASK(1, 0)
+#define VC4_TEX_P1_WRAP_S_SHIFT                    0
+# define VC4_TEX_P1_WRAP_REPEAT                    0
+# define VC4_TEX_P1_WRAP_CLAMP                     1
+# define VC4_TEX_P1_WRAP_MIRROR                    2
+# define VC4_TEX_P1_WRAP_BORDER                    3
+
+#define VC4_TEX_P2_PTYPE_MASK                      VC4_MASK(31, 30)
+#define VC4_TEX_P2_PTYPE_SHIFT                     30
+# define VC4_TEX_P2_PTYPE_IGNORED                  0
+# define VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE          1
+# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS   2
+# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS      3
+
+/* VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE bits */
+#define VC4_TEX_P2_CMST_MASK                       VC4_MASK(29, 12)
+#define VC4_TEX_P2_CMST_SHIFT                      12
+#define VC4_TEX_P2_BSLOD_MASK                      VC4_MASK(0, 0)
+#define VC4_TEX_P2_BSLOD_SHIFT                     0
+
+/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS */
+#define VC4_TEX_P2_CHEIGHT_MASK                    VC4_MASK(22, 12)
+#define VC4_TEX_P2_CHEIGHT_SHIFT                   12
+#define VC4_TEX_P2_CWIDTH_MASK                     VC4_MASK(10, 0)
+#define VC4_TEX_P2_CWIDTH_SHIFT                    0
+
+/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS */
+#define VC4_TEX_P2_CYOFF_MASK                      VC4_MASK(22, 12)
+#define VC4_TEX_P2_CYOFF_SHIFT                     12
+#define VC4_TEX_P2_CXOFF_MASK                      VC4_MASK(10, 0)
+#define VC4_TEX_P2_CXOFF_SHIFT                     0
+
+#endif /* VC4_PACKET_H */
index 887f3ca..0addbad 100644 (file)
@@ -29,6 +29,14 @@ struct vc4_plane_state {
        u32 *dlist;
        u32 dlist_size; /* Number of dwords in allocated for the display list */
        u32 dlist_count; /* Number of used dwords in the display list. */
+
+       /* Offset in the dlist to pointer word 0. */
+       u32 pw0_offset;
+
+       /* Offset where the plane's dlist was last stored in the
+          hardware at vc4_crtc_atomic_flush() time.
+       */
+       u32 *hw_dlist;
 };
 
 static inline struct vc4_plane_state *
@@ -207,6 +215,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
        /* Position Word 3: Context.  Written by the HVS. */
        vc4_dlist_write(vc4_state, 0xc0c0c0c0);
 
+       vc4_state->pw0_offset = vc4_state->dlist_count;
+
        /* Pointer Word 0: RGB / Y Pointer */
        vc4_dlist_write(vc4_state, bo->paddr + offset);
 
@@ -258,6 +268,8 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
        struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
        int i;
 
+       vc4_state->hw_dlist = dlist;
+
        /* Can't memcpy_toio() because it needs to be 32-bit writes. */
        for (i = 0; i < vc4_state->dlist_count; i++)
                writel(vc4_state->dlist[i], &dlist[i]);
@@ -272,6 +284,34 @@ u32 vc4_plane_dlist_size(struct drm_plane_state *state)
        return vc4_state->dlist_count;
 }
 
+/* Updates the plane to immediately (well, once the FIFO needs
+ * refilling) scan out from at a new framebuffer.
+ */
+void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
+{
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
+       struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
+       uint32_t addr;
+
+       /* We're skipping the address adjustment for negative origin,
+        * because this is only called on the primary plane.
+        */
+       WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
+       addr = bo->paddr + fb->offsets[0];
+
+       /* Write the new address into the hardware immediately.  The
+        * scanout will start from this address as soon as the FIFO
+        * needs to refill with pixels.
+        */
+       writel(addr, &vc4_state->hw_dlist[vc4_state->pw0_offset]);
+
+       /* Also update the CPU-side dlist copy, so that any later
+        * atomic updates that don't do a new modeset on our plane
+        * also use our updated address.
+        */
+       vc4_state->dlist[vc4_state->pw0_offset] = addr;
+}
+
 static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
        .prepare_fb = NULL,
        .cleanup_fb = NULL,
@@ -317,7 +357,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
        ret = drm_universal_plane_init(dev, plane, 0xff,
                                       &vc4_plane_funcs,
                                       formats, ARRAY_SIZE(formats),
-                                      type);
+                                      type, NULL);
 
        drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
 
diff --git a/drivers/gpu/drm/vc4/vc4_qpu_defines.h b/drivers/gpu/drm/vc4/vc4_qpu_defines.h
new file mode 100644 (file)
index 0000000..d5c2f3c
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright © 2014 Broadcom
+ *
+ * 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.
+ */
+
+#ifndef VC4_QPU_DEFINES_H
+#define VC4_QPU_DEFINES_H
+
+enum qpu_op_add {
+       QPU_A_NOP,
+       QPU_A_FADD,
+       QPU_A_FSUB,
+       QPU_A_FMIN,
+       QPU_A_FMAX,
+       QPU_A_FMINABS,
+       QPU_A_FMAXABS,
+       QPU_A_FTOI,
+       QPU_A_ITOF,
+       QPU_A_ADD = 12,
+       QPU_A_SUB,
+       QPU_A_SHR,
+       QPU_A_ASR,
+       QPU_A_ROR,
+       QPU_A_SHL,
+       QPU_A_MIN,
+       QPU_A_MAX,
+       QPU_A_AND,
+       QPU_A_OR,
+       QPU_A_XOR,
+       QPU_A_NOT,
+       QPU_A_CLZ,
+       QPU_A_V8ADDS = 30,
+       QPU_A_V8SUBS = 31,
+};
+
+enum qpu_op_mul {
+       QPU_M_NOP,
+       QPU_M_FMUL,
+       QPU_M_MUL24,
+       QPU_M_V8MULD,
+       QPU_M_V8MIN,
+       QPU_M_V8MAX,
+       QPU_M_V8ADDS,
+       QPU_M_V8SUBS,
+};
+
+enum qpu_raddr {
+       QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */
+       /* 0-31 are the plain regfile a or b fields */
+       QPU_R_UNIF = 32,
+       QPU_R_VARY = 35,
+       QPU_R_ELEM_QPU = 38,
+       QPU_R_NOP,
+       QPU_R_XY_PIXEL_COORD = 41,
+       QPU_R_MS_REV_FLAGS = 41,
+       QPU_R_VPM = 48,
+       QPU_R_VPM_LD_BUSY,
+       QPU_R_VPM_LD_WAIT,
+       QPU_R_MUTEX_ACQUIRE,
+};
+
+enum qpu_waddr {
+       /* 0-31 are the plain regfile a or b fields */
+       QPU_W_ACC0 = 32, /* aka r0 */
+       QPU_W_ACC1,
+       QPU_W_ACC2,
+       QPU_W_ACC3,
+       QPU_W_TMU_NOSWAP,
+       QPU_W_ACC5,
+       QPU_W_HOST_INT,
+       QPU_W_NOP,
+       QPU_W_UNIFORMS_ADDRESS,
+       QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */
+       QPU_W_MS_FLAGS = 42,
+       QPU_W_REV_FLAG = 42,
+       QPU_W_TLB_STENCIL_SETUP = 43,
+       QPU_W_TLB_Z,
+       QPU_W_TLB_COLOR_MS,
+       QPU_W_TLB_COLOR_ALL,
+       QPU_W_TLB_ALPHA_MASK,
+       QPU_W_VPM,
+       QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */
+       QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */
+       QPU_W_MUTEX_RELEASE,
+       QPU_W_SFU_RECIP,
+       QPU_W_SFU_RECIPSQRT,
+       QPU_W_SFU_EXP,
+       QPU_W_SFU_LOG,
+       QPU_W_TMU0_S,
+       QPU_W_TMU0_T,
+       QPU_W_TMU0_R,
+       QPU_W_TMU0_B,
+       QPU_W_TMU1_S,
+       QPU_W_TMU1_T,
+       QPU_W_TMU1_R,
+       QPU_W_TMU1_B,
+};
+
+enum qpu_sig_bits {
+       QPU_SIG_SW_BREAKPOINT,
+       QPU_SIG_NONE,
+       QPU_SIG_THREAD_SWITCH,
+       QPU_SIG_PROG_END,
+       QPU_SIG_WAIT_FOR_SCOREBOARD,
+       QPU_SIG_SCOREBOARD_UNLOCK,
+       QPU_SIG_LAST_THREAD_SWITCH,
+       QPU_SIG_COVERAGE_LOAD,
+       QPU_SIG_COLOR_LOAD,
+       QPU_SIG_COLOR_LOAD_END,
+       QPU_SIG_LOAD_TMU0,
+       QPU_SIG_LOAD_TMU1,
+       QPU_SIG_ALPHA_MASK_LOAD,
+       QPU_SIG_SMALL_IMM,
+       QPU_SIG_LOAD_IMM,
+       QPU_SIG_BRANCH
+};
+
+enum qpu_mux {
+       /* hardware mux values */
+       QPU_MUX_R0,
+       QPU_MUX_R1,
+       QPU_MUX_R2,
+       QPU_MUX_R3,
+       QPU_MUX_R4,
+       QPU_MUX_R5,
+       QPU_MUX_A,
+       QPU_MUX_B,
+
+       /* non-hardware mux values */
+       QPU_MUX_IMM,
+};
+
+enum qpu_cond {
+       QPU_COND_NEVER,
+       QPU_COND_ALWAYS,
+       QPU_COND_ZS,
+       QPU_COND_ZC,
+       QPU_COND_NS,
+       QPU_COND_NC,
+       QPU_COND_CS,
+       QPU_COND_CC,
+};
+
+enum qpu_pack_mul {
+       QPU_PACK_MUL_NOP,
+       /* replicated to each 8 bits of the 32-bit dst. */
+       QPU_PACK_MUL_8888 = 3,
+       QPU_PACK_MUL_8A,
+       QPU_PACK_MUL_8B,
+       QPU_PACK_MUL_8C,
+       QPU_PACK_MUL_8D,
+};
+
+enum qpu_pack_a {
+       QPU_PACK_A_NOP,
+       /* convert to 16 bit float if float input, or to int16. */
+       QPU_PACK_A_16A,
+       QPU_PACK_A_16B,
+       /* replicated to each 8 bits of the 32-bit dst. */
+       QPU_PACK_A_8888,
+       /* Convert to 8-bit unsigned int. */
+       QPU_PACK_A_8A,
+       QPU_PACK_A_8B,
+       QPU_PACK_A_8C,
+       QPU_PACK_A_8D,
+
+       /* Saturating variants of the previous instructions. */
+       QPU_PACK_A_32_SAT, /* int-only */
+       QPU_PACK_A_16A_SAT, /* int or float */
+       QPU_PACK_A_16B_SAT,
+       QPU_PACK_A_8888_SAT,
+       QPU_PACK_A_8A_SAT,
+       QPU_PACK_A_8B_SAT,
+       QPU_PACK_A_8C_SAT,
+       QPU_PACK_A_8D_SAT,
+};
+
+enum qpu_unpack_r4 {
+       QPU_UNPACK_R4_NOP,
+       QPU_UNPACK_R4_F16A_TO_F32,
+       QPU_UNPACK_R4_F16B_TO_F32,
+       QPU_UNPACK_R4_8D_REP,
+       QPU_UNPACK_R4_8A,
+       QPU_UNPACK_R4_8B,
+       QPU_UNPACK_R4_8C,
+       QPU_UNPACK_R4_8D,
+};
+
+#define QPU_MASK(high, low) \
+       ((((uint64_t)1 << ((high) - (low) + 1)) - 1) << (low))
+
+#define QPU_GET_FIELD(word, field) \
+       ((uint32_t)(((word)  & field ## _MASK) >> field ## _SHIFT))
+
+#define QPU_SIG_SHIFT                   60
+#define QPU_SIG_MASK                    QPU_MASK(63, 60)
+
+#define QPU_UNPACK_SHIFT                57
+#define QPU_UNPACK_MASK                 QPU_MASK(59, 57)
+
+/**
+ * If set, the pack field means PACK_MUL or R4 packing, instead of normal
+ * regfile a packing.
+ */
+#define QPU_PM                          ((uint64_t)1 << 56)
+
+#define QPU_PACK_SHIFT                  52
+#define QPU_PACK_MASK                   QPU_MASK(55, 52)
+
+#define QPU_COND_ADD_SHIFT              49
+#define QPU_COND_ADD_MASK               QPU_MASK(51, 49)
+#define QPU_COND_MUL_SHIFT              46
+#define QPU_COND_MUL_MASK               QPU_MASK(48, 46)
+
+#define QPU_SF                          ((uint64_t)1 << 45)
+
+#define QPU_WADDR_ADD_SHIFT             38
+#define QPU_WADDR_ADD_MASK              QPU_MASK(43, 38)
+#define QPU_WADDR_MUL_SHIFT             32
+#define QPU_WADDR_MUL_MASK              QPU_MASK(37, 32)
+
+#define QPU_OP_MUL_SHIFT                29
+#define QPU_OP_MUL_MASK                 QPU_MASK(31, 29)
+
+#define QPU_RADDR_A_SHIFT               18
+#define QPU_RADDR_A_MASK                QPU_MASK(23, 18)
+#define QPU_RADDR_B_SHIFT               12
+#define QPU_RADDR_B_MASK                QPU_MASK(17, 12)
+#define QPU_SMALL_IMM_SHIFT             12
+#define QPU_SMALL_IMM_MASK              QPU_MASK(17, 12)
+
+#define QPU_ADD_A_SHIFT                 9
+#define QPU_ADD_A_MASK                  QPU_MASK(11, 9)
+#define QPU_ADD_B_SHIFT                 6
+#define QPU_ADD_B_MASK                  QPU_MASK(8, 6)
+#define QPU_MUL_A_SHIFT                 3
+#define QPU_MUL_A_MASK                  QPU_MASK(5, 3)
+#define QPU_MUL_B_SHIFT                 0
+#define QPU_MUL_B_MASK                  QPU_MASK(2, 0)
+
+#define QPU_WS                          ((uint64_t)1 << 44)
+
+#define QPU_OP_ADD_SHIFT                24
+#define QPU_OP_ADD_MASK                 QPU_MASK(28, 24)
+
+#endif /* VC4_QPU_DEFINES_H */
index 9e4e904..4e52a0a 100644 (file)
 #define V3D_PCTRS14  0x006f4
 #define V3D_PCTR15   0x006f8
 #define V3D_PCTRS15  0x006fc
-#define V3D_BGE      0x00f00
+#define V3D_DBGE     0x00f00
 #define V3D_FDBGO    0x00f04
 #define V3D_FDBGB    0x00f08
 #define V3D_FDBGR    0x00f0c
diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c
new file mode 100644 (file)
index 0000000..8a2a312
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+ * Copyright © 2014-2015 Broadcom
+ *
+ * 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: Render command list generation
+ *
+ * In the VC4 driver, render command list generation is performed by the
+ * kernel instead of userspace.  We do this because validating a
+ * user-submitted command list is hard to get right and has high CPU overhead,
+ * while the number of valid configurations for render command lists is
+ * actually fairly low.
+ */
+
+#include "uapi/drm/vc4_drm.h"
+#include "vc4_drv.h"
+#include "vc4_packet.h"
+
+struct vc4_rcl_setup {
+       struct drm_gem_cma_object *color_read;
+       struct drm_gem_cma_object *color_write;
+       struct drm_gem_cma_object *zs_read;
+       struct drm_gem_cma_object *zs_write;
+       struct drm_gem_cma_object *msaa_color_write;
+       struct drm_gem_cma_object *msaa_zs_write;
+
+       struct drm_gem_cma_object *rcl;
+       u32 next_offset;
+};
+
+static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val)
+{
+       *(u8 *)(setup->rcl->vaddr + setup->next_offset) = val;
+       setup->next_offset += 1;
+}
+
+static inline void rcl_u16(struct vc4_rcl_setup *setup, u16 val)
+{
+       *(u16 *)(setup->rcl->vaddr + setup->next_offset) = val;
+       setup->next_offset += 2;
+}
+
+static inline void rcl_u32(struct vc4_rcl_setup *setup, u32 val)
+{
+       *(u32 *)(setup->rcl->vaddr + setup->next_offset) = val;
+       setup->next_offset += 4;
+}
+
+/*
+ * Emits a no-op STORE_TILE_BUFFER_GENERAL.
+ *
+ * If we emit a PACKET_TILE_COORDINATES, it must be followed by a store of
+ * some sort before another load is triggered.
+ */
+static void vc4_store_before_load(struct vc4_rcl_setup *setup)
+{
+       rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL);
+       rcl_u16(setup,
+               VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_NONE,
+                             VC4_LOADSTORE_TILE_BUFFER_BUFFER) |
+               VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR |
+               VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR |
+               VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR);
+       rcl_u32(setup, 0); /* no address, since we're in None mode */
+}
+
+/*
+ * Calculates the physical address of the start of a tile in a RCL surface.
+ *
+ * Unlike the other load/store packets,
+ * VC4_PACKET_LOAD/STORE_FULL_RES_TILE_BUFFER don't look at the tile
+ * coordinates packet, and instead just store to the address given.
+ */
+static uint32_t vc4_full_res_offset(struct vc4_exec_info *exec,
+                                   struct drm_gem_cma_object *bo,
+                                   struct drm_vc4_submit_rcl_surface *surf,
+                                   uint8_t x, uint8_t y)
+{
+       return bo->paddr + surf->offset + VC4_TILE_BUFFER_SIZE *
+               (DIV_ROUND_UP(exec->args->width, 32) * y + x);
+}
+
+/*
+ * Emits a PACKET_TILE_COORDINATES if one isn't already pending.
+ *
+ * The tile coordinates packet triggers a pending load if there is one, are
+ * used for clipping during rendering, and determine where loads/stores happen
+ * relative to their base address.
+ */
+static void vc4_tile_coordinates(struct vc4_rcl_setup *setup,
+                                uint32_t x, uint32_t y)
+{
+       rcl_u8(setup, VC4_PACKET_TILE_COORDINATES);
+       rcl_u8(setup, x);
+       rcl_u8(setup, y);
+}
+
+static void emit_tile(struct vc4_exec_info *exec,
+                     struct vc4_rcl_setup *setup,
+                     uint8_t x, uint8_t y, bool first, bool last)
+{
+       struct drm_vc4_submit_cl *args = exec->args;
+       bool has_bin = args->bin_cl_size != 0;
+
+       /* Note that the load doesn't actually occur until the
+        * tile coords packet is processed, and only one load
+        * may be outstanding at a time.
+        */
+       if (setup->color_read) {
+               if (args->color_read.flags &
+                   VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) {
+                       rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER);
+                       rcl_u32(setup,
+                               vc4_full_res_offset(exec, setup->color_read,
+                                                   &args->color_read, x, y) |
+                               VC4_LOADSTORE_FULL_RES_DISABLE_ZS);
+               } else {
+                       rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL);
+                       rcl_u16(setup, args->color_read.bits);
+                       rcl_u32(setup, setup->color_read->paddr +
+                               args->color_read.offset);
+               }
+       }
+
+       if (setup->zs_read) {
+               if (args->zs_read.flags &
+                   VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) {
+                       rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER);
+                       rcl_u32(setup,
+                               vc4_full_res_offset(exec, setup->zs_read,
+                                                   &args->zs_read, x, y) |
+                               VC4_LOADSTORE_FULL_RES_DISABLE_COLOR);
+               } else {
+                       if (setup->color_read) {
+                               /* Exec previous load. */
+                               vc4_tile_coordinates(setup, x, y);
+                               vc4_store_before_load(setup);
+                       }
+
+                       rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL);
+                       rcl_u16(setup, args->zs_read.bits);
+                       rcl_u32(setup, setup->zs_read->paddr +
+                               args->zs_read.offset);
+               }
+       }
+
+       /* Clipping depends on tile coordinates having been
+        * emitted, so we always need one here.
+        */
+       vc4_tile_coordinates(setup, x, y);
+
+       /* Wait for the binner before jumping to the first
+        * tile's lists.
+        */
+       if (first && has_bin)
+               rcl_u8(setup, VC4_PACKET_WAIT_ON_SEMAPHORE);
+
+       if (has_bin) {
+               rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST);
+               rcl_u32(setup, (exec->tile_bo->paddr +
+                               exec->tile_alloc_offset +
+                               (y * exec->bin_tiles_x + x) * 32));
+       }
+
+       if (setup->msaa_color_write) {
+               bool last_tile_write = (!setup->msaa_zs_write &&
+                                       !setup->zs_write &&
+                                       !setup->color_write);
+               uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_ZS;
+
+               if (!last_tile_write)
+                       bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL;
+               else if (last)
+                       bits |= VC4_LOADSTORE_FULL_RES_EOF;
+               rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER);
+               rcl_u32(setup,
+                       vc4_full_res_offset(exec, setup->msaa_color_write,
+                                           &args->msaa_color_write, x, y) |
+                       bits);
+       }
+
+       if (setup->msaa_zs_write) {
+               bool last_tile_write = (!setup->zs_write &&
+                                       !setup->color_write);
+               uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_COLOR;
+
+               if (setup->msaa_color_write)
+                       vc4_tile_coordinates(setup, x, y);
+               if (!last_tile_write)
+                       bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL;
+               else if (last)
+                       bits |= VC4_LOADSTORE_FULL_RES_EOF;
+               rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER);
+               rcl_u32(setup,
+                       vc4_full_res_offset(exec, setup->msaa_zs_write,
+                                           &args->msaa_zs_write, x, y) |
+                       bits);
+       }
+
+       if (setup->zs_write) {
+               bool last_tile_write = !setup->color_write;
+
+               if (setup->msaa_color_write || setup->msaa_zs_write)
+                       vc4_tile_coordinates(setup, x, y);
+
+               rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL);
+               rcl_u16(setup, args->zs_write.bits |
+                       (last_tile_write ?
+                        0 : VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR));
+               rcl_u32(setup,
+                       (setup->zs_write->paddr + args->zs_write.offset) |
+                       ((last && last_tile_write) ?
+                        VC4_LOADSTORE_TILE_BUFFER_EOF : 0));
+       }
+
+       if (setup->color_write) {
+               if (setup->msaa_color_write || setup->msaa_zs_write ||
+                   setup->zs_write) {
+                       vc4_tile_coordinates(setup, x, y);
+               }
+
+               if (last)
+                       rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF);
+               else
+                       rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER);
+       }
+}
+
+static int vc4_create_rcl_bo(struct drm_device *dev, struct vc4_exec_info *exec,
+                            struct vc4_rcl_setup *setup)
+{
+       struct drm_vc4_submit_cl *args = exec->args;
+       bool has_bin = args->bin_cl_size != 0;
+       uint8_t min_x_tile = args->min_x_tile;
+       uint8_t min_y_tile = args->min_y_tile;
+       uint8_t max_x_tile = args->max_x_tile;
+       uint8_t max_y_tile = args->max_y_tile;
+       uint8_t xtiles = max_x_tile - min_x_tile + 1;
+       uint8_t ytiles = max_y_tile - min_y_tile + 1;
+       uint8_t x, y;
+       uint32_t size, loop_body_size;
+
+       size = VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE;
+       loop_body_size = VC4_PACKET_TILE_COORDINATES_SIZE;
+
+       if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) {
+               size += VC4_PACKET_CLEAR_COLORS_SIZE +
+                       VC4_PACKET_TILE_COORDINATES_SIZE +
+                       VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE;
+       }
+
+       if (setup->color_read) {
+               if (args->color_read.flags &
+                   VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) {
+                       loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE;
+               } else {
+                       loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE;
+               }
+       }
+       if (setup->zs_read) {
+               if (args->zs_read.flags &
+                   VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) {
+                       loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE;
+               } else {
+                       if (setup->color_read &&
+                           !(args->color_read.flags &
+                             VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES)) {
+                               loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE;
+                               loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE;
+                       }
+                       loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE;
+               }
+       }
+
+       if (has_bin) {
+               size += VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE;
+               loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE;
+       }
+
+       if (setup->msaa_color_write)
+               loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE;
+       if (setup->msaa_zs_write)
+               loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE;
+
+       if (setup->zs_write)
+               loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE;
+       if (setup->color_write)
+               loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE;
+
+       /* We need a VC4_PACKET_TILE_COORDINATES in between each store. */
+       loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE *
+               ((setup->msaa_color_write != NULL) +
+                (setup->msaa_zs_write != NULL) +
+                (setup->color_write != NULL) +
+                (setup->zs_write != NULL) - 1);
+
+       size += xtiles * ytiles * loop_body_size;
+
+       setup->rcl = &vc4_bo_create(dev, size, true)->base;
+       if (!setup->rcl)
+               return -ENOMEM;
+       list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head,
+                     &exec->unref_list);
+
+       rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG);
+       rcl_u32(setup,
+               (setup->color_write ? (setup->color_write->paddr +
+                                      args->color_write.offset) :
+                0));
+       rcl_u16(setup, args->width);
+       rcl_u16(setup, args->height);
+       rcl_u16(setup, args->color_write.bits);
+
+       /* The tile buffer gets cleared when the previous tile is stored.  If
+        * the clear values changed between frames, then the tile buffer has
+        * stale clear values in it, so we have to do a store in None mode (no
+        * writes) so that we trigger the tile buffer clear.
+        */
+       if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) {
+               rcl_u8(setup, VC4_PACKET_CLEAR_COLORS);
+               rcl_u32(setup, args->clear_color[0]);
+               rcl_u32(setup, args->clear_color[1]);
+               rcl_u32(setup, args->clear_z);
+               rcl_u8(setup, args->clear_s);
+
+               vc4_tile_coordinates(setup, 0, 0);
+
+               rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL);
+               rcl_u16(setup, VC4_LOADSTORE_TILE_BUFFER_NONE);
+               rcl_u32(setup, 0); /* no address, since we're in None mode */
+       }
+
+       for (y = min_y_tile; y <= max_y_tile; y++) {
+               for (x = min_x_tile; x <= max_x_tile; x++) {
+                       bool first = (x == min_x_tile && y == min_y_tile);
+                       bool last = (x == max_x_tile && y == max_y_tile);
+
+                       emit_tile(exec, setup, x, y, first, last);
+               }
+       }
+
+       BUG_ON(setup->next_offset != size);
+       exec->ct1ca = setup->rcl->paddr;
+       exec->ct1ea = setup->rcl->paddr + setup->next_offset;
+
+       return 0;
+}
+
+static int vc4_full_res_bounds_check(struct vc4_exec_info *exec,
+                                    struct drm_gem_cma_object *obj,
+                                    struct drm_vc4_submit_rcl_surface *surf)
+{
+       struct drm_vc4_submit_cl *args = exec->args;
+       u32 render_tiles_stride = DIV_ROUND_UP(exec->args->width, 32);
+
+       if (surf->offset > obj->base.size) {
+               DRM_ERROR("surface offset %d > BO size %zd\n",
+                         surf->offset, obj->base.size);
+               return -EINVAL;
+       }
+
+       if ((obj->base.size - surf->offset) / VC4_TILE_BUFFER_SIZE <
+           render_tiles_stride * args->max_y_tile + args->max_x_tile) {
+               DRM_ERROR("MSAA tile %d, %d out of bounds "
+                         "(bo size %zd, offset %d).\n",
+                         args->max_x_tile, args->max_y_tile,
+                         obj->base.size,
+                         surf->offset);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec,
+                                     struct drm_gem_cma_object **obj,
+                                     struct drm_vc4_submit_rcl_surface *surf)
+{
+       if (surf->flags != 0 || surf->bits != 0) {
+               DRM_ERROR("MSAA surface had nonzero flags/bits\n");
+               return -EINVAL;
+       }
+
+       if (surf->hindex == ~0)
+               return 0;
+
+       *obj = vc4_use_bo(exec, surf->hindex);
+       if (!*obj)
+               return -EINVAL;
+
+       if (surf->offset & 0xf) {
+               DRM_ERROR("MSAA write must be 16b aligned.\n");
+               return -EINVAL;
+       }
+
+       return vc4_full_res_bounds_check(exec, *obj, surf);
+}
+
+static int vc4_rcl_surface_setup(struct vc4_exec_info *exec,
+                                struct drm_gem_cma_object **obj,
+                                struct drm_vc4_submit_rcl_surface *surf)
+{
+       uint8_t tiling = VC4_GET_FIELD(surf->bits,
+                                      VC4_LOADSTORE_TILE_BUFFER_TILING);
+       uint8_t buffer = VC4_GET_FIELD(surf->bits,
+                                      VC4_LOADSTORE_TILE_BUFFER_BUFFER);
+       uint8_t format = VC4_GET_FIELD(surf->bits,
+                                      VC4_LOADSTORE_TILE_BUFFER_FORMAT);
+       int cpp;
+       int ret;
+
+       if (surf->flags & ~VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) {
+               DRM_ERROR("Extra flags set\n");
+               return -EINVAL;
+       }
+
+       if (surf->hindex == ~0)
+               return 0;
+
+       *obj = vc4_use_bo(exec, surf->hindex);
+       if (!*obj)
+               return -EINVAL;
+
+       if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) {
+               if (surf == &exec->args->zs_write) {
+                       DRM_ERROR("general zs write may not be a full-res.\n");
+                       return -EINVAL;
+               }
+
+               if (surf->bits != 0) {
+                       DRM_ERROR("load/store general bits set with "
+                                 "full res load/store.\n");
+                       return -EINVAL;
+               }
+
+               ret = vc4_full_res_bounds_check(exec, *obj, surf);
+               if (!ret)
+                       return ret;
+
+               return 0;
+       }
+
+       if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK |
+                          VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK |
+                          VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) {
+               DRM_ERROR("Unknown bits in load/store: 0x%04x\n",
+                         surf->bits);
+               return -EINVAL;
+       }
+
+       if (tiling > VC4_TILING_FORMAT_LT) {
+               DRM_ERROR("Bad tiling format\n");
+               return -EINVAL;
+       }
+
+       if (buffer == VC4_LOADSTORE_TILE_BUFFER_ZS) {
+               if (format != 0) {
+                       DRM_ERROR("No color format should be set for ZS\n");
+                       return -EINVAL;
+               }
+               cpp = 4;
+       } else if (buffer == VC4_LOADSTORE_TILE_BUFFER_COLOR) {
+               switch (format) {
+               case VC4_LOADSTORE_TILE_BUFFER_BGR565:
+               case VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER:
+                       cpp = 2;
+                       break;
+               case VC4_LOADSTORE_TILE_BUFFER_RGBA8888:
+                       cpp = 4;
+                       break;
+               default:
+                       DRM_ERROR("Bad tile buffer format\n");
+                       return -EINVAL;
+               }
+       } else {
+               DRM_ERROR("Bad load/store buffer %d.\n", buffer);
+               return -EINVAL;
+       }
+
+       if (surf->offset & 0xf) {
+               DRM_ERROR("load/store buffer must be 16b aligned.\n");
+               return -EINVAL;
+       }
+
+       if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling,
+                               exec->args->width, exec->args->height, cpp)) {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+vc4_rcl_render_config_surface_setup(struct vc4_exec_info *exec,
+                                   struct vc4_rcl_setup *setup,
+                                   struct drm_gem_cma_object **obj,
+                                   struct drm_vc4_submit_rcl_surface *surf)
+{
+       uint8_t tiling = VC4_GET_FIELD(surf->bits,
+                                      VC4_RENDER_CONFIG_MEMORY_FORMAT);
+       uint8_t format = VC4_GET_FIELD(surf->bits,
+                                      VC4_RENDER_CONFIG_FORMAT);
+       int cpp;
+
+       if (surf->flags != 0) {
+               DRM_ERROR("No flags supported on render config.\n");
+               return -EINVAL;
+       }
+
+       if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK |
+                          VC4_RENDER_CONFIG_FORMAT_MASK |
+                          VC4_RENDER_CONFIG_MS_MODE_4X |
+                          VC4_RENDER_CONFIG_DECIMATE_MODE_4X)) {
+               DRM_ERROR("Unknown bits in render config: 0x%04x\n",
+                         surf->bits);
+               return -EINVAL;
+       }
+
+       if (surf->hindex == ~0)
+               return 0;
+
+       *obj = vc4_use_bo(exec, surf->hindex);
+       if (!*obj)
+               return -EINVAL;
+
+       if (tiling > VC4_TILING_FORMAT_LT) {
+               DRM_ERROR("Bad tiling format\n");
+               return -EINVAL;
+       }
+
+       switch (format) {
+       case VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED:
+       case VC4_RENDER_CONFIG_FORMAT_BGR565:
+               cpp = 2;
+               break;
+       case VC4_RENDER_CONFIG_FORMAT_RGBA8888:
+               cpp = 4;
+               break;
+       default:
+               DRM_ERROR("Bad tile buffer format\n");
+               return -EINVAL;
+       }
+
+       if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling,
+                               exec->args->width, exec->args->height, cpp)) {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec)
+{
+       struct vc4_rcl_setup setup = {0};
+       struct drm_vc4_submit_cl *args = exec->args;
+       bool has_bin = args->bin_cl_size != 0;
+       int ret;
+
+       if (args->min_x_tile > args->max_x_tile ||
+           args->min_y_tile > args->max_y_tile) {
+               DRM_ERROR("Bad render tile set (%d,%d)-(%d,%d)\n",
+                         args->min_x_tile, args->min_y_tile,
+                         args->max_x_tile, args->max_y_tile);
+               return -EINVAL;
+       }
+
+       if (has_bin &&
+           (args->max_x_tile > exec->bin_tiles_x ||
+            args->max_y_tile > exec->bin_tiles_y)) {
+               DRM_ERROR("Render tiles (%d,%d) outside of bin config "
+                         "(%d,%d)\n",
+                         args->max_x_tile, args->max_y_tile,
+                         exec->bin_tiles_x, exec->bin_tiles_y);
+               return -EINVAL;
+       }
+
+       ret = vc4_rcl_render_config_surface_setup(exec, &setup,
+                                                 &setup.color_write,
+                                                 &args->color_write);
+       if (ret)
+               return ret;
+
+       ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read);
+       if (ret)
+               return ret;
+
+       ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read);
+       if (ret)
+               return ret;
+
+       ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write);
+       if (ret)
+               return ret;
+
+       ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_color_write,
+                                        &args->msaa_color_write);
+       if (ret)
+               return ret;
+
+       ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_zs_write,
+                                        &args->msaa_zs_write);
+       if (ret)
+               return ret;
+
+       /* We shouldn't even have the job submitted to us if there's no
+        * surface to write out.
+        */
+       if (!setup.color_write && !setup.zs_write &&
+           !setup.msaa_color_write && !setup.msaa_zs_write) {
+               DRM_ERROR("RCL requires color or Z/S write\n");
+               return -EINVAL;
+       }
+
+       return vc4_create_rcl_bo(dev, exec, &setup);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_trace.h b/drivers/gpu/drm/vc4/vc4_trace.h
new file mode 100644 (file)
index 0000000..ad7b1ea
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#if !defined(_VC4_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _VC4_TRACE_H_
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM vc4
+#define TRACE_INCLUDE_FILE vc4_trace
+
+TRACE_EVENT(vc4_wait_for_seqno_begin,
+           TP_PROTO(struct drm_device *dev, uint64_t seqno, uint64_t timeout),
+           TP_ARGS(dev, seqno, timeout),
+
+           TP_STRUCT__entry(
+                            __field(u32, dev)
+                            __field(u64, seqno)
+                            __field(u64, timeout)
+                            ),
+
+           TP_fast_assign(
+                          __entry->dev = dev->primary->index;
+                          __entry->seqno = seqno;
+                          __entry->timeout = timeout;
+                          ),
+
+           TP_printk("dev=%u, seqno=%llu, timeout=%llu",
+                     __entry->dev, __entry->seqno, __entry->timeout)
+);
+
+TRACE_EVENT(vc4_wait_for_seqno_end,
+           TP_PROTO(struct drm_device *dev, uint64_t seqno),
+           TP_ARGS(dev, seqno),
+
+           TP_STRUCT__entry(
+                            __field(u32, dev)
+                            __field(u64, seqno)
+                            ),
+
+           TP_fast_assign(
+                          __entry->dev = dev->primary->index;
+                          __entry->seqno = seqno;
+                          ),
+
+           TP_printk("dev=%u, seqno=%llu",
+                     __entry->dev, __entry->seqno)
+);
+
+#endif /* _VC4_TRACE_H_ */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/drm/vc4/vc4_trace_points.c b/drivers/gpu/drm/vc4/vc4_trace_points.c
new file mode 100644 (file)
index 0000000..e6278f2
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "vc4_drv.h"
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "vc4_trace.h"
+#endif
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
new file mode 100644 (file)
index 0000000..424d515
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "linux/component.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#ifdef CONFIG_DEBUG_FS
+#define REGDEF(reg) { reg, #reg }
+static const struct {
+       uint32_t reg;
+       const char *name;
+} vc4_reg_defs[] = {
+       REGDEF(V3D_IDENT0),
+       REGDEF(V3D_IDENT1),
+       REGDEF(V3D_IDENT2),
+       REGDEF(V3D_SCRATCH),
+       REGDEF(V3D_L2CACTL),
+       REGDEF(V3D_SLCACTL),
+       REGDEF(V3D_INTCTL),
+       REGDEF(V3D_INTENA),
+       REGDEF(V3D_INTDIS),
+       REGDEF(V3D_CT0CS),
+       REGDEF(V3D_CT1CS),
+       REGDEF(V3D_CT0EA),
+       REGDEF(V3D_CT1EA),
+       REGDEF(V3D_CT0CA),
+       REGDEF(V3D_CT1CA),
+       REGDEF(V3D_CT00RA0),
+       REGDEF(V3D_CT01RA0),
+       REGDEF(V3D_CT0LC),
+       REGDEF(V3D_CT1LC),
+       REGDEF(V3D_CT0PC),
+       REGDEF(V3D_CT1PC),
+       REGDEF(V3D_PCS),
+       REGDEF(V3D_BFC),
+       REGDEF(V3D_RFC),
+       REGDEF(V3D_BPCA),
+       REGDEF(V3D_BPCS),
+       REGDEF(V3D_BPOA),
+       REGDEF(V3D_BPOS),
+       REGDEF(V3D_BXCF),
+       REGDEF(V3D_SQRSV0),
+       REGDEF(V3D_SQRSV1),
+       REGDEF(V3D_SQCNTL),
+       REGDEF(V3D_SRQPC),
+       REGDEF(V3D_SRQUA),
+       REGDEF(V3D_SRQUL),
+       REGDEF(V3D_SRQCS),
+       REGDEF(V3D_VPACNTL),
+       REGDEF(V3D_VPMBASE),
+       REGDEF(V3D_PCTRC),
+       REGDEF(V3D_PCTRE),
+       REGDEF(V3D_PCTR0),
+       REGDEF(V3D_PCTRS0),
+       REGDEF(V3D_PCTR1),
+       REGDEF(V3D_PCTRS1),
+       REGDEF(V3D_PCTR2),
+       REGDEF(V3D_PCTRS2),
+       REGDEF(V3D_PCTR3),
+       REGDEF(V3D_PCTRS3),
+       REGDEF(V3D_PCTR4),
+       REGDEF(V3D_PCTRS4),
+       REGDEF(V3D_PCTR5),
+       REGDEF(V3D_PCTRS5),
+       REGDEF(V3D_PCTR6),
+       REGDEF(V3D_PCTRS6),
+       REGDEF(V3D_PCTR7),
+       REGDEF(V3D_PCTRS7),
+       REGDEF(V3D_PCTR8),
+       REGDEF(V3D_PCTRS8),
+       REGDEF(V3D_PCTR9),
+       REGDEF(V3D_PCTRS9),
+       REGDEF(V3D_PCTR10),
+       REGDEF(V3D_PCTRS10),
+       REGDEF(V3D_PCTR11),
+       REGDEF(V3D_PCTRS11),
+       REGDEF(V3D_PCTR12),
+       REGDEF(V3D_PCTRS12),
+       REGDEF(V3D_PCTR13),
+       REGDEF(V3D_PCTRS13),
+       REGDEF(V3D_PCTR14),
+       REGDEF(V3D_PCTRS14),
+       REGDEF(V3D_PCTR15),
+       REGDEF(V3D_PCTRS15),
+       REGDEF(V3D_DBGE),
+       REGDEF(V3D_FDBGO),
+       REGDEF(V3D_FDBGB),
+       REGDEF(V3D_FDBGR),
+       REGDEF(V3D_FDBGS),
+       REGDEF(V3D_ERRSTAT),
+};
+
+int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *)m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(vc4_reg_defs); i++) {
+               seq_printf(m, "%s (0x%04x): 0x%08x\n",
+                          vc4_reg_defs[i].name, vc4_reg_defs[i].reg,
+                          V3D_READ(vc4_reg_defs[i].reg));
+       }
+
+       return 0;
+}
+
+int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *)m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       uint32_t ident1 = V3D_READ(V3D_IDENT1);
+       uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC);
+       uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS);
+       uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS);
+
+       seq_printf(m, "Revision:   %d\n",
+                  VC4_GET_FIELD(ident1, V3D_IDENT1_REV));
+       seq_printf(m, "Slices:     %d\n", nslc);
+       seq_printf(m, "TMUs:       %d\n", nslc * tups);
+       seq_printf(m, "QPUs:       %d\n", nslc * qups);
+       seq_printf(m, "Semaphores: %d\n",
+                  VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM));
+
+       return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+/*
+ * Asks the firmware to turn on power to the V3D engine.
+ *
+ * This may be doable with just the clocks interface, though this
+ * packet does some other register setup from the firmware, too.
+ */
+int
+vc4_v3d_set_power(struct vc4_dev *vc4, bool on)
+{
+       if (on)
+               return pm_generic_poweroff(&vc4->v3d->pdev->dev);
+       else
+               return pm_generic_resume(&vc4->v3d->pdev->dev);
+}
+
+static void vc4_v3d_init_hw(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       /* Take all the memory that would have been reserved for user
+        * QPU programs, since we don't have an interface for running
+        * them, anyway.
+        */
+       V3D_WRITE(V3D_VPMBASE, 0);
+}
+
+static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm = dev_get_drvdata(master);
+       struct vc4_dev *vc4 = to_vc4_dev(drm);
+       struct vc4_v3d *v3d = NULL;
+       int ret;
+
+       v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL);
+       if (!v3d)
+               return -ENOMEM;
+
+       v3d->pdev = pdev;
+
+       v3d->regs = vc4_ioremap_regs(pdev, 0);
+       if (IS_ERR(v3d->regs))
+               return PTR_ERR(v3d->regs);
+
+       vc4->v3d = v3d;
+
+       if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
+               DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
+                         V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0);
+               return -EINVAL;
+       }
+
+       /* Reset the binner overflow address/size at setup, to be sure
+        * we don't reuse an old one.
+        */
+       V3D_WRITE(V3D_BPOA, 0);
+       V3D_WRITE(V3D_BPOS, 0);
+
+       vc4_v3d_init_hw(drm);
+
+       ret = drm_irq_install(drm, platform_get_irq(pdev, 0));
+       if (ret) {
+               DRM_ERROR("Failed to install IRQ handler\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void vc4_v3d_unbind(struct device *dev, struct device *master,
+                          void *data)
+{
+       struct drm_device *drm = dev_get_drvdata(master);
+       struct vc4_dev *vc4 = to_vc4_dev(drm);
+
+       drm_irq_uninstall(drm);
+
+       /* Disable the binner's overflow memory address, so the next
+        * driver probe (if any) doesn't try to reuse our old
+        * allocation.
+        */
+       V3D_WRITE(V3D_BPOA, 0);
+       V3D_WRITE(V3D_BPOS, 0);
+
+       vc4->v3d = NULL;
+}
+
+static const struct component_ops vc4_v3d_ops = {
+       .bind   = vc4_v3d_bind,
+       .unbind = vc4_v3d_unbind,
+};
+
+static int vc4_v3d_dev_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &vc4_v3d_ops);
+}
+
+static int vc4_v3d_dev_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &vc4_v3d_ops);
+       return 0;
+}
+
+static const struct of_device_id vc4_v3d_dt_match[] = {
+       { .compatible = "brcm,vc4-v3d" },
+       {}
+};
+
+struct platform_driver vc4_v3d_driver = {
+       .probe = vc4_v3d_dev_probe,
+       .remove = vc4_v3d_dev_remove,
+       .driver = {
+               .name = "vc4_v3d",
+               .of_match_table = vc4_v3d_dt_match,
+       },
+};
diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c
new file mode 100644 (file)
index 0000000..0fb5b99
--- /dev/null
@@ -0,0 +1,900 @@
+/*
+ * Copyright © 2014 Broadcom
+ *
+ * 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.
+ */
+
+/**
+ * Command list validator for VC4.
+ *
+ * The VC4 has no IOMMU between it and system memory.  So, a user with
+ * access to execute command lists could escalate privilege by
+ * overwriting system memory (drawing to it as a framebuffer) or
+ * reading system memory it shouldn't (reading it as a texture, or
+ * uniform data, or vertex data).
+ *
+ * This validates command lists to ensure that all accesses are within
+ * the bounds of the GEM objects referenced.  It explicitly whitelists
+ * packets, and looks at the offsets in any address fields to make
+ * sure they're constrained within the BOs they reference.
+ *
+ * Note that because of the validation that's happening anyway, this
+ * is where GEM relocation processing happens.
+ */
+
+#include "uapi/drm/vc4_drm.h"
+#include "vc4_drv.h"
+#include "vc4_packet.h"
+
+#define VALIDATE_ARGS \
+       struct vc4_exec_info *exec,                     \
+       void *validated,                                \
+       void *untrusted
+
+/** Return the width in pixels of a 64-byte microtile. */
+static uint32_t
+utile_width(int cpp)
+{
+       switch (cpp) {
+       case 1:
+       case 2:
+               return 8;
+       case 4:
+               return 4;
+       case 8:
+               return 2;
+       default:
+               DRM_ERROR("unknown cpp: %d\n", cpp);
+               return 1;
+       }
+}
+
+/** Return the height in pixels of a 64-byte microtile. */
+static uint32_t
+utile_height(int cpp)
+{
+       switch (cpp) {
+       case 1:
+               return 8;
+       case 2:
+       case 4:
+       case 8:
+               return 4;
+       default:
+               DRM_ERROR("unknown cpp: %d\n", cpp);
+               return 1;
+       }
+}
+
+/**
+ * The texture unit decides what tiling format a particular miplevel is using
+ * this function, so we lay out our miptrees accordingly.
+ */
+static bool
+size_is_lt(uint32_t width, uint32_t height, int cpp)
+{
+       return (width <= 4 * utile_width(cpp) ||
+               height <= 4 * utile_height(cpp));
+}
+
+struct drm_gem_cma_object *
+vc4_use_bo(struct vc4_exec_info *exec, uint32_t hindex)
+{
+       struct drm_gem_cma_object *obj;
+       struct vc4_bo *bo;
+
+       if (hindex >= exec->bo_count) {
+               DRM_ERROR("BO index %d greater than BO count %d\n",
+                         hindex, exec->bo_count);
+               return NULL;
+       }
+       obj = exec->bo[hindex];
+       bo = to_vc4_bo(&obj->base);
+
+       if (bo->validated_shader) {
+               DRM_ERROR("Trying to use shader BO as something other than "
+                         "a shader\n");
+               return NULL;
+       }
+
+       return obj;
+}
+
+static struct drm_gem_cma_object *
+vc4_use_handle(struct vc4_exec_info *exec, uint32_t gem_handles_packet_index)
+{
+       return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index]);
+}
+
+static bool
+validate_bin_pos(struct vc4_exec_info *exec, void *untrusted, uint32_t pos)
+{
+       /* Note that the untrusted pointer passed to these functions is
+        * incremented past the packet byte.
+        */
+       return (untrusted - 1 == exec->bin_u + pos);
+}
+
+static uint32_t
+gl_shader_rec_size(uint32_t pointer_bits)
+{
+       uint32_t attribute_count = pointer_bits & 7;
+       bool extended = pointer_bits & 8;
+
+       if (attribute_count == 0)
+               attribute_count = 8;
+
+       if (extended)
+               return 100 + attribute_count * 4;
+       else
+               return 36 + attribute_count * 8;
+}
+
+bool
+vc4_check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo,
+                  uint32_t offset, uint8_t tiling_format,
+                  uint32_t width, uint32_t height, uint8_t cpp)
+{
+       uint32_t aligned_width, aligned_height, stride, size;
+       uint32_t utile_w = utile_width(cpp);
+       uint32_t utile_h = utile_height(cpp);
+
+       /* The shaded vertex format stores signed 12.4 fixed point
+        * (-2048,2047) offsets from the viewport center, so we should
+        * never have a render target larger than 4096.  The texture
+        * unit can only sample from 2048x2048, so it's even more
+        * restricted.  This lets us avoid worrying about overflow in
+        * our math.
+        */
+       if (width > 4096 || height > 4096) {
+               DRM_ERROR("Surface dimesions (%d,%d) too large", width, height);
+               return false;
+       }
+
+       switch (tiling_format) {
+       case VC4_TILING_FORMAT_LINEAR:
+               aligned_width = round_up(width, utile_w);
+               aligned_height = height;
+               break;
+       case VC4_TILING_FORMAT_T:
+               aligned_width = round_up(width, utile_w * 8);
+               aligned_height = round_up(height, utile_h * 8);
+               break;
+       case VC4_TILING_FORMAT_LT:
+               aligned_width = round_up(width, utile_w);
+               aligned_height = round_up(height, utile_h);
+               break;
+       default:
+               DRM_ERROR("buffer tiling %d unsupported\n", tiling_format);
+               return false;
+       }
+
+       stride = aligned_width * cpp;
+       size = stride * aligned_height;
+
+       if (size + offset < size ||
+           size + offset > fbo->base.size) {
+               DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %zd)\n",
+                         width, height,
+                         aligned_width, aligned_height,
+                         size, offset, fbo->base.size);
+               return false;
+       }
+
+       return true;
+}
+
+static int
+validate_flush(VALIDATE_ARGS)
+{
+       if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 1)) {
+               DRM_ERROR("Bin CL must end with VC4_PACKET_FLUSH\n");
+               return -EINVAL;
+       }
+       exec->found_flush = true;
+
+       return 0;
+}
+
+static int
+validate_start_tile_binning(VALIDATE_ARGS)
+{
+       if (exec->found_start_tile_binning_packet) {
+               DRM_ERROR("Duplicate VC4_PACKET_START_TILE_BINNING\n");
+               return -EINVAL;
+       }
+       exec->found_start_tile_binning_packet = true;
+
+       if (!exec->found_tile_binning_mode_config_packet) {
+               DRM_ERROR("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+validate_increment_semaphore(VALIDATE_ARGS)
+{
+       if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 2)) {
+               DRM_ERROR("Bin CL must end with "
+                         "VC4_PACKET_INCREMENT_SEMAPHORE\n");
+               return -EINVAL;
+       }
+       exec->found_increment_semaphore_packet = true;
+
+       return 0;
+}
+
+static int
+validate_indexed_prim_list(VALIDATE_ARGS)
+{
+       struct drm_gem_cma_object *ib;
+       uint32_t length = *(uint32_t *)(untrusted + 1);
+       uint32_t offset = *(uint32_t *)(untrusted + 5);
+       uint32_t max_index = *(uint32_t *)(untrusted + 9);
+       uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1;
+       struct vc4_shader_state *shader_state;
+
+       /* Check overflow condition */
+       if (exec->shader_state_count == 0) {
+               DRM_ERROR("shader state must precede primitives\n");
+               return -EINVAL;
+       }
+       shader_state = &exec->shader_state[exec->shader_state_count - 1];
+
+       if (max_index > shader_state->max_index)
+               shader_state->max_index = max_index;
+
+       ib = vc4_use_handle(exec, 0);
+       if (!ib)
+               return -EINVAL;
+
+       if (offset > ib->base.size ||
+           (ib->base.size - offset) / index_size < length) {
+               DRM_ERROR("IB access overflow (%d + %d*%d > %zd)\n",
+                         offset, length, index_size, ib->base.size);
+               return -EINVAL;
+       }
+
+       *(uint32_t *)(validated + 5) = ib->paddr + offset;
+
+       return 0;
+}
+
+static int
+validate_gl_array_primitive(VALIDATE_ARGS)
+{
+       uint32_t length = *(uint32_t *)(untrusted + 1);
+       uint32_t base_index = *(uint32_t *)(untrusted + 5);
+       uint32_t max_index;
+       struct vc4_shader_state *shader_state;
+
+       /* Check overflow condition */
+       if (exec->shader_state_count == 0) {
+               DRM_ERROR("shader state must precede primitives\n");
+               return -EINVAL;
+       }
+       shader_state = &exec->shader_state[exec->shader_state_count - 1];
+
+       if (length + base_index < length) {
+               DRM_ERROR("primitive vertex count overflow\n");
+               return -EINVAL;
+       }
+       max_index = length + base_index - 1;
+
+       if (max_index > shader_state->max_index)
+               shader_state->max_index = max_index;
+
+       return 0;
+}
+
+static int
+validate_gl_shader_state(VALIDATE_ARGS)
+{
+       uint32_t i = exec->shader_state_count++;
+
+       if (i >= exec->shader_state_size) {
+               DRM_ERROR("More requests for shader states than declared\n");
+               return -EINVAL;
+       }
+
+       exec->shader_state[i].addr = *(uint32_t *)untrusted;
+       exec->shader_state[i].max_index = 0;
+
+       if (exec->shader_state[i].addr & ~0xf) {
+               DRM_ERROR("high bits set in GL shader rec reference\n");
+               return -EINVAL;
+       }
+
+       *(uint32_t *)validated = (exec->shader_rec_p +
+                                 exec->shader_state[i].addr);
+
+       exec->shader_rec_p +=
+               roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16);
+
+       return 0;
+}
+
+static int
+validate_tile_binning_config(VALIDATE_ARGS)
+{
+       struct drm_device *dev = exec->exec_bo->base.dev;
+       struct vc4_bo *tile_bo;
+       uint8_t flags;
+       uint32_t tile_state_size, tile_alloc_size;
+       uint32_t tile_count;
+
+       if (exec->found_tile_binning_mode_config_packet) {
+               DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
+               return -EINVAL;
+       }
+       exec->found_tile_binning_mode_config_packet = true;
+
+       exec->bin_tiles_x = *(uint8_t *)(untrusted + 12);
+       exec->bin_tiles_y = *(uint8_t *)(untrusted + 13);
+       tile_count = exec->bin_tiles_x * exec->bin_tiles_y;
+       flags = *(uint8_t *)(untrusted + 14);
+
+       if (exec->bin_tiles_x == 0 ||
+           exec->bin_tiles_y == 0) {
+               DRM_ERROR("Tile binning config of %dx%d too small\n",
+                         exec->bin_tiles_x, exec->bin_tiles_y);
+               return -EINVAL;
+       }
+
+       if (flags & (VC4_BIN_CONFIG_DB_NON_MS |
+                    VC4_BIN_CONFIG_TILE_BUFFER_64BIT)) {
+               DRM_ERROR("unsupported binning config flags 0x%02x\n", flags);
+               return -EINVAL;
+       }
+
+       /* The tile state data array is 48 bytes per tile, and we put it at
+        * the start of a BO containing both it and the tile alloc.
+        */
+       tile_state_size = 48 * tile_count;
+
+       /* Since the tile alloc array will follow us, align. */
+       exec->tile_alloc_offset = roundup(tile_state_size, 4096);
+
+       *(uint8_t *)(validated + 14) =
+               ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
+                           VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK)) |
+                VC4_BIN_CONFIG_AUTO_INIT_TSDA |
+                VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32,
+                              VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE) |
+                VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128,
+                              VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE));
+
+       /* Initial block size. */
+       tile_alloc_size = 32 * tile_count;
+
+       /*
+        * The initial allocation gets rounded to the next 256 bytes before
+        * the hardware starts fulfilling further allocations.
+        */
+       tile_alloc_size = roundup(tile_alloc_size, 256);
+
+       /* Add space for the extra allocations.  This is what gets used first,
+        * before overflow memory.  It must have at least 4096 bytes, but we
+        * want to avoid overflow memory usage if possible.
+        */
+       tile_alloc_size += 1024 * 1024;
+
+       tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size,
+                               true);
+       exec->tile_bo = &tile_bo->base;
+       if (!exec->tile_bo)
+               return -ENOMEM;
+       list_add_tail(&tile_bo->unref_head, &exec->unref_list);
+
+       /* tile alloc address. */
+       *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr +
+                                       exec->tile_alloc_offset);
+       /* tile alloc size. */
+       *(uint32_t *)(validated + 4) = tile_alloc_size;
+       /* tile state address. */
+       *(uint32_t *)(validated + 8) = exec->tile_bo->paddr;
+
+       return 0;
+}
+
+static int
+validate_gem_handles(VALIDATE_ARGS)
+{
+       memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index));
+       return 0;
+}
+
+#define VC4_DEFINE_PACKET(packet, func) \
+       [packet] = { packet ## _SIZE, #packet, func }
+
+static const struct cmd_info {
+       uint16_t len;
+       const char *name;
+       int (*func)(struct vc4_exec_info *exec, void *validated,
+                   void *untrusted);
+} cmd_info[] = {
+       VC4_DEFINE_PACKET(VC4_PACKET_HALT, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_NOP, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, validate_flush),
+       VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING,
+                         validate_start_tile_binning),
+       VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE,
+                         validate_increment_semaphore),
+
+       VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE,
+                         validate_indexed_prim_list),
+       VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE,
+                         validate_gl_array_primitive),
+
+       VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, NULL),
+
+       VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, validate_gl_shader_state),
+
+       VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, NULL),
+       VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, NULL),
+       /* Note: The docs say this was also 105, but it was 106 in the
+        * initial userland code drop.
+        */
+       VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, NULL),
+
+       VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG,
+                         validate_tile_binning_config),
+
+       VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, validate_gem_handles),
+};
+
+int
+vc4_validate_bin_cl(struct drm_device *dev,
+                   void *validated,
+                   void *unvalidated,
+                   struct vc4_exec_info *exec)
+{
+       uint32_t len = exec->args->bin_cl_size;
+       uint32_t dst_offset = 0;
+       uint32_t src_offset = 0;
+
+       while (src_offset < len) {
+               void *dst_pkt = validated + dst_offset;
+               void *src_pkt = unvalidated + src_offset;
+               u8 cmd = *(uint8_t *)src_pkt;
+               const struct cmd_info *info;
+
+               if (cmd >= ARRAY_SIZE(cmd_info)) {
+                       DRM_ERROR("0x%08x: packet %d out of bounds\n",
+                                 src_offset, cmd);
+                       return -EINVAL;
+               }
+
+               info = &cmd_info[cmd];
+               if (!info->name) {
+                       DRM_ERROR("0x%08x: packet %d invalid\n",
+                                 src_offset, cmd);
+                       return -EINVAL;
+               }
+
+               if (src_offset + info->len > len) {
+                       DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x "
+                                 "exceeds bounds (0x%08x)\n",
+                                 src_offset, cmd, info->name, info->len,
+                                 src_offset + len);
+                       return -EINVAL;
+               }
+
+               if (cmd != VC4_PACKET_GEM_HANDLES)
+                       memcpy(dst_pkt, src_pkt, info->len);
+
+               if (info->func && info->func(exec,
+                                            dst_pkt + 1,
+                                            src_pkt + 1)) {
+                       DRM_ERROR("0x%08x: packet %d (%s) failed to validate\n",
+                                 src_offset, cmd, info->name);
+                       return -EINVAL;
+               }
+
+               src_offset += info->len;
+               /* GEM handle loading doesn't produce HW packets. */
+               if (cmd != VC4_PACKET_GEM_HANDLES)
+                       dst_offset += info->len;
+
+               /* When the CL hits halt, it'll stop reading anything else. */
+               if (cmd == VC4_PACKET_HALT)
+                       break;
+       }
+
+       exec->ct0ea = exec->ct0ca + dst_offset;
+
+       if (!exec->found_start_tile_binning_packet) {
+               DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n");
+               return -EINVAL;
+       }
+
+       /* The bin CL must be ended with INCREMENT_SEMAPHORE and FLUSH.  The
+        * semaphore is used to trigger the render CL to start up, and the
+        * FLUSH is what caps the bin lists with
+        * VC4_PACKET_RETURN_FROM_SUB_LIST (so they jump back to the main
+        * render CL when they get called to) and actually triggers the queued
+        * semaphore increment.
+        */
+       if (!exec->found_increment_semaphore_packet || !exec->found_flush) {
+               DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE + "
+                         "VC4_PACKET_FLUSH\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static bool
+reloc_tex(struct vc4_exec_info *exec,
+         void *uniform_data_u,
+         struct vc4_texture_sample_info *sample,
+         uint32_t texture_handle_index)
+
+{
+       struct drm_gem_cma_object *tex;
+       uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]);
+       uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]);
+       uint32_t p2 = (sample->p_offset[2] != ~0 ?
+                      *(uint32_t *)(uniform_data_u + sample->p_offset[2]) : 0);
+       uint32_t p3 = (sample->p_offset[3] != ~0 ?
+                      *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0);
+       uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0];
+       uint32_t offset = p0 & VC4_TEX_P0_OFFSET_MASK;
+       uint32_t miplevels = VC4_GET_FIELD(p0, VC4_TEX_P0_MIPLVLS);
+       uint32_t width = VC4_GET_FIELD(p1, VC4_TEX_P1_WIDTH);
+       uint32_t height = VC4_GET_FIELD(p1, VC4_TEX_P1_HEIGHT);
+       uint32_t cpp, tiling_format, utile_w, utile_h;
+       uint32_t i;
+       uint32_t cube_map_stride = 0;
+       enum vc4_texture_data_type type;
+
+       tex = vc4_use_bo(exec, texture_handle_index);
+       if (!tex)
+               return false;
+
+       if (sample->is_direct) {
+               uint32_t remaining_size = tex->base.size - p0;
+
+               if (p0 > tex->base.size - 4) {
+                       DRM_ERROR("UBO offset greater than UBO size\n");
+                       goto fail;
+               }
+               if (p1 > remaining_size - 4) {
+                       DRM_ERROR("UBO clamp would allow reads "
+                                 "outside of UBO\n");
+                       goto fail;
+               }
+               *validated_p0 = tex->paddr + p0;
+               return true;
+       }
+
+       if (width == 0)
+               width = 2048;
+       if (height == 0)
+               height = 2048;
+
+       if (p0 & VC4_TEX_P0_CMMODE_MASK) {
+               if (VC4_GET_FIELD(p2, VC4_TEX_P2_PTYPE) ==
+                   VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE)
+                       cube_map_stride = p2 & VC4_TEX_P2_CMST_MASK;
+               if (VC4_GET_FIELD(p3, VC4_TEX_P2_PTYPE) ==
+                   VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) {
+                       if (cube_map_stride) {
+                               DRM_ERROR("Cube map stride set twice\n");
+                               goto fail;
+                       }
+
+                       cube_map_stride = p3 & VC4_TEX_P2_CMST_MASK;
+               }
+               if (!cube_map_stride) {
+                       DRM_ERROR("Cube map stride not set\n");
+                       goto fail;
+               }
+       }
+
+       type = (VC4_GET_FIELD(p0, VC4_TEX_P0_TYPE) |
+               (VC4_GET_FIELD(p1, VC4_TEX_P1_TYPE4) << 4));
+
+       switch (type) {
+       case VC4_TEXTURE_TYPE_RGBA8888:
+       case VC4_TEXTURE_TYPE_RGBX8888:
+       case VC4_TEXTURE_TYPE_RGBA32R:
+               cpp = 4;
+               break;
+       case VC4_TEXTURE_TYPE_RGBA4444:
+       case VC4_TEXTURE_TYPE_RGBA5551:
+       case VC4_TEXTURE_TYPE_RGB565:
+       case VC4_TEXTURE_TYPE_LUMALPHA:
+       case VC4_TEXTURE_TYPE_S16F:
+       case VC4_TEXTURE_TYPE_S16:
+               cpp = 2;
+               break;
+       case VC4_TEXTURE_TYPE_LUMINANCE:
+       case VC4_TEXTURE_TYPE_ALPHA:
+       case VC4_TEXTURE_TYPE_S8:
+               cpp = 1;
+               break;
+       case VC4_TEXTURE_TYPE_ETC1:
+       case VC4_TEXTURE_TYPE_BW1:
+       case VC4_TEXTURE_TYPE_A4:
+       case VC4_TEXTURE_TYPE_A1:
+       case VC4_TEXTURE_TYPE_RGBA64:
+       case VC4_TEXTURE_TYPE_YUV422R:
+       default:
+               DRM_ERROR("Texture format %d unsupported\n", type);
+               goto fail;
+       }
+       utile_w = utile_width(cpp);
+       utile_h = utile_height(cpp);
+
+       if (type == VC4_TEXTURE_TYPE_RGBA32R) {
+               tiling_format = VC4_TILING_FORMAT_LINEAR;
+       } else {
+               if (size_is_lt(width, height, cpp))
+                       tiling_format = VC4_TILING_FORMAT_LT;
+               else
+                       tiling_format = VC4_TILING_FORMAT_T;
+       }
+
+       if (!vc4_check_tex_size(exec, tex, offset + cube_map_stride * 5,
+                               tiling_format, width, height, cpp)) {
+               goto fail;
+       }
+
+       /* The mipmap levels are stored before the base of the texture.  Make
+        * sure there is actually space in the BO.
+        */
+       for (i = 1; i <= miplevels; i++) {
+               uint32_t level_width = max(width >> i, 1u);
+               uint32_t level_height = max(height >> i, 1u);
+               uint32_t aligned_width, aligned_height;
+               uint32_t level_size;
+
+               /* Once the levels get small enough, they drop from T to LT. */
+               if (tiling_format == VC4_TILING_FORMAT_T &&
+                   size_is_lt(level_width, level_height, cpp)) {
+                       tiling_format = VC4_TILING_FORMAT_LT;
+               }
+
+               switch (tiling_format) {
+               case VC4_TILING_FORMAT_T:
+                       aligned_width = round_up(level_width, utile_w * 8);
+                       aligned_height = round_up(level_height, utile_h * 8);
+                       break;
+               case VC4_TILING_FORMAT_LT:
+                       aligned_width = round_up(level_width, utile_w);
+                       aligned_height = round_up(level_height, utile_h);
+                       break;
+               default:
+                       aligned_width = round_up(level_width, utile_w);
+                       aligned_height = level_height;
+                       break;
+               }
+
+               level_size = aligned_width * cpp * aligned_height;
+
+               if (offset < level_size) {
+                       DRM_ERROR("Level %d (%dx%d -> %dx%d) size %db "
+                                 "overflowed buffer bounds (offset %d)\n",
+                                 i, level_width, level_height,
+                                 aligned_width, aligned_height,
+                                 level_size, offset);
+                       goto fail;
+               }
+
+               offset -= level_size;
+       }
+
+       *validated_p0 = tex->paddr + p0;
+
+       return true;
+ fail:
+       DRM_INFO("Texture p0 at %d: 0x%08x\n", sample->p_offset[0], p0);
+       DRM_INFO("Texture p1 at %d: 0x%08x\n", sample->p_offset[1], p1);
+       DRM_INFO("Texture p2 at %d: 0x%08x\n", sample->p_offset[2], p2);
+       DRM_INFO("Texture p3 at %d: 0x%08x\n", sample->p_offset[3], p3);
+       return false;
+}
+
+static int
+validate_gl_shader_rec(struct drm_device *dev,
+                      struct vc4_exec_info *exec,
+                      struct vc4_shader_state *state)
+{
+       uint32_t *src_handles;
+       void *pkt_u, *pkt_v;
+       static const uint32_t shader_reloc_offsets[] = {
+               4, /* fs */
+               16, /* vs */
+               28, /* cs */
+       };
+       uint32_t shader_reloc_count = ARRAY_SIZE(shader_reloc_offsets);
+       struct drm_gem_cma_object *bo[shader_reloc_count + 8];
+       uint32_t nr_attributes, nr_relocs, packet_size;
+       int i;
+
+       nr_attributes = state->addr & 0x7;
+       if (nr_attributes == 0)
+               nr_attributes = 8;
+       packet_size = gl_shader_rec_size(state->addr);
+
+       nr_relocs = ARRAY_SIZE(shader_reloc_offsets) + nr_attributes;
+       if (nr_relocs * 4 > exec->shader_rec_size) {
+               DRM_ERROR("overflowed shader recs reading %d handles "
+                         "from %d bytes left\n",
+                         nr_relocs, exec->shader_rec_size);
+               return -EINVAL;
+       }
+       src_handles = exec->shader_rec_u;
+       exec->shader_rec_u += nr_relocs * 4;
+       exec->shader_rec_size -= nr_relocs * 4;
+
+       if (packet_size > exec->shader_rec_size) {
+               DRM_ERROR("overflowed shader recs copying %db packet "
+                         "from %d bytes left\n",
+                         packet_size, exec->shader_rec_size);
+               return -EINVAL;
+       }
+       pkt_u = exec->shader_rec_u;
+       pkt_v = exec->shader_rec_v;
+       memcpy(pkt_v, pkt_u, packet_size);
+       exec->shader_rec_u += packet_size;
+       /* Shader recs have to be aligned to 16 bytes (due to the attribute
+        * flags being in the low bytes), so round the next validated shader
+        * rec address up.  This should be safe, since we've got so many
+        * relocations in a shader rec packet.
+        */
+       BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4);
+       exec->shader_rec_v += roundup(packet_size, 16);
+       exec->shader_rec_size -= packet_size;
+
+       if (!(*(uint16_t *)pkt_u & VC4_SHADER_FLAG_FS_SINGLE_THREAD)) {
+               DRM_ERROR("Multi-threaded fragment shaders not supported.\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < shader_reloc_count; i++) {
+               if (src_handles[i] > exec->bo_count) {
+                       DRM_ERROR("Shader handle %d too big\n", src_handles[i]);
+                       return -EINVAL;
+               }
+
+               bo[i] = exec->bo[src_handles[i]];
+               if (!bo[i])
+                       return -EINVAL;
+       }
+       for (i = shader_reloc_count; i < nr_relocs; i++) {
+               bo[i] = vc4_use_bo(exec, src_handles[i]);
+               if (!bo[i])
+                       return -EINVAL;
+       }
+
+       for (i = 0; i < shader_reloc_count; i++) {
+               struct vc4_validated_shader_info *validated_shader;
+               uint32_t o = shader_reloc_offsets[i];
+               uint32_t src_offset = *(uint32_t *)(pkt_u + o);
+               uint32_t *texture_handles_u;
+               void *uniform_data_u;
+               uint32_t tex;
+
+               *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset;
+
+               if (src_offset != 0) {
+                       DRM_ERROR("Shaders must be at offset 0 of "
+                                 "the BO.\n");
+                       return -EINVAL;
+               }
+
+               validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader;
+               if (!validated_shader)
+                       return -EINVAL;
+
+               if (validated_shader->uniforms_src_size >
+                   exec->uniforms_size) {
+                       DRM_ERROR("Uniforms src buffer overflow\n");
+                       return -EINVAL;
+               }
+
+               texture_handles_u = exec->uniforms_u;
+               uniform_data_u = (texture_handles_u +
+                                 validated_shader->num_texture_samples);
+
+               memcpy(exec->uniforms_v, uniform_data_u,
+                      validated_shader->uniforms_size);
+
+               for (tex = 0;
+                    tex < validated_shader->num_texture_samples;
+                    tex++) {
+                       if (!reloc_tex(exec,
+                                      uniform_data_u,
+                                      &validated_shader->texture_samples[tex],
+                                      texture_handles_u[tex])) {
+                               return -EINVAL;
+                       }
+               }
+
+               *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p;
+
+               exec->uniforms_u += validated_shader->uniforms_src_size;
+               exec->uniforms_v += validated_shader->uniforms_size;
+               exec->uniforms_p += validated_shader->uniforms_size;
+       }
+
+       for (i = 0; i < nr_attributes; i++) {
+               struct drm_gem_cma_object *vbo =
+                       bo[ARRAY_SIZE(shader_reloc_offsets) + i];
+               uint32_t o = 36 + i * 8;
+               uint32_t offset = *(uint32_t *)(pkt_u + o + 0);
+               uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1;
+               uint32_t stride = *(uint8_t *)(pkt_u + o + 5);
+               uint32_t max_index;
+
+               if (state->addr & 0x8)
+                       stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff;
+
+               if (vbo->base.size < offset ||
+                   vbo->base.size - offset < attr_size) {
+                       DRM_ERROR("BO offset overflow (%d + %d > %d)\n",
+                                 offset, attr_size, vbo->base.size);
+                       return -EINVAL;
+               }
+
+               if (stride != 0) {
+                       max_index = ((vbo->base.size - offset - attr_size) /
+                                    stride);
+                       if (state->max_index > max_index) {
+                               DRM_ERROR("primitives use index %d out of "
+                                         "supplied %d\n",
+                                         state->max_index, max_index);
+                               return -EINVAL;
+                       }
+               }
+
+               *(uint32_t *)(pkt_v + o) = vbo->paddr + offset;
+       }
+
+       return 0;
+}
+
+int
+vc4_validate_shader_recs(struct drm_device *dev,
+                        struct vc4_exec_info *exec)
+{
+       uint32_t i;
+       int ret = 0;
+
+       for (i = 0; i < exec->shader_state_count; i++) {
+               ret = validate_gl_shader_rec(dev, exec, &exec->shader_state[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
new file mode 100644 (file)
index 0000000..f67124b
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright © 2014 Broadcom
+ *
+ * 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: Shader validator for VC4.
+ *
+ * The VC4 has no IOMMU between it and system memory, so a user with
+ * access to execute shaders could escalate privilege by overwriting
+ * system memory (using the VPM write address register in the
+ * general-purpose DMA mode) or reading system memory it shouldn't
+ * (reading it as a texture, or uniform data, or vertex data).
+ *
+ * This walks over a shader BO, ensuring that its accesses are
+ * appropriately bounded, and recording how many texture accesses are
+ * made and where so that we can do relocations for them in the
+ * uniform stream.
+ */
+
+#include "vc4_drv.h"
+#include "vc4_qpu_defines.h"
+
+struct vc4_shader_validation_state {
+       struct vc4_texture_sample_info tmu_setup[2];
+       int tmu_write_count[2];
+
+       /* For registers that were last written to by a MIN instruction with
+        * one argument being a uniform, the address of the uniform.
+        * Otherwise, ~0.
+        *
+        * This is used for the validation of direct address memory reads.
+        */
+       uint32_t live_min_clamp_offsets[32 + 32 + 4];
+       bool live_max_clamp_regs[32 + 32 + 4];
+};
+
+static uint32_t
+waddr_to_live_reg_index(uint32_t waddr, bool is_b)
+{
+       if (waddr < 32) {
+               if (is_b)
+                       return 32 + waddr;
+               else
+                       return waddr;
+       } else if (waddr <= QPU_W_ACC3) {
+               return 64 + waddr - QPU_W_ACC0;
+       } else {
+               return ~0;
+       }
+}
+
+static uint32_t
+raddr_add_a_to_live_reg_index(uint64_t inst)
+{
+       uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG);
+       uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A);
+       uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A);
+       uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B);
+
+       if (add_a == QPU_MUX_A)
+               return raddr_a;
+       else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM)
+               return 32 + raddr_b;
+       else if (add_a <= QPU_MUX_R3)
+               return 64 + add_a;
+       else
+               return ~0;
+}
+
+static bool
+is_tmu_submit(uint32_t waddr)
+{
+       return (waddr == QPU_W_TMU0_S ||
+               waddr == QPU_W_TMU1_S);
+}
+
+static bool
+is_tmu_write(uint32_t waddr)
+{
+       return (waddr >= QPU_W_TMU0_S &&
+               waddr <= QPU_W_TMU1_B);
+}
+
+static bool
+record_texture_sample(struct vc4_validated_shader_info *validated_shader,
+                     struct vc4_shader_validation_state *validation_state,
+                     int tmu)
+{
+       uint32_t s = validated_shader->num_texture_samples;
+       int i;
+       struct vc4_texture_sample_info *temp_samples;
+
+       temp_samples = krealloc(validated_shader->texture_samples,
+                               (s + 1) * sizeof(*temp_samples),
+                               GFP_KERNEL);
+       if (!temp_samples)
+               return false;
+
+       memcpy(&temp_samples[s],
+              &validation_state->tmu_setup[tmu],
+              sizeof(*temp_samples));
+
+       validated_shader->num_texture_samples = s + 1;
+       validated_shader->texture_samples = temp_samples;
+
+       for (i = 0; i < 4; i++)
+               validation_state->tmu_setup[tmu].p_offset[i] = ~0;
+
+       return true;
+}
+
+static bool
+check_tmu_write(uint64_t inst,
+               struct vc4_validated_shader_info *validated_shader,
+               struct vc4_shader_validation_state *validation_state,
+               bool is_mul)
+{
+       uint32_t waddr = (is_mul ?
+                         QPU_GET_FIELD(inst, QPU_WADDR_MUL) :
+                         QPU_GET_FIELD(inst, QPU_WADDR_ADD));
+       uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A);
+       uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B);
+       int tmu = waddr > QPU_W_TMU0_B;
+       bool submit = is_tmu_submit(waddr);
+       bool is_direct = submit && validation_state->tmu_write_count[tmu] == 0;
+       uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG);
+
+       if (is_direct) {
+               uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B);
+               uint32_t clamp_reg, clamp_offset;
+
+               if (sig == QPU_SIG_SMALL_IMM) {
+                       DRM_ERROR("direct TMU read used small immediate\n");
+                       return false;
+               }
+
+               /* Make sure that this texture load is an add of the base
+                * address of the UBO to a clamped offset within the UBO.
+                */
+               if (is_mul ||
+                   QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_ADD) {
+                       DRM_ERROR("direct TMU load wasn't an add\n");
+                       return false;
+               }
+
+               /* We assert that the the clamped address is the first
+                * argument, and the UBO base address is the second argument.
+                * This is arbitrary, but simpler than supporting flipping the
+                * two either way.
+                */
+               clamp_reg = raddr_add_a_to_live_reg_index(inst);
+               if (clamp_reg == ~0) {
+                       DRM_ERROR("direct TMU load wasn't clamped\n");
+                       return false;
+               }
+
+               clamp_offset = validation_state->live_min_clamp_offsets[clamp_reg];
+               if (clamp_offset == ~0) {
+                       DRM_ERROR("direct TMU load wasn't clamped\n");
+                       return false;
+               }
+
+               /* Store the clamp value's offset in p1 (see reloc_tex() in
+                * vc4_validate.c).
+                */
+               validation_state->tmu_setup[tmu].p_offset[1] =
+                       clamp_offset;
+
+               if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) &&
+                   !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF)) {
+                       DRM_ERROR("direct TMU load didn't add to a uniform\n");
+                       return false;
+               }
+
+               validation_state->tmu_setup[tmu].is_direct = true;
+       } else {
+               if (raddr_a == QPU_R_UNIF || (sig != QPU_SIG_SMALL_IMM &&
+                                             raddr_b == QPU_R_UNIF)) {
+                       DRM_ERROR("uniform read in the same instruction as "
+                                 "texture setup.\n");
+                       return false;
+               }
+       }
+
+       if (validation_state->tmu_write_count[tmu] >= 4) {
+               DRM_ERROR("TMU%d got too many parameters before dispatch\n",
+                         tmu);
+               return false;
+       }
+       validation_state->tmu_setup[tmu].p_offset[validation_state->tmu_write_count[tmu]] =
+               validated_shader->uniforms_size;
+       validation_state->tmu_write_count[tmu]++;
+       /* Since direct uses a RADDR uniform reference, it will get counted in
+        * check_instruction_reads()
+        */
+       if (!is_direct)
+               validated_shader->uniforms_size += 4;
+
+       if (submit) {
+               if (!record_texture_sample(validated_shader,
+                                          validation_state, tmu)) {
+                       return false;
+               }
+
+               validation_state->tmu_write_count[tmu] = 0;
+       }
+
+       return true;
+}
+
+static bool
+check_reg_write(uint64_t inst,
+               struct vc4_validated_shader_info *validated_shader,
+               struct vc4_shader_validation_state *validation_state,
+               bool is_mul)
+{
+       uint32_t waddr = (is_mul ?
+                         QPU_GET_FIELD(inst, QPU_WADDR_MUL) :
+                         QPU_GET_FIELD(inst, QPU_WADDR_ADD));
+
+       switch (waddr) {
+       case QPU_W_UNIFORMS_ADDRESS:
+               /* XXX: We'll probably need to support this for reladdr, but
+                * it's definitely a security-related one.
+                */
+               DRM_ERROR("uniforms address load unsupported\n");
+               return false;
+
+       case QPU_W_TLB_COLOR_MS:
+       case QPU_W_TLB_COLOR_ALL:
+       case QPU_W_TLB_Z:
+               /* These only interact with the tile buffer, not main memory,
+                * so they're safe.
+                */
+               return true;
+
+       case QPU_W_TMU0_S:
+       case QPU_W_TMU0_T:
+       case QPU_W_TMU0_R:
+       case QPU_W_TMU0_B:
+       case QPU_W_TMU1_S:
+       case QPU_W_TMU1_T:
+       case QPU_W_TMU1_R:
+       case QPU_W_TMU1_B:
+               return check_tmu_write(inst, validated_shader, validation_state,
+                                      is_mul);
+
+       case QPU_W_HOST_INT:
+       case QPU_W_TMU_NOSWAP:
+       case QPU_W_TLB_ALPHA_MASK:
+       case QPU_W_MUTEX_RELEASE:
+               /* XXX: I haven't thought about these, so don't support them
+                * for now.
+                */
+               DRM_ERROR("Unsupported waddr %d\n", waddr);
+               return false;
+
+       case QPU_W_VPM_ADDR:
+               DRM_ERROR("General VPM DMA unsupported\n");
+               return false;
+
+       case QPU_W_VPM:
+       case QPU_W_VPMVCD_SETUP:
+               /* We allow VPM setup in general, even including VPM DMA
+                * configuration setup, because the (unsafe) DMA can only be
+                * triggered by QPU_W_VPM_ADDR writes.
+                */
+               return true;
+
+       case QPU_W_TLB_STENCIL_SETUP:
+               return true;
+       }
+
+       return true;
+}
+
+static void
+track_live_clamps(uint64_t inst,
+                 struct vc4_validated_shader_info *validated_shader,
+                 struct vc4_shader_validation_state *validation_state)
+{
+       uint32_t op_add = QPU_GET_FIELD(inst, QPU_OP_ADD);
+       uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD);
+       uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL);
+       uint32_t cond_add = QPU_GET_FIELD(inst, QPU_COND_ADD);
+       uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A);
+       uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B);
+       uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A);
+       uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B);
+       uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG);
+       bool ws = inst & QPU_WS;
+       uint32_t lri_add_a, lri_add, lri_mul;
+       bool add_a_is_min_0;
+
+       /* Check whether OP_ADD's A argumennt comes from a live MAX(x, 0),
+        * before we clear previous live state.
+        */
+       lri_add_a = raddr_add_a_to_live_reg_index(inst);
+       add_a_is_min_0 = (lri_add_a != ~0 &&
+                         validation_state->live_max_clamp_regs[lri_add_a]);
+
+       /* Clear live state for registers written by our instruction. */
+       lri_add = waddr_to_live_reg_index(waddr_add, ws);
+       lri_mul = waddr_to_live_reg_index(waddr_mul, !ws);
+       if (lri_mul != ~0) {
+               validation_state->live_max_clamp_regs[lri_mul] = false;
+               validation_state->live_min_clamp_offsets[lri_mul] = ~0;
+       }
+       if (lri_add != ~0) {
+               validation_state->live_max_clamp_regs[lri_add] = false;
+               validation_state->live_min_clamp_offsets[lri_add] = ~0;
+       } else {
+               /* Nothing further to do for live tracking, since only ADDs
+                * generate new live clamp registers.
+                */
+               return;
+       }
+
+       /* Now, handle remaining live clamp tracking for the ADD operation. */
+
+       if (cond_add != QPU_COND_ALWAYS)
+               return;
+
+       if (op_add == QPU_A_MAX) {
+               /* Track live clamps of a value to a minimum of 0 (in either
+                * arg).
+                */
+               if (sig != QPU_SIG_SMALL_IMM || raddr_b != 0 ||
+                   (add_a != QPU_MUX_B && add_b != QPU_MUX_B)) {
+                       return;
+               }
+
+               validation_state->live_max_clamp_regs[lri_add] = true;
+       } else if (op_add == QPU_A_MIN) {
+               /* Track live clamps of a value clamped to a minimum of 0 and
+                * a maximum of some uniform's offset.
+                */
+               if (!add_a_is_min_0)
+                       return;
+
+               if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) &&
+                   !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF &&
+                     sig != QPU_SIG_SMALL_IMM)) {
+                       return;
+               }
+
+               validation_state->live_min_clamp_offsets[lri_add] =
+                       validated_shader->uniforms_size;
+       }
+}
+
+static bool
+check_instruction_writes(uint64_t inst,
+                        struct vc4_validated_shader_info *validated_shader,
+                        struct vc4_shader_validation_state *validation_state)
+{
+       uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD);
+       uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL);
+       bool ok;
+
+       if (is_tmu_write(waddr_add) && is_tmu_write(waddr_mul)) {
+               DRM_ERROR("ADD and MUL both set up textures\n");
+               return false;
+       }
+
+       ok = (check_reg_write(inst, validated_shader, validation_state,
+                             false) &&
+             check_reg_write(inst, validated_shader, validation_state,
+                             true));
+
+       track_live_clamps(inst, validated_shader, validation_state);
+
+       return ok;
+}
+
+static bool
+check_instruction_reads(uint64_t inst,
+                       struct vc4_validated_shader_info *validated_shader)
+{
+       uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A);
+       uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B);
+       uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG);
+
+       if (raddr_a == QPU_R_UNIF ||
+           (raddr_b == QPU_R_UNIF && sig != QPU_SIG_SMALL_IMM)) {
+               /* This can't overflow the uint32_t, because we're reading 8
+                * bytes of instruction to increment by 4 here, so we'd
+                * already be OOM.
+                */
+               validated_shader->uniforms_size += 4;
+       }
+
+       return true;
+}
+
+struct vc4_validated_shader_info *
+vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
+{
+       bool found_shader_end = false;
+       int shader_end_ip = 0;
+       uint32_t ip, max_ip;
+       uint64_t *shader;
+       struct vc4_validated_shader_info *validated_shader;
+       struct vc4_shader_validation_state validation_state;
+       int i;
+
+       memset(&validation_state, 0, sizeof(validation_state));
+
+       for (i = 0; i < 8; i++)
+               validation_state.tmu_setup[i / 4].p_offset[i % 4] = ~0;
+       for (i = 0; i < ARRAY_SIZE(validation_state.live_min_clamp_offsets); i++)
+               validation_state.live_min_clamp_offsets[i] = ~0;
+
+       shader = shader_obj->vaddr;
+       max_ip = shader_obj->base.size / sizeof(uint64_t);
+
+       validated_shader = kcalloc(1, sizeof(*validated_shader), GFP_KERNEL);
+       if (!validated_shader)
+               return NULL;
+
+       for (ip = 0; ip < max_ip; ip++) {
+               uint64_t inst = shader[ip];
+               uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG);
+
+               switch (sig) {
+               case QPU_SIG_NONE:
+               case QPU_SIG_WAIT_FOR_SCOREBOARD:
+               case QPU_SIG_SCOREBOARD_UNLOCK:
+               case QPU_SIG_COLOR_LOAD:
+               case QPU_SIG_LOAD_TMU0:
+               case QPU_SIG_LOAD_TMU1:
+               case QPU_SIG_PROG_END:
+               case QPU_SIG_SMALL_IMM:
+                       if (!check_instruction_writes(inst, validated_shader,
+                                                     &validation_state)) {
+                               DRM_ERROR("Bad write at ip %d\n", ip);
+                               goto fail;
+                       }
+
+                       if (!check_instruction_reads(inst, validated_shader))
+                               goto fail;
+
+                       if (sig == QPU_SIG_PROG_END) {
+                               found_shader_end = true;
+                               shader_end_ip = ip;
+                       }
+
+                       break;
+
+               case QPU_SIG_LOAD_IMM:
+                       if (!check_instruction_writes(inst, validated_shader,
+                                                     &validation_state)) {
+                               DRM_ERROR("Bad LOAD_IMM write at ip %d\n", ip);
+                               goto fail;
+                       }
+                       break;
+
+               default:
+                       DRM_ERROR("Unsupported QPU signal %d at "
+                                 "instruction %d\n", sig, ip);
+                       goto fail;
+               }
+
+               /* There are two delay slots after program end is signaled
+                * that are still executed, then we're finished.
+                */
+               if (found_shader_end && ip == shader_end_ip + 2)
+                       break;
+       }
+
+       if (ip == max_ip) {
+               DRM_ERROR("shader failed to terminate before "
+                         "shader BO end at %zd\n",
+                         shader_obj->base.size);
+               goto fail;
+       }
+
+       /* Again, no chance of integer overflow here because the worst case
+        * scenario is 8 bytes of uniforms plus handles per 8-byte
+        * instruction.
+        */
+       validated_shader->uniforms_src_size =
+               (validated_shader->uniforms_size +
+                4 * validated_shader->num_texture_samples);
+
+       return validated_shader;
+
+fail:
+       if (validated_shader) {
+               kfree(validated_shader->texture_samples);
+               kfree(validated_shader);
+       }
+       return NULL;
+}
index 8e6044d..a165f03 100644 (file)
@@ -374,16 +374,6 @@ static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
        .best_encoder = virtio_gpu_best_encoder,
 };
 
-static void virtio_gpu_conn_save(struct drm_connector *connector)
-{
-       DRM_DEBUG("\n");
-}
-
-static void virtio_gpu_conn_restore(struct drm_connector *connector)
-{
-       DRM_DEBUG("\n");
-}
-
 static enum drm_connector_status virtio_gpu_conn_detect(
                        struct drm_connector *connector,
                        bool force)
@@ -409,10 +399,8 @@ static void virtio_gpu_conn_destroy(struct drm_connector *connector)
 
 static const struct drm_connector_funcs virtio_gpu_connector_funcs = {
        .dpms = drm_atomic_helper_connector_dpms,
-       .save = virtio_gpu_conn_save,
-       .restore = virtio_gpu_conn_restore,
        .detect = virtio_gpu_conn_detect,
-       .fill_modes = drm_helper_probe_single_connector_modes_nomerge,
+       .fill_modes = drm_helper_probe_single_connector_modes,
        .destroy = virtio_gpu_conn_destroy,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -443,7 +431,7 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
        if (IS_ERR(plane))
                return PTR_ERR(plane);
        drm_crtc_init_with_planes(dev, crtc, plane, NULL,
-                                 &virtio_gpu_crtc_funcs);
+                                 &virtio_gpu_crtc_funcs, NULL);
        drm_mode_crtc_set_gamma_size(crtc, 256);
        drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs);
        plane->crtc = crtc;
@@ -453,7 +441,7 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
        drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs);
 
        drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
-                        DRM_MODE_ENCODER_VIRTUAL);
+                        DRM_MODE_ENCODER_VIRTUAL, NULL);
        drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs);
        encoder->possible_crtcs = 1 << index;
 
index 4a74129..572fb35 100644 (file)
@@ -107,7 +107,7 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
                                       &virtio_gpu_plane_funcs,
                                       virtio_gpu_formats,
                                       ARRAY_SIZE(virtio_gpu_formats),
-                                      DRM_PLANE_TYPE_PRIMARY);
+                                      DRM_PLANE_TYPE_PRIMARY, NULL);
        if (ret)
                goto err_plane_init;
 
index e38db35..9394c35 100644 (file)
@@ -1331,14 +1331,6 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
        return 0;
 }
 
-void vmw_du_crtc_save(struct drm_crtc *crtc)
-{
-}
-
-void vmw_du_crtc_restore(struct drm_crtc *crtc)
-{
-}
-
 void vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
                           u16 *r, u16 *g, u16 *b,
                           uint32_t start, uint32_t size)
@@ -1360,14 +1352,6 @@ int vmw_du_connector_dpms(struct drm_connector *connector, int mode)
        return 0;
 }
 
-void vmw_du_connector_save(struct drm_connector *connector)
-{
-}
-
-void vmw_du_connector_restore(struct drm_connector *connector)
-{
-}
-
 enum drm_connector_status
 vmw_du_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -1554,7 +1538,7 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
                drm_mode_probed_add(connector, mode);
        }
 
-       drm_mode_connector_list_update(connector, true);
+       drm_mode_connector_list_update(connector);
        /* Move the prefered mode first, help apps pick the right mode. */
        drm_mode_sort(&connector->modes);
 
index bb63e4d..2aff5e5 100644 (file)
@@ -295,8 +295,6 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
 }
 
 static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
-       .save = vmw_du_crtc_save,
-       .restore = vmw_du_crtc_restore,
        .cursor_set = vmw_du_crtc_cursor_set,
        .cursor_move = vmw_du_crtc_cursor_move,
        .gamma_set = vmw_du_crtc_gamma_set,
@@ -329,8 +327,6 @@ static void vmw_ldu_connector_destroy(struct drm_connector *connector)
 
 static struct drm_connector_funcs vmw_legacy_connector_funcs = {
        .dpms = vmw_du_connector_dpms,
-       .save = vmw_du_connector_save,
-       .restore = vmw_du_connector_restore,
        .detect = vmw_du_connector_detect,
        .fill_modes = vmw_du_connector_fill_modes,
        .set_property = vmw_du_connector_set_property,
@@ -367,7 +363,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
        connector->status = vmw_du_connector_detect(connector, true);
 
        drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs,
-                        DRM_MODE_ENCODER_VIRTUAL);
+                        DRM_MODE_ENCODER_VIRTUAL, NULL);
        drm_mode_connector_attach_encoder(connector, encoder);
        encoder->possible_crtcs = (1 << unit);
        encoder->possible_clones = 0;
index b96d1ab..6bb7af3 100644 (file)
@@ -531,8 +531,6 @@ out_no_fence:
 }
 
 static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
-       .save = vmw_du_crtc_save,
-       .restore = vmw_du_crtc_restore,
        .cursor_set = vmw_du_crtc_cursor_set,
        .cursor_move = vmw_du_crtc_cursor_move,
        .gamma_set = vmw_du_crtc_gamma_set,
@@ -565,10 +563,6 @@ static void vmw_sou_connector_destroy(struct drm_connector *connector)
 
 static struct drm_connector_funcs vmw_sou_connector_funcs = {
        .dpms = vmw_du_connector_dpms,
-       .save = vmw_du_connector_save,
-       .restore = vmw_du_connector_restore,
-       .detect = vmw_du_connector_detect,
-       .fill_modes = vmw_du_connector_fill_modes,
        .set_property = vmw_du_connector_set_property,
        .destroy = vmw_sou_connector_destroy,
 };
@@ -603,7 +597,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
        connector->status = vmw_du_connector_detect(connector, true);
 
        drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs,
-                        DRM_MODE_ENCODER_VIRTUAL);
+                        DRM_MODE_ENCODER_VIRTUAL, NULL);
        drm_mode_connector_attach_encoder(connector, encoder);
        encoder->possible_crtcs = (1 << unit);
        encoder->possible_clones = 0;
index b1fc1c0..45e72c2 100644 (file)
@@ -1041,8 +1041,6 @@ out_finish:
  *  Screen Target CRTC dispatch table
  */
 static struct drm_crtc_funcs vmw_stdu_crtc_funcs = {
-       .save = vmw_du_crtc_save,
-       .restore = vmw_du_crtc_restore,
        .cursor_set = vmw_du_crtc_cursor_set,
        .cursor_move = vmw_du_crtc_cursor_move,
        .gamma_set = vmw_du_crtc_gamma_set,
@@ -1101,8 +1099,6 @@ static void vmw_stdu_connector_destroy(struct drm_connector *connector)
 
 static struct drm_connector_funcs vmw_stdu_connector_funcs = {
        .dpms = vmw_du_connector_dpms,
-       .save = vmw_du_connector_save,
-       .restore = vmw_du_connector_restore,
        .detect = vmw_du_connector_detect,
        .fill_modes = vmw_du_connector_fill_modes,
        .set_property = vmw_du_connector_set_property,
@@ -1149,7 +1145,7 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
        connector->status = vmw_du_connector_detect(connector, false);
 
        drm_encoder_init(dev, encoder, &vmw_stdu_encoder_funcs,
-                        DRM_MODE_ENCODER_VIRTUAL);
+                        DRM_MODE_ENCODER_VIRTUAL, NULL);
        drm_mode_connector_attach_encoder(connector, encoder);
        encoder->possible_crtcs = (1 << unit);
        encoder->possible_clones = 0;
index a8e01aa..5531d7b 100644 (file)
@@ -587,6 +587,13 @@ struct drm_driver {
        int (*gem_open_object) (struct drm_gem_object *, struct drm_file *);
        void (*gem_close_object) (struct drm_gem_object *, struct drm_file *);
 
+       /**
+        * Hook for allocating the GEM object struct, for use by core
+        * helpers.
+        */
+       struct drm_gem_object *(*gem_create_object)(struct drm_device *dev,
+                                                   size_t size);
+
        /* prime: */
        /* export handle -> fd (see drm_gem_prime_handle_to_fd() helper) */
        int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv,
index 4765df3..c2f98ba 100644 (file)
@@ -162,23 +162,55 @@ struct drm_tile_group {
        u8 group_data[8];
 };
 
+/**
+ * struct drm_framebuffer_funcs - framebuffer hooks
+ */
 struct drm_framebuffer_funcs {
-       /* note: use drm_framebuffer_remove() */
+       /**
+        * @destroy:
+        *
+        * Clean up framebuffer resources, specifically also unreference the
+        * backing storage. The core guarantees to call this function for every
+        * framebuffer successfully created by ->fb_create() in
+        * &drm_mode_config_funcs.
+        */
        void (*destroy)(struct drm_framebuffer *framebuffer);
+
+       /**
+        * @create_handle:
+        *
+        * Create a buffer handle in the driver-specific buffer manager (either
+        * GEM or TTM) valid for the passed-in struct &drm_file. This is used by
+        * the core to implement the GETFB IOCTL, which returns (for
+        * sufficiently priviledged user) also a native buffer handle. This can
+        * be used for seamless transitions between modesetting clients by
+        * copying the current screen contents to a private buffer and blending
+        * between that and the new contents.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*create_handle)(struct drm_framebuffer *fb,
                             struct drm_file *file_priv,
                             unsigned int *handle);
-       /*
-        * Optional callback for the dirty fb ioctl.
+       /**
+        * @dirty:
         *
-        * Userspace can notify the driver via this callback
-        * that a area of the framebuffer has changed and should
-        * be flushed to the display hardware.
+        * Optional callback for the dirty fb IOCTL.
         *
-        * See documentation in drm_mode.h for the struct
-        * drm_mode_fb_dirty_cmd for more information as all
-        * the semantics and arguments have a one to one mapping
-        * on this function.
+        * Userspace can notify the driver via this callback that an area of the
+        * framebuffer has changed and should be flushed to the display
+        * hardware. This can also be used internally, e.g. by the fbdev
+        * emulation, though that's not the case currently.
+        *
+        * See documentation in drm_mode.h for the struct drm_mode_fb_dirty_cmd
+        * for more information as all the semantics and arguments have a one to
+        * one mapping on this function.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
         */
        int (*dirty)(struct drm_framebuffer *framebuffer,
                     struct drm_file *file_priv, unsigned flags,
@@ -254,6 +286,11 @@ struct drm_plane;
 struct drm_bridge;
 struct drm_atomic_state;
 
+struct drm_crtc_helper_funcs;
+struct drm_encoder_helper_funcs;
+struct drm_connector_helper_funcs;
+struct drm_plane_helper_funcs;
+
 /**
  * struct drm_crtc_state - mutable CRTC state
  * @crtc: backpointer to the CRTC
@@ -315,23 +352,6 @@ struct drm_crtc_state {
 
 /**
  * struct drm_crtc_funcs - control CRTCs for a given device
- * @save: save CRTC state
- * @restore: restore CRTC state
- * @reset: reset CRTC after state has been invalidated (e.g. resume)
- * @cursor_set: setup the cursor
- * @cursor_set2: setup the cursor with hotspot, superseeds @cursor_set if set
- * @cursor_move: move the cursor
- * @gamma_set: specify color ramp for CRTC
- * @destroy: deinit and free object
- * @set_property: called when a property is changed
- * @set_config: apply a new CRTC configuration
- * @page_flip: initiate a page flip
- * @atomic_duplicate_state: duplicate the atomic state for this CRTC
- * @atomic_destroy_state: destroy an atomic state for this CRTC
- * @atomic_set_property: set a property on an atomic state for this CRTC
- *    (do not call directly, use drm_atomic_crtc_set_property())
- * @atomic_get_property: get a property on an atomic state for this CRTC
- *    (do not call directly, use drm_atomic_crtc_get_property())
  *
  * The drm_crtc_funcs structure is the central CRTC management structure
  * in the DRM.  Each CRTC controls one or more connectors (note that the name
@@ -343,54 +363,316 @@ struct drm_crtc_state {
  * bus accessors.
  */
 struct drm_crtc_funcs {
-       /* Save CRTC state */
-       void (*save)(struct drm_crtc *crtc); /* suspend? */
-       /* Restore CRTC state */
-       void (*restore)(struct drm_crtc *crtc); /* resume? */
-       /* Reset CRTC state */
+       /**
+        * @reset:
+        *
+        * Reset CRTC hardware and software state to off. This function isn't
+        * called by the core directly, only through drm_mode_config_reset().
+        * It's not a helper hook only for historical reasons.
+        *
+        * Atomic drivers can use drm_atomic_helper_crtc_reset() to reset
+        * atomic state using this hook.
+        */
        void (*reset)(struct drm_crtc *crtc);
 
-       /* cursor controls */
+       /**
+        * @cursor_set:
+        *
+        * Update the cursor image. The cursor position is relative to the CRTC
+        * and can be partially or fully outside of the visible area.
+        *
+        * Note that contrary to all other KMS functions the legacy cursor entry
+        * points don't take a framebuffer object, but instead take directly a
+        * raw buffer object id from the driver's buffer manager (which is
+        * either GEM or TTM for current drivers).
+        *
+        * This entry point is deprecated, drivers should instead implement
+        * universal plane support and register a proper cursor plane using
+        * drm_crtc_init_with_planes().
+        *
+        * This callback is optional
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv,
                          uint32_t handle, uint32_t width, uint32_t height);
+
+       /**
+        * @cursor_set2:
+        *
+        * Update the cursor image, including hotspot information. The hotspot
+        * must not affect the cursor position in CRTC coordinates, but is only
+        * meant as a hint for virtualized display hardware to coordinate the
+        * guests and hosts cursor position. The cursor hotspot is relative to
+        * the cursor image. Otherwise this works exactly like @cursor_set.
+        *
+        * This entry point is deprecated, drivers should instead implement
+        * universal plane support and register a proper cursor plane using
+        * drm_crtc_init_with_planes().
+        *
+        * This callback is optional.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*cursor_set2)(struct drm_crtc *crtc, struct drm_file *file_priv,
                           uint32_t handle, uint32_t width, uint32_t height,
                           int32_t hot_x, int32_t hot_y);
+
+       /**
+        * @cursor_move:
+        *
+        * Update the cursor position. The cursor does not need to be visible
+        * when this hook is called.
+        *
+        * This entry point is deprecated, drivers should instead implement
+        * universal plane support and register a proper cursor plane using
+        * drm_crtc_init_with_planes().
+        *
+        * This callback is optional.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*cursor_move)(struct drm_crtc *crtc, int x, int y);
 
-       /* Set gamma on the CRTC */
+       /**
+        * @gamma_set:
+        *
+        * Set gamma on the CRTC.
+        *
+        * This callback is optional.
+        *
+        * NOTE:
+        *
+        * Drivers that support gamma tables and also fbdev emulation through
+        * the provided helper library need to take care to fill out the gamma
+        * hooks for both. Currently there's a bit an unfortunate duplication
+        * going on, which should eventually be unified to just one set of
+        * hooks.
+        */
        void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
                          uint32_t start, uint32_t size);
-       /* Object destroy routine */
+
+       /**
+        * @destroy:
+        *
+        * Clean up plane resources. This is only called at driver unload time
+        * through drm_mode_config_cleanup() since a CRTC cannot be hotplugged
+        * in DRM.
+        */
        void (*destroy)(struct drm_crtc *crtc);
 
+       /**
+        * @set_config:
+        *
+        * This is the main legacy entry point to change the modeset state on a
+        * CRTC. All the details of the desired configuration are passed in a
+        * struct &drm_mode_set - see there for details.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_set_config() to implement this hook.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*set_config)(struct drm_mode_set *set);
 
-       /*
-        * Flip to the given framebuffer.  This implements the page
-        * flip ioctl described in drm_mode.h, specifically, the
-        * implementation must return immediately and block all
-        * rendering to the current fb until the flip has completed.
-        * If userspace set the event flag in the ioctl, the event
-        * argument will point to an event to send back when the flip
-        * completes, otherwise it will be NULL.
+       /**
+        * @page_flip:
+        *
+        * Legacy entry point to schedule a flip to the given framebuffer.
+        *
+        * Page flipping is a synchronization mechanism that replaces the frame
+        * buffer being scanned out by the CRTC with a new frame buffer during
+        * vertical blanking, avoiding tearing (except when requested otherwise
+        * through the DRM_MODE_PAGE_FLIP_ASYNC flag). When an application
+        * requests a page flip the DRM core verifies that the new frame buffer
+        * is large enough to be scanned out by the CRTC in the currently
+        * configured mode and then calls the CRTC ->page_flip() operation with a
+        * pointer to the new frame buffer.
+        *
+        * The driver must wait for any pending rendering to the new framebuffer
+        * to complete before executing the flip. It should also wait for any
+        * pending rendering from other drivers if the underlying buffer is a
+        * shared dma-buf.
+        *
+        * An application can request to be notified when the page flip has
+        * completed. The drm core will supply a struct &drm_event in the event
+        * parameter in this case. This can be handled by the
+        * drm_crtc_send_vblank_event() function, which the driver should call on
+        * the provided event upon completion of the flip. Note that if
+        * the driver supports vblank signalling and timestamping the vblank
+        * counters and timestamps must agree with the ones returned from page
+        * flip events. With the current vblank helper infrastructure this can
+        * be achieved by holding a vblank reference while the page flip is
+        * pending, acquired through drm_crtc_vblank_get() and released with
+        * drm_crtc_vblank_put(). Drivers are free to implement their own vblank
+        * counter and timestamp tracking though, e.g. if they have accurate
+        * timestamp registers in hardware.
+        *
+        * FIXME:
+        *
+        * Up to that point drivers need to manage events themselves and can use
+        * even->base.list freely for that. Specifically they need to ensure
+        * that they don't send out page flip (or vblank) events for which the
+        * corresponding drm file has been closed already. The drm core
+        * unfortunately does not (yet) take care of that. Therefore drivers
+        * currently must clean up and release pending events in their
+        * ->preclose driver function.
+        *
+        * This callback is optional.
+        *
+        * NOTE:
+        *
+        * Very early versions of the KMS ABI mandated that the driver must
+        * block (but not reject) any rendering to the old framebuffer until the
+        * flip operation has completed and the old framebuffer is no longer
+        * visible. This requirement has been lifted, and userspace is instead
+        * expected to request delivery of an event and wait with recycling old
+        * buffers until such has been received.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure. Note that if a
+        * ->page_flip() operation is already pending the callback should return
+        * -EBUSY. Pageflips on a disabled CRTC (either by setting a NULL mode
+        * or just runtime disabled through DPMS respectively the new atomic
+        * "ACTIVE" state) should result in an -EINVAL error code.
         */
        int (*page_flip)(struct drm_crtc *crtc,
                         struct drm_framebuffer *fb,
                         struct drm_pending_vblank_event *event,
                         uint32_t flags);
 
+       /**
+        * @set_property:
+        *
+        * This is the legacy entry point to update a property attached to the
+        * CRTC.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_crtc_set_property() to implement this hook.
+        *
+        * This callback is optional if the driver does not support any legacy
+        * driver-private properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*set_property)(struct drm_crtc *crtc,
                            struct drm_property *property, uint64_t val);
 
-       /* atomic update handling */
+       /**
+        * @atomic_duplicate_state:
+        *
+        * Duplicate the current atomic state for this CRTC and return it.
+        * The core and helpers gurantee that any atomic state duplicated with
+        * this hook and still owned by the caller (i.e. not transferred to the
+        * driver by calling ->atomic_commit() from struct
+        * &drm_mode_config_funcs) will be cleaned up by calling the
+        * @atomic_destroy_state hook in this structure.
+        *
+        * Atomic drivers which don't subclass struct &drm_crtc should use
+        * drm_atomic_helper_crtc_duplicate_state(). Drivers that subclass the
+        * state structure to extend it with driver-private state should use
+        * __drm_atomic_helper_crtc_duplicate_state() to make sure shared state is
+        * duplicated in a consistent fashion across drivers.
+        *
+        * It is an error to call this hook before crtc->state has been
+        * initialized correctly.
+        *
+        * NOTE:
+        *
+        * If the duplicate state references refcounted resources this hook must
+        * acquire a reference for each of them. The driver must release these
+        * references again in @atomic_destroy_state.
+        *
+        * RETURNS:
+        *
+        * Duplicated atomic state or NULL when the allocation failed.
+        */
        struct drm_crtc_state *(*atomic_duplicate_state)(struct drm_crtc *crtc);
+
+       /**
+        * @atomic_destroy_state:
+        *
+        * Destroy a state duplicated with @atomic_duplicate_state and release
+        * or unreference all resources it references
+        */
        void (*atomic_destroy_state)(struct drm_crtc *crtc,
                                     struct drm_crtc_state *state);
+
+       /**
+        * @atomic_set_property:
+        *
+        * Decode a driver-private property value and store the decoded value
+        * into the passed-in state structure. Since the atomic core decodes all
+        * standardized properties (even for extensions beyond the core set of
+        * properties which might not be implemented by all drivers) this
+        * requires drivers to subclass the state structure.
+        *
+        * Such driver-private properties should really only be implemented for
+        * truly hardware/vendor specific state. Instead it is preferred to
+        * standardize atomic extension and decode the properties used to expose
+        * such an extension in the core.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_crtc_set_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * NOTE:
+        *
+        * This function is called in the state assembly phase of atomic
+        * modesets, which can be aborted for any reason (including on
+        * userspace's request to just check whether a configuration would be
+        * possible). Drivers MUST NOT touch any persistent state (hardware or
+        * software) or data structures except the passed in @state parameter.
+        *
+        * Also since userspace controls in which order properties are set this
+        * function must not do any input validation (since the state update is
+        * incomplete and hence likely inconsistent). Instead any such input
+        * validation must be done in the various atomic_check callbacks.
+        *
+        * RETURNS:
+        *
+        * 0 if the property has been found, -EINVAL if the property isn't
+        * implemented by the driver (which should never happen, the core only
+        * asks for properties attached to this CRTC). No other validation is
+        * allowed by the driver. The core already checks that the property
+        * value is within the range (integer, valid enum value, ...) the driver
+        * set when registering the property.
+        */
        int (*atomic_set_property)(struct drm_crtc *crtc,
                                   struct drm_crtc_state *state,
                                   struct drm_property *property,
                                   uint64_t val);
+       /**
+        * @atomic_get_property:
+        *
+        * Reads out the decoded driver-private property. This is used to
+        * implement the GETCRTC IOCTL.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_crtc_get_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success, -EINVAL if the property isn't implemented by the
+        * driver (which should never happen, the core only asks for
+        * properties attached to this CRTC).
+        */
        int (*atomic_get_property)(struct drm_crtc *crtc,
                                   const struct drm_crtc_state *state,
                                   struct drm_property *property,
@@ -420,7 +702,7 @@ struct drm_crtc_funcs {
  * @properties: property tracking for this CRTC
  * @state: current atomic state for this CRTC
  * @acquire_ctx: per-CRTC implicit acquire context used by atomic drivers for
- *     legacy ioctls
+ *     legacy IOCTLs
  *
  * Each CRTC may have one or more connectors associated with it.  This structure
  * allows the CRTC to be controlled.
@@ -430,6 +712,8 @@ struct drm_crtc {
        struct device_node *port;
        struct list_head head;
 
+       char *name;
+
        /*
         * crtc mutex
         *
@@ -467,14 +751,14 @@ struct drm_crtc {
        uint16_t *gamma_store;
 
        /* if you are using the helper */
-       const void *helper_private;
+       const struct drm_crtc_helper_funcs *helper_private;
 
        struct drm_object_properties properties;
 
        struct drm_crtc_state *state;
 
        /*
-        * For legacy crtc ioctls so that atomic drivers can get at the locking
+        * For legacy crtc IOCTLs so that atomic drivers can get at the locking
         * acquire context.
         */
        struct drm_modeset_acquire_ctx *acquire_ctx;
@@ -499,54 +783,239 @@ struct drm_connector_state {
 
 /**
  * struct drm_connector_funcs - control connectors on a given device
- * @dpms: set power state
- * @save: save connector state
- * @restore: restore connector state
- * @reset: reset connector after state has been invalidated (e.g. resume)
- * @detect: is this connector active?
- * @fill_modes: fill mode list for this connector
- * @set_property: property for this connector may need an update
- * @destroy: make object go away
- * @force: notify the driver that the connector is forced on
- * @atomic_duplicate_state: duplicate the atomic state for this connector
- * @atomic_destroy_state: destroy an atomic state for this connector
- * @atomic_set_property: set a property on an atomic state for this connector
- *    (do not call directly, use drm_atomic_connector_set_property())
- * @atomic_get_property: get a property on an atomic state for this connector
- *    (do not call directly, use drm_atomic_connector_get_property())
  *
  * Each CRTC may have one or more connectors attached to it.  The functions
  * below allow the core DRM code to control connectors, enumerate available modes,
  * etc.
  */
 struct drm_connector_funcs {
+       /**
+        * @dpms:
+        *
+        * Legacy entry point to set the per-connector DPMS state. Legacy DPMS
+        * is exposed as a standard property on the connector, but diverted to
+        * this callback in the drm core. Note that atomic drivers don't
+        * implement the 4 level DPMS support on the connector any more, but
+        * instead only have an on/off "ACTIVE" property on the CRTC object.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_connector_dpms() to implement this hook.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*dpms)(struct drm_connector *connector, int mode);
-       void (*save)(struct drm_connector *connector);
-       void (*restore)(struct drm_connector *connector);
+
+       /**
+        * @reset:
+        *
+        * Reset connector hardware and software state to off. This function isn't
+        * called by the core directly, only through drm_mode_config_reset().
+        * It's not a helper hook only for historical reasons.
+        *
+        * Atomic drivers can use drm_atomic_helper_connector_reset() to reset
+        * atomic state using this hook.
+        */
        void (*reset)(struct drm_connector *connector);
 
-       /* Check to see if anything is attached to the connector.
-        * @force is set to false whilst polling, true when checking the
-        * connector due to user request. @force can be used by the driver
-        * to avoid expensive, destructive operations during automated
-        * probing.
+       /**
+        * @detect:
+        *
+        * Check to see if anything is attached to the connector. The parameter
+        * force is set to false whilst polling, true when checking the
+        * connector due to a user request. force can be used by the driver to
+        * avoid expensive, destructive operations during automated probing.
+        *
+        * FIXME:
+        *
+        * Note that this hook is only called by the probe helper. It's not in
+        * the helper library vtable purely for historical reasons. The only DRM
+        * core entry point to probe connector state is @fill_modes.
+        *
+        * RETURNS:
+        *
+        * drm_connector_status indicating the connector's status.
         */
        enum drm_connector_status (*detect)(struct drm_connector *connector,
                                            bool force);
+
+       /**
+        * @force:
+        *
+        * This function is called to update internal encoder state when the
+        * connector is forced to a certain state by userspace, either through
+        * the sysfs interfaces or on the kernel cmdline. In that case the
+        * @detect callback isn't called.
+        *
+        * FIXME:
+        *
+        * Note that this hook is only called by the probe helper. It's not in
+        * the helper library vtable purely for historical reasons. The only DRM
+        * core entry point to probe connector state is @fill_modes.
+        */
+       void (*force)(struct drm_connector *connector);
+
+       /**
+        * @fill_modes:
+        *
+        * Entry point for output detection and basic mode validation. The
+        * driver should reprobe the output if needed (e.g. when hotplug
+        * handling is unreliable), add all detected modes to connector->modes
+        * and filter out any the device can't support in any configuration. It
+        * also needs to filter out any modes wider or higher than the
+        * parameters max_width and max_height indicate.
+        *
+        * The drivers must also prune any modes no longer valid from
+        * connector->modes. Furthermore it must update connector->status and
+        * connector->edid.  If no EDID has been received for this output
+        * connector->edid must be NULL.
+        *
+        * Drivers using the probe helpers should use
+        * drm_helper_probe_single_connector_modes() or
+        * drm_helper_probe_single_connector_modes_nomerge() to implement this
+        * function.
+        *
+        * RETURNS:
+        *
+        * The number of modes detected and filled into connector->modes.
+        */
        int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);
+
+       /**
+        * @set_property:
+        *
+        * This is the legacy entry point to update a property attached to the
+        * connector.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_connector_set_property() to implement this hook.
+        *
+        * This callback is optional if the driver does not support any legacy
+        * driver-private properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*set_property)(struct drm_connector *connector, struct drm_property *property,
                             uint64_t val);
+
+       /**
+        * @destroy:
+        *
+        * Clean up connector resources. This is called at driver unload time
+        * through drm_mode_config_cleanup(). It can also be called at runtime
+        * when a connector is being hot-unplugged for drivers that support
+        * connector hotplugging (e.g. DisplayPort MST).
+        */
        void (*destroy)(struct drm_connector *connector);
-       void (*force)(struct drm_connector *connector);
 
-       /* atomic update handling */
+       /**
+        * @atomic_duplicate_state:
+        *
+        * Duplicate the current atomic state for this connector and return it.
+        * The core and helpers gurantee that any atomic state duplicated with
+        * this hook and still owned by the caller (i.e. not transferred to the
+        * driver by calling ->atomic_commit() from struct
+        * &drm_mode_config_funcs) will be cleaned up by calling the
+        * @atomic_destroy_state hook in this structure.
+        *
+        * Atomic drivers which don't subclass struct &drm_connector_state should use
+        * drm_atomic_helper_connector_duplicate_state(). Drivers that subclass the
+        * state structure to extend it with driver-private state should use
+        * __drm_atomic_helper_connector_duplicate_state() to make sure shared state is
+        * duplicated in a consistent fashion across drivers.
+        *
+        * It is an error to call this hook before connector->state has been
+        * initialized correctly.
+        *
+        * NOTE:
+        *
+        * If the duplicate state references refcounted resources this hook must
+        * acquire a reference for each of them. The driver must release these
+        * references again in @atomic_destroy_state.
+        *
+        * RETURNS:
+        *
+        * Duplicated atomic state or NULL when the allocation failed.
+        */
        struct drm_connector_state *(*atomic_duplicate_state)(struct drm_connector *connector);
+
+       /**
+        * @atomic_destroy_state:
+        *
+        * Destroy a state duplicated with @atomic_duplicate_state and release
+        * or unreference all resources it references
+        */
        void (*atomic_destroy_state)(struct drm_connector *connector,
                                     struct drm_connector_state *state);
+
+       /**
+        * @atomic_set_property:
+        *
+        * Decode a driver-private property value and store the decoded value
+        * into the passed-in state structure. Since the atomic core decodes all
+        * standardized properties (even for extensions beyond the core set of
+        * properties which might not be implemented by all drivers) this
+        * requires drivers to subclass the state structure.
+        *
+        * Such driver-private properties should really only be implemented for
+        * truly hardware/vendor specific state. Instead it is preferred to
+        * standardize atomic extension and decode the properties used to expose
+        * such an extension in the core.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_connector_set_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * NOTE:
+        *
+        * This function is called in the state assembly phase of atomic
+        * modesets, which can be aborted for any reason (including on
+        * userspace's request to just check whether a configuration would be
+        * possible). Drivers MUST NOT touch any persistent state (hardware or
+        * software) or data structures except the passed in @state parameter.
+        *
+        * Also since userspace controls in which order properties are set this
+        * function must not do any input validation (since the state update is
+        * incomplete and hence likely inconsistent). Instead any such input
+        * validation must be done in the various atomic_check callbacks.
+        *
+        * RETURNS:
+        *
+        * 0 if the property has been found, -EINVAL if the property isn't
+        * implemented by the driver (which shouldn't ever happen, the core only
+        * asks for properties attached to this connector). No other validation
+        * is allowed by the driver. The core already checks that the property
+        * value is within the range (integer, valid enum value, ...) the driver
+        * set when registering the property.
+        */
        int (*atomic_set_property)(struct drm_connector *connector,
                                   struct drm_connector_state *state,
                                   struct drm_property *property,
                                   uint64_t val);
+
+       /**
+        * @atomic_get_property:
+        *
+        * Reads out the decoded driver-private property. This is used to
+        * implement the GETCONNECTOR IOCTL.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_connector_get_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success, -EINVAL if the property isn't implemented by the
+        * driver (which shouldn't ever happen, the core only asks for
+        * properties attached to this connector).
+        */
        int (*atomic_get_property)(struct drm_connector *connector,
                                   const struct drm_connector_state *state,
                                   struct drm_property *property,
@@ -555,13 +1024,26 @@ struct drm_connector_funcs {
 
 /**
  * struct drm_encoder_funcs - encoder controls
- * @reset: reset state (e.g. at init or resume time)
- * @destroy: cleanup and free associated data
  *
  * Encoders sit between CRTCs and connectors.
  */
 struct drm_encoder_funcs {
+       /**
+        * @reset:
+        *
+        * Reset encoder hardware and software state to off. This function isn't
+        * called by the core directly, only through drm_mode_config_reset().
+        * It's not a helper hook only for historical reasons.
+        */
        void (*reset)(struct drm_encoder *encoder);
+
+       /**
+        * @destroy:
+        *
+        * Clean up encoder resources. This is only called at driver unload time
+        * through drm_mode_config_cleanup() since an encoder cannot be
+        * hotplugged in DRM.
+        */
        void (*destroy)(struct drm_encoder *encoder);
 };
 
@@ -597,7 +1079,7 @@ struct drm_encoder {
        struct drm_crtc *crtc;
        struct drm_bridge *bridge;
        const struct drm_encoder_funcs *funcs;
-       const void *helper_private;
+       const struct drm_encoder_helper_funcs *helper_private;
 };
 
 /* should we poll this connector for connects and disconnects */
@@ -702,7 +1184,7 @@ struct drm_connector {
        /* requested DPMS state */
        int dpms;
 
-       const void *helper_private;
+       const struct drm_connector_helper_funcs *helper_private;
 
        /* forced on connector */
        struct drm_cmdline_mode cmdline_mode;
@@ -782,40 +1264,203 @@ struct drm_plane_state {
 
 /**
  * struct drm_plane_funcs - driver plane control functions
- * @update_plane: update the plane configuration
- * @disable_plane: shut down the plane
- * @destroy: clean up plane resources
- * @reset: reset plane after state has been invalidated (e.g. resume)
- * @set_property: called when a property is changed
- * @atomic_duplicate_state: duplicate the atomic state for this plane
- * @atomic_destroy_state: destroy an atomic state for this plane
- * @atomic_set_property: set a property on an atomic state for this plane
- *    (do not call directly, use drm_atomic_plane_set_property())
- * @atomic_get_property: get a property on an atomic state for this plane
- *    (do not call directly, use drm_atomic_plane_get_property())
  */
 struct drm_plane_funcs {
+       /**
+        * @update_plane:
+        *
+        * This is the legacy entry point to enable and configure the plane for
+        * the given CRTC and framebuffer. It is never called to disable the
+        * plane, i.e. the passed-in crtc and fb paramters are never NULL.
+        *
+        * The source rectangle in frame buffer memory coordinates is given by
+        * the src_x, src_y, src_w and src_h parameters (as 16.16 fixed point
+        * values). Devices that don't support subpixel plane coordinates can
+        * ignore the fractional part.
+        *
+        * The destination rectangle in CRTC coordinates is given by the
+        * crtc_x, crtc_y, crtc_w and crtc_h parameters (as integer values).
+        * Devices scale the source rectangle to the destination rectangle. If
+        * scaling is not supported, and the source rectangle size doesn't match
+        * the destination rectangle size, the driver must return a
+        * -<errorname>EINVAL</errorname> error.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_update_plane() to implement this hook.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*update_plane)(struct drm_plane *plane,
                            struct drm_crtc *crtc, struct drm_framebuffer *fb,
                            int crtc_x, int crtc_y,
                            unsigned int crtc_w, unsigned int crtc_h,
                            uint32_t src_x, uint32_t src_y,
                            uint32_t src_w, uint32_t src_h);
+
+       /**
+        * @disable_plane:
+        *
+        * This is the legacy entry point to disable the plane. The DRM core
+        * calls this method in response to a DRM_IOCTL_MODE_SETPLANE IOCTL call
+        * with the frame buffer ID set to 0.  Disabled planes must not be
+        * processed by the CRTC.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_disable_plane() to implement this hook.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*disable_plane)(struct drm_plane *plane);
+
+       /**
+        * @destroy:
+        *
+        * Clean up plane resources. This is only called at driver unload time
+        * through drm_mode_config_cleanup() since a plane cannot be hotplugged
+        * in DRM.
+        */
        void (*destroy)(struct drm_plane *plane);
+
+       /**
+        * @reset:
+        *
+        * Reset plane hardware and software state to off. This function isn't
+        * called by the core directly, only through drm_mode_config_reset().
+        * It's not a helper hook only for historical reasons.
+        *
+        * Atomic drivers can use drm_atomic_helper_plane_reset() to reset
+        * atomic state using this hook.
+        */
        void (*reset)(struct drm_plane *plane);
 
+       /**
+        * @set_property:
+        *
+        * This is the legacy entry point to update a property attached to the
+        * plane.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_plane_set_property() to implement this hook.
+        *
+        * This callback is optional if the driver does not support any legacy
+        * driver-private properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
        int (*set_property)(struct drm_plane *plane,
                            struct drm_property *property, uint64_t val);
 
-       /* atomic update handling */
+       /**
+        * @atomic_duplicate_state:
+        *
+        * Duplicate the current atomic state for this plane and return it.
+        * The core and helpers gurantee that any atomic state duplicated with
+        * this hook and still owned by the caller (i.e. not transferred to the
+        * driver by calling ->atomic_commit() from struct
+        * &drm_mode_config_funcs) will be cleaned up by calling the
+        * @atomic_destroy_state hook in this structure.
+        *
+        * Atomic drivers which don't subclass struct &drm_plane_state should use
+        * drm_atomic_helper_plane_duplicate_state(). Drivers that subclass the
+        * state structure to extend it with driver-private state should use
+        * __drm_atomic_helper_plane_duplicate_state() to make sure shared state is
+        * duplicated in a consistent fashion across drivers.
+        *
+        * It is an error to call this hook before plane->state has been
+        * initialized correctly.
+        *
+        * NOTE:
+        *
+        * If the duplicate state references refcounted resources this hook must
+        * acquire a reference for each of them. The driver must release these
+        * references again in @atomic_destroy_state.
+        *
+        * RETURNS:
+        *
+        * Duplicated atomic state or NULL when the allocation failed.
+        */
        struct drm_plane_state *(*atomic_duplicate_state)(struct drm_plane *plane);
+
+       /**
+        * @atomic_destroy_state:
+        *
+        * Destroy a state duplicated with @atomic_duplicate_state and release
+        * or unreference all resources it references
+        */
        void (*atomic_destroy_state)(struct drm_plane *plane,
                                     struct drm_plane_state *state);
+
+       /**
+        * @atomic_set_property:
+        *
+        * Decode a driver-private property value and store the decoded value
+        * into the passed-in state structure. Since the atomic core decodes all
+        * standardized properties (even for extensions beyond the core set of
+        * properties which might not be implemented by all drivers) this
+        * requires drivers to subclass the state structure.
+        *
+        * Such driver-private properties should really only be implemented for
+        * truly hardware/vendor specific state. Instead it is preferred to
+        * standardize atomic extension and decode the properties used to expose
+        * such an extension in the core.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_plane_set_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * NOTE:
+        *
+        * This function is called in the state assembly phase of atomic
+        * modesets, which can be aborted for any reason (including on
+        * userspace's request to just check whether a configuration would be
+        * possible). Drivers MUST NOT touch any persistent state (hardware or
+        * software) or data structures except the passed in @state parameter.
+        *
+        * Also since userspace controls in which order properties are set this
+        * function must not do any input validation (since the state update is
+        * incomplete and hence likely inconsistent). Instead any such input
+        * validation must be done in the various atomic_check callbacks.
+        *
+        * RETURNS:
+        *
+        * 0 if the property has been found, -EINVAL if the property isn't
+        * implemented by the driver (which shouldn't ever happen, the core only
+        * asks for properties attached to this plane). No other validation is
+        * allowed by the driver. The core already checks that the property
+        * value is within the range (integer, valid enum value, ...) the driver
+        * set when registering the property.
+        */
        int (*atomic_set_property)(struct drm_plane *plane,
                                   struct drm_plane_state *state,
                                   struct drm_property *property,
                                   uint64_t val);
+
+       /**
+        * @atomic_get_property:
+        *
+        * Reads out the decoded driver-private property. This is used to
+        * implement the GETPLANE IOCTL.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_plane_get_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success, -EINVAL if the property isn't implemented by the
+        * driver (which should never happen, the core only asks for
+        * properties attached to this plane).
+        */
        int (*atomic_get_property)(struct drm_plane *plane,
                                   const struct drm_plane_state *state,
                                   struct drm_property *property,
@@ -828,6 +1473,7 @@ enum drm_plane_type {
        DRM_PLANE_TYPE_CURSOR,
 };
 
+
 /**
  * struct drm_plane - central DRM plane control structure
  * @dev: DRM device this plane belongs to
@@ -850,6 +1496,8 @@ struct drm_plane {
        struct drm_device *dev;
        struct list_head head;
 
+       char *name;
+
        struct drm_modeset_lock mutex;
 
        struct drm_mode_object base;
@@ -870,7 +1518,7 @@ struct drm_plane {
 
        enum drm_plane_type type;
 
-       const void *helper_private;
+       const struct drm_plane_helper_funcs *helper_private;
 
        struct drm_plane_state *state;
 };
@@ -878,24 +1526,114 @@ struct drm_plane {
 /**
  * struct drm_bridge_funcs - drm_bridge control functions
  * @attach: Called during drm_bridge_attach
- * @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
- * @disable: Called right before encoder prepare, disables the bridge
- * @post_disable: Called right after encoder prepare, for lockstepped disable
- * @mode_set: Set this mode to the bridge
- * @pre_enable: Called right before encoder commit, for lockstepped commit
- * @enable: Called right after encoder commit, enables the bridge
  */
 struct drm_bridge_funcs {
        int (*attach)(struct drm_bridge *bridge);
+
+       /**
+        * @mode_fixup:
+        *
+        * This callback is used to validate and adjust a mode. The paramater
+        * mode is the display mode that should be fed to the next element in
+        * the display chain, either the final &drm_connector or the next
+        * &drm_bridge. The parameter adjusted_mode is the input mode the bridge
+        * requires. It can be modified by this callback and does not need to
+        * match mode.
+        *
+        * This is the only hook that allows a bridge to reject a modeset. If
+        * this function passes all other callbacks must succeed for this
+        * configuration.
+        *
+        * NOTE:
+        *
+        * This function is called in the check phase of atomic modesets, which
+        * can be aborted for any reason (including on userspace's request to
+        * just check whether a configuration would be possible). Drivers MUST
+        * NOT touch any persistent state (hardware or software) or data
+        * structures except the passed in @state parameter.
+        *
+        * RETURNS:
+        *
+        * True if an acceptable configuration is possible, false if the modeset
+        * operation should be rejected.
+        */
        bool (*mode_fixup)(struct drm_bridge *bridge,
                           const struct drm_display_mode *mode,
                           struct drm_display_mode *adjusted_mode);
+       /**
+        * @disable:
+        *
+        * This callback should disable the bridge. It is called right before
+        * the preceding element in the display pipe is disabled. If the
+        * preceding element is a bridge this means it's called before that
+        * bridge's ->disable() function. If the preceding element is a
+        * &drm_encoder it's called right before the encoder's ->disable(),
+        * ->prepare() or ->dpms() hook from struct &drm_encoder_helper_funcs.
+        *
+        * The bridge can assume that the display pipe (i.e. clocks and timing
+        * signals) feeding it is still running when this callback is called.
+        */
        void (*disable)(struct drm_bridge *bridge);
+
+       /**
+        * @post_disable:
+        *
+        * This callback should disable the bridge. It is called right after
+        * the preceding element in the display pipe is disabled. If the
+        * preceding element is a bridge this means it's called after that
+        * bridge's ->post_disable() function. If the preceding element is a
+        * &drm_encoder it's called right after the encoder's ->disable(),
+        * ->prepare() or ->dpms() hook from struct &drm_encoder_helper_funcs.
+        *
+        * The bridge must assume that the display pipe (i.e. clocks and timing
+        * singals) feeding it is no longer running when this callback is
+        * called.
+        */
        void (*post_disable)(struct drm_bridge *bridge);
+
+       /**
+        * @mode_set:
+        *
+        * This callback should set the given mode on the bridge. It is called
+        * after the ->mode_set() callback for the preceding element in the
+        * display pipeline has been called already. The display pipe (i.e.
+        * clocks and timing signals) is off when this function is called.
+        */
        void (*mode_set)(struct drm_bridge *bridge,
                         struct drm_display_mode *mode,
                         struct drm_display_mode *adjusted_mode);
+       /**
+        * @pre_enable:
+        *
+        * This callback should enable the bridge. It is called right before
+        * the preceding element in the display pipe is enabled. If the
+        * preceding element is a bridge this means it's called before that
+        * bridge's ->pre_enable() function. If the preceding element is a
+        * &drm_encoder it's called right before the encoder's ->enable(),
+        * ->commit() or ->dpms() hook from struct &drm_encoder_helper_funcs.
+        *
+        * The display pipe (i.e. clocks and timing signals) feeding this bridge
+        * will not yet be running when this callback is called. The bridge must
+        * not enable the display link feeding the next bridge in the chain (if
+        * there is one) when this callback is called.
+        */
        void (*pre_enable)(struct drm_bridge *bridge);
+
+       /**
+        * @enable:
+        *
+        * This callback should enable the bridge. It is called right after
+        * the preceding element in the display pipe is enabled. If the
+        * preceding element is a bridge this means it's called after that
+        * bridge's ->enable() function. If the preceding element is a
+        * &drm_encoder it's called right after the encoder's ->enable(),
+        * ->commit() or ->dpms() hook from struct &drm_encoder_helper_funcs.
+        *
+        * The bridge can assume that the display pipe (i.e. clocks and timing
+        * signals) feeding it is running when this callback is called. This
+        * callback must enable the display link feeding the next bridge in the
+        * chain if there is one.
+        */
        void (*enable)(struct drm_bridge *bridge);
 };
 
@@ -926,7 +1664,7 @@ struct drm_bridge {
  * struct drm_atomic_state - the global state object for atomic updates
  * @dev: parent DRM device
  * @allow_modeset: allow full modeset
- * @legacy_cursor_update: hint to enforce legacy cursor ioctl semantics
+ * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics
  * @planes: pointer to array of plane pointers
  * @plane_states: pointer to array of plane states pointers
  * @crtcs: pointer to array of CRTC pointers
@@ -981,31 +1719,254 @@ struct drm_mode_set {
 
 /**
  * struct drm_mode_config_funcs - basic driver provided mode setting functions
- * @fb_create: create a new framebuffer object
- * @output_poll_changed: function to handle output configuration changes
- * @atomic_check: check whether a given atomic state update is possible
- * @atomic_commit: commit an atomic state update previously verified with
- *     atomic_check()
- * @atomic_state_alloc: allocate a new atomic state
- * @atomic_state_clear: clear the atomic state
- * @atomic_state_free: free the atomic state
  *
  * Some global (i.e. not per-CRTC, connector, etc) mode setting functions that
  * involve drivers.
  */
 struct drm_mode_config_funcs {
+       /**
+        * @fb_create:
+        *
+        * Create a new framebuffer object. The core does basic checks on the
+        * requested metadata, but most of that is left to the driver. See
+        * struct &drm_mode_fb_cmd2 for details.
+        *
+        * RETURNS:
+        *
+        * A new framebuffer with an initial reference count of 1 or a negative
+        * error code encoded with ERR_PTR().
+        */
        struct drm_framebuffer *(*fb_create)(struct drm_device *dev,
                                             struct drm_file *file_priv,
                                             const struct drm_mode_fb_cmd2 *mode_cmd);
+
+       /**
+        * @output_poll_changed:
+        *
+        * Callback used by helpers to inform the driver of output configuration
+        * changes.
+        *
+        * Drivers implementing fbdev emulation with the helpers can call
+        * drm_fb_helper_hotplug_changed from this hook to inform the fbdev
+        * helper of output changes.
+        *
+        * FIXME:
+        *
+        * Except that there's no vtable for device-level helper callbacks
+        * there's no reason this is a core function.
+        */
        void (*output_poll_changed)(struct drm_device *dev);
 
+       /**
+        * @atomic_check:
+        *
+        * This is the only hook to validate an atomic modeset update. This
+        * function must reject any modeset and state changes which the hardware
+        * or driver doesn't support. This includes but is of course not limited
+        * to:
+        *
+        *  - Checking that the modes, framebuffers, scaling and placement
+        *    requirements and so on are within the limits of the hardware.
+        *
+        *  - Checking that any hidden shared resources are not oversubscribed.
+        *    This can be shared PLLs, shared lanes, overall memory bandwidth,
+        *    display fifo space (where shared between planes or maybe even
+        *    CRTCs).
+        *
+        *  - Checking that virtualized resources exported to userspace are not
+        *    oversubscribed. For various reasons it can make sense to expose
+        *    more planes, crtcs or encoders than which are physically there. One
+        *    example is dual-pipe operations (which generally should be hidden
+        *    from userspace if when lockstepped in hardware, exposed otherwise),
+        *    where a plane might need 1 hardware plane (if it's just on one
+        *    pipe), 2 hardware planes (when it spans both pipes) or maybe even
+        *    shared a hardware plane with a 2nd plane (if there's a compatible
+        *    plane requested on the area handled by the other pipe).
+        *
+        *  - Check that any transitional state is possible and that if
+        *    requested, the update can indeed be done in the vblank period
+        *    without temporarily disabling some functions.
+        *
+        *  - Check any other constraints the driver or hardware might have.
+        *
+        *  - This callback also needs to correctly fill out the &drm_crtc_state
+        *    in this update to make sure that drm_atomic_crtc_needs_modeset()
+        *    reflects the nature of the possible update and returns true if and
+        *    only if the update cannot be applied without tearing within one
+        *    vblank on that CRTC. The core uses that information to reject
+        *    updates which require a full modeset (i.e. blanking the screen, or
+        *    at least pausing updates for a substantial amount of time) if
+        *    userspace has disallowed that in its request.
+        *
+        *  - The driver also does not need to repeat basic input validation
+        *    like done for the corresponding legacy entry points. The core does
+        *    that before calling this hook.
+        *
+        * See the documentation of @atomic_commit for an exhaustive list of
+        * error conditions which don't have to be checked at the
+        * ->atomic_check() stage?
+        *
+        * See the documentation for struct &drm_atomic_state for how exactly
+        * an atomic modeset update is described.
+        *
+        * Drivers using the atomic helpers can implement this hook using
+        * drm_atomic_helper_check(), or one of the exported sub-functions of
+        * it.
+        *
+        * RETURNS:
+        *
+        * 0 on success or one of the below negative error codes:
+        *
+        *  - -EINVAL, if any of the above constraints are violated.
+        *
+        *  - -EDEADLK, when returned from an attempt to acquire an additional
+        *    &drm_modeset_lock through drm_modeset_lock().
+        *
+        *  - -ENOMEM, if allocating additional state sub-structures failed due
+        *    to lack of memory.
+        *
+        *  - -EINTR, -EAGAIN or -ERESTARTSYS, if the IOCTL should be restarted.
+        *    This can either be due to a pending signal, or because the driver
+        *    needs to completely bail out to recover from an exceptional
+        *    situation like a GPU hang. From a userspace point all errors are
+        *    treated equally.
+        */
        int (*atomic_check)(struct drm_device *dev,
-                           struct drm_atomic_state *a);
+                           struct drm_atomic_state *state);
+
+       /**
+        * @atomic_commit:
+        *
+        * This is the only hook to commit an atomic modeset update. The core
+        * guarantees that @atomic_check has been called successfully before
+        * calling this function, and that nothing has been changed in the
+        * interim.
+        *
+        * See the documentation for struct &drm_atomic_state for how exactly
+        * an atomic modeset update is described.
+        *
+        * Drivers using the atomic helpers can implement this hook using
+        * drm_atomic_helper_commit(), or one of the exported sub-functions of
+        * it.
+        *
+        * Asynchronous commits (as indicated with the async parameter) must
+        * do any preparatory work which might result in an unsuccessful commit
+        * in the context of this callback. The only exceptions are hardware
+        * errors resulting in -EIO. But even in that case the driver must
+        * ensure that the display pipe is at least running, to avoid
+        * compositors crashing when pageflips don't work. Anything else,
+        * specifically committing the update to the hardware, should be done
+        * without blocking the caller. For updates which do not require a
+        * modeset this must be guaranteed.
+        *
+        * The driver must wait for any pending rendering to the new
+        * framebuffers to complete before executing the flip. It should also
+        * wait for any pending rendering from other drivers if the underlying
+        * buffer is a shared dma-buf. Asynchronous commits must not wait for
+        * rendering in the context of this callback.
+        *
+        * An application can request to be notified when the atomic commit has
+        * completed. These events are per-CRTC and can be distinguished by the
+        * CRTC index supplied in &drm_event to userspace.
+        *
+        * The drm core will supply a struct &drm_event in the event
+        * member of each CRTC's &drm_crtc_state structure. This can be handled by the
+        * drm_crtc_send_vblank_event() function, which the driver should call on
+        * the provided event upon completion of the atomic commit. Note that if
+        * the driver supports vblank signalling and timestamping the vblank
+        * counters and timestamps must agree with the ones returned from page
+        * flip events. With the current vblank helper infrastructure this can
+        * be achieved by holding a vblank reference while the page flip is
+        * pending, acquired through drm_crtc_vblank_get() and released with
+        * drm_crtc_vblank_put(). Drivers are free to implement their own vblank
+        * counter and timestamp tracking though, e.g. if they have accurate
+        * timestamp registers in hardware.
+        *
+        * NOTE:
+        *
+        * Drivers are not allowed to shut down any display pipe successfully
+        * enabled through an atomic commit on their own. Doing so can result in
+        * compositors crashing if a page flip is suddenly rejected because the
+        * pipe is off.
+        *
+        * RETURNS:
+        *
+        * 0 on success or one of the below negative error codes:
+        *
+        *  - -EBUSY, if an asynchronous updated is requested and there is
+        *    an earlier updated pending. Drivers are allowed to support a queue
+        *    of outstanding updates, but currently no driver supports that.
+        *    Note that drivers must wait for preceding updates to complete if a
+        *    synchronous update is requested, they are not allowed to fail the
+        *    commit in that case.
+        *
+        *  - -ENOMEM, if the driver failed to allocate memory. Specifically
+        *    this can happen when trying to pin framebuffers, which must only
+        *    be done when committing the state.
+        *
+        *  - -ENOSPC, as a refinement of the more generic -ENOMEM to indicate
+        *    that the driver has run out of vram, iommu space or similar GPU
+        *    address space needed for framebuffer.
+        *
+        *  - -EIO, if the hardware completely died.
+        *
+        *  - -EINTR, -EAGAIN or -ERESTARTSYS, if the IOCTL should be restarted.
+        *    This can either be due to a pending signal, or because the driver
+        *    needs to completely bail out to recover from an exceptional
+        *    situation like a GPU hang. From a userspace point of view all errors are
+        *    treated equally.
+        *
+        * This list is exhaustive. Specifically this hook is not allowed to
+        * return -EINVAL (any invalid requests should be caught in
+        * @atomic_check) or -EDEADLK (this function must not acquire
+        * additional modeset locks).
+        */
        int (*atomic_commit)(struct drm_device *dev,
-                            struct drm_atomic_state *a,
+                            struct drm_atomic_state *state,
                             bool async);
+
+       /**
+        * @atomic_state_alloc:
+        *
+        * This optional hook can be used by drivers that want to subclass struct
+        * &drm_atomic_state to be able to track their own driver-private global
+        * state easily. If this hook is implemented, drivers must also
+        * implement @atomic_state_clear and @atomic_state_free.
+        *
+        * RETURNS:
+        *
+        * A new &drm_atomic_state on success or NULL on failure.
+        */
        struct drm_atomic_state *(*atomic_state_alloc)(struct drm_device *dev);
+
+       /**
+        * @atomic_state_clear:
+        *
+        * This hook must clear any driver private state duplicated into the
+        * passed-in &drm_atomic_state. This hook is called when the caller
+        * encountered a &drm_modeset_lock deadlock and needs to drop all
+        * already acquired locks as part of the deadlock avoidance dance
+        * implemented in drm_modeset_lock_backoff().
+        *
+        * Any duplicated state must be invalidated since a concurrent atomic
+        * update might change it, and the drm atomic interfaces always apply
+        * updates as relative changes to the current state.
+        *
+        * Drivers that implement this must call drm_atomic_state_default_clear()
+        * to clear common state.
+        */
        void (*atomic_state_clear)(struct drm_atomic_state *state);
+
+       /**
+        * @atomic_state_free:
+        *
+        * This hook needs driver private resources and the &drm_atomic_state
+        * itself. Note that the core first calls drm_atomic_state_clear() to
+        * avoid code duplicate between the clear and free hooks.
+        *
+        * Drivers that implement this must call drm_atomic_state_default_free()
+        * to release common resources.
+        */
        void (*atomic_state_free)(struct drm_atomic_state *state);
 };
 
@@ -1014,7 +1975,7 @@ struct drm_mode_config_funcs {
  * @mutex: mutex protecting KMS related lists and structures
  * @connection_mutex: ww mutex protecting connector state and routing
  * @acquire_ctx: global implicit acquire context used by atomic drivers for
- *     legacy ioctls
+ *     legacy IOCTLs
  * @idr_mutex: mutex for KMS ID allocation and management
  * @crtc_idr: main KMS ID tracking object
  * @fb_lock: mutex to protect fb state and lists
@@ -1187,11 +2148,13 @@ struct drm_prop_enum_list {
        char *name;
 };
 
-extern int drm_crtc_init_with_planes(struct drm_device *dev,
-                                    struct drm_crtc *crtc,
-                                    struct drm_plane *primary,
-                                    struct drm_plane *cursor,
-                                    const struct drm_crtc_funcs *funcs);
+extern __printf(6, 7)
+int drm_crtc_init_with_planes(struct drm_device *dev,
+                             struct drm_crtc *crtc,
+                             struct drm_plane *primary,
+                             struct drm_plane *cursor,
+                             const struct drm_crtc_funcs *funcs,
+                             const char *name, ...);
 extern void drm_crtc_cleanup(struct drm_crtc *crtc);
 extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
 
@@ -1237,10 +2200,11 @@ void drm_bridge_mode_set(struct drm_bridge *bridge,
 void drm_bridge_pre_enable(struct drm_bridge *bridge);
 void drm_bridge_enable(struct drm_bridge *bridge);
 
-extern int drm_encoder_init(struct drm_device *dev,
-                           struct drm_encoder *encoder,
-                           const struct drm_encoder_funcs *funcs,
-                           int encoder_type);
+extern __printf(5, 6)
+int drm_encoder_init(struct drm_device *dev,
+                    struct drm_encoder *encoder,
+                    const struct drm_encoder_funcs *funcs,
+                    int encoder_type, const char *name, ...);
 
 /**
  * drm_encoder_crtc_ok - can a given crtc drive a given encoder?
@@ -1255,13 +2219,15 @@ static inline bool drm_encoder_crtc_ok(struct drm_encoder *encoder,
        return !!(encoder->possible_crtcs & drm_crtc_mask(crtc));
 }
 
-extern int drm_universal_plane_init(struct drm_device *dev,
-                                   struct drm_plane *plane,
-                                   unsigned long possible_crtcs,
-                                   const struct drm_plane_funcs *funcs,
-                                   const uint32_t *formats,
-                                   unsigned int format_count,
-                                   enum drm_plane_type type);
+extern __printf(8, 9)
+int drm_universal_plane_init(struct drm_device *dev,
+                            struct drm_plane *plane,
+                            unsigned long possible_crtcs,
+                            const struct drm_plane_funcs *funcs,
+                            const uint32_t *formats,
+                            unsigned int format_count,
+                            enum drm_plane_type type,
+                            const char *name, ...);
 extern int drm_plane_init(struct drm_device *dev,
                          struct drm_plane *plane,
                          unsigned long possible_crtcs,
index e22ab29..4b37afa 100644 (file)
 #include <linux/fb.h>
 
 #include <drm/drm_crtc.h>
-
-enum mode_set_atomic {
-       LEAVE_ATOMIC_MODE_SET,
-       ENTER_ATOMIC_MODE_SET,
-};
-
-/**
- * struct drm_crtc_helper_funcs - helper operations for CRTCs
- * @dpms: set power state
- * @prepare: prepare the CRTC, called before @mode_set
- * @commit: commit changes to CRTC, called after @mode_set
- * @mode_fixup: try to fixup proposed mode for this CRTC
- * @mode_set: set this mode
- * @mode_set_nofb: set mode only (no scanout buffer attached)
- * @mode_set_base: update the scanout buffer
- * @mode_set_base_atomic: non-blocking mode set (used for kgdb support)
- * @load_lut: load color palette
- * @disable: disable CRTC when no longer in use
- * @enable: enable CRTC
- * @atomic_check: check for validity of an atomic state
- * @atomic_begin: begin atomic update
- * @atomic_flush: flush atomic update
- *
- * The helper operations are called by the mid-layer CRTC helper.
- *
- * Note that with atomic helpers @dpms, @prepare and @commit hooks are
- * deprecated. Used @enable and @disable instead exclusively.
- *
- * With legacy crtc helpers there's a big semantic difference between @disable
- * and the other hooks: @disable also needs to release any resources acquired in
- * @mode_set (like shared PLLs).
- */
-struct drm_crtc_helper_funcs {
-       /*
-        * Control power levels on the CRTC.  If the mode passed in is
-        * unsupported, the provider must use the next lowest power level.
-        */
-       void (*dpms)(struct drm_crtc *crtc, int mode);
-       void (*prepare)(struct drm_crtc *crtc);
-       void (*commit)(struct drm_crtc *crtc);
-
-       /* Provider can fixup or change mode timings before modeset occurs */
-       bool (*mode_fixup)(struct drm_crtc *crtc,
-                          const struct drm_display_mode *mode,
-                          struct drm_display_mode *adjusted_mode);
-       /* Actually set the mode */
-       int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
-                       struct drm_display_mode *adjusted_mode, int x, int y,
-                       struct drm_framebuffer *old_fb);
-       /* Actually set the mode for atomic helpers, optional */
-       void (*mode_set_nofb)(struct drm_crtc *crtc);
-
-       /* Move the crtc on the current fb to the given position *optional* */
-       int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
-                            struct drm_framebuffer *old_fb);
-       int (*mode_set_base_atomic)(struct drm_crtc *crtc,
-                                   struct drm_framebuffer *fb, int x, int y,
-                                   enum mode_set_atomic);
-
-       /* reload the current crtc LUT */
-       void (*load_lut)(struct drm_crtc *crtc);
-
-       void (*disable)(struct drm_crtc *crtc);
-       void (*enable)(struct drm_crtc *crtc);
-
-       /* atomic helpers */
-       int (*atomic_check)(struct drm_crtc *crtc,
-                           struct drm_crtc_state *state);
-       void (*atomic_begin)(struct drm_crtc *crtc,
-                            struct drm_crtc_state *old_crtc_state);
-       void (*atomic_flush)(struct drm_crtc *crtc,
-                            struct drm_crtc_state *old_crtc_state);
-};
-
-/**
- * struct drm_encoder_helper_funcs - helper operations for encoders
- * @dpms: set power state
- * @save: save connector state
- * @restore: restore connector state
- * @mode_fixup: try to fixup proposed mode for this connector
- * @prepare: part of the disable sequence, called before the CRTC modeset
- * @commit: called after the CRTC modeset
- * @mode_set: set this mode, optional for atomic helpers
- * @get_crtc: return CRTC that the encoder is currently attached to
- * @detect: connection status detection
- * @disable: disable encoder when not in use (overrides DPMS off)
- * @enable: enable encoder
- * @atomic_check: check for validity of an atomic update
- *
- * The helper operations are called by the mid-layer CRTC helper.
- *
- * Note that with atomic helpers @dpms, @prepare and @commit hooks are
- * deprecated. Used @enable and @disable instead exclusively.
- *
- * With legacy crtc helpers there's a big semantic difference between @disable
- * and the other hooks: @disable also needs to release any resources acquired in
- * @mode_set (like shared PLLs).
- */
-struct drm_encoder_helper_funcs {
-       void (*dpms)(struct drm_encoder *encoder, int mode);
-       void (*save)(struct drm_encoder *encoder);
-       void (*restore)(struct drm_encoder *encoder);
-
-       bool (*mode_fixup)(struct drm_encoder *encoder,
-                          const struct drm_display_mode *mode,
-                          struct drm_display_mode *adjusted_mode);
-       void (*prepare)(struct drm_encoder *encoder);
-       void (*commit)(struct drm_encoder *encoder);
-       void (*mode_set)(struct drm_encoder *encoder,
-                        struct drm_display_mode *mode,
-                        struct drm_display_mode *adjusted_mode);
-       struct drm_crtc *(*get_crtc)(struct drm_encoder *encoder);
-       /* detect for DAC style encoders */
-       enum drm_connector_status (*detect)(struct drm_encoder *encoder,
-                                           struct drm_connector *connector);
-       void (*disable)(struct drm_encoder *encoder);
-
-       void (*enable)(struct drm_encoder *encoder);
-
-       /* atomic helpers */
-       int (*atomic_check)(struct drm_encoder *encoder,
-                           struct drm_crtc_state *crtc_state,
-                           struct drm_connector_state *conn_state);
-};
-
-/**
- * struct drm_connector_helper_funcs - helper operations for connectors
- * @get_modes: get mode list for this connector
- * @mode_valid: is this mode valid on the given connector? (optional)
- * @best_encoder: return the preferred encoder for this connector
- * @atomic_best_encoder: atomic version of @best_encoder
- *
- * The helper operations are called by the mid-layer CRTC helper.
- */
-struct drm_connector_helper_funcs {
-       int (*get_modes)(struct drm_connector *connector);
-       enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
-                                          struct drm_display_mode *mode);
-       struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
-       struct drm_encoder *(*atomic_best_encoder)(struct drm_connector *connector,
-                                                  struct drm_connector_state *connector_state);
-};
+#include <drm/drm_modeset_helper_vtables.h>
 
 extern void drm_helper_disable_unused_functions(struct drm_device *dev);
 extern int drm_crtc_helper_set_config(struct drm_mode_set *set);
@@ -199,24 +58,6 @@ extern void drm_helper_move_panel_connectors_to_head(struct drm_device *);
 extern void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
                                           const struct drm_mode_fb_cmd2 *mode_cmd);
 
-static inline void drm_crtc_helper_add(struct drm_crtc *crtc,
-                                      const struct drm_crtc_helper_funcs *funcs)
-{
-       crtc->helper_private = funcs;
-}
-
-static inline void drm_encoder_helper_add(struct drm_encoder *encoder,
-                                         const struct drm_encoder_helper_funcs *funcs)
-{
-       encoder->helper_private = funcs;
-}
-
-static inline void drm_connector_helper_add(struct drm_connector *connector,
-                                           const struct drm_connector_helper_funcs *funcs)
-{
-       connector->helper_private = funcs;
-}
-
 extern void drm_helper_resume_force_mode(struct drm_device *dev);
 
 int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
@@ -229,10 +70,6 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 extern int drm_helper_probe_single_connector_modes(struct drm_connector
                                                   *connector, uint32_t maxX,
                                                   uint32_t maxY);
-extern int drm_helper_probe_single_connector_modes_nomerge(struct drm_connector
-                                                          *connector,
-                                                          uint32_t maxX,
-                                                          uint32_t maxY);
 extern void drm_kms_helper_poll_init(struct drm_device *dev);
 extern void drm_kms_helper_poll_fini(struct drm_device *dev);
 extern bool drm_helper_hpd_irq_event(struct drm_device *dev);
index 87b090c..d8a40df 100644 (file)
@@ -34,6 +34,11 @@ struct drm_fb_helper;
 
 #include <linux/kgdb.h>
 
+enum mode_set_atomic {
+       LEAVE_ATOMIC_MODE_SET,
+       ENTER_ATOMIC_MODE_SET,
+};
+
 struct drm_fb_offset {
        int x, y;
 };
@@ -74,25 +79,76 @@ struct drm_fb_helper_surface_size {
 
 /**
  * struct drm_fb_helper_funcs - driver callbacks for the fbdev emulation library
- * @gamma_set: Set the given gamma lut register on the given crtc.
- * @gamma_get: Read the given gamma lut register on the given crtc, used to
- *             save the current lut when force-restoring the fbdev for e.g.
- *             kdbg.
- * @fb_probe: Driver callback to allocate and initialize the fbdev info
- *            structure. Furthermore it also needs to allocate the drm
- *            framebuffer used to back the fbdev.
- * @initial_config: Setup an initial fbdev display configuration
  *
  * Driver callbacks used by the fbdev emulation helper library.
  */
 struct drm_fb_helper_funcs {
+       /**
+        * @gamma_set:
+        *
+        * Set the given gamma LUT register on the given CRTC.
+        *
+        * This callback is optional.
+        *
+        * FIXME:
+        *
+        * This callback is functionally redundant with the core gamma table
+        * support and simply exists because the fbdev hasn't yet been
+        * refactored to use the core gamma table interfaces.
+        */
        void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green,
                          u16 blue, int regno);
+       /**
+        * @gamma_get:
+        *
+        * Read the given gamma LUT register on the given CRTC, used to save the
+        * current LUT when force-restoring the fbdev for e.g. kdbg.
+        *
+        * This callback is optional.
+        *
+        * FIXME:
+        *
+        * This callback is functionally redundant with the core gamma table
+        * support and simply exists because the fbdev hasn't yet been
+        * refactored to use the core gamma table interfaces.
+        */
        void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green,
                          u16 *blue, int regno);
 
+       /**
+        * @fb_probe:
+        *
+        * Driver callback to allocate and initialize the fbdev info structure.
+        * Furthermore it also needs to allocate the DRM framebuffer used to
+        * back the fbdev.
+        *
+        * This callback is mandatory.
+        *
+        * RETURNS:
+        *
+        * The driver should return 0 on success and a negative error code on
+        * failure.
+        */
        int (*fb_probe)(struct drm_fb_helper *helper,
                        struct drm_fb_helper_surface_size *sizes);
+
+       /**
+        * @initial_config:
+        *
+        * Driver callback to setup an initial fbdev display configuration.
+        * Drivers can use this callback to tell the fbdev emulation what the
+        * preferred initial configuration is. This is useful to implement
+        * smooth booting where the fbdev (and subsequently all userspace) never
+        * changes the mode, but always inherits the existing configuration.
+        *
+        * This callback is optional.
+        *
+        * RETURNS:
+        *
+        * The driver should return true if a suitable initial configuration has
+        * been filled out and false when the fbdev helper should fall back to
+        * the default probing logic.
+        */
        bool (*initial_config)(struct drm_fb_helper *fb_helper,
                               struct drm_fb_helper_crtc **crtcs,
                               struct drm_display_mode **modes,
@@ -105,18 +161,22 @@ struct drm_fb_helper_connector {
 };
 
 /**
- * struct drm_fb_helper - helper to emulate fbdev on top of kms
- * @fb:  Scanout framebuffer object
- * @dev:  DRM device
+ * struct drm_fb_helper - main structure to emulate fbdev on top of KMS
+ * @fb: Scanout framebuffer object
+ * @dev: DRM device
  * @crtc_count: number of possible CRTCs
  * @crtc_info: per-CRTC helper state (mode, x/y offset, etc)
  * @connector_count: number of connected connectors
  * @connector_info_alloc_count: size of connector_info
+ * @connector_info: array of per-connector information
  * @funcs: driver callbacks for fb helper
  * @fbdev: emulated fbdev device info struct
  * @pseudo_palette: fake palette of 16 colors
- * @kernel_fb_list: list_head in kernel_fb_helper_list
- * @delayed_hotplug: was there a hotplug while kms master active?
+ *
+ * This is the main structure used by the fbdev helpers. Drivers supporting
+ * fbdev emulation should embedded this into their overall driver structure.
+ * Drivers must also fill out a struct &drm_fb_helper_funcs with a few
+ * operations.
  */
 struct drm_fb_helper {
        struct drm_framebuffer *fb;
@@ -129,10 +189,21 @@ struct drm_fb_helper {
        const struct drm_fb_helper_funcs *funcs;
        struct fb_info *fbdev;
        u32 pseudo_palette[17];
+
+       /**
+        * @kernel_fb_list:
+        *
+        * Entry on the global kernel_fb_helper_list, used for kgdb entry/exit.
+        */
        struct list_head kernel_fb_list;
 
-       /* we got a hotplug but fbdev wasn't running the console
-          delay until next set_par */
+       /**
+        * @delayed_hotplug:
+        *
+        * A hotplug was received while fbdev wasn't in control of the DRM
+        * device, i.e. another KMS master was active. The output configuration
+        * needs to be reprobe when fbdev is in control again.
+        */
        bool delayed_hotplug;
 
        /**
index f1d8d0d..1b3b1f8 100644 (file)
@@ -163,9 +163,36 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
        return container_of(dev, struct mipi_dsi_device, dev);
 }
 
+/**
+ * mipi_dsi_pixel_format_to_bpp - obtain the number of bits per pixel for any
+ *                                given pixel format defined by the MIPI DSI
+ *                                specification
+ * @fmt: MIPI DSI pixel format
+ *
+ * Returns: The number of bits per pixel of the given pixel format.
+ */
+static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
+{
+       switch (fmt) {
+       case MIPI_DSI_FMT_RGB888:
+       case MIPI_DSI_FMT_RGB666:
+               return 24;
+
+       case MIPI_DSI_FMT_RGB666_PACKED:
+               return 18;
+
+       case MIPI_DSI_FMT_RGB565:
+               return 16;
+       }
+
+       return -EINVAL;
+}
+
 struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np);
 int mipi_dsi_attach(struct mipi_dsi_device *dsi);
 int mipi_dsi_detach(struct mipi_dsi_device *dsi);
+int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi);
+int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
 int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
                                            u16 value);
 
index f9115ae..625966a 100644 (file)
  * structures).
  */
 
+/**
+ * enum drm_mode_status - hardware support status of a mode
+ * @MODE_OK: Mode OK
+ * @MODE_HSYNC: hsync out of range
+ * @MODE_VSYNC: vsync out of range
+ * @MODE_H_ILLEGAL: mode has illegal horizontal timings
+ * @MODE_V_ILLEGAL: mode has illegal horizontal timings
+ * @MODE_BAD_WIDTH: requires an unsupported linepitch
+ * @MODE_NOMODE: no mode with a matching name
+ * @MODE_NO_INTERLACE: interlaced mode not supported
+ * @MODE_NO_DBLESCAN: doublescan mode not supported
+ * @MODE_NO_VSCAN: multiscan mode not supported
+ * @MODE_MEM: insufficient video memory
+ * @MODE_VIRTUAL_X: mode width too large for specified virtual size
+ * @MODE_VIRTUAL_Y: mode height too large for specified virtual size
+ * @MODE_MEM_VIRT: insufficient video memory given virtual size
+ * @MODE_NOCLOCK: no fixed clock available
+ * @MODE_CLOCK_HIGH: clock required is too high
+ * @MODE_CLOCK_LOW: clock required is too low
+ * @MODE_CLOCK_RANGE: clock/mode isn't in a ClockRange
+ * @MODE_BAD_HVALUE: horizontal timing was out of range
+ * @MODE_BAD_VVALUE: vertical timing was out of range
+ * @MODE_BAD_VSCAN: VScan value out of range
+ * @MODE_HSYNC_NARROW: horizontal sync too narrow
+ * @MODE_HSYNC_WIDE: horizontal sync too wide
+ * @MODE_HBLANK_NARROW: horizontal blanking too narrow
+ * @MODE_HBLANK_WIDE: horizontal blanking too wide
+ * @MODE_VSYNC_NARROW: vertical sync too narrow
+ * @MODE_VSYNC_WIDE: vertical sync too wide
+ * @MODE_VBLANK_NARROW: vertical blanking too narrow
+ * @MODE_VBLANK_WIDE: vertical blanking too wide
+ * @MODE_PANEL: exceeds panel dimensions
+ * @MODE_INTERLACE_WIDTH: width too large for interlaced mode
+ * @MODE_ONE_WIDTH: only one width is supported
+ * @MODE_ONE_HEIGHT: only one height is supported
+ * @MODE_ONE_SIZE: only one resolution is supported
+ * @MODE_NO_REDUCED: monitor doesn't accept reduced blanking
+ * @MODE_NO_STEREO: stereo modes not supported
+ * @MODE_STALE: mode has become stale
+ * @MODE_BAD: unspecified reason
+ * @MODE_ERROR: error condition
+ *
+ * This enum is used to filter out modes not supported by the driver/hardware
+ * combination.
+ */
 enum drm_mode_status {
-    MODE_OK    = 0,    /* Mode OK */
-    MODE_HSYNC,                /* hsync out of range */
-    MODE_VSYNC,                /* vsync out of range */
-    MODE_H_ILLEGAL,    /* mode has illegal horizontal timings */
-    MODE_V_ILLEGAL,    /* mode has illegal horizontal timings */
-    MODE_BAD_WIDTH,    /* requires an unsupported linepitch */
-    MODE_NOMODE,       /* no mode with a matching name */
-    MODE_NO_INTERLACE, /* interlaced mode not supported */
-    MODE_NO_DBLESCAN,  /* doublescan mode not supported */
-    MODE_NO_VSCAN,     /* multiscan mode not supported */
-    MODE_MEM,          /* insufficient video memory */
-    MODE_VIRTUAL_X,    /* mode width too large for specified virtual size */
-    MODE_VIRTUAL_Y,    /* mode height too large for specified virtual size */
-    MODE_MEM_VIRT,     /* insufficient video memory given virtual size */
-    MODE_NOCLOCK,      /* no fixed clock available */
-    MODE_CLOCK_HIGH,   /* clock required is too high */
-    MODE_CLOCK_LOW,    /* clock required is too low */
-    MODE_CLOCK_RANGE,  /* clock/mode isn't in a ClockRange */
-    MODE_BAD_HVALUE,   /* horizontal timing was out of range */
-    MODE_BAD_VVALUE,   /* vertical timing was out of range */
-    MODE_BAD_VSCAN,    /* VScan value out of range */
-    MODE_HSYNC_NARROW, /* horizontal sync too narrow */
-    MODE_HSYNC_WIDE,   /* horizontal sync too wide */
-    MODE_HBLANK_NARROW,        /* horizontal blanking too narrow */
-    MODE_HBLANK_WIDE,  /* horizontal blanking too wide */
-    MODE_VSYNC_NARROW, /* vertical sync too narrow */
-    MODE_VSYNC_WIDE,   /* vertical sync too wide */
-    MODE_VBLANK_NARROW,        /* vertical blanking too narrow */
-    MODE_VBLANK_WIDE,  /* vertical blanking too wide */
-    MODE_PANEL,         /* exceeds panel dimensions */
-    MODE_INTERLACE_WIDTH, /* width too large for interlaced mode */
-    MODE_ONE_WIDTH,     /* only one width is supported */
-    MODE_ONE_HEIGHT,    /* only one height is supported */
-    MODE_ONE_SIZE,      /* only one resolution is supported */
-    MODE_NO_REDUCED,    /* monitor doesn't accept reduced blanking */
-    MODE_NO_STEREO,    /* stereo modes not supported */
-    MODE_UNVERIFIED = -3, /* mode needs to reverified */
-    MODE_BAD = -2,     /* unspecified reason */
-    MODE_ERROR = -1    /* error condition */
+       MODE_OK = 0,
+       MODE_HSYNC,
+       MODE_VSYNC,
+       MODE_H_ILLEGAL,
+       MODE_V_ILLEGAL,
+       MODE_BAD_WIDTH,
+       MODE_NOMODE,
+       MODE_NO_INTERLACE,
+       MODE_NO_DBLESCAN,
+       MODE_NO_VSCAN,
+       MODE_MEM,
+       MODE_VIRTUAL_X,
+       MODE_VIRTUAL_Y,
+       MODE_MEM_VIRT,
+       MODE_NOCLOCK,
+       MODE_CLOCK_HIGH,
+       MODE_CLOCK_LOW,
+       MODE_CLOCK_RANGE,
+       MODE_BAD_HVALUE,
+       MODE_BAD_VVALUE,
+       MODE_BAD_VSCAN,
+       MODE_HSYNC_NARROW,
+       MODE_HSYNC_WIDE,
+       MODE_HBLANK_NARROW,
+       MODE_HBLANK_WIDE,
+       MODE_VSYNC_NARROW,
+       MODE_VSYNC_WIDE,
+       MODE_VBLANK_NARROW,
+       MODE_VBLANK_WIDE,
+       MODE_PANEL,
+       MODE_INTERLACE_WIDTH,
+       MODE_ONE_WIDTH,
+       MODE_ONE_HEIGHT,
+       MODE_ONE_SIZE,
+       MODE_NO_REDUCED,
+       MODE_NO_STEREO,
+       MODE_STALE = -3,
+       MODE_BAD = -2,
+       MODE_ERROR = -1
 };
 
 #define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \
@@ -96,17 +141,125 @@ enum drm_mode_status {
 
 #define DRM_MODE_FLAG_3D_MAX   DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF
 
+/**
+ * struct drm_display_mode - DRM kernel-internal display mode structure
+ * @hdisplay: horizontal display size
+ * @hsync_start: horizontal sync start
+ * @hsync_end: horizontal sync end
+ * @htotal: horizontal total size
+ * @hskew: horizontal skew?!
+ * @vdisplay: vertical display size
+ * @vsync_start: vertical sync start
+ * @vsync_end: vertical sync end
+ * @vtotal: vertical total size
+ * @vscan: vertical scan?!
+ * @crtc_hdisplay: hardware mode horizontal display size
+ * @crtc_hblank_start: hardware mode horizontal blank start
+ * @crtc_hblank_end: hardware mode horizontal blank end
+ * @crtc_hsync_start: hardware mode horizontal sync start
+ * @crtc_hsync_end: hardware mode horizontal sync end
+ * @crtc_htotal: hardware mode horizontal total size
+ * @crtc_hskew: hardware mode horizontal skew?!
+ * @crtc_vdisplay: hardware mode vertical display size
+ * @crtc_vblank_start: hardware mode vertical blank start
+ * @crtc_vblank_end: hardware mode vertical blank end
+ * @crtc_vsync_start: hardware mode vertical sync start
+ * @crtc_vsync_end: hardware mode vertical sync end
+ * @crtc_vtotal: hardware mode vertical total size
+ *
+ * The horizontal and vertical timings are defined per the following diagram.
+ *
+ *
+ *               Active                 Front           Sync           Back
+ *              Region                 Porch                          Porch
+ *     <-----------------------><----------------><-------------><-------------->
+ *       //////////////////////|
+ *      ////////////////////// |
+ *     //////////////////////  |..................               ................
+ *                                                _______________
+ *     <----- [hv]display ----->
+ *     <------------- [hv]sync_start ------------>
+ *     <--------------------- [hv]sync_end --------------------->
+ *     <-------------------------------- [hv]total ----------------------------->*
+ *
+ * This structure contains two copies of timings. First are the plain timings,
+ * which specify the logical mode, as it would be for a progressive 1:1 scanout
+ * at the refresh rate userspace can observe through vblank timestamps. Then
+ * there's the hardware timings, which are corrected for interlacing,
+ * double-clocking and similar things. They are provided as a convenience, and
+ * can be appropriately computed using drm_mode_set_crtcinfo().
+ */
 struct drm_display_mode {
-       /* Header */
+       /**
+        * @head:
+        *
+        * struct list_head for mode lists.
+        */
        struct list_head head;
+
+       /**
+        * @base:
+        *
+        * A display mode is a normal modeset object, possibly including public
+        * userspace id.
+        *
+        * FIXME:
+        *
+        * This can probably be removed since the entire concept of userspace
+        * managing modes explicitly has never landed in upstream kernel mode
+        * setting support.
+        */
        struct drm_mode_object base;
 
+       /**
+        * @name:
+        *
+        * Human-readable name of the mode, filled out with drm_mode_set_name().
+        */
        char name[DRM_DISPLAY_MODE_LEN];
 
+       /**
+        * @status:
+        *
+        * Status of the mode, used to filter out modes not supported by the
+        * hardware. See enum &drm_mode_status.
+        */
        enum drm_mode_status status;
+
+       /**
+        * @type:
+        *
+        * A bitmask of flags, mostly about the source of a mode. Possible flags
+        * are:
+        *
+        *  - DRM_MODE_TYPE_BUILTIN: Meant for hard-coded modes, effectively
+        *    unused.
+        *  - DRM_MODE_TYPE_PREFERRED: Preferred mode, usually the native
+        *    resolution of an LCD panel. There should only be one preferred
+        *    mode per connector at any given time.
+        *  - DRM_MODE_TYPE_DRIVER: Mode created by the driver, which is all of
+        *    them really. Drivers must set this bit for all modes they create
+        *    and expose to userspace.
+        *
+        * Plus a big list of flags which shouldn't be used at all, but are
+        * still around since these flags are also used in the userspace ABI:
+        *
+        *  - DRM_MODE_TYPE_DEFAULT: Again a leftover, use
+        *    DRM_MODE_TYPE_PREFERRED instead.
+        *  - DRM_MODE_TYPE_CLOCK_C and DRM_MODE_TYPE_CRTC_C: Define leftovers
+        *    which are stuck around for hysterical raisins only. No one has an
+        *    idea what they were meant for. Don't use.
+        *  - DRM_MODE_TYPE_USERDEF: Mode defined by userspace, again a vestige
+        *    from older kms designs where userspace had to first add a custom
+        *    mode to the kernel's mode list before it could use it. Don't use.
+        */
        unsigned int type;
 
-       /* Proposed mode values */
+       /**
+        * @clock:
+        *
+        * Pixel clock in kHz.
+        */
        int clock;              /* in kHz */
        int hdisplay;
        int hsync_start;
@@ -118,14 +271,74 @@ struct drm_display_mode {
        int vsync_end;
        int vtotal;
        int vscan;
+       /**
+        * @flags:
+        *
+        * Sync and timing flags:
+        *
+        *  - DRM_MODE_FLAG_PHSYNC: horizontal sync is active high.
+        *  - DRM_MODE_FLAG_NHSYNC: horizontal sync is active low.
+        *  - DRM_MODE_FLAG_PVSYNC: vertical sync is active high.
+        *  - DRM_MODE_FLAG_NVSYNC: vertical sync is active low.
+        *  - DRM_MODE_FLAG_INTERLACE: mode is interlaced.
+        *  - DRM_MODE_FLAG_DBLSCAN: mode uses doublescan.
+        *  - DRM_MODE_FLAG_CSYNC: mode uses composite sync.
+        *  - DRM_MODE_FLAG_PCSYNC: composite sync is active high.
+        *  - DRM_MODE_FLAG_NCSYNC: composite sync is active low.
+        *  - DRM_MODE_FLAG_HSKEW: hskew provided (not used?).
+        *  - DRM_MODE_FLAG_BCAST: not used?
+        *  - DRM_MODE_FLAG_PIXMUX: not used?
+        *  - DRM_MODE_FLAG_DBLCLK: double-clocked mode.
+        *  - DRM_MODE_FLAG_CLKDIV2: half-clocked mode.
+        *
+        * Additionally there's flags to specify how 3D modes are packed:
+        *
+        *  - DRM_MODE_FLAG_3D_NONE: normal, non-3D mode.
+        *  - DRM_MODE_FLAG_3D_FRAME_PACKING: 2 full frames for left and right.
+        *  - DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: interleaved like fields.
+        *  - DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: interleaved lines.
+        *  - DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: side-by-side full frames.
+        *  - DRM_MODE_FLAG_3D_L_DEPTH: ?
+        *  - DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: ?
+        *  - DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: frame split into top and bottom
+        *    parts.
+        *  - DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: frame split into left and
+        *    right parts.
+        */
        unsigned int flags;
 
-       /* Addressable image size (may be 0 for projectors, etc.) */
+       /**
+        * @width_mm:
+        *
+        * Addressable size of the output in mm, projectors should set this to
+        * 0.
+        */
        int width_mm;
+
+       /**
+        * @height_mm:
+        *
+        * Addressable size of the output in mm, projectors should set this to
+        * 0.
+        */
        int height_mm;
 
-       /* Actual mode we give to hw */
-       int crtc_clock;         /* in KHz */
+       /**
+        * @crtc_clock:
+        *
+        * Actual pixel or dot clock in the hardware. This differs from the
+        * logical @clock when e.g. using interlacing, double-clocking, stereo
+        * modes or other fancy stuff that changes the timings and signals
+        * actually sent over the wire.
+        *
+        * This is again in kHz.
+        *
+        * Note that with digital outputs like HDMI or DP there's usually a
+        * massive confusion between the dot clock and the signal clock at the
+        * bit encoding level. Especially when a 8b/10b encoding is used and the
+        * difference is exactly a factor of 10.
+        */
+       int crtc_clock;
        int crtc_hdisplay;
        int crtc_hblank_start;
        int crtc_hblank_end;
@@ -140,12 +353,48 @@ struct drm_display_mode {
        int crtc_vsync_end;
        int crtc_vtotal;
 
-       /* Driver private mode info */
+       /**
+        * @private:
+        *
+        * Pointer for driver private data. This can only be used for mode
+        * objects passed to drivers in modeset operations. It shouldn't be used
+        * by atomic drivers since they can store any additional data by
+        * subclassing state structures.
+        */
        int *private;
+
+       /**
+        * @private_flags:
+        *
+        * Similar to @private, but just an integer.
+        */
        int private_flags;
 
-       int vrefresh;           /* in Hz */
-       int hsync;              /* in kHz */
+       /**
+        * @vrefresh:
+        *
+        * Vertical refresh rate, for debug output in human readable form. Not
+        * used in a functional way.
+        *
+        * This value is in Hz.
+        */
+       int vrefresh;
+
+       /**
+        * @hsync:
+        *
+        * Horizontal refresh rate, for debug output in human readable form. Not
+        * used in a functional way.
+        *
+        * This value is in kHz.
+        */
+       int hsync;
+
+       /**
+        * @picture_aspect_ratio:
+        *
+        * Field for setting the HDMI picture aspect ratio of a mode.
+        */
        enum hdmi_picture_aspect picture_aspect_ratio;
 };
 
@@ -234,7 +483,7 @@ enum drm_mode_status drm_mode_validate_size(const struct drm_display_mode *mode,
 void drm_mode_prune_invalid(struct drm_device *dev,
                            struct list_head *mode_list, bool verbose);
 void drm_mode_sort(struct list_head *mode_list);
-void drm_mode_connector_list_update(struct drm_connector *connector, bool merge_type_bits);
+void drm_mode_connector_list_update(struct drm_connector *connector);
 
 /* parsing cmdline modes */
 bool
diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
new file mode 100644 (file)
index 0000000..29e0dc5
--- /dev/null
@@ -0,0 +1,890 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007-2008 Dave Airlie
+ * Copyright © 2007-2008 Intel Corporation
+ *   Jesse Barnes <jesse.barnes@intel.com>
+ * Copyright © 2011-2013 Intel Corporation
+ * Copyright © 2015 Intel Corporation
+ *   Daniel Vetter <daniel.vetter@ffwll.ch>
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ */
+
+#ifndef __DRM_MODESET_HELPER_VTABLES_H__
+#define __DRM_MODESET_HELPER_VTABLES_H__
+
+#include <drm/drm_crtc.h>
+
+/**
+ * DOC: overview
+ *
+ * The DRM mode setting helper functions are common code for drivers to use if
+ * they wish.  Drivers are not forced to use this code in their
+ * implementations but it would be useful if the code they do use at least
+ * provides a consistent interface and operation to userspace. Therefore it is
+ * highly recommended to use the provided helpers as much as possible.
+ *
+ * Because there is only one pointer per modeset object to hold a vfunc table
+ * for helper libraries they are by necessity shared among the different
+ * helpers.
+ *
+ * To make this clear all the helper vtables are pulled together in this location here.
+ */
+
+enum mode_set_atomic;
+
+/**
+ * struct drm_crtc_helper_funcs - helper operations for CRTCs
+ *
+ * These hooks are used by the legacy CRTC helpers, the transitional plane
+ * helpers and the new atomic modesetting helpers.
+ */
+struct drm_crtc_helper_funcs {
+       /**
+        * @dpms:
+        *
+        * Callback to control power levels on the CRTC.  If the mode passed in
+        * is unsupported, the provider must use the next lowest power level.
+        * This is used by the legacy CRTC helpers to implement DPMS
+        * functionality in drm_helper_connector_dpms().
+        *
+        * This callback is also used to disable a CRTC by calling it with
+        * DRM_MODE_DPMS_OFF if the @disable hook isn't used.
+        *
+        * This callback is used by the legacy CRTC helpers.  Atomic helpers
+        * also support using this hook for enabling and disabling a CRTC to
+        * facilitate transitions to atomic, but it is deprecated. Instead
+        * @enable and @disable should be used.
+        */
+       void (*dpms)(struct drm_crtc *crtc, int mode);
+
+       /**
+        * @prepare:
+        *
+        * This callback should prepare the CRTC for a subsequent modeset, which
+        * in practice means the driver should disable the CRTC if it is
+        * running. Most drivers ended up implementing this by calling their
+        * @dpms hook with DRM_MODE_DPMS_OFF.
+        *
+        * This callback is used by the legacy CRTC helpers.  Atomic helpers
+        * also support using this hook for disabling a CRTC to facilitate
+        * transitions to atomic, but it is deprecated. Instead @disable should
+        * be used.
+        */
+       void (*prepare)(struct drm_crtc *crtc);
+
+       /**
+        * @commit:
+        *
+        * This callback should commit the new mode on the CRTC after a modeset,
+        * which in practice means the driver should enable the CRTC.  Most
+        * drivers ended up implementing this by calling their @dpms hook with
+        * DRM_MODE_DPMS_ON.
+        *
+        * This callback is used by the legacy CRTC helpers.  Atomic helpers
+        * also support using this hook for enabling a CRTC to facilitate
+        * transitions to atomic, but it is deprecated. Instead @enable should
+        * be used.
+        */
+       void (*commit)(struct drm_crtc *crtc);
+
+       /**
+        * @mode_fixup:
+        *
+        * This callback is used to validate a mode. The parameter mode is the
+        * display mode that userspace requested, adjusted_mode is the mode the
+        * encoders need to be fed with. Note that this is the inverse semantics
+        * of the meaning for the &drm_encoder and &drm_bridge
+        * ->mode_fixup() functions. If the CRTC cannot support the requested
+        * conversion from mode to adjusted_mode it should reject the modeset.
+        *
+        * This function is used by both legacy CRTC helpers and atomic helpers.
+        * With atomic helpers it is optional.
+        *
+        * NOTE:
+        *
+        * This function is called in the check phase of atomic modesets, which
+        * can be aborted for any reason (including on userspace's request to
+        * just check whether a configuration would be possible). Atomic drivers
+        * MUST NOT touch any persistent state (hardware or software) or data
+        * structures except the passed in adjusted_mode parameter.
+        *
+        * This is in contrast to the legacy CRTC helpers where this was
+        * allowed.
+        *
+        * Atomic drivers which need to inspect and adjust more state should
+        * instead use the @atomic_check callback.
+        *
+        * RETURNS:
+        *
+        * True if an acceptable configuration is possible, false if the modeset
+        * operation should be rejected.
+        */
+       bool (*mode_fixup)(struct drm_crtc *crtc,
+                          const struct drm_display_mode *mode,
+                          struct drm_display_mode *adjusted_mode);
+
+       /**
+        * @mode_set:
+        *
+        * This callback is used by the legacy CRTC helpers to set a new mode,
+        * position and framebuffer. Since it ties the primary plane to every
+        * mode change it is incompatible with universal plane support. And
+        * since it can't update other planes it's incompatible with atomic
+        * modeset support.
+        *
+        * This callback is only used by CRTC helpers and deprecated.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
+                       struct drm_display_mode *adjusted_mode, int x, int y,
+                       struct drm_framebuffer *old_fb);
+
+       /**
+        * @mode_set_nofb:
+        *
+        * This callback is used to update the display mode of a CRTC without
+        * changing anything of the primary plane configuration. This fits the
+        * requirement of atomic and hence is used by the atomic helpers. It is
+        * also used by the transitional plane helpers to implement a
+        * @mode_set hook in drm_helper_crtc_mode_set().
+        *
+        * Note that the display pipe is completely off when this function is
+        * called. Atomic drivers which need hardware to be running before they
+        * program the new display mode (e.g. because they implement runtime PM)
+        * should not use this hook. This is because the helper library calls
+        * this hook only once per mode change and not every time the display
+        * pipeline is suspended using either DPMS or the new "ACTIVE" property.
+        * Which means register values set in this callback might get reset when
+        * the CRTC is suspended, but not restored.  Such drivers should instead
+        * move all their CRTC setup into the @enable callback.
+        *
+        * This callback is optional.
+        */
+       void (*mode_set_nofb)(struct drm_crtc *crtc);
+
+       /**
+        * @mode_set_base:
+        *
+        * This callback is used by the legacy CRTC helpers to set a new
+        * framebuffer and scanout position. It is optional and used as an
+        * optimized fast-path instead of a full mode set operation with all the
+        * resulting flickering. Since it can't update other planes it's
+        * incompatible with atomic modeset support.
+        *
+        * This callback is only used by the CRTC helpers and deprecated.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
+                            struct drm_framebuffer *old_fb);
+
+       /**
+        * @mode_set_base_atomic:
+        *
+        * This callback is used by the fbdev helpers to set a new framebuffer
+        * and scanout without sleeping, i.e. from an atomic calling context. It
+        * is only used to implement kgdb support.
+        *
+        * This callback is optional and only needed for kgdb support in the fbdev
+        * helpers.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*mode_set_base_atomic)(struct drm_crtc *crtc,
+                                   struct drm_framebuffer *fb, int x, int y,
+                                   enum mode_set_atomic);
+
+       /**
+        * @load_lut:
+        *
+        * Load a LUT prepared with the @gamma_set functions from
+        * &drm_fb_helper_funcs.
+        *
+        * This callback is optional and is only used by the fbdev emulation
+        * helpers.
+        *
+        * FIXME:
+        *
+        * This callback is functionally redundant with the core gamma table
+        * support and simply exists because the fbdev hasn't yet been
+        * refactored to use the core gamma table interfaces.
+        */
+       void (*load_lut)(struct drm_crtc *crtc);
+
+       /**
+        * @disable:
+        *
+        * This callback should be used to disable the CRTC. With the atomic
+        * drivers it is called after all encoders connected to this CRTC have
+        * been shut off already using their own ->disable hook. If that
+        * sequence is too simple drivers can just add their own hooks and call
+        * it from this CRTC callback here by looping over all encoders
+        * connected to it using for_each_encoder_on_crtc().
+        *
+        * This hook is used both by legacy CRTC helpers and atomic helpers.
+        * Atomic drivers don't need to implement it if there's no need to
+        * disable anything at the CRTC level. To ensure that runtime PM
+        * handling (using either DPMS or the new "ACTIVE" property) works
+        * @disable must be the inverse of @enable for atomic drivers.
+        *
+        * NOTE:
+        *
+        * With legacy CRTC helpers there's a big semantic difference between
+        * @disable and other hooks (like @prepare or @dpms) used to shut down a
+        * CRTC: @disable is only called when also logically disabling the
+        * display pipeline and needs to release any resources acquired in
+        * @mode_set (like shared PLLs, or again release pinned framebuffers).
+        *
+        * Therefore @disable must be the inverse of @mode_set plus @commit for
+        * drivers still using legacy CRTC helpers, which is different from the
+        * rules under atomic.
+        */
+       void (*disable)(struct drm_crtc *crtc);
+
+       /**
+        * @enable:
+        *
+        * This callback should be used to enable the CRTC. With the atomic
+        * drivers it is called before all encoders connected to this CRTC are
+        * enabled through the encoder's own ->enable hook.  If that sequence is
+        * too simple drivers can just add their own hooks and call it from this
+        * CRTC callback here by looping over all encoders connected to it using
+        * for_each_encoder_on_crtc().
+        *
+        * This hook is used only by atomic helpers, for symmetry with @disable.
+        * Atomic drivers don't need to implement it if there's no need to
+        * enable anything at the CRTC level. To ensure that runtime PM handling
+        * (using either DPMS or the new "ACTIVE" property) works
+        * @enable must be the inverse of @disable for atomic drivers.
+        */
+       void (*enable)(struct drm_crtc *crtc);
+
+       /**
+        * @atomic_check:
+        *
+        * Drivers should check plane-update related CRTC constraints in this
+        * hook. They can also check mode related limitations but need to be
+        * aware of the calling order, since this hook is used by
+        * drm_atomic_helper_check_planes() whereas the preparations needed to
+        * check output routing and the display mode is done in
+        * drm_atomic_helper_check_modeset(). Therefore drivers that want to
+        * check output routing and display mode constraints in this callback
+        * must ensure that drm_atomic_helper_check_modeset() has been called
+        * beforehand. This is calling order used by the default helper
+        * implementation in drm_atomic_helper_check().
+        *
+        * When using drm_atomic_helper_check_planes() CRTCs' ->atomic_check()
+        * hooks are called after the ones for planes, which allows drivers to
+        * assign shared resources requested by planes in the CRTC callback
+        * here. For more complicated dependencies the driver can call the provided
+        * check helpers multiple times until the computed state has a final
+        * configuration and everything has been checked.
+        *
+        * This function is also allowed to inspect any other object's state and
+        * can add more state objects to the atomic commit if needed. Care must
+        * be taken though to ensure that state check&compute functions for
+        * these added states are all called, and derived state in other objects
+        * all updated. Again the recommendation is to just call check helpers
+        * until a maximal configuration is reached.
+        *
+        * This callback is used by the atomic modeset helpers and by the
+        * transitional plane helpers, but it is optional.
+        *
+        * NOTE:
+        *
+        * This function is called in the check phase of an atomic update. The
+        * driver is not allowed to change anything outside of the free-standing
+        * state objects passed-in or assembled in the overall &drm_atomic_state
+        * update tracking structure.
+        *
+        * RETURNS:
+        *
+        * 0 on success, -EINVAL if the state or the transition can't be
+        * supported, -ENOMEM on memory allocation failure and -EDEADLK if an
+        * attempt to obtain another state object ran into a &drm_modeset_lock
+        * deadlock.
+        */
+       int (*atomic_check)(struct drm_crtc *crtc,
+                           struct drm_crtc_state *state);
+
+       /**
+        * @atomic_begin:
+        *
+        * Drivers should prepare for an atomic update of multiple planes on
+        * a CRTC in this hook. Depending upon hardware this might be vblank
+        * evasion, blocking updates by setting bits or doing preparatory work
+        * for e.g. manual update display.
+        *
+        * This hook is called before any plane commit functions are called.
+        *
+        * Note that the power state of the display pipe when this function is
+        * called depends upon the exact helpers and calling sequence the driver
+        * has picked. See drm_atomic_commit_planes() for a discussion of the
+        * tradeoffs and variants of plane commit helpers.
+        *
+        * This callback is used by the atomic modeset helpers and by the
+        * transitional plane helpers, but it is optional.
+        */
+       void (*atomic_begin)(struct drm_crtc *crtc,
+                            struct drm_crtc_state *old_crtc_state);
+       /**
+        * @atomic_flush:
+        *
+        * Drivers should finalize an atomic update of multiple planes on
+        * a CRTC in this hook. Depending upon hardware this might include
+        * checking that vblank evasion was successful, unblocking updates by
+        * setting bits or setting the GO bit to flush out all updates.
+        *
+        * Simple hardware or hardware with special requirements can commit and
+        * flush out all updates for all planes from this hook and forgo all the
+        * other commit hooks for plane updates.
+        *
+        * This hook is called after any plane commit functions are called.
+        *
+        * Note that the power state of the display pipe when this function is
+        * called depends upon the exact helpers and calling sequence the driver
+        * has picked. See drm_atomic_commit_planes() for a discussion of the
+        * tradeoffs and variants of plane commit helpers.
+        *
+        * This callback is used by the atomic modeset helpers and by the
+        * transitional plane helpers, but it is optional.
+        */
+       void (*atomic_flush)(struct drm_crtc *crtc,
+                            struct drm_crtc_state *old_crtc_state);
+};
+
+/**
+ * drm_crtc_helper_add - sets the helper vtable for a crtc
+ * @crtc: DRM CRTC
+ * @funcs: helper vtable to set for @crtc
+ */
+static inline void drm_crtc_helper_add(struct drm_crtc *crtc,
+                                      const struct drm_crtc_helper_funcs *funcs)
+{
+       crtc->helper_private = funcs;
+}
+
+/**
+ * struct drm_encoder_helper_funcs - helper operations for encoders
+ *
+ * These hooks are used by the legacy CRTC helpers, the transitional plane
+ * helpers and the new atomic modesetting helpers.
+ */
+struct drm_encoder_helper_funcs {
+       /**
+        * @dpms:
+        *
+        * Callback to control power levels on the encoder.  If the mode passed in
+        * is unsupported, the provider must use the next lowest power level.
+        * This is used by the legacy encoder helpers to implement DPMS
+        * functionality in drm_helper_connector_dpms().
+        *
+        * This callback is also used to disable an encoder by calling it with
+        * DRM_MODE_DPMS_OFF if the @disable hook isn't used.
+        *
+        * This callback is used by the legacy CRTC helpers.  Atomic helpers
+        * also support using this hook for enabling and disabling an encoder to
+        * facilitate transitions to atomic, but it is deprecated. Instead
+        * @enable and @disable should be used.
+        */
+       void (*dpms)(struct drm_encoder *encoder, int mode);
+
+       /**
+        * @mode_fixup:
+        *
+        * This callback is used to validate and adjust a mode. The parameter
+        * mode is the display mode that should be fed to the next element in
+        * the display chain, either the final &drm_connector or a &drm_bridge.
+        * The parameter adjusted_mode is the input mode the encoder requires. It
+        * can be modified by this callback and does not need to match mode.
+        *
+        * This function is used by both legacy CRTC helpers and atomic helpers.
+        * With atomic helpers it is optional.
+        *
+        * NOTE:
+        *
+        * This function is called in the check phase of atomic modesets, which
+        * can be aborted for any reason (including on userspace's request to
+        * just check whether a configuration would be possible). Atomic drivers
+        * MUST NOT touch any persistent state (hardware or software) or data
+        * structures except the passed in adjusted_mode parameter.
+        *
+        * This is in contrast to the legacy CRTC helpers where this was
+        * allowed.
+        *
+        * Atomic drivers which need to inspect and adjust more state should
+        * instead use the @atomic_check callback.
+        *
+        * RETURNS:
+        *
+        * True if an acceptable configuration is possible, false if the modeset
+        * operation should be rejected.
+        */
+       bool (*mode_fixup)(struct drm_encoder *encoder,
+                          const struct drm_display_mode *mode,
+                          struct drm_display_mode *adjusted_mode);
+
+       /**
+        * @prepare:
+        *
+        * This callback should prepare the encoder for a subsequent modeset,
+        * which in practice means the driver should disable the encoder if it
+        * is running. Most drivers ended up implementing this by calling their
+        * @dpms hook with DRM_MODE_DPMS_OFF.
+        *
+        * This callback is used by the legacy CRTC helpers.  Atomic helpers
+        * also support using this hook for disabling an encoder to facilitate
+        * transitions to atomic, but it is deprecated. Instead @disable should
+        * be used.
+        */
+       void (*prepare)(struct drm_encoder *encoder);
+
+       /**
+        * @commit:
+        *
+        * This callback should commit the new mode on the encoder after a modeset,
+        * which in practice means the driver should enable the encoder.  Most
+        * drivers ended up implementing this by calling their @dpms hook with
+        * DRM_MODE_DPMS_ON.
+        *
+        * This callback is used by the legacy CRTC helpers.  Atomic helpers
+        * also support using this hook for enabling an encoder to facilitate
+        * transitions to atomic, but it is deprecated. Instead @enable should
+        * be used.
+        */
+       void (*commit)(struct drm_encoder *encoder);
+
+       /**
+        * @mode_set:
+        *
+        * This callback is used to update the display mode of an encoder.
+        *
+        * Note that the display pipe is completely off when this function is
+        * called. Drivers which need hardware to be running before they program
+        * the new display mode (because they implement runtime PM) should not
+        * use this hook, because the helper library calls it only once and not
+        * every time the display pipeline is suspend using either DPMS or the
+        * new "ACTIVE" property. Such drivers should instead move all their
+        * encoder setup into the ->enable() callback.
+        *
+        * This callback is used both by the legacy CRTC helpers and the atomic
+        * modeset helpers. It is optional in the atomic helpers.
+        */
+       void (*mode_set)(struct drm_encoder *encoder,
+                        struct drm_display_mode *mode,
+                        struct drm_display_mode *adjusted_mode);
+
+       /**
+        * @get_crtc:
+        *
+        * This callback is used by the legacy CRTC helpers to work around
+        * deficiencies in its own book-keeping.
+        *
+        * Do not use, use atomic helpers instead, which get the book keeping
+        * right.
+        *
+        * FIXME:
+        *
+        * Currently only nouveau is using this, and as soon as nouveau is
+        * atomic we can ditch this hook.
+        */
+       struct drm_crtc *(*get_crtc)(struct drm_encoder *encoder);
+
+       /**
+        * @detect:
+        *
+        * This callback can be used by drivers who want to do detection on the
+        * encoder object instead of in connector functions.
+        *
+        * It is not used by any helper and therefore has purely driver-specific
+        * semantics. New drivers shouldn't use this and instead just implement
+        * their own private callbacks.
+        *
+        * FIXME:
+        *
+        * This should just be converted into a pile of driver vfuncs.
+        * Currently radeon, amdgpu and nouveau are using it.
+        */
+       enum drm_connector_status (*detect)(struct drm_encoder *encoder,
+                                           struct drm_connector *connector);
+
+       /**
+        * @disable:
+        *
+        * This callback should be used to disable the encoder. With the atomic
+        * drivers it is called before this encoder's CRTC has been shut off
+        * using the CRTC's own ->disable hook.  If that sequence is too simple
+        * drivers can just add their own driver private encoder hooks and call
+        * them from CRTC's callback by looping over all encoders connected to
+        * it using for_each_encoder_on_crtc().
+        *
+        * This hook is used both by legacy CRTC helpers and atomic helpers.
+        * Atomic drivers don't need to implement it if there's no need to
+        * disable anything at the encoder level. To ensure that runtime PM
+        * handling (using either DPMS or the new "ACTIVE" property) works
+        * @disable must be the inverse of @enable for atomic drivers.
+        *
+        * NOTE:
+        *
+        * With legacy CRTC helpers there's a big semantic difference between
+        * @disable and other hooks (like @prepare or @dpms) used to shut down a
+        * encoder: @disable is only called when also logically disabling the
+        * display pipeline and needs to release any resources acquired in
+        * @mode_set (like shared PLLs, or again release pinned framebuffers).
+        *
+        * Therefore @disable must be the inverse of @mode_set plus @commit for
+        * drivers still using legacy CRTC helpers, which is different from the
+        * rules under atomic.
+        */
+       void (*disable)(struct drm_encoder *encoder);
+
+       /**
+        * @enable:
+        *
+        * This callback should be used to enable the encoder. With the atomic
+        * drivers it is called after this encoder's CRTC has been enabled using
+        * the CRTC's own ->enable hook.  If that sequence is too simple drivers
+        * can just add their own driver private encoder hooks and call them
+        * from CRTC's callback by looping over all encoders connected to it
+        * using for_each_encoder_on_crtc().
+        *
+        * This hook is used only by atomic helpers, for symmetry with @disable.
+        * Atomic drivers don't need to implement it if there's no need to
+        * enable anything at the encoder level. To ensure that runtime PM handling
+        * (using either DPMS or the new "ACTIVE" property) works
+        * @enable must be the inverse of @disable for atomic drivers.
+        */
+       void (*enable)(struct drm_encoder *encoder);
+
+       /**
+        * @atomic_check:
+        *
+        * This callback is used to validate encoder state for atomic drivers.
+        * Since the encoder is the object connecting the CRTC and connector it
+        * gets passed both states, to be able to validate interactions and
+        * update the CRTC to match what the encoder needs for the requested
+        * connector.
+        *
+        * This function is used by the atomic helpers, but it is optional.
+        *
+        * NOTE:
+        *
+        * This function is called in the check phase of an atomic update. The
+        * driver is not allowed to change anything outside of the free-standing
+        * state objects passed-in or assembled in the overall &drm_atomic_state
+        * update tracking structure.
+        *
+        * RETURNS:
+        *
+        * 0 on success, -EINVAL if the state or the transition can't be
+        * supported, -ENOMEM on memory allocation failure and -EDEADLK if an
+        * attempt to obtain another state object ran into a &drm_modeset_lock
+        * deadlock.
+        */
+       int (*atomic_check)(struct drm_encoder *encoder,
+                           struct drm_crtc_state *crtc_state,
+                           struct drm_connector_state *conn_state);
+};
+
+/**
+ * drm_encoder_helper_add - sets the helper vtable for an encoder
+ * @encoder: DRM encoder
+ * @funcs: helper vtable to set for @encoder
+ */
+static inline void drm_encoder_helper_add(struct drm_encoder *encoder,
+                                         const struct drm_encoder_helper_funcs *funcs)
+{
+       encoder->helper_private = funcs;
+}
+
+/**
+ * struct drm_connector_helper_funcs - helper operations for connectors
+ *
+ * These functions are used by the atomic and legacy modeset helpers and by the
+ * probe helpers.
+ */
+struct drm_connector_helper_funcs {
+       /**
+        * @get_modes:
+        *
+        * This function should fill in all modes currently valid for the sink
+        * into the connector->probed_modes list. It should also update the
+        * EDID property by calling drm_mode_connector_update_edid_property().
+        *
+        * The usual way to implement this is to cache the EDID retrieved in the
+        * probe callback somewhere in the driver-private connector structure.
+        * In this function drivers then parse the modes in the EDID and add
+        * them by calling drm_add_edid_modes(). But connectors that driver a
+        * fixed panel can also manually add specific modes using
+        * drm_mode_probed_add(). Finally drivers that support audio probably
+        * want to update the ELD data, too, using drm_edid_to_eld().
+        *
+        * This function is only called after the ->detect() hook has indicated
+        * that a sink is connected and when the EDID isn't overridden through
+        * sysfs or the kernel commandline.
+        *
+        * This callback is used by the probe helpers in e.g.
+        * drm_helper_probe_single_connector_modes().
+        *
+        * RETURNS:
+        *
+        * The number of modes added by calling drm_mode_probed_add().
+        */
+       int (*get_modes)(struct drm_connector *connector);
+
+       /**
+        * @mode_valid:
+        *
+        * Callback to validate a mode for a connector, irrespective of the
+        * specific display configuration.
+        *
+        * This callback is used by the probe helpers to filter the mode list
+        * (which is usually derived from the EDID data block from the sink).
+        * See e.g. drm_helper_probe_single_connector_modes().
+        *
+        * NOTE:
+        *
+        * This only filters the mode list supplied to userspace in the
+        * GETCONNECOTR IOCTL. Userspace is free to create modes of its own and
+        * ask the kernel to use them. It this case the atomic helpers or legacy
+        * CRTC helpers will not call this function. Drivers therefore must
+        * still fully validate any mode passed in in a modeset request.
+        *
+        * RETURNS:
+        *
+        * Either MODE_OK or one of the failure reasons in enum
+        * &drm_mode_status.
+        */
+       enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+                                          struct drm_display_mode *mode);
+       /**
+        * @best_encoder:
+        *
+        * This function should select the best encoder for the given connector.
+        *
+        * This function is used by both the atomic helpers (in the
+        * drm_atomic_helper_check_modeset() function) and in the legacy CRTC
+        * helpers.
+        *
+        * NOTE:
+        *
+        * In atomic drivers this function is called in the check phase of an
+        * atomic update. The driver is not allowed to change or inspect
+        * anything outside of arguments passed-in. Atomic drivers which need to
+        * inspect dynamic configuration state should instead use
+        * @atomic_best_encoder.
+        *
+        * RETURNS:
+        *
+        * Encoder that should be used for the given connector and connector
+        * state, or NULL if no suitable encoder exists. Note that the helpers
+        * will ensure that encoders aren't used twice, drivers should not check
+        * for this.
+        */
+       struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
+
+       /**
+        * @atomic_best_encoder:
+        *
+        * This is the atomic version of @best_encoder for atomic drivers which
+        * need to select the best encoder depending upon the desired
+        * configuration and can't select it statically.
+        *
+        * This function is used by drm_atomic_helper_check_modeset() and either
+        * this or @best_encoder is required.
+        *
+        * NOTE:
+        *
+        * This function is called in the check phase of an atomic update. The
+        * driver is not allowed to change anything outside of the free-standing
+        * state objects passed-in or assembled in the overall &drm_atomic_state
+        * update tracking structure.
+        *
+        * RETURNS:
+        *
+        * Encoder that should be used for the given connector and connector
+        * state, or NULL if no suitable encoder exists. Note that the helpers
+        * will ensure that encoders aren't used twice, drivers should not check
+        * for this.
+        */
+       struct drm_encoder *(*atomic_best_encoder)(struct drm_connector *connector,
+                                                  struct drm_connector_state *connector_state);
+};
+
+/**
+ * drm_connector_helper_add - sets the helper vtable for a connector
+ * @connector: DRM connector
+ * @funcs: helper vtable to set for @connector
+ */
+static inline void drm_connector_helper_add(struct drm_connector *connector,
+                                           const struct drm_connector_helper_funcs *funcs)
+{
+       connector->helper_private = funcs;
+}
+
+/**
+ * struct drm_plane_helper_funcs - helper operations for planes
+ *
+ * These functions are used by the atomic helpers and by the transitional plane
+ * helpers.
+ */
+struct drm_plane_helper_funcs {
+       /**
+        * @prepare_fb:
+        *
+        * This hook is to prepare a framebuffer for scanout by e.g. pinning
+        * it's backing storage or relocating it into a contiguous block of
+        * VRAM. Other possible preparatory work includes flushing caches.
+        *
+        * This function must not block for outstanding rendering, since it is
+        * called in the context of the atomic IOCTL even for async commits to
+        * be able to return any errors to userspace. Instead the recommended
+        * way is to fill out the fence member of the passed-in
+        * &drm_plane_state. If the driver doesn't support native fences then
+        * equivalent functionality should be implemented through private
+        * members in the plane structure.
+        *
+        * The helpers will call @cleanup_fb with matching arguments for every
+        * successful call to this hook.
+        *
+        * This callback is used by the atomic modeset helpers and by the
+        * transitional plane helpers, but it is optional.
+        *
+        * RETURNS:
+        *
+        * 0 on success or one of the following negative error codes allowed by
+        * the atomic_commit hook in &drm_mode_config_funcs. When using helpers
+        * this callback is the only one which can fail an atomic commit,
+        * everything else must complete successfully.
+        */
+       int (*prepare_fb)(struct drm_plane *plane,
+                         const struct drm_plane_state *new_state);
+       /**
+        * @cleanup_fb:
+        *
+        * This hook is called to clean up any resources allocated for the given
+        * framebuffer and plane configuration in @prepare_fb.
+        *
+        * This callback is used by the atomic modeset helpers and by the
+        * transitional plane helpers, but it is optional.
+        */
+       void (*cleanup_fb)(struct drm_plane *plane,
+                          const struct drm_plane_state *old_state);
+
+       /**
+        * @atomic_check:
+        *
+        * Drivers should check plane specific constraints in this hook.
+        *
+        * When using drm_atomic_helper_check_planes() plane's ->atomic_check()
+        * hooks are called before the ones for CRTCs, which allows drivers to
+        * request shared resources that the CRTC controls here. For more
+        * complicated dependencies the driver can call the provided check helpers
+        * multiple times until the computed state has a final configuration and
+        * everything has been checked.
+        *
+        * This function is also allowed to inspect any other object's state and
+        * can add more state objects to the atomic commit if needed. Care must
+        * be taken though to ensure that state check&compute functions for
+        * these added states are all called, and derived state in other objects
+        * all updated. Again the recommendation is to just call check helpers
+        * until a maximal configuration is reached.
+        *
+        * This callback is used by the atomic modeset helpers and by the
+        * transitional plane helpers, but it is optional.
+        *
+        * NOTE:
+        *
+        * This function is called in the check phase of an atomic update. The
+        * driver is not allowed to change anything outside of the free-standing
+        * state objects passed-in or assembled in the overall &drm_atomic_state
+        * update tracking structure.
+        *
+        * RETURNS:
+        *
+        * 0 on success, -EINVAL if the state or the transition can't be
+        * supported, -ENOMEM on memory allocation failure and -EDEADLK if an
+        * attempt to obtain another state object ran into a &drm_modeset_lock
+        * deadlock.
+        */
+       int (*atomic_check)(struct drm_plane *plane,
+                           struct drm_plane_state *state);
+
+       /**
+        * @atomic_update:
+        *
+        * Drivers should use this function to update the plane state.  This
+        * hook is called in-between the ->atomic_begin() and
+        * ->atomic_flush() of &drm_crtc_helper_funcs.
+        *
+        * Note that the power state of the display pipe when this function is
+        * called depends upon the exact helpers and calling sequence the driver
+        * has picked. See drm_atomic_commit_planes() for a discussion of the
+        * tradeoffs and variants of plane commit helpers.
+        *
+        * This callback is used by the atomic modeset helpers and by the
+        * transitional plane helpers, but it is optional.
+        */
+       void (*atomic_update)(struct drm_plane *plane,
+                             struct drm_plane_state *old_state);
+       /**
+        * @atomic_disable:
+        *
+        * Drivers should use this function to unconditionally disable a plane.
+        * This hook is called in-between the ->atomic_begin() and
+        * ->atomic_flush() of &drm_crtc_helper_funcs. It is an alternative to
+        * @atomic_update, which will be called for disabling planes, too, if
+        * the @atomic_disable hook isn't implemented.
+        *
+        * This hook is also useful to disable planes in preparation of a modeset,
+        * by calling drm_atomic_helper_disable_planes_on_crtc() from the
+        * ->disable() hook in &drm_crtc_helper_funcs.
+        *
+        * Note that the power state of the display pipe when this function is
+        * called depends upon the exact helpers and calling sequence the driver
+        * has picked. See drm_atomic_commit_planes() for a discussion of the
+        * tradeoffs and variants of plane commit helpers.
+        *
+        * This callback is used by the atomic modeset helpers and by the
+        * transitional plane helpers, but it is optional.
+        */
+       void (*atomic_disable)(struct drm_plane *plane,
+                              struct drm_plane_state *old_state);
+};
+
+/**
+ * drm_plane_helper_add - sets the helper vtable for a plane
+ * @plane: DRM plane
+ * @funcs: helper vtable to set for @plane
+ */
+static inline void drm_plane_helper_add(struct drm_plane *plane,
+                                       const struct drm_plane_helper_funcs *funcs)
+{
+       plane->helper_private = funcs;
+}
+
+#endif
index 5a7f9d4..4421f3f 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <drm/drm_rect.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_modeset_helper_vtables.h>
 
 /*
  * Drivers that don't allow primary plane scaling may pass this macro in place
  */
 #define DRM_PLANE_HELPER_NO_SCALING (1<<16)
 
-/**
- * DOC: plane helpers
- *
- * Helper functions to assist with creation and handling of CRTC primary
- * planes.
- */
-
 int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
                  const struct drm_crtc_funcs *funcs);
 
-/**
- * drm_plane_helper_funcs - helper operations for CRTCs
- * @prepare_fb: prepare a framebuffer for use by the plane
- * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
- * @atomic_check: check that a given atomic state is valid and can be applied
- * @atomic_update: apply an atomic state to the plane (mandatory)
- * @atomic_disable: disable the plane
- *
- * The helper operations are called by the mid-layer CRTC helper.
- */
-struct drm_plane_helper_funcs {
-       int (*prepare_fb)(struct drm_plane *plane,
-                         const struct drm_plane_state *new_state);
-       void (*cleanup_fb)(struct drm_plane *plane,
-                          const struct drm_plane_state *old_state);
-
-       int (*atomic_check)(struct drm_plane *plane,
-                           struct drm_plane_state *state);
-       void (*atomic_update)(struct drm_plane *plane,
-                             struct drm_plane_state *old_state);
-       void (*atomic_disable)(struct drm_plane *plane,
-                              struct drm_plane_state *old_state);
-};
-
-static inline void drm_plane_helper_add(struct drm_plane *plane,
-                                       const struct drm_plane_helper_funcs *funcs)
-{
-       plane->helper_private = funcs;
-}
-
 int drm_plane_helper_check_update(struct drm_plane *plane,
                                  struct drm_crtc *crtc,
                                  struct drm_framebuffer *fb,
index f1a113e..f970209 100644 (file)
 #define INTEL_SKL_GT3_IDS(info) \
        INTEL_VGA_DEVICE(0x1926, info), /* ULT GT3 */ \
        INTEL_VGA_DEVICE(0x192B, info), /* Halo GT3 */ \
-       INTEL_VGA_DEVICE(0x192A, info) /* SRV GT3 */ \
+       INTEL_VGA_DEVICE(0x192A, info)  /* SRV GT3 */
 
-#define INTEL_SKL_IDS(info) \
+#define INTEL_SKL_GT4_IDS(info) \
+       INTEL_VGA_DEVICE(0x1932, info), /* DT GT4 */ \
+       INTEL_VGA_DEVICE(0x193B, info), /* Halo GT4 */ \
+       INTEL_VGA_DEVICE(0x193D, info), /* WKS GT4 */ \
+       INTEL_VGA_DEVICE(0x193A, info)  /* SRV GT4 */
+
+#define INTEL_SKL_IDS(info)     \
        INTEL_SKL_GT1_IDS(info), \
        INTEL_SKL_GT2_IDS(info), \
-       INTEL_SKL_GT3_IDS(info)
+       INTEL_SKL_GT3_IDS(info), \
+       INTEL_SKL_GT4_IDS(info)
 
 #define INTEL_BXT_IDS(info) \
        INTEL_VGA_DEVICE(0x0A84, info), \
index 813042c..3d4bf08 100644 (file)
@@ -826,10 +826,10 @@ static inline int __ttm_bo_reserve(struct ttm_buffer_object *bo,
  * reserved, the validation sequence is checked against the validation
  * sequence of the process currently reserving the buffer,
  * and if the current validation sequence is greater than that of the process
- * holding the reservation, the function returns -EAGAIN. Otherwise it sleeps
+ * holding the reservation, the function returns -EDEADLK. Otherwise it sleeps
  * waiting for the buffer to become unreserved, after which it retries
  * reserving.
- * The caller should, when receiving an -EAGAIN error
+ * The caller should, when receiving an -EDEADLK error
  * release all its buffer reservations, wait for @bo to become unreserved, and
  * then rerun the validation with the same validation sequence. This procedure
  * will always guarantee that the process with the lowest validation sequence
index 159551f..9355dd8 100644 (file)
@@ -18,4 +18,5 @@ header-y += tegra_drm.h
 header-y += via_drm.h
 header-y += vmwgfx_drm.h
 header-y += msm_drm.h
+header-y += vc4_drm.h
 header-y += virtgpu_drm.h
index e995ffb..81e6e0d 100644 (file)
@@ -121,7 +121,7 @@ struct drm_msm_gem_cpu_fini {
 struct drm_msm_gem_submit_reloc {
        __u32 submit_offset;  /* in, offset from submit_bo */
        __u32 or;             /* in, value OR'd with result */
-       __s32  shift;          /* in, amount of left shift (can be negative) */
+       __s32 shift;          /* in, amount of left shift (can be negative) */
        __u32 reloc_idx;      /* in, index of reloc_bo buffer */
        __u64 reloc_offset;   /* in, offset from start of reloc_bo */
 };
diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h
new file mode 100644 (file)
index 0000000..eeb37e3
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright © 2014-2015 Broadcom
+ *
+ * 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.
+ */
+
+#ifndef _UAPI_VC4_DRM_H_
+#define _UAPI_VC4_DRM_H_
+
+#include "drm.h"
+
+#define DRM_VC4_SUBMIT_CL                         0x00
+#define DRM_VC4_WAIT_SEQNO                        0x01
+#define DRM_VC4_WAIT_BO                           0x02
+#define DRM_VC4_CREATE_BO                         0x03
+#define DRM_VC4_MMAP_BO                           0x04
+#define DRM_VC4_CREATE_SHADER_BO                  0x05
+#define DRM_VC4_GET_HANG_STATE                    0x06
+
+#define DRM_IOCTL_VC4_SUBMIT_CL           DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl)
+#define DRM_IOCTL_VC4_WAIT_SEQNO          DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno)
+#define DRM_IOCTL_VC4_WAIT_BO             DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo)
+#define DRM_IOCTL_VC4_CREATE_BO           DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo)
+#define DRM_IOCTL_VC4_MMAP_BO             DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo)
+#define DRM_IOCTL_VC4_CREATE_SHADER_BO    DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo)
+#define DRM_IOCTL_VC4_GET_HANG_STATE      DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state)
+
+struct drm_vc4_submit_rcl_surface {
+       __u32 hindex; /* Handle index, or ~0 if not present. */
+       __u32 offset; /* Offset to start of buffer. */
+       /*
+        * Bits for either render config (color_write) or load/store packet.
+        * Bits should all be 0 for MSAA load/stores.
+        */
+       __u16 bits;
+
+#define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES                (1 << 0)
+       __u16 flags;
+};
+
+/**
+ * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D
+ * engine.
+ *
+ * Drivers typically use GPU BOs to store batchbuffers / command lists and
+ * their associated state.  However, because the VC4 lacks an MMU, we have to
+ * do validation of memory accesses by the GPU commands.  If we were to store
+ * our commands in BOs, we'd need to do uncached readback from them to do the
+ * validation process, which is too expensive.  Instead, userspace accumulates
+ * commands and associated state in plain memory, then the kernel copies the
+ * data to its own address space, and then validates and stores it in a GPU
+ * BO.
+ */
+struct drm_vc4_submit_cl {
+       /* Pointer to the binner command list.
+        *
+        * This is the first set of commands executed, which runs the
+        * coordinate shader to determine where primitives land on the screen,
+        * then writes out the state updates and draw calls necessary per tile
+        * to the tile allocation BO.
+        */
+       __u64 bin_cl;
+
+       /* Pointer to the shader records.
+        *
+        * Shader records are the structures read by the hardware that contain
+        * pointers to uniforms, shaders, and vertex attributes.  The
+        * reference to the shader record has enough information to determine
+        * how many pointers are necessary (fixed number for shaders/uniforms,
+        * and an attribute count), so those BO indices into bo_handles are
+        * just stored as __u32s before each shader record passed in.
+        */
+       __u64 shader_rec;
+
+       /* Pointer to uniform data and texture handles for the textures
+        * referenced by the shader.
+        *
+        * For each shader state record, there is a set of uniform data in the
+        * order referenced by the record (FS, VS, then CS).  Each set of
+        * uniform data has a __u32 index into bo_handles per texture
+        * sample operation, in the order the QPU_W_TMUn_S writes appear in
+        * the program.  Following the texture BO handle indices is the actual
+        * uniform data.
+        *
+        * The individual uniform state blocks don't have sizes passed in,
+        * because the kernel has to determine the sizes anyway during shader
+        * code validation.
+        */
+       __u64 uniforms;
+       __u64 bo_handles;
+
+       /* Size in bytes of the binner command list. */
+       __u32 bin_cl_size;
+       /* Size in bytes of the set of shader records. */
+       __u32 shader_rec_size;
+       /* Number of shader records.
+        *
+        * This could just be computed from the contents of shader_records and
+        * the address bits of references to them from the bin CL, but it
+        * keeps the kernel from having to resize some allocations it makes.
+        */
+       __u32 shader_rec_count;
+       /* Size in bytes of the uniform state. */
+       __u32 uniforms_size;
+
+       /* Number of BO handles passed in (size is that times 4). */
+       __u32 bo_handle_count;
+
+       /* RCL setup: */
+       __u16 width;
+       __u16 height;
+       __u8 min_x_tile;
+       __u8 min_y_tile;
+       __u8 max_x_tile;
+       __u8 max_y_tile;
+       struct drm_vc4_submit_rcl_surface color_read;
+       struct drm_vc4_submit_rcl_surface color_write;
+       struct drm_vc4_submit_rcl_surface zs_read;
+       struct drm_vc4_submit_rcl_surface zs_write;
+       struct drm_vc4_submit_rcl_surface msaa_color_write;
+       struct drm_vc4_submit_rcl_surface msaa_zs_write;
+       __u32 clear_color[2];
+       __u32 clear_z;
+       __u8 clear_s;
+
+       __u32 pad:24;
+
+#define VC4_SUBMIT_CL_USE_CLEAR_COLOR                  (1 << 0)
+       __u32 flags;
+
+       /* Returned value of the seqno of this render job (for the
+        * wait ioctl).
+        */
+       __u64 seqno;
+};
+
+/**
+ * struct drm_vc4_wait_seqno - ioctl argument for waiting for
+ * DRM_VC4_SUBMIT_CL completion using its returned seqno.
+ *
+ * timeout_ns is the timeout in nanoseconds, where "0" means "don't
+ * block, just return the status."
+ */
+struct drm_vc4_wait_seqno {
+       __u64 seqno;
+       __u64 timeout_ns;
+};
+
+/**
+ * struct drm_vc4_wait_bo - ioctl argument for waiting for
+ * completion of the last DRM_VC4_SUBMIT_CL on a BO.
+ *
+ * This is useful for cases where multiple processes might be
+ * rendering to a BO and you want to wait for all rendering to be
+ * completed.
+ */
+struct drm_vc4_wait_bo {
+       __u32 handle;
+       __u32 pad;
+       __u64 timeout_ns;
+};
+
+/**
+ * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs.
+ *
+ * There are currently no values for the flags argument, but it may be
+ * used in a future extension.
+ */
+struct drm_vc4_create_bo {
+       __u32 size;
+       __u32 flags;
+       /** Returned GEM handle for the BO. */
+       __u32 handle;
+       __u32 pad;
+};
+
+/**
+ * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs.
+ *
+ * This doesn't actually perform an mmap.  Instead, it returns the
+ * offset you need to use in an mmap on the DRM device node.  This
+ * means that tools like valgrind end up knowing about the mapped
+ * memory.
+ *
+ * There are currently no values for the flags argument, but it may be
+ * used in a future extension.
+ */
+struct drm_vc4_mmap_bo {
+       /** Handle for the object being mapped. */
+       __u32 handle;
+       __u32 flags;
+       /** offset into the drm node to use for subsequent mmap call. */
+       __u64 offset;
+};
+
+/**
+ * struct drm_vc4_create_shader_bo - ioctl argument for creating VC4
+ * shader BOs.
+ *
+ * Since allowing a shader to be overwritten while it's also being
+ * executed from would allow privlege escalation, shaders must be
+ * created using this ioctl, and they can't be mmapped later.
+ */
+struct drm_vc4_create_shader_bo {
+       /* Size of the data argument. */
+       __u32 size;
+       /* Flags, currently must be 0. */
+       __u32 flags;
+
+       /* Pointer to the data. */
+       __u64 data;
+
+       /** Returned GEM handle for the BO. */
+       __u32 handle;
+       /* Pad, must be 0. */
+       __u32 pad;
+};
+
+struct drm_vc4_get_hang_state_bo {
+       __u32 handle;
+       __u32 paddr;
+       __u32 size;
+       __u32 pad;
+};
+
+/**
+ * struct drm_vc4_hang_state - ioctl argument for collecting state
+ * from a GPU hang for analysis.
+*/
+struct drm_vc4_get_hang_state {
+       /** Pointer to array of struct drm_vc4_get_hang_state_bo. */
+       __u64 bo;
+       /**
+        * On input, the size of the bo array.  Output is the number
+        * of bos to be returned.
+        */
+       __u32 bo_count;
+
+       __u32 start_bin, start_render;
+
+       __u32 ct0ca, ct0ea;
+       __u32 ct1ca, ct1ea;
+       __u32 ct0cs, ct1cs;
+       __u32 ct0ra0, ct1ra0;
+
+       __u32 bpca, bpcs;
+       __u32 bpoa, bpos;
+
+       __u32 vpmbase;
+
+       __u32 dbge;
+       __u32 fdbgo;
+       __u32 fdbgb;
+       __u32 fdbgr;
+       __u32 fdbgs;
+       __u32 errstat;
+
+       /* Pad that we may save more registers into in the future. */
+       __u32 pad[16];
+};
+
+#endif /* _UAPI_VC4_DRM_H_ */