]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
Merge tag 'drm-misc-next-2017-09-20' of git://anongit.freedesktop.org/git/drm-misc...
authorDave Airlie <airlied@redhat.com>
Wed, 27 Sep 2017 19:45:27 +0000 (05:45 +1000)
committerDave Airlie <airlied@redhat.com>
Wed, 27 Sep 2017 19:46:15 +0000 (05:46 +1000)
UAPI Changes:

Cross-subsystem Changes:

Core Changes:
- DP SDP defines (Ville)
- polish for scdc helpers (Thierry Reding)
- fix lifetimes for connector/plane state across crtc changes (Maarten
  Lankhorst).
- sparse fixes (Ville+Thierry)
- make legacy kms ioctls all interruptible (Maarten)
- push edid override into the edid helpers (out of probe helpers)
  (Jani)
- DP ESI defines for link status (DK)

Driver Changes:
- drm-panel is now in drm-misc!
- minor panel-simple cleanups/refactoring by various folks
- drm_bridge_add cleanup (Inki Dae)
- constify a few i2c_device_id structs (Arvind Yadav)
- More patches from Noralf's fb/gem helper cleanup
- bridge/synopsis: reset fix (Philippe Cornu)
- fix tracepoint include handling in drivers (Thierry)
- rockchip: lvds support (Sandy Huang)
- move sun4i into drm-misc fold (Maxime Ripard)
- sun4i: refactor driver load + support TCON backend/layer muxing
  (Chen-Yu Tsai)
- pl111: support more pl11x variants (Linus Walleij)
- bridge/adv7511: robustify probing/edid handling (Lars-Petersen
  Clausen)

New hw support:
- S6E63J0X03 panel (Hoegeun Kwon)
- OTM8009A panel (Philippe CORNU)
- Seiko 43WVF1G panel (Marco Franchi)
- tve200 driver (Linus Walleij)

Plus assorted of tiny patches all over, including our first outreachy
patches from applicants for the winter round!

* tag 'drm-misc-next-2017-09-20' of git://anongit.freedesktop.org/git/drm-misc: (101 commits)
  drm: add backwards compatibility support for drm_kms_helper.edid_firmware
  drm: handle override and firmware EDID at drm_do_get_edid() level
  drm/dp: DPCD register defines for link status within ESI field
  drm/rockchip: Replace dev_* with DRM_DEV_*
  drm/tinydrm: Drop driver registered message
  drm/gem-fb-helper: Use debug message on gem lookup failure
  drm/imx: Use drm_gem_fb_create() and drm_gem_fb_prepare_fb()
  drm/bridge: adv7511: Constify HDMI CODEC platform data
  drm/bridge: adv7511: Enable connector polling when no interrupt is specified
  drm/bridge: adv7511: Remove private copy of the EDID
  drm/bridge: adv7511: Properly update EDID when no EDID was found
  drm/crtc: Convert setcrtc ioctl locking to interruptible.
  drm/atomic: Convert pageflip ioctl locking to interruptible.
  drm/legacy: Convert setplane ioctl locking to interruptible.
  drm/legacy: Convert cursor ioctl locking to interruptible.
  drm/atomic: Convert atomic ioctl locking to interruptible.
  drm/atomic: Prepare drm_modeset_lock infrastructure for interruptible waiting, v2.
  drm/tve200: Clean up panel bridging
  drm/doc: Update todo.rst
  drm/dp/mst: Sideband message transaction to power up/down nodes
  ...

124 files changed:
Documentation/admin-guide/kernel-parameters.txt
Documentation/devicetree/bindings/display/faraday,tve200.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/samsung,s6e63j0x03.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/seiko,43wvf1g.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/rockchip/rockchip-lvds.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/gpu/drm-uapi.rst
Documentation/gpu/index.rst
Documentation/gpu/todo.rst
Documentation/gpu/tve200.rst [new file with mode: 0644]
MAINTAINERS
drivers/dma-buf/sw_sync.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/arc/arcpgu_drv.c
drivers/gpu/drm/arm/hdlcd_drv.c
drivers/gpu/drm/arm/malidp_drv.c
drivers/gpu/drm/armada/Makefile
drivers/gpu/drm/armada/armada_drv.c
drivers/gpu/drm/armada/armada_gem.c
drivers/gpu/drm/armada/armada_gem.h
drivers/gpu/drm/armada/armada_trace.h
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
drivers/gpu/drm/bridge/adv7511/adv7511.h
drivers/gpu/drm/bridge/adv7511/adv7511_audio.c
drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_bridge.c
drivers/gpu/drm/drm_connector.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_debugfs_crc.c
drivers/gpu/drm/drm_dp_mst_topology.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_edid_load.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_gem_framebuffer_helper.c
drivers/gpu/drm/drm_kms_helper_common.c
drivers/gpu/drm/drm_mode_object.c
drivers/gpu/drm/drm_modeset_lock.c
drivers/gpu/drm/drm_plane.c
drivers/gpu/drm/drm_probe_helper.c
drivers/gpu/drm/drm_scdc_helper.c
drivers/gpu/drm/drm_syncobj.c
drivers/gpu/drm/drm_trace.h
drivers/gpu/drm/exynos/exynos_drm_mic.c
drivers/gpu/drm/gma500/cdv_intel_dp.c
drivers/gpu/drm/gma500/mdfld_intel_display.c
drivers/gpu/drm/i2c/ch7006_drv.c
drivers/gpu/drm/i2c/sil164_drv.c
drivers/gpu/drm/i2c/tda998x_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/imx/imx-drm-core.c
drivers/gpu/drm/imx/ipuv3-plane.c
drivers/gpu/drm/mediatek/mtk_hdmi.c
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/Makefile
drivers/gpu/drm/panel/panel-orisetech-otm8009a.c [new file with mode: 0644]
drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c [new file with mode: 0644]
drivers/gpu/drm/panel/panel-seiko-43wvf1g.c [new file with mode: 0644]
drivers/gpu/drm/panel/panel-simple.c
drivers/gpu/drm/pl111/Kconfig
drivers/gpu/drm/pl111/Makefile
drivers/gpu/drm/pl111/pl111_connector.c [deleted file]
drivers/gpu/drm/pl111/pl111_debugfs.c
drivers/gpu/drm/pl111/pl111_display.c
drivers/gpu/drm/pl111/pl111_drm.h
drivers/gpu/drm/pl111/pl111_drv.c
drivers/gpu/drm/pl111/pl111_versatile.c [new file with mode: 0644]
drivers/gpu/drm/pl111/pl111_versatile.h [new file with mode: 0644]
drivers/gpu/drm/rockchip/Kconfig
drivers/gpu/drm/rockchip/Makefile
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
drivers/gpu/drm/rockchip/cdn-dp-reg.c
drivers/gpu/drm/rockchip/dw-mipi-dsi.c
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
drivers/gpu/drm/rockchip/inno_hdmi.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_fb.c
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_lvds.c [new file with mode: 0644]
drivers/gpu/drm/rockchip/rockchip_lvds.h [new file with mode: 0644]
drivers/gpu/drm/rockchip/rockchip_vop_reg.c
drivers/gpu/drm/sti/sti_drv.c
drivers/gpu/drm/sti/sti_dvo.c
drivers/gpu/drm/stm/drv.c
drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
drivers/gpu/drm/sun4i/sun4i_drv.c
drivers/gpu/drm/sun4i/sun4i_tcon.c
drivers/gpu/drm/sun4i/sun4i_tcon.h
drivers/gpu/drm/tilcdc/tilcdc_drv.c
drivers/gpu/drm/tinydrm/mi0283qt.c
drivers/gpu/drm/tinydrm/mipi-dbi.c
drivers/gpu/drm/tinydrm/repaper.c
drivers/gpu/drm/tinydrm/st7586.c
drivers/gpu/drm/tve200/Kconfig [new file with mode: 0644]
drivers/gpu/drm/tve200/Makefile [new file with mode: 0644]
drivers/gpu/drm/tve200/tve200_display.c [new file with mode: 0644]
drivers/gpu/drm/tve200/tve200_drm.h [new file with mode: 0644]
drivers/gpu/drm/tve200/tve200_drv.c [new file with mode: 0644]
drivers/gpu/drm/vc4/Makefile
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_trace.h
drivers/gpu/drm/zte/zx_drm_drv.c
include/drm/drm_atomic.h
include/drm/drm_bridge.h
include/drm/drm_connector.h
include/drm/drm_crtc.h
include/drm/drm_dp_helper.h
include/drm/drm_dp_mst_helper.h
include/drm/drm_edid.h
include/drm/drm_gem_framebuffer_helper.h
include/drm/drm_modeset_helper_vtables.h
include/drm/drm_modeset_lock.h
include/drm/drm_plane.h
include/linux/sync_file.h
include/uapi/drm/drm_mode.h

index 05496622b4effb8212eb2175bea67f9a7bebe0d0..b24108f2a438d83ca88622ff51fca2279c8d8639 100644 (file)
                        The filter can be disabled or changed to another
                        driver later using sysfs.
 
-       drm_kms_helper.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>]
+       drm.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>]
                        Broken monitors, graphic adapters, KVMs and EDIDless
                        panels may send no or incorrect EDID data sets.
                        This parameter allows to specify an EDID data sets
diff --git a/Documentation/devicetree/bindings/display/faraday,tve200.txt b/Documentation/devicetree/bindings/display/faraday,tve200.txt
new file mode 100644 (file)
index 0000000..82e3bc0
--- /dev/null
@@ -0,0 +1,54 @@
+* Faraday TV Encoder TVE200
+
+Required properties:
+
+- compatible: must be one of:
+       "faraday,tve200"
+       "cortina,gemini-tvc", "faraday,tve200"
+
+- reg: base address and size of the control registers block
+
+- interrupts: contains an interrupt specifier for the interrupt
+       line from the TVE200
+
+- clock-names: should contain "PCLK" for the clock line clocking the
+       silicon and "TVE" for the 27MHz clock to the video driver
+
+- clocks: contains phandle and clock specifier pairs for the entries
+       in the clock-names property. See
+       Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Optional properties:
+
+- resets: contains the reset line phandle for the block
+
+Required sub-nodes:
+
+- port: describes LCD panel signals, following the common binding
+       for video transmitter interfaces; see
+       Documentation/devicetree/bindings/media/video-interfaces.txt
+       This port should have the properties:
+       reg = <0>;
+       It should have one endpoint connected to a remote endpoint where
+       the display is connected.
+
+Example:
+
+display-controller@6a000000 {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       compatible = "faraday,tve200";
+       reg = <0x6a000000 0x1000>;
+       interrupts = <13 IRQ_TYPE_EDGE_RISING>;
+       resets = <&syscon GEMINI_RESET_TVC>;
+       clocks = <&syscon GEMINI_CLK_GATE_TVC>,
+                <&syscon GEMINI_CLK_TVC>;
+       clock-names = "PCLK", "TVE";
+
+       port@0 {
+               reg = <0>;
+               display_out: endpoint {
+                       remote-endpoint = <&panel_in>;
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt
new file mode 100644 (file)
index 0000000..6862028
--- /dev/null
@@ -0,0 +1,21 @@
+Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode)
+
+The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using
+a MIPI-DSI video interface. Its backlight is managed through the DSI link.
+
+Required properties:
+  - compatible: "orisetech,otm8009a"
+  - reg: the virtual channel number of a DSI peripheral
+
+Optional properties:
+  - reset-gpios: a GPIO spec for the reset pin (active low).
+
+Example:
+&dsi {
+       ...
+       panel@0 {
+               compatible = "orisetech,otm8009a";
+               reg = <0>;
+               reset-gpios = <&gpioh 7 GPIO_ACTIVE_LOW>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e63j0x03.txt b/Documentation/devicetree/bindings/display/panel/samsung,s6e63j0x03.txt
new file mode 100644 (file)
index 0000000..3f1a839
--- /dev/null
@@ -0,0 +1,24 @@
+Samsung S6E63J0X03 1.63" 320x320 AMOLED panel (interface: MIPI-DSI command mode)
+
+Required properties:
+  - compatible: "samsung,s6e63j0x03"
+  - reg: the virtual channel number of a DSI peripheral
+  - vdd3-supply: I/O voltage supply
+  - vci-supply: voltage supply for analog circuits
+  - reset-gpios: a GPIO spec for the reset pin (active low)
+  - te-gpios: a GPIO spec for the tearing effect synchronization signal
+    gpio pin (active high)
+
+Example:
+&dsi {
+       ...
+
+       panel@0 {
+               compatible = "samsung,s6e63j0x03";
+               reg = <0>;
+               vdd3-supply = <&ldo16_reg>;
+               vci-supply = <&ldo20_reg>;
+               reset-gpios = <&gpe0 1 GPIO_ACTIVE_LOW>;
+               te-gpios = <&gpx0 6 GPIO_ACTIVE_HIGH>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/display/panel/seiko,43wvf1g.txt b/Documentation/devicetree/bindings/display/panel/seiko,43wvf1g.txt
new file mode 100644 (file)
index 0000000..aae57ef
--- /dev/null
@@ -0,0 +1,23 @@
+Seiko Instruments Inc. 4.3" WVGA (800 x RGB x 480) TFT with Touch-Panel
+
+Required properties:
+- compatible: should be "sii,43wvf1g".
+- "dvdd-supply": 3v3 digital regulator.
+- "avdd-supply": 5v analog regulator.
+
+Optional properties:
+- backlight: phandle for the backlight control.
+
+Example:
+
+       panel {
+               compatible = "sii,43wvf1g";
+               backlight = <&backlight_display>;
+               dvdd-supply = <&reg_lcd_3v3>;
+               avdd-supply = <&reg_lcd_5v>;
+               port {
+                       panel_in: endpoint {
+                               remote-endpoint = <&display_out>;
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-lvds.txt b/Documentation/devicetree/bindings/display/rockchip/rockchip-lvds.txt
new file mode 100644 (file)
index 0000000..da6939e
--- /dev/null
@@ -0,0 +1,99 @@
+Rockchip RK3288 LVDS interface
+================================
+
+Required properties:
+- compatible: matching the soc type, one of
+       - "rockchip,rk3288-lvds";
+
+- reg: physical base address of the controller and length
+       of memory mapped region.
+- clocks: must include clock specifiers corresponding to entries in the
+       clock-names property.
+- clock-names: must contain "pclk_lvds"
+
+- avdd1v0-supply: regulator phandle for 1.0V analog power
+- avdd1v8-supply: regulator phandle for 1.8V analog power
+- avdd3v3-supply: regulator phandle for 3.3V analog power
+
+- rockchip,grf: phandle to the general register files syscon
+- rockchip,output: "rgb", "lvds" or "duallvds", This describes the output interface
+
+Optional properties:
+- pinctrl-names: must contain a "lcdc" entry.
+- pinctrl-0: pin control group to be used for this controller.
+
+Required nodes:
+
+The lvds has two video ports as described by
+       Documentation/devicetree/bindings/media/video-interfaces.txt
+Their connections are modeled using the OF graph bindings specified in
+       Documentation/devicetree/bindings/graph.txt.
+
+- video port 0 for the VOP input, the remote endpoint maybe vopb or vopl
+- video port 1 for either a panel or subsequent encoder
+
+the lvds panel described by
+       Documentation/devicetree/bindings/display/panel/simple-panel.txt
+
+Panel required properties:
+- ports for remote LVDS output
+
+Panel optional properties:
+- data-mapping: should be "vesa-24","jeida-24" or "jeida-18".
+This describes decribed by:
+       Documentation/devicetree/bindings/display/panel/panel-lvds.txt
+
+Example:
+
+lvds_panel: lvds-panel {
+       compatible = "auo,b101ean01";
+       enable-gpios = <&gpio7 21 GPIO_ACTIVE_HIGH>;
+       data-mapping = "jeida-24";
+
+       ports {
+               panel_in_lvds: endpoint {
+                       remote-endpoint = <&lvds_out_panel>;
+               };
+       };
+};
+
+For Rockchip RK3288:
+
+       lvds: lvds@ff96c000 {
+               compatible = "rockchip,rk3288-lvds";
+               rockchip,grf = <&grf>;
+               reg = <0xff96c000 0x4000>;
+               clocks = <&cru PCLK_LVDS_PHY>;
+               clock-names = "pclk_lvds";
+               pinctrl-names = "lcdc";
+               pinctrl-0 = <&lcdc_ctl>;
+               avdd1v0-supply = <&vdd10_lcd>;
+               avdd1v8-supply = <&vcc18_lcd>;
+               avdd3v3-supply = <&vcca_33>;
+               rockchip,output = "rgb";
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       lvds_in: port@0 {
+                               reg = <0>;
+
+                               lvds_in_vopb: endpoint@0 {
+                                       reg = <0>;
+                                       remote-endpoint = <&vopb_out_lvds>;
+                               };
+                               lvds_in_vopl: endpoint@1 {
+                                       reg = <1>;
+                                       remote-endpoint = <&vopl_out_lvds>;
+                               };
+                       };
+
+                       lvds_out: port@1 {
+                               reg = <1>;
+
+                               lvds_out_panel: endpoint {
+                                       remote-endpoint = <&panel_in_lvds>;
+                               };
+                       };
+               };
+       };
index 1afd298eddd73147ebf6a1dcbc56bf404559cff1..6cf1dc5bc77ed4a38a19385dcbeac5541b21c3a0 100644 (file)
@@ -248,6 +248,7 @@ ontat       On Tat Industrial Company
 opencores      OpenCores.org
 option Option NV
 ORCL   Oracle Corporation
+orisetech      Orise Technology
 ortustech      Ortus Technology Co., Ltd.
 ovti   OmniVision Technologies
 oxsemi Oxford Semiconductor, Ltd.
index 679373b4a03f2189e14cce4c30686bdc947eb5a1..a2214cc1f821a3bb4b9b096adf3df5e29874a838 100644 (file)
@@ -168,6 +168,61 @@ IOCTL Support on Device Nodes
 .. kernel-doc:: drivers/gpu/drm/drm_ioctl.c
    :doc: driver specific ioctls
 
+Recommended IOCTL Return Values
+-------------------------------
+
+In theory a driver's IOCTL callback is only allowed to return very few error
+codes. In practice it's good to abuse a few more. This section documents common
+practice within the DRM subsystem:
+
+ENOENT:
+        Strictly this should only be used when a file doesn't exist e.g. when
+        calling the open() syscall. We reuse that to signal any kind of object
+        lookup failure, e.g. for unknown GEM buffer object handles, unknown KMS
+        object handles and similar cases.
+
+ENOSPC:
+        Some drivers use this to differentiate "out of kernel memory" from "out
+        of VRAM". Sometimes also applies to other limited gpu resources used for
+        rendering (e.g. when you have a special limited compression buffer).
+        Sometimes resource allocation/reservation issues in command submission
+        IOCTLs are also signalled through EDEADLK.
+
+        Simply running out of kernel/system memory is signalled through ENOMEM.
+
+EPERM/EACCESS:
+        Returned for an operation that is valid, but needs more privileges.
+        E.g. root-only or much more common, DRM master-only operations return
+        this when when called by unpriviledged clients. There's no clear
+        difference between EACCESS and EPERM.
+
+ENODEV:
+        Feature (like PRIME, modesetting, GEM) is not supported by the driver.
+
+ENXIO:
+        Remote failure, either a hardware transaction (like i2c), but also used
+        when the exporting driver of a shared dma-buf or fence doesn't support a
+        feature needed.
+
+EINTR:
+        DRM drivers assume that userspace restarts all IOCTLs. Any DRM IOCTL can
+        return EINTR and in such a case should be restarted with the IOCTL
+        parameters left unchanged.
+
+EIO:
+        The GPU died and couldn't be resurrected through a reset. Modesetting
+        hardware failures are signalled through the "link status" connector
+        property.
+
+EINVAL:
+        Catch-all for anything that is an invalid argument combination which
+        cannot work.
+
+IOCTL also use other error codes like ETIME, EFAULT, EBUSY, ENOTTY but their
+usage is in line with the common meanings. The above list tries to just document
+DRM specific patterns. Note that ENOTTY has the slightly unintuitive meaning of
+"this IOCTL does not exist", and is used exactly as such in DRM.
+
 .. kernel-doc:: include/drm/drm_ioctl.h
    :internal:
 
index 35d673bf9b569537321d8268c6f594cb9ff6475b..c36586dad29d20bf7b1704efa87f69945c31a542 100644 (file)
@@ -15,6 +15,7 @@ Linux GPU Driver Developer's Guide
    pl111
    tegra
    tinydrm
+   tve200
    vc4
    vga-switcheroo
    vgaarbiter
index 22af55d06ab8d6be092cf9d8699a9c4e09cd5260..de9512afd6117ea289f365bdb9fec82e4d99a51e 100644 (file)
@@ -75,17 +75,6 @@ helpers.
 
 Contact: Ville Syrjälä, Daniel Vetter, driver maintainers
 
-Implement deferred fbdev setup in the helper
---------------------------------------------
-
-Many (especially embedded drivers) want to delay fbdev setup until there's a
-real screen plugged in. This is to avoid the dreaded fallback to the low-res
-fbdev default. Many drivers have a hacked-up (and often broken) version of this,
-better to do it once in the shared helpers. Thierry has a patch series, but that
-one needs to be rebased and final polish applied.
-
-Contact: Thierry Reding, Daniel Vetter, driver maintainers
-
 Convert early atomic drivers to async commit helpers
 ----------------------------------------------------
 
@@ -138,6 +127,8 @@ interfaces to fix these issues:
   the acquire context explicitly on stack and then also pass it down into
   drivers explicitly so that the legacy-on-atomic functions can use them.
 
+  Except for some driver code this is done.
+
 * A bunch of the vtable hooks are now in the wrong place: DRM has a split
   between core vfunc tables (named ``drm_foo_funcs``), which are used to
   implement the userspace ABI. And then there's the optional hooks for the
@@ -151,6 +142,8 @@ interfaces to fix these issues:
   connector at runtime. That's almost all of them, and would allow us to get
   rid of a lot of ``best_encoder`` boilerplate in drivers.
 
+  This was almost done, but new drivers added a few more cases again.
+
 Contact: Daniel Vetter
 
 Get rid of dev->struct_mutex from GEM drivers
@@ -177,6 +170,17 @@ following drivers still use ``struct_mutex``: ``msm``, ``omapdrm`` and
 
 Contact: Daniel Vetter, respective driver maintainers
 
+Convert instances of dev_info/dev_err/dev_warn to their DRM_DEV_* equivalent
+----------------------------------------------------------------------------
+
+For drivers which could have multiple instances, it is necessary to
+differentiate between which is which in the logs. Since DRM_INFO/WARN/ERROR
+don't do this, drivers used dev_info/warn/err to make this differentiation. We
+now have DRM_DEV_* variants of the drm print macros, so we can start to convert
+those drivers back to using drm-formwatted specific log messages.
+
+Contact: Sean Paul, Maintainer of the driver you plan to convert
+
 Core refactorings
 =================
 
diff --git a/Documentation/gpu/tve200.rst b/Documentation/gpu/tve200.rst
new file mode 100644 (file)
index 0000000..69b17b3
--- /dev/null
@@ -0,0 +1,6 @@
+==================================
+ drm/tve200 Faraday TV Encoder 200
+==================================
+
+.. kernel-doc:: drivers/gpu/drm/tve200/tve200_drv.c
+   :doc: Faraday TV Encoder 200
index 6671f375f7fcdd1cf9e40f62c884f1ef9592dab9..7ab4c373c37031f54923bc6dc76ad91e8978600d 100644 (file)
@@ -4366,6 +4366,12 @@ T:       git git://anongit.freedesktop.org/drm/drm-misc
 S:     Maintained
 F:     drivers/gpu/drm/bochs/
 
+DRM DRIVER FOR FARADAY TVE200 TV ENCODER
+M:     Linus Walleij <linus.walleij@linaro.org>
+T:     git git://anongit.freedesktop.org/drm/drm-misc
+S:     Maintained
+F:     drivers/gpu/drm/tve200/
+
 DRM DRIVER FOR INTEL I810 VIDEO CARDS
 S:     Orphan / Obsolete
 F:     drivers/gpu/drm/i810/
@@ -4509,7 +4515,7 @@ L:        dri-devel@lists.freedesktop.org
 S:     Supported
 F:     drivers/gpu/drm/sun4i/
 F:     Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux.git
+T:     git git://anongit.freedesktop.org/drm/drm-misc
 
 DRM DRIVERS FOR AMLOGIC SOCS
 M:     Neil Armstrong <narmstrong@baylibre.com>
@@ -4693,7 +4699,7 @@ T:        git git://anongit.freedesktop.org/drm/drm-misc
 DRM PANEL DRIVERS
 M:     Thierry Reding <thierry.reding@gmail.com>
 L:     dri-devel@lists.freedesktop.org
-T:     git git://anongit.freedesktop.org/tegra/linux.git
+T:     git git://anongit.freedesktop.org/drm/drm-misc
 S:     Maintained
 F:     drivers/gpu/drm/drm_panel.c
 F:     drivers/gpu/drm/panel/
index 38cc7389a6c172c4f22ac89ec3694220a2083dc1..24f83f9eeaedce12904ce8190edd1e5ed0b67e76 100644 (file)
@@ -321,8 +321,16 @@ static int sw_sync_debugfs_open(struct inode *inode, struct file *file)
 static int sw_sync_debugfs_release(struct inode *inode, struct file *file)
 {
        struct sync_timeline *obj = file->private_data;
+       struct sync_pt *pt, *next;
+
+       spin_lock_irq(&obj->lock);
+
+       list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
+               dma_fence_set_error(&pt->base, -ENOENT);
+               dma_fence_signal_locked(&pt->base);
+       }
 
-       smp_wmb();
+       spin_unlock_irq(&obj->lock);
 
        sync_timeline_put(obj);
        return 0;
index 83cb2a88c204f7c6824396ceb296c7ea0a20577f..c9f09fc298bb0ddd5a61a3827526e5ee9000c220 100644 (file)
@@ -110,7 +110,7 @@ config DRM_FBDEV_OVERALLOC
 
 config DRM_LOAD_EDID_FIRMWARE
        bool "Allow to specify an EDID data set instead of probing for it"
-       depends on DRM_KMS_HELPER
+       depends on DRM
        help
          Say Y here, if you want to use EDID data to be loaded from the
          /lib/firmware directory or one of the provided built-in
@@ -278,6 +278,8 @@ source "drivers/gpu/drm/tinydrm/Kconfig"
 
 source "drivers/gpu/drm/pl111/Kconfig"
 
+source "drivers/gpu/drm/tve200/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
index a8acc197dec37760f6e6b6c44eec0abda5e97e0c..0ee184f56a602285ad17a25c75964d99693979cc 100644 (file)
@@ -28,6 +28,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o
 drm-$(CONFIG_OF) += drm_of.o
 drm-$(CONFIG_AGP) += drm_agpsupport.o
 drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
+drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 
 drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
                drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
@@ -36,7 +37,6 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
                drm_scdc_helper.o drm_gem_framebuffer_helper.o
 
 drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
-drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
 drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
@@ -44,8 +44,6 @@ drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 obj-$(CONFIG_DRM_DEBUG_MM_SELFTEST) += selftests/
 
-CFLAGS_drm_trace_points.o := -I$(src)
-
 obj-$(CONFIG_DRM)      += drm.o
 obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
 obj-$(CONFIG_DRM_ARM)  += arm/
@@ -100,3 +98,4 @@ obj-$(CONFIG_DRM_ZTE)        += zte/
 obj-$(CONFIG_DRM_MXSFB)        += mxsfb/
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
 obj-$(CONFIG_DRM_PL111) += pl111/
+obj-$(CONFIG_DRM_TVE200) += tve200/
index 289eda54e5aa83b23216af852445f44f70893b80..074fd4ea7ece499c13e79dc66204fc603dcb3cfa 100644 (file)
@@ -18,6 +18,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_atomic_helper.h>
 #include <linux/of_reserved_mem.h>
 
@@ -32,7 +33,7 @@ static void arcpgu_fb_output_poll_changed(struct drm_device *dev)
 }
 
 static const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = {
-       .fb_create  = drm_fb_cma_create,
+       .fb_create  = drm_gem_fb_create,
        .output_poll_changed = arcpgu_fb_output_poll_changed,
        .atomic_check = drm_atomic_helper_check,
        .atomic_commit = drm_atomic_helper_commit,
index f9bda7b0d2ec275996979e70822fc56b5279c53a..764d0c83710ca563554672d06332fc10883b5de7 100644 (file)
@@ -25,6 +25,7 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_of.h>
 
 #include "hdlcd_drv.h"
@@ -106,7 +107,7 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm)
 }
 
 static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = {
-       .fb_create = drm_fb_cma_create,
+       .fb_create = drm_gem_fb_create,
        .output_poll_changed = hdlcd_fb_output_poll_changed,
        .atomic_check = drm_atomic_helper_check,
        .atomic_commit = drm_atomic_helper_commit,
index 1a57cc28955e7fe7cbc595c718ba62570f476c0c..b8944666a18f0e72ea4715ff8742c5675b90d8a4 100644 (file)
@@ -26,6 +26,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_of.h>
 
 #include "malidp_drv.h"
@@ -249,7 +250,7 @@ static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = {
 };
 
 static const struct drm_mode_config_funcs malidp_mode_config_funcs = {
-       .fb_create = drm_fb_cma_create,
+       .fb_create = drm_gem_fb_create,
        .output_poll_changed = malidp_output_poll_changed,
        .atomic_check = drm_atomic_helper_check,
        .atomic_commit = drm_atomic_helper_commit,
index 64c0b4546fb240f92d0e91a8bdb4faff69417a1f..a18f156c8b663627ee250a2f9e2867688bafb163 100644 (file)
@@ -4,5 +4,3 @@ armada-y        += armada_510.o
 armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
 
 obj-$(CONFIG_DRM_ARMADA) := armada.o
-
-CFLAGS_armada_trace.o := -I$(src)
index 0b3227c039d768ada13307e99892ff1b9f78ff55..2fbd9d3393e820c777360aa97747c3eecd7beb89 100644 (file)
@@ -70,8 +70,6 @@ static struct drm_driver armada_drm_driver = {
        .gem_prime_export       = armada_gem_prime_export,
        .gem_prime_import       = armada_gem_prime_import,
        .dumb_create            = armada_gem_dumb_create,
-       .dumb_map_offset        = armada_gem_dumb_map_offset,
-       .dumb_destroy           = armada_gem_dumb_destroy,
        .gem_vm_ops             = &armada_gem_vm_ops,
        .major                  = 1,
        .minor                  = 0,
index a76ca21d063b5fb6ac00aea6b97cf2e1f00885f1..79835380d5c6c576ff6bd16dfe4f051a5337c86b 100644 (file)
@@ -270,42 +270,6 @@ int armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
        return ret;
 }
 
-int armada_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
-       uint32_t handle, uint64_t *offset)
-{
-       struct armada_gem_object *obj;
-       int ret = 0;
-
-       obj = armada_gem_object_lookup(file, handle);
-       if (!obj) {
-               DRM_ERROR("failed to lookup gem object\n");
-               return -EINVAL;
-       }
-
-       /* Don't allow imported objects to be mapped */
-       if (obj->obj.import_attach) {
-               ret = -EINVAL;
-               goto err_unref;
-       }
-
-       ret = drm_gem_create_mmap_offset(&obj->obj);
-       if (ret == 0) {
-               *offset = drm_vma_node_offset_addr(&obj->obj.vma_node);
-               DRM_DEBUG_DRIVER("handle %#x offset %llx\n", handle, *offset);
-       }
-
- err_unref:
-       drm_gem_object_unreference_unlocked(&obj->obj);
-
-       return ret;
-}
-
-int armada_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
-       uint32_t handle)
-{
-       return drm_gem_handle_delete(file, handle);
-}
-
 /* Private driver gem ioctls */
 int armada_gem_create_ioctl(struct drm_device *dev, void *data,
        struct drm_file *file)
index 6e524e0676bb319bda6c7ffa5e2ae10c5510ead0..1ac90792b166c20cfe580051200802b6fd140a2a 100644 (file)
@@ -35,10 +35,6 @@ struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *,
        size_t);
 int armada_gem_dumb_create(struct drm_file *, struct drm_device *,
        struct drm_mode_create_dumb *);
-int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *,
-       uint32_t, uint64_t *);
-int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *,
-       uint32_t);
 struct dma_buf *armada_gem_prime_export(struct drm_device *dev,
        struct drm_gem_object *obj, int flags);
 struct drm_gem_object *armada_gem_prime_import(struct drm_device *,
index dc0cba70fd1a3195bec5fe342974ca3524f6284f..be245a24610f06d6a71d878695b7209c4cf3ef2c 100644 (file)
@@ -62,5 +62,5 @@ TRACE_EVENT(armada_ovl_plane_work,
 
 /* This part must be outside protection */
 #undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/armada
 #include <trace/define_trace.h>
index 74d66e11f68876c622e5e516aae3f266a2a037fd..c6e8061ffcfc181415f67a697646c77d3fbb1417 100644 (file)
@@ -458,7 +458,7 @@ static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
 static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
                struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd)
 {
-       return drm_fb_cma_create(dev, file_priv, mode_cmd);
+       return drm_gem_fb_create(dev, file_priv, mode_cmd);
 }
 
 static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
index 4237b0446721ef925a6f3663823ea937c8cbfe47..6833ee253cfacafbbbf218340a3e1f8983ca805a 100644 (file)
@@ -34,6 +34,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drmP.h>
index fe18a5d2d84bbb2fd13eaff90fab683ce7f31e43..12ef2d8ee1101a5f5ea5ccc9781ba85734714023 100644 (file)
@@ -328,8 +328,6 @@ struct adv7511 {
        enum adv7511_sync_polarity hsync_polarity;
        bool rgb;
 
-       struct edid *edid;
-
        struct gpio_desc *gpio_pd;
 
        struct regulator_bulk_data *supplies;
index 67469c26bae88f9ada64e0866d73fbdcfe793886..1b4783d45c53f4b417e7f56b09ccfdbf21010930 100644 (file)
@@ -210,7 +210,7 @@ static const struct hdmi_codec_ops adv7511_codec_ops = {
        .get_dai_id     = adv7511_hdmi_i2s_get_dai_id,
 };
 
-static struct hdmi_codec_pdata codec_data = {
+static const struct hdmi_codec_pdata codec_data = {
        .ops = &adv7511_codec_ops,
        .max_i2s_channels = 2,
        .i2s = 1,
index b2431aee788795cfa078dbf4b5515c75dbe1961c..bd7dbae1119efc5895e93c89a0053936d1e50b11 100644 (file)
@@ -199,17 +199,14 @@ static const uint16_t adv7511_csc_ycbcr_to_rgb[] = {
 
 static void adv7511_set_config_csc(struct adv7511 *adv7511,
                                   struct drm_connector *connector,
-                                  bool rgb)
+                                  bool rgb, bool hdmi_mode)
 {
        struct adv7511_video_config config;
        bool output_format_422, output_format_ycbcr;
        unsigned int mode;
        uint8_t infoframe[17];
 
-       if (adv7511->edid)
-               config.hdmi_mode = drm_detect_hdmi_monitor(adv7511->edid);
-       else
-               config.hdmi_mode = false;
+       config.hdmi_mode = hdmi_mode;
 
        hdmi_avi_infoframe_init(&config.avi_infoframe);
 
@@ -589,15 +586,14 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
        if (!adv7511->powered)
                __adv7511_power_off(adv7511);
 
-       kfree(adv7511->edid);
-       adv7511->edid = edid;
-       if (!edid)
-               return 0;
 
        drm_mode_connector_update_edid_property(connector, edid);
        count = drm_add_edid_modes(connector, edid);
 
-       adv7511_set_config_csc(adv7511, connector, adv7511->rgb);
+       adv7511_set_config_csc(adv7511, connector, adv7511->rgb,
+                              drm_detect_hdmi_monitor(edid));
+
+       kfree(edid);
 
        return count;
 }
@@ -833,7 +829,11 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge)
                return -ENODEV;
        }
 
-       adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
+       if (adv->i2c_main->irq)
+               adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
+       else
+               adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
+                               DRM_CONNECTOR_POLL_DISCONNECT;
 
        ret = drm_connector_init(bridge->dev, &adv->connector,
                                 &adv7511_connector_funcs,
@@ -1158,8 +1158,6 @@ static int adv7511_remove(struct i2c_client *i2c)
 
        i2c_unregister_device(adv7511->i2c_edid);
 
-       kfree(adv7511->edid);
-
        return 0;
 }
 
index 63c7a01b7053eb80dfc852b83bfbd04b79d0a75a..f4f633a0dffa5c731779c76e37fa085e4be8133d 100644 (file)
 #include <video/mipi_display.h>
 
 #define DSI_VERSION                    0x00
+
 #define DSI_PWR_UP                     0x04
 #define RESET                          0
 #define POWERUP                                BIT(0)
 
 #define DSI_CLKMGR_CFG                 0x08
-#define TO_CLK_DIVIDSION(div)          (((div) & 0xff) << 8)
-#define TX_ESC_CLK_DIVIDSION(div)      (((div) & 0xff) << 0)
+#define TO_CLK_DIVISION(div)           (((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVISION(div)       ((div) & 0xff)
 
 #define DSI_DPI_VCID                   0x0c
-#define DPI_VID(vid)                   (((vid) & 0x3) << 0)
+#define DPI_VCID(vcid)                 ((vcid) & 0x3)
 
 #define DSI_DPI_COLOR_CODING           0x10
-#define EN18_LOOSELY                   BIT(8)
+#define LOOSELY18_EN                   BIT(8)
 #define DPI_COLOR_CODING_16BIT_1       0x0
 #define DPI_COLOR_CODING_16BIT_2       0x1
 #define DPI_COLOR_CODING_16BIT_3       0x2
 #define OUTVACT_LPCMD_TIME(p)          (((p) & 0xff) << 16)
 #define INVACT_LPCMD_TIME(p)           ((p) & 0xff)
 
+#define DSI_DBI_VCID                   0x1c
 #define DSI_DBI_CFG                    0x20
+#define DSI_DBI_PARTITIONING_EN                0x24
 #define DSI_DBI_CMDSIZE                        0x28
 
 #define DSI_PCKHDL_CFG                 0x2c
-#define EN_CRC_RX                      BIT(4)
-#define EN_ECC_RX                      BIT(3)
-#define EN_BTA                         BIT(2)
-#define EN_EOTP_RX                     BIT(1)
-#define EN_EOTP_TX                     BIT(0)
+#define CRC_RX_EN                      BIT(4)
+#define ECC_RX_EN                      BIT(3)
+#define BTA_EN                         BIT(2)
+#define EOTP_RX_EN                     BIT(1)
+#define EOTP_TX_EN                     BIT(0)
+
+#define DSI_GEN_VCID                   0x30
 
 #define DSI_MODE_CFG                   0x34
 #define ENABLE_VIDEO_MODE              0
 #define ENABLE_CMD_MODE                        BIT(0)
 
 #define DSI_VID_MODE_CFG               0x38
-#define FRAME_BTA_ACK                  BIT(14)
 #define ENABLE_LOW_POWER               (0x3f << 8)
 #define ENABLE_LOW_POWER_MASK          (0x3f << 8)
 #define VID_MODE_TYPE_NON_BURST_SYNC_PULSES    0x0
 #define VID_MODE_TYPE_MASK                     0x3
 
 #define DSI_VID_PKT_SIZE               0x3c
-#define VID_PKT_SIZE(p)                        (((p) & 0x3fff) << 0)
-#define VID_PKT_MAX_SIZE               0x3fff
+#define VID_PKT_SIZE(p)                        ((p) & 0x3fff)
+
+#define DSI_VID_NUM_CHUNKS             0x40
+#define VID_NUM_CHUNKS(c)              ((c) & 0x1fff)
+
+#define DSI_VID_NULL_SIZE              0x44
+#define VID_NULL_SIZE(b)               ((b) & 0x1fff)
 
 #define DSI_VID_HSA_TIME               0x48
 #define DSI_VID_HBP_TIME               0x4c
 #define DSI_VID_VBP_LINES              0x58
 #define DSI_VID_VFP_LINES              0x5c
 #define DSI_VID_VACTIVE_LINES          0x60
+#define DSI_EDPI_CMD_SIZE              0x64
+
 #define DSI_CMD_MODE_CFG               0x68
 #define MAX_RD_PKT_SIZE_LP             BIT(24)
 #define DCS_LW_TX_LP                   BIT(19)
 #define GEN_SW_2P_TX_LP                        BIT(10)
 #define GEN_SW_1P_TX_LP                        BIT(9)
 #define GEN_SW_0P_TX_LP                        BIT(8)
-#define EN_ACK_RQST                    BIT(1)
-#define EN_TEAR_FX                     BIT(0)
+#define ACK_RQST_EN                    BIT(1)
+#define TEAR_FX_EN                     BIT(0)
 
 #define CMD_MODE_ALL_LP                        (MAX_RD_PKT_SIZE_LP | \
                                         DCS_LW_TX_LP | \
                                         GEN_SW_0P_TX_LP)
 
 #define DSI_GEN_HDR                    0x6c
+/* TODO These 2 defines will be reworked thanks to mipi_dsi_create_packet() */
 #define GEN_HDATA(data)                        (((data) & 0xffff) << 8)
-#define GEN_HDATA_MASK                 (0xffff << 8)
 #define GEN_HTYPE(type)                        (((type) & 0xff) << 0)
-#define GEN_HTYPE_MASK                 0xff
 
 #define DSI_GEN_PLD_DATA               0x70
 
 #define DSI_CMD_PKT_STATUS             0x74
-#define GEN_CMD_EMPTY                  BIT(0)
-#define GEN_CMD_FULL                   BIT(1)
-#define GEN_PLD_W_EMPTY                        BIT(2)
-#define GEN_PLD_W_FULL                 BIT(3)
-#define GEN_PLD_R_EMPTY                        BIT(4)
-#define GEN_PLD_R_FULL                 BIT(5)
 #define GEN_RD_CMD_BUSY                        BIT(6)
+#define GEN_PLD_R_FULL                 BIT(5)
+#define GEN_PLD_R_EMPTY                        BIT(4)
+#define GEN_PLD_W_FULL                 BIT(3)
+#define GEN_PLD_W_EMPTY                        BIT(2)
+#define GEN_CMD_FULL                   BIT(1)
+#define GEN_CMD_EMPTY                  BIT(0)
 
 #define DSI_TO_CNT_CFG                 0x78
 #define HSTX_TO_CNT(p)                 (((p) & 0xffff) << 16)
 #define LPRX_TO_CNT(p)                 ((p) & 0xffff)
 
+#define DSI_HS_RD_TO_CNT               0x7c
+#define DSI_LP_RD_TO_CNT               0x80
+#define DSI_HS_WR_TO_CNT               0x84
+#define DSI_LP_WR_TO_CNT               0x88
 #define DSI_BTA_TO_CNT                 0x8c
+
 #define DSI_LPCLK_CTRL                 0x94
 #define AUTO_CLKLANE_CTRL              BIT(1)
 #define PHY_TXREQUESTCLKHS             BIT(0)
 #define PHY_CLKHS2LP_TIME(lbcc)                (((lbcc) & 0x3ff) << 16)
 #define PHY_CLKLP2HS_TIME(lbcc)                ((lbcc) & 0x3ff)
 
+/* TODO Next register is slightly different between 1.30 & 1.31 IP version */
 #define DSI_PHY_TMR_CFG                        0x9c
 #define PHY_HS2LP_TIME(lbcc)           (((lbcc) & 0xff) << 24)
 #define PHY_LP2HS_TIME(lbcc)           (((lbcc) & 0xff) << 16)
 #define PHY_UNSHUTDOWNZ                        BIT(0)
 
 #define DSI_PHY_IF_CFG                 0xa4
-#define N_LANES(n)                     ((((n) - 1) & 0x3) << 0)
 #define PHY_STOP_WAIT_TIME(cycle)      (((cycle) & 0xff) << 8)
+#define N_LANES(n)                     (((n) - 1) & 0x3)
+
+#define DSI_PHY_ULPS_CTRL              0xa8
+#define DSI_PHY_TX_TRIGGERS            0xac
 
 #define DSI_PHY_STATUS                 0xb0
-#define LOCK                           BIT(0)
-#define STOP_STATE_CLK_LANE            BIT(2)
+#define PHY_STOP_STATE_CLK_LANE                BIT(2)
+#define PHY_LOCK                       BIT(0)
 
 #define DSI_PHY_TST_CTRL0              0xb4
 #define PHY_TESTCLK                    BIT(1)
 #define PHY_TESTEN                     BIT(16)
 #define PHY_UNTESTEN                   0
 #define PHY_TESTDOUT(n)                        (((n) & 0xff) << 8)
-#define PHY_TESTDIN(n)                 (((n) & 0xff) << 0)
+#define PHY_TESTDIN(n)                 ((n) & 0xff)
 
 #define DSI_INT_ST0                    0xbc
 #define DSI_INT_ST1                    0xc0
 #define DSI_INT_MSK0                   0xc4
 #define DSI_INT_MSK1                   0xc8
+#define DSI_PHY_TMR_RD_CFG             0xf4
 
 #define PHY_STATUS_TIMEOUT_US          10000
 #define CMD_PKT_STATUS_TIMEOUT_US      20000
@@ -307,7 +327,7 @@ static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
        u32 val = 0;
 
        if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
-               val |= EN_ACK_RQST;
+               val |= ACK_RQST_EN;
        if (lpm)
                val |= CMD_MODE_ALL_LP;
 
@@ -506,8 +526,8 @@ static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
         * timeout clock division should be computed with the
         * high speed transmission counter timeout and byte lane...
         */
-       dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
-                 TX_ESC_CLK_DIVIDSION(esc_clk_division));
+       dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) |
+                 TX_ESC_CLK_DIVISION(esc_clk_division));
 }
 
 static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
@@ -520,7 +540,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
                color = DPI_COLOR_CODING_24BIT;
                break;
        case MIPI_DSI_FMT_RGB666:
-               color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
+               color = DPI_COLOR_CODING_18BIT_2 | LOOSELY18_EN;
                break;
        case MIPI_DSI_FMT_RGB666_PACKED:
                color = DPI_COLOR_CODING_18BIT_1;
@@ -535,7 +555,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
        if (mode->flags & DRM_MODE_FLAG_NHSYNC)
                val |= HSYNC_ACTIVE_LOW;
 
-       dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
+       dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
        dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
        dsi_write(dsi, DSI_DPI_CFG_POL, val);
        /*
@@ -550,7 +570,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
 
 static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
 {
-       dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
+       dsi_write(dsi, DSI_PCKHDL_CFG, CRC_RX_EN | ECC_RX_EN | BTA_EN);
 }
 
 static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
@@ -571,7 +591,7 @@ static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
        /*
         * TODO dw drv improvements
         * compute high speed transmission counter timeout according
-        * to the timeout clock division (TO_CLK_DIVIDSION) and byte lane...
+        * to the timeout clock division (TO_CLK_DIVISION) and byte lane...
         */
        dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
        /*
@@ -684,13 +704,13 @@ static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
        dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
                  PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
 
-       ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
-                                val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
+       ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, val,
+                                val & PHY_LOCK, 1000, PHY_STATUS_TIMEOUT_US);
        if (ret < 0)
                DRM_DEBUG_DRIVER("failed to wait phy lock state\n");
 
        ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
-                                val, val & STOP_STATE_CLK_LANE, 1000,
+                                val, val & PHY_STOP_STATE_CLK_LANE, 1000,
                                 PHY_STATUS_TIMEOUT_US);
        if (ret < 0)
                DRM_DEBUG_DRIVER("failed to wait phy clk lane stop state\n");
@@ -865,15 +885,14 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
         * Note that the reset was not defined in the initial device tree, so
         * we have to be prepared for it not being found.
         */
-       apb_rst = devm_reset_control_get(dev, "apb");
+       apb_rst = devm_reset_control_get_optional_exclusive(dev, "apb");
        if (IS_ERR(apb_rst)) {
                ret = PTR_ERR(apb_rst);
-               if (ret == -ENOENT) {
-                       apb_rst = NULL;
-               } else {
+
+               if (ret != -EPROBE_DEFER)
                        dev_err(dev, "Unable to get reset control: %d\n", ret);
-                       return ERR_PTR(ret);
-               }
+
+               return ERR_PTR(ret);
        }
 
        if (apb_rst) {
index 2fd383d7253a0db87a569458a017a4d155aef51b..366c56fe5f58409b5269875c1e9cf09d451c9c55 100644 (file)
@@ -163,13 +163,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
                crtc->funcs->atomic_destroy_state(crtc,
                                                  state->crtcs[i].state);
 
-               if (state->crtcs[i].commit) {
-                       kfree(state->crtcs[i].commit->event);
-                       state->crtcs[i].commit->event = NULL;
-                       drm_crtc_commit_put(state->crtcs[i].commit);
-               }
-
-               state->crtcs[i].commit = NULL;
                state->crtcs[i].ptr = NULL;
                state->crtcs[i].state = NULL;
        }
@@ -199,6 +192,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
        }
        state->num_private_objs = 0;
 
+       if (state->fake_commit) {
+               drm_crtc_commit_put(state->fake_commit);
+               state->fake_commit = NULL;
+       }
 }
 EXPORT_SYMBOL(drm_atomic_state_default_clear);
 
@@ -2237,7 +2234,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
                        (arg->flags & DRM_MODE_PAGE_FLIP_EVENT))
                return -EINVAL;
 
-       drm_modeset_acquire_init(&ctx, 0);
+       drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
 
        state = drm_atomic_state_alloc(dev);
        if (!state)
@@ -2350,8 +2347,9 @@ out:
 
        if (ret == -EDEADLK) {
                drm_atomic_state_clear(state);
-               drm_modeset_backoff(&ctx);
-               goto retry;
+               ret = drm_modeset_backoff(&ctx);
+               if (!ret)
+                       goto retry;
        }
 
        drm_atomic_state_put(state);
index 4e53aae9a1fb19fc6710202188a53119c566aef7..01c34bc5b5b0ce5c09d866cdfe600bdeedb5346e 100644 (file)
@@ -1262,12 +1262,12 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
 void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev,
                                          struct drm_atomic_state *old_state)
 {
-       struct drm_crtc_state *unused;
+       struct drm_crtc_state *new_crtc_state;
        struct drm_crtc *crtc;
        int i;
 
-       for_each_new_crtc_in_state(old_state, crtc, unused, i) {
-               struct drm_crtc_commit *commit = old_state->crtcs[i].commit;
+       for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
+               struct drm_crtc_commit *commit = new_crtc_state->commit;
                int ret;
 
                if (!commit)
@@ -1388,35 +1388,31 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *crtc_state;
-       struct drm_crtc_commit *commit;
-       struct drm_plane *__plane, *plane = NULL;
-       struct drm_plane_state *__plane_state, *plane_state = NULL;
+       struct drm_plane *plane;
+       struct drm_plane_state *old_plane_state, *new_plane_state;
        const struct drm_plane_helper_funcs *funcs;
-       int i, j, n_planes = 0;
+       int i, n_planes = 0;
 
        for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
                if (drm_atomic_crtc_needs_modeset(crtc_state))
                        return -EINVAL;
        }
 
-       for_each_new_plane_in_state(state, __plane, __plane_state, i) {
+       for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i)
                n_planes++;
-               plane = __plane;
-               plane_state = __plane_state;
-       }
 
        /* FIXME: we support only single plane updates for now */
-       if (!plane || n_planes != 1)
+       if (n_planes != 1)
                return -EINVAL;
 
-       if (!plane_state->crtc)
+       if (!new_plane_state->crtc)
                return -EINVAL;
 
        funcs = plane->helper_private;
        if (!funcs->atomic_async_update)
                return -EINVAL;
 
-       if (plane_state->fence)
+       if (new_plane_state->fence)
                return -EINVAL;
 
        /*
@@ -1424,31 +1420,11 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
         * the plane.  This prevents our async update's changes from getting
         * overridden by a previous synchronous update's state.
         */
-       for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
-               if (plane->crtc != crtc)
-                       continue;
+       if (old_plane_state->commit &&
+           !try_wait_for_completion(&old_plane_state->commit->hw_done))
+               return -EBUSY;
 
-               spin_lock(&crtc->commit_lock);
-               commit = list_first_entry_or_null(&crtc->commit_list,
-                                                 struct drm_crtc_commit,
-                                                 commit_entry);
-               if (!commit) {
-                       spin_unlock(&crtc->commit_lock);
-                       continue;
-               }
-               spin_unlock(&crtc->commit_lock);
-
-               if (!crtc->state->state)
-                       continue;
-
-               for_each_plane_in_state(crtc->state->state, __plane,
-                                       __plane_state, j) {
-                       if (__plane == plane)
-                               return -EINVAL;
-               }
-       }
-
-       return funcs->atomic_async_check(plane, plane_state);
+       return funcs->atomic_async_check(plane, new_plane_state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_async_check);
 
@@ -1633,8 +1609,7 @@ static int stall_checks(struct drm_crtc *crtc, bool nonblock)
                                return -EBUSY;
                        }
                } else if (i == 1) {
-                       stall_commit = commit;
-                       drm_crtc_commit_get(stall_commit);
+                       stall_commit = drm_crtc_commit_get(commit);
                        break;
                }
 
@@ -1668,6 +1643,38 @@ static void release_crtc_commit(struct completion *completion)
        drm_crtc_commit_put(commit);
 }
 
+static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc *crtc)
+{
+       init_completion(&commit->flip_done);
+       init_completion(&commit->hw_done);
+       init_completion(&commit->cleanup_done);
+       INIT_LIST_HEAD(&commit->commit_entry);
+       kref_init(&commit->ref);
+       commit->crtc = crtc;
+}
+
+static struct drm_crtc_commit *
+crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
+{
+       if (crtc) {
+               struct drm_crtc_state *new_crtc_state;
+
+               new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+               return new_crtc_state->commit;
+       }
+
+       if (!state->fake_commit) {
+               state->fake_commit = kzalloc(sizeof(*state->fake_commit), GFP_KERNEL);
+               if (!state->fake_commit)
+                       return NULL;
+
+               init_commit(state->fake_commit, NULL);
+       }
+
+       return state->fake_commit;
+}
+
 /**
  * drm_atomic_helper_setup_commit - setup possibly nonblocking commit
  * @state: new modeset state to be committed
@@ -1716,6 +1723,10 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+       struct drm_connector *conn;
+       struct drm_connector_state *old_conn_state, *new_conn_state;
+       struct drm_plane *plane;
+       struct drm_plane_state *old_plane_state, *new_plane_state;
        struct drm_crtc_commit *commit;
        int i, ret;
 
@@ -1724,14 +1735,9 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
                if (!commit)
                        return -ENOMEM;
 
-               init_completion(&commit->flip_done);
-               init_completion(&commit->hw_done);
-               init_completion(&commit->cleanup_done);
-               INIT_LIST_HEAD(&commit->commit_entry);
-               kref_init(&commit->ref);
-               commit->crtc = crtc;
+               init_commit(commit, crtc);
 
-               state->crtcs[i].commit = commit;
+               new_crtc_state->commit = commit;
 
                ret = stall_checks(crtc, nonblock);
                if (ret)
@@ -1765,25 +1771,46 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
                drm_crtc_commit_get(commit);
        }
 
-       return 0;
-}
-EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
+       for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {
+               /* commit tracked through new_crtc_state->commit, no need to do it explicitly */
+               if (new_conn_state->crtc)
+                       continue;
 
+               /* Userspace is not allowed to get ahead of the previous
+                * commit with nonblocking ones. */
+               if (nonblock && old_conn_state->commit &&
+                   !try_wait_for_completion(&old_conn_state->commit->flip_done))
+                       return -EBUSY;
 
-static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
-{
-       struct drm_crtc_commit *commit;
-       int i = 0;
+               commit = crtc_or_fake_commit(state, old_conn_state->crtc);
+               if (!commit)
+                       return -ENOMEM;
 
-       list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
-               /* skip the first entry, that's the current commit */
-               if (i == 1)
-                       return commit;
-               i++;
+               new_conn_state->commit = drm_crtc_commit_get(commit);
+       }
+
+       for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
+               /*
+                * Unlike connectors, always track planes explicitly for
+                * async pageflip support.
+                */
+
+               /* Userspace is not allowed to get ahead of the previous
+                * commit with nonblocking ones. */
+               if (nonblock && old_plane_state->commit &&
+                   !try_wait_for_completion(&old_plane_state->commit->flip_done))
+                       return -EBUSY;
+
+               commit = crtc_or_fake_commit(state, old_plane_state->crtc);
+               if (!commit)
+                       return -ENOMEM;
+
+               new_plane_state->commit = drm_crtc_commit_get(commit);
        }
 
-       return NULL;
+       return 0;
 }
+EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
 
 /**
  * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits
@@ -1800,17 +1827,17 @@ static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
 void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
 {
        struct drm_crtc *crtc;
-       struct drm_crtc_state *new_crtc_state;
+       struct drm_crtc_state *old_crtc_state;
+       struct drm_plane *plane;
+       struct drm_plane_state *old_plane_state;
+       struct drm_connector *conn;
+       struct drm_connector_state *old_conn_state;
        struct drm_crtc_commit *commit;
        int i;
        long ret;
 
-       for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
-               spin_lock(&crtc->commit_lock);
-               commit = preceeding_commit(crtc);
-               if (commit)
-                       drm_crtc_commit_get(commit);
-               spin_unlock(&crtc->commit_lock);
+       for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               commit = old_crtc_state->commit;
 
                if (!commit)
                        continue;
@@ -1828,8 +1855,48 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
                if (ret == 0)
                        DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
                                  crtc->base.id, crtc->name);
+       }
+
+       for_each_old_connector_in_state(old_state, conn, old_conn_state, i) {
+               commit = old_conn_state->commit;
 
-               drm_crtc_commit_put(commit);
+               if (!commit)
+                       continue;
+
+               ret = wait_for_completion_timeout(&commit->hw_done,
+                                                 10*HZ);
+               if (ret == 0)
+                       DRM_ERROR("[CONNECTOR:%d:%s] hw_done timed out\n",
+                                 conn->base.id, conn->name);
+
+               /* Currently no support for overwriting flips, hence
+                * stall for previous one to execute completely. */
+               ret = wait_for_completion_timeout(&commit->flip_done,
+                                                 10*HZ);
+               if (ret == 0)
+                       DRM_ERROR("[CONNECTOR:%d:%s] flip_done timed out\n",
+                                 conn->base.id, conn->name);
+       }
+
+       for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
+               commit = old_plane_state->commit;
+
+               if (!commit)
+                       continue;
+
+               ret = wait_for_completion_timeout(&commit->hw_done,
+                                                 10*HZ);
+               if (ret == 0)
+                       DRM_ERROR("[PLANE:%d:%s] hw_done timed out\n",
+                                 plane->base.id, plane->name);
+
+               /* Currently no support for overwriting flips, hence
+                * stall for previous one to execute completely. */
+               ret = wait_for_completion_timeout(&commit->flip_done,
+                                                 10*HZ);
+               if (ret == 0)
+                       DRM_ERROR("[PLANE:%d:%s] flip_done timed out\n",
+                                 plane->base.id, plane->name);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
@@ -1852,19 +1919,34 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
 void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state)
 {
        struct drm_crtc *crtc;
-       struct drm_crtc_state *new_crtc_state;
+       struct drm_crtc_state *old_crtc_state, *new_crtc_state;
        struct drm_crtc_commit *commit;
        int i;
 
-       for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
-               commit = old_state->crtcs[i].commit;
+       for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
+               commit = new_crtc_state->commit;
                if (!commit)
                        continue;
 
+               /*
+                * copy new_crtc_state->commit to old_crtc_state->commit,
+                * it's unsafe to touch new_crtc_state after hw_done,
+                * but we still need to do so in cleanup_done().
+                */
+               if (old_crtc_state->commit)
+                       drm_crtc_commit_put(old_crtc_state->commit);
+
+               old_crtc_state->commit = drm_crtc_commit_get(commit);
+
                /* backend must have consumed any event by now */
                WARN_ON(new_crtc_state->event);
                complete_all(&commit->hw_done);
        }
+
+       if (old_state->fake_commit) {
+               complete_all(&old_state->fake_commit->hw_done);
+               complete_all(&old_state->fake_commit->flip_done);
+       }
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
 
@@ -1882,39 +1964,25 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
 void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state)
 {
        struct drm_crtc *crtc;
-       struct drm_crtc_state *new_crtc_state;
+       struct drm_crtc_state *old_crtc_state;
        struct drm_crtc_commit *commit;
        int i;
-       long ret;
 
-       for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
-               commit = old_state->crtcs[i].commit;
+       for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               commit = old_crtc_state->commit;
                if (WARN_ON(!commit))
                        continue;
 
                complete_all(&commit->cleanup_done);
                WARN_ON(!try_wait_for_completion(&commit->hw_done));
 
-               /* commit_list borrows our reference, need to remove before we
-                * clean up our drm_atomic_state. But only after it actually
-                * completed, otherwise subsequent commits won't stall properly. */
-               if (try_wait_for_completion(&commit->flip_done))
-                       goto del_commit;
-
-               /* We must wait for the vblank event to signal our completion
-                * before releasing our reference, since the vblank work does
-                * not hold a reference of its own. */
-               ret = wait_for_completion_timeout(&commit->flip_done,
-                                                 10*HZ);
-               if (ret == 0)
-                       DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
-                                 crtc->base.id, crtc->name);
-
-del_commit:
                spin_lock(&crtc->commit_lock);
                list_del(&commit->commit_entry);
                spin_unlock(&crtc->commit_lock);
        }
+
+       if (old_state->fake_commit)
+               complete_all(&old_state->fake_commit->cleanup_done);
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
 
@@ -2294,20 +2362,44 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
        struct drm_private_state *old_obj_state, *new_obj_state;
 
        if (stall) {
-               for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
-                       spin_lock(&crtc->commit_lock);
-                       commit = list_first_entry_or_null(&crtc->commit_list,
-                                       struct drm_crtc_commit, commit_entry);
-                       if (commit)
-                               drm_crtc_commit_get(commit);
-                       spin_unlock(&crtc->commit_lock);
+               /*
+                * We have to stall for hw_done here before
+                * drm_atomic_helper_wait_for_dependencies() because flip
+                * depth > 1 is not yet supported by all drivers. As long as
+                * obj->state is directly dereferenced anywhere in the drivers
+                * atomic_commit_tail function, then it's unsafe to swap state
+                * before drm_atomic_helper_commit_hw_done() is called.
+                */
+
+               for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
+                       commit = old_crtc_state->commit;
+
+                       if (!commit)
+                               continue;
+
+                       ret = wait_for_completion_interruptible(&commit->hw_done);
+                       if (ret)
+                               return ret;
+               }
+
+               for_each_old_connector_in_state(state, connector, old_conn_state, i) {
+                       commit = old_conn_state->commit;
 
                        if (!commit)
                                continue;
 
                        ret = wait_for_completion_interruptible(&commit->hw_done);
-                       drm_crtc_commit_put(commit);
+                       if (ret)
+                               return ret;
+               }
 
+               for_each_old_plane_in_state(state, plane, old_plane_state, i) {
+                       commit = old_plane_state->commit;
+
+                       if (!commit)
+                               continue;
+
+                       ret = wait_for_completion_interruptible(&commit->hw_done);
                        if (ret)
                                return ret;
                }
@@ -2332,13 +2424,13 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
                state->crtcs[i].state = old_crtc_state;
                crtc->state = new_crtc_state;
 
-               if (state->crtcs[i].commit) {
+               if (new_crtc_state->commit) {
                        spin_lock(&crtc->commit_lock);
-                       list_add(&state->crtcs[i].commit->commit_entry,
+                       list_add(&new_crtc_state->commit->commit_entry,
                                 &crtc->commit_list);
                        spin_unlock(&crtc->commit_lock);
 
-                       state->crtcs[i].commit->event = NULL;
+                       new_crtc_state->commit->event = NULL;
                }
        }
 
@@ -3186,6 +3278,7 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
        state->connectors_changed = false;
        state->color_mgmt_changed = false;
        state->zpos_changed = false;
+       state->commit = NULL;
        state->event = NULL;
        state->pageflip_flags = 0;
 }
@@ -3224,6 +3317,12 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
  */
 void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
 {
+       if (state->commit) {
+               kfree(state->commit->event);
+               state->commit->event = NULL;
+               drm_crtc_commit_put(state->commit);
+       }
+
        drm_property_blob_put(state->mode_blob);
        drm_property_blob_put(state->degamma_lut);
        drm_property_blob_put(state->ctm);
@@ -3286,6 +3385,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
                drm_framebuffer_get(state->fb);
 
        state->fence = NULL;
+       state->commit = NULL;
 }
 EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
 
@@ -3327,6 +3427,9 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
 
        if (state->fence)
                dma_fence_put(state->fence);
+
+       if (state->commit)
+               drm_crtc_commit_put(state->commit);
 }
 EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
 
@@ -3405,6 +3508,7 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
        memcpy(state, connector->state, sizeof(*state));
        if (state->crtc)
                drm_connector_get(connector);
+       state->commit = NULL;
 }
 EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
 
@@ -3531,6 +3635,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
 {
        if (state->crtc)
                drm_connector_put(state->connector);
+
+       if (state->commit)
+               drm_crtc_commit_put(state->commit);
 }
 EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
 
index dc8cdfe1dcacf6dfd2b5e987888820f2524f02a8..1638bfe9627ce21c08ccb3e17b868d13c466bb16 100644 (file)
@@ -67,17 +67,12 @@ static LIST_HEAD(bridge_list);
  * drm_bridge_add - add the given bridge to the global bridge list
  *
  * @bridge: bridge control structure
- *
- * RETURNS:
- * Unconditionally returns Zero.
  */
-int drm_bridge_add(struct drm_bridge *bridge)
+void drm_bridge_add(struct drm_bridge *bridge)
 {
        mutex_lock(&bridge_lock);
        list_add_tail(&bridge->list, &bridge_list);
        mutex_unlock(&bridge_lock);
-
-       return 0;
 }
 EXPORT_SYMBOL(drm_bridge_add);
 
index ba9f36cef68c69d2d9601c75363537ca1a93800e..bb2e60f5feb6340c6b26e3e1a695d0fe9d10a555 100644 (file)
@@ -615,7 +615,6 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = {
        { DRM_MODE_LINK_STATUS_GOOD, "Good" },
        { DRM_MODE_LINK_STATUS_BAD, "Bad" },
 };
-DRM_ENUM_NAME_FN(drm_get_link_status_name, drm_link_status_enum_list)
 
 /**
  * drm_display_info_set_bus_formats - set the supported bus formats
index 5af25ce5bf7c2779a62aa5c74096d7ca3a73a588..68b4e976d5e0f37fca1ac42a2d2b469c64c73e4b 100644 (file)
@@ -577,7 +577,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
        DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
 
        mutex_lock(&crtc->dev->mode_config.mutex);
-       drm_modeset_acquire_init(&ctx, 0);
+       drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
 retry:
        ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx);
        if (ret)
@@ -717,8 +717,9 @@ out:
        kfree(connector_set);
        drm_mode_destroy(dev, mode);
        if (ret == -EDEADLK) {
-               drm_modeset_backoff(&ctx);
-               goto retry;
+               ret = drm_modeset_backoff(&ctx);
+               if (!ret)
+                       goto retry;
        }
        drm_modeset_drop_locks(&ctx);
        drm_modeset_acquire_fini(&ctx);
index f9e26dda56d6d48a618932f804e365444505b998..9dd879589a2c56d701f4604dadff0040851b854e 100644 (file)
@@ -155,7 +155,7 @@ static int crtc_crc_open(struct inode *inode, struct file *filep)
        int ret = 0;
 
        if (drm_drv_uses_atomic_modeset(crtc->dev)) {
-               ret = drm_modeset_lock_interruptible(&crtc->mutex, NULL);
+               ret = drm_modeset_lock_single_interruptible(&crtc->mutex);
                if (ret)
                        return ret;
 
index 41b492f99955f8d6778197e7102c854e41530b8f..70dcfa58d3c22bb7156c6ecc1b41dc602ec198ba 100644 (file)
@@ -294,6 +294,12 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
                memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
                idx += req->u.i2c_write.num_bytes;
                break;
+
+       case DP_POWER_DOWN_PHY:
+       case DP_POWER_UP_PHY:
+               buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
+               idx++;
+               break;
        }
        raw->cur_len = idx;
 }
@@ -538,6 +544,21 @@ fail_len:
        return false;
 }
 
+static bool drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                      struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.port_number.port_number = (raw->msg[idx] >> 4) & 0xf;
+       idx++;
+       if (idx > raw->curlen) {
+               DRM_DEBUG_KMS("power up/down phy parse length fail %d %d\n",
+                             idx, raw->curlen);
+               return false;
+       }
+       return true;
+}
+
 static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
                                        struct drm_dp_sideband_msg_reply_body *msg)
 {
@@ -567,6 +588,9 @@ static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
                return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
        case DP_ALLOCATE_PAYLOAD:
                return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
+       case DP_POWER_DOWN_PHY:
+       case DP_POWER_UP_PHY:
+               return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg);
        default:
                DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type);
                return false;
@@ -693,6 +717,22 @@ static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_n
        return 0;
 }
 
+static int build_power_updown_phy(struct drm_dp_sideband_msg_tx *msg,
+                                 int port_num, bool power_up)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       if (power_up)
+               req.req_type = DP_POWER_UP_PHY;
+       else
+               req.req_type = DP_POWER_DOWN_PHY;
+
+       req.u.port_num.port_number = port_num;
+       drm_dp_encode_sideband_req(&req, msg);
+       msg->path_msg = true;
+       return 0;
+}
+
 static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr,
                                        struct drm_dp_vcpi *vcpi)
 {
@@ -1724,6 +1764,40 @@ fail_put:
        return ret;
 }
 
+int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_dp_mst_port *port, bool power_up)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int len, ret;
+
+       port = drm_dp_get_validated_port_ref(mgr, port);
+       if (!port)
+               return -EINVAL;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               drm_dp_put_port(port);
+               return -ENOMEM;
+       }
+
+       txmsg->dst = port->parent;
+       len = build_power_updown_phy(txmsg, port->port_num, power_up);
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(port->parent, txmsg);
+       if (ret > 0) {
+               if (txmsg->reply.reply_type == 1)
+                       ret = -EINVAL;
+               else
+                       ret = 0;
+       }
+       kfree(txmsg);
+       drm_dp_put_port(port);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_send_power_updown_phy);
+
 static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
                                       int id,
                                       struct drm_dp_payload *payload)
index 6bb6337be920c6b440ef33f579832368feed86e5..00ddabfbf980401f8d58a4d46eff8ee60282ee5f 100644 (file)
@@ -1533,6 +1533,10 @@ static void connector_bad_edid(struct drm_connector *connector,
  * level, drivers must make all reasonable efforts to expose it as an I2C
  * adapter and use drm_get_edid() instead of abusing this function.
  *
+ * The EDID may be overridden using debugfs override_edid or firmare EDID
+ * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority
+ * order. Having either of them bypasses actual EDID reads.
+ *
  * Return: Pointer to valid EDID or NULL if we couldn't find any.
  */
 struct edid *drm_do_get_edid(struct drm_connector *connector,
@@ -1542,6 +1546,17 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
 {
        int i, j = 0, valid_extensions = 0;
        u8 *edid, *new;
+       struct edid *override = NULL;
+
+       if (connector->override_edid)
+               override = drm_edid_duplicate((const struct edid *)
+                                             connector->edid_blob_ptr->data);
+
+       if (!override)
+               override = drm_load_edid_firmware(connector);
+
+       if (!IS_ERR_OR_NULL(override))
+               return override;
 
        if ((edid = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
                return NULL;
index 1c0495acf341964f7684f9e2bf5336473e7f525a..a4915099aaa99223b56de4181c26510303769966 100644 (file)
@@ -31,6 +31,22 @@ module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
 MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
        "from built-in data or /lib/firmware instead. ");
 
+/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
+int __drm_set_edid_firmware_path(const char *path)
+{
+       scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path);
+
+       return 0;
+}
+EXPORT_SYMBOL(__drm_set_edid_firmware_path);
+
+/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
+int __drm_get_edid_firmware_path(char *buf, size_t bufsize)
+{
+       return scnprintf(buf, bufsize, "%s", edid_firmware);
+}
+EXPORT_SYMBOL(__drm_get_edid_firmware_path);
+
 #define GENERIC_EDIDS 6
 static const char * const generic_edid_name[GENERIC_EDIDS] = {
        "edid/800x600.bin",
index 1b8f013ffa6503ec960cee3bdb9cb7c3fa78d2bc..6a31d13f2f81deda566074ac75b12618ff9c75ec 100644 (file)
@@ -910,6 +910,9 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
        if (!drm_fbdev_emulation || !fb_helper)
                return;
 
+       cancel_work_sync(&fb_helper->resume_work);
+       cancel_work_sync(&fb_helper->dirty_work);
+
        info = fb_helper->fbdev;
        if (info) {
                if (info->cmap.len)
@@ -918,9 +921,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
        }
        fb_helper->fbdev = NULL;
 
-       cancel_work_sync(&fb_helper->resume_work);
-       cancel_work_sync(&fb_helper->dirty_work);
-
        mutex_lock(&kernel_fb_helper_lock);
        if (!list_empty(&fb_helper->kernel_fb_list)) {
                list_del(&fb_helper->kernel_fb_list);
index c55f338e380b9d0d86da89012cd9109689ff6bd2..7199bba68c379385a2a3fb2b9fd47f853a1f9c76 100644 (file)
@@ -334,6 +334,12 @@ int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
        if (!obj)
                return -ENOENT;
 
+       /* Don't allow imported objects to be mapped */
+       if (obj->import_attach) {
+               ret = -EINVAL;
+               goto out;
+       }
+
        ret = drm_gem_create_mmap_offset(obj);
        if (ret)
                goto out;
index d54a083dc5dd076581da6cd1b81df6b5f1083a28..fc7e995541c9859d0711e3c39f9cadfc1e7d1ea4 100644 (file)
@@ -154,7 +154,7 @@ drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
 
                objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
                if (!objs[i]) {
-                       DRM_DEV_ERROR(dev->dev, "Failed to lookup GEM\n");
+                       DRM_DEBUG_KMS("Failed to lookup GEM object\n");
                        ret = -ENOENT;
                        goto err_gem_object_put;
                }
@@ -232,7 +232,7 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane,
        struct dma_buf *dma_buf;
        struct dma_fence *fence;
 
-       if ((plane->state->fb == state->fb) || !state->fb)
+       if (plane->state->fb == state->fb || !state->fb)
                return 0;
 
        dma_buf = drm_gem_fb_get_obj(state->fb, 0)->dma_buf;
index 6e35a56a6102ddb6b4b08d9803defa573b9eeea5..93e2b30fe1a54f436840f15612b5fa56a3f8e75a 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 #include <linux/module.h>
+#include <drm/drmP.h>
 
 #include "drm_crtc_helper_internal.h"
 
@@ -33,6 +34,33 @@ MODULE_AUTHOR("David Airlie, Jesse Barnes");
 MODULE_DESCRIPTION("DRM KMS helper");
 MODULE_LICENSE("GPL and additional rights");
 
+#if IS_ENABLED(CONFIG_DRM_LOAD_EDID_FIRMWARE)
+
+/* Backward compatibility for drm_kms_helper.edid_firmware */
+static int edid_firmware_set(const char *val, const struct kernel_param *kp)
+{
+       DRM_NOTE("drm_kms_firmware.edid_firmware is deprecated, please use drm.edid_firmware intead.\n");
+
+       return __drm_set_edid_firmware_path(val);
+}
+
+static int edid_firmware_get(char *buffer, const struct kernel_param *kp)
+{
+       return __drm_get_edid_firmware_path(buffer, PAGE_SIZE);
+}
+
+static const struct kernel_param_ops edid_firmware_ops = {
+       .set = edid_firmware_set,
+       .get = edid_firmware_get,
+};
+
+module_param_cb(edid_firmware, &edid_firmware_ops, NULL, 0644);
+__MODULE_PARM_TYPE(edid_firmware, "charp");
+MODULE_PARM_DESC(edid_firmware,
+                "DEPRECATED. Use drm.edid_firmware module parameter instead.");
+
+#endif
+
 static int __init drm_kms_helper_init(void)
 {
        int ret;
index 1055533792f3a538b65f0c202b6d81f5e0718175..7a1ea91d3343cfc13531b9dfd1e290db0490edda 100644 (file)
@@ -247,8 +247,9 @@ int drm_object_property_set_value(struct drm_mode_object *obj,
 }
 EXPORT_SYMBOL(drm_object_property_set_value);
 
-int __drm_object_property_get_value(struct drm_mode_object *obj,
-                                 struct drm_property *property, uint64_t *val)
+static int __drm_object_property_get_value(struct drm_mode_object *obj,
+                                          struct drm_property *property,
+                                          uint64_t *val)
 {
        int i;
 
index af4e906c630d5bc20f08eb62107946fe70c0a107..e123497da0ca5d13d7730050bb2091de429b1564 100644 (file)
  *
  * The basic usage pattern is to::
  *
- *     drm_modeset_acquire_init(&ctx)
+ *     drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
  *     retry:
  *     foreach (lock in random_ordered_set_of_locks) {
- *         ret = drm_modeset_lock(lock, &ctx)
+ *         ret = drm_modeset_lock(lock, ctx)
  *         if (ret == -EDEADLK) {
- *             drm_modeset_backoff(&ctx);
- *             goto retry;
+ *             ret = drm_modeset_backoff(ctx);
+ *             if (!ret)
+ *                 goto retry;
  *         }
+ *         if (ret)
+ *             goto out;
  *     }
  *     ... do stuff ...
- *     drm_modeset_drop_locks(&ctx);
- *     drm_modeset_acquire_fini(&ctx);
+ *     out:
+ *     drm_modeset_drop_locks(ctx);
+ *     drm_modeset_acquire_fini(ctx);
  *
  * If all that is needed is a single modeset lock, then the &struct
  * drm_modeset_acquire_ctx is not needed and the locking can be simplified
- * by passing a NULL instead of ctx in the drm_modeset_lock()
- * call and, when done, by calling drm_modeset_unlock().
+ * by passing a NULL instead of ctx in the drm_modeset_lock() call or
+ * calling  drm_modeset_lock_single_interruptible(). To unlock afterwards
+ * call drm_modeset_unlock().
  *
  * On top of these per-object locks using &ww_mutex there's also an overall
  * &drm_mode_config.mutex, for protecting everything else. Mostly this means
@@ -178,7 +183,11 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
 /**
  * drm_modeset_acquire_init - initialize acquire context
  * @ctx: the acquire context
- * @flags: for future
+ * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE
+ *
+ * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags,
+ * all calls to drm_modeset_lock() will perform an interruptible
+ * wait.
  */
 void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
                uint32_t flags)
@@ -186,6 +195,9 @@ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
        memset(ctx, 0, sizeof(*ctx));
        ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
        INIT_LIST_HEAD(&ctx->locked);
+
+       if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
+               ctx->interruptible = true;
 }
 EXPORT_SYMBOL(drm_modeset_acquire_init);
 
@@ -261,8 +273,19 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
        return ret;
 }
 
-static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
-               bool interruptible)
+/**
+ * drm_modeset_backoff - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
+ * you must call this function to drop all currently held locks and
+ * block until the contended lock becomes available.
+ *
+ * This function returns 0 on success, or -ERESTARTSYS if this context
+ * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the
+ * wait has been interrupted.
+ */
+int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
 {
        struct drm_modeset_lock *contended = ctx->contended;
 
@@ -273,35 +296,10 @@ static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
 
        drm_modeset_drop_locks(ctx);
 
-       return modeset_lock(contended, ctx, interruptible, true);
-}
-
-/**
- * drm_modeset_backoff - deadlock avoidance backoff
- * @ctx: the acquire context
- *
- * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
- * you must call this function to drop all currently held locks and
- * block until the contended lock becomes available.
- */
-void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
-{
-       modeset_backoff(ctx, false);
+       return modeset_lock(contended, ctx, ctx->interruptible, true);
 }
 EXPORT_SYMBOL(drm_modeset_backoff);
 
-/**
- * drm_modeset_backoff_interruptible - deadlock avoidance backoff
- * @ctx: the acquire context
- *
- * Interruptible version of drm_modeset_backoff()
- */
-int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
-{
-       return modeset_backoff(ctx, true);
-}
-EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
-
 /**
  * drm_modeset_lock_init - initialize lock
  * @lock: lock to init
@@ -324,14 +322,18 @@ EXPORT_SYMBOL(drm_modeset_lock_init);
  * deadlock scenario has been detected and it is an error to attempt
  * to take any more locks without first calling drm_modeset_backoff().
  *
+ * If the @ctx is not NULL and initialized with
+ * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with
+ * -ERESTARTSYS when interrupted.
+ *
  * If @ctx is NULL then the function call behaves like a normal,
- * non-nesting mutex_lock() call.
+ * uninterruptible non-nesting mutex_lock() call.
  */
 int drm_modeset_lock(struct drm_modeset_lock *lock,
                struct drm_modeset_acquire_ctx *ctx)
 {
        if (ctx)
-               return modeset_lock(lock, ctx, false, false);
+               return modeset_lock(lock, ctx, ctx->interruptible, false);
 
        ww_mutex_lock(&lock->mutex, NULL);
        return 0;
@@ -339,21 +341,19 @@ int drm_modeset_lock(struct drm_modeset_lock *lock,
 EXPORT_SYMBOL(drm_modeset_lock);
 
 /**
- * drm_modeset_lock_interruptible - take modeset lock
+ * drm_modeset_lock_single_interruptible - take a single modeset lock
  * @lock: lock to take
- * @ctx: acquire ctx
  *
- * Interruptible version of drm_modeset_lock()
+ * This function behaves as drm_modeset_lock() with a NULL context,
+ * but performs interruptible waits.
+ *
+ * This function returns 0 on success, or -ERESTARTSYS when interrupted.
  */
-int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
-               struct drm_modeset_acquire_ctx *ctx)
+int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock)
 {
-       if (ctx)
-               return modeset_lock(lock, ctx, true, false);
-
        return ww_mutex_lock_interruptible(&lock->mutex, NULL);
 }
-EXPORT_SYMBOL(drm_modeset_lock_interruptible);
+EXPORT_SYMBOL(drm_modeset_lock_single_interruptible);
 
 /**
  * drm_modeset_unlock - drop modeset lock
index 7a00351d5b5def435de5c55c40bb659da31ab8ca..72cba9805edcd939071a43952cfc49a546aed945 100644 (file)
@@ -667,7 +667,7 @@ static int setplane_internal(struct drm_plane *plane,
        struct drm_modeset_acquire_ctx ctx;
        int ret;
 
-       drm_modeset_acquire_init(&ctx, 0);
+       drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
 retry:
        ret = drm_modeset_lock_all_ctx(plane->dev, &ctx);
        if (ret)
@@ -678,8 +678,9 @@ retry:
 
 fail:
        if (ret == -EDEADLK) {
-               drm_modeset_backoff(&ctx);
-               goto retry;
+               ret = drm_modeset_backoff(&ctx);
+               if (!ret)
+                       goto retry;
        }
        drm_modeset_drop_locks(&ctx);
        drm_modeset_acquire_fini(&ctx);
@@ -834,7 +835,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
                return -ENOENT;
        }
 
-       drm_modeset_acquire_init(&ctx, 0);
+       drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
 retry:
        ret = drm_modeset_lock(&crtc->mutex, &ctx);
        if (ret)
@@ -876,8 +877,9 @@ retry:
        }
 out:
        if (ret == -EDEADLK) {
-               drm_modeset_backoff(&ctx);
-               goto retry;
+               ret = drm_modeset_backoff(&ctx);
+               if (!ret)
+                       goto retry;
        }
 
        drm_modeset_drop_locks(&ctx);
@@ -985,7 +987,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                return -EINVAL;
        }
 
-       drm_modeset_acquire_init(&ctx, 0);
+       drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
 retry:
        ret = drm_modeset_lock(&crtc->mutex, &ctx);
        if (ret)
@@ -1074,8 +1076,9 @@ out:
        crtc->primary->old_fb = NULL;
 
        if (ret == -EDEADLK) {
-               drm_modeset_backoff(&ctx);
-               goto retry;
+               ret = drm_modeset_backoff(&ctx);
+               if (!ret)
+                       goto retry;
        }
 
        drm_modeset_drop_locks(&ctx);
index 904966cde32b51886fc5ee9e876a1d2ec992745b..5840aabbf24e51611cb825ab6fc8b017a6ce6701 100644 (file)
@@ -353,8 +353,6 @@ EXPORT_SYMBOL(drm_helper_probe_detect);
  *    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())
  *    - &drm_connector_helper_funcs.get_modes vfunc
  *    - if the connector status is connector_status_connected, standard
  *      VESA DMT modes up to 1024x768 are automatically added
@@ -483,22 +481,7 @@ retry:
                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 {
-               struct edid *edid = drm_load_edid_firmware(connector);
-               if (!IS_ERR_OR_NULL(edid)) {
-                       drm_mode_connector_update_edid_property(connector, edid);
-                       count = drm_add_edid_modes(connector, edid);
-                       drm_edid_to_eld(connector, edid);
-                       kfree(edid);
-               }
-               if (count == 0)
-                       count = (*connector_funcs->get_modes)(connector);
-       }
+       count = (*connector_funcs->get_modes)(connector);
 
        if (count == 0 && connector->status == connector_status_connected)
                count = drm_add_modes_noedid(connector, 1024, 768);
index 935653eb3616c997064fb166cb0d1ce8e839e4b7..657ea5ab6c3f896821672b3568e5808d39987f0b 100644 (file)
@@ -134,7 +134,6 @@ EXPORT_SYMBOL(drm_scdc_write);
  * Returns:
  * True if the scrambling is enabled, false otherwise.
  */
-
 bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
 {
        u8 status;
@@ -142,7 +141,7 @@ bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
 
        ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status);
        if (ret < 0) {
-               DRM_ERROR("Failed to read scrambling status, error %d\n", ret);
+               DRM_ERROR("Failed to read scrambling status: %d\n", ret);
                return false;
        }
 
@@ -162,7 +161,6 @@ EXPORT_SYMBOL(drm_scdc_get_scrambling_status);
  * Returns:
  * True if scrambling is set/reset successfully, false otherwise.
  */
-
 bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
 {
        u8 config;
@@ -170,7 +168,7 @@ bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
 
        ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
        if (ret < 0) {
-               DRM_ERROR("Failed to read tmds config, err=%d\n", ret);
+               DRM_ERROR("Failed to read TMDS config: %d\n", ret);
                return false;
        }
 
@@ -181,7 +179,7 @@ bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
 
        ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
        if (ret < 0) {
-               DRM_ERROR("Failed to enable scrambling, error %d\n", ret);
+               DRM_ERROR("Failed to enable scrambling: %d\n", ret);
                return false;
        }
 
@@ -225,7 +223,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
 
        ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
        if (ret < 0) {
-               DRM_ERROR("Failed to read tmds config, err=%d\n", ret);
+               DRM_ERROR("Failed to read TMDS config: %d\n", ret);
                return false;
        }
 
@@ -236,7 +234,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
 
        ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
        if (ret < 0) {
-               DRM_ERROR("Failed to set TMDS clock ratio, error %d\n", ret);
+               DRM_ERROR("Failed to set TMDS clock ratio: %d\n", ret);
                return false;
        }
 
index 0422b8c2c2e7392c24107cd9d07b771b25722065..26d60615b4d422b56899dea572cfd811bb2394a1 100644 (file)
@@ -417,8 +417,8 @@ static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
        return 0;
 }
 
-int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
-                                      int fd, int handle)
+static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
+                                             int fd, int handle)
 {
        struct dma_fence *fence = sync_file_get_fence(fd);
        struct drm_syncobj *syncobj;
@@ -438,8 +438,8 @@ int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
        return 0;
 }
 
-int drm_syncobj_export_sync_file(struct drm_file *file_private,
-                                int handle, int *p_fd)
+static int drm_syncobj_export_sync_file(struct drm_file *file_private,
+                                       int handle, int *p_fd)
 {
        int ret;
        struct dma_fence *fence;
index 14c5a777682e54b236e257e85c9f5699ea62e12d..16c64d067e675ac510eafceb61ee0c264702325c 100644 (file)
@@ -61,5 +61,5 @@ TRACE_EVENT(drm_vblank_event_delivered,
 
 /* This part must be outside protection */
 #undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm
 #include <trace/define_trace.h>
index ba4a32b132baada502df485477d85281600b872f..2174814273e2071934c817e180848498627f6e14 100644 (file)
@@ -420,11 +420,7 @@ static int exynos_mic_probe(struct platform_device *pdev)
        mic->bridge.funcs = &mic_bridge_funcs;
        mic->bridge.of_node = dev->of_node;
 
-       ret = drm_bridge_add(&mic->bridge);
-       if (ret) {
-               DRM_ERROR("mic: Failed to add MIC to the global bridge list\n");
-               return ret;
-       }
+       drm_bridge_add(&mic->bridge);
 
        pm_runtime_enable(dev);
 
index c52f9adf5e04c620cee1043edaa84f828ccffb43..a4bb89b7878f9f966f09c570d47035c365de1110 100644 (file)
@@ -1901,10 +1901,8 @@ cdv_intel_dp_destroy(struct drm_connector *connector)
 
        if (is_edp(gma_encoder)) {
        /*      cdv_intel_panel_destroy_backlight(connector->dev); */
-               if (intel_dp->panel_fixed_mode) {
-                       kfree(intel_dp->panel_fixed_mode);
-                       intel_dp->panel_fixed_mode = NULL;
-               }
+               kfree(intel_dp->panel_fixed_mode);
+               intel_dp->panel_fixed_mode = NULL;
        }
        i2c_del_adapter(&intel_dp->adapter);
        drm_connector_unregister(connector);
index 531e4450c0009b7189490207182e033783113f3b..5c066448be5b7d6a69ab52259fea38f29e349d35 100644 (file)
@@ -99,7 +99,7 @@ void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe)
        /* Wait for for the pipe enable to take effect. */
        for (count = 0; count < COUNT_MAX; count++) {
                temp = REG_READ(map->conf);
-               if ((temp & PIPEACONF_PIPE_STATE) == 1)
+               if (temp & PIPEACONF_PIPE_STATE)
                        break;
        }
 }
index e9e8ae2ec06b2d52b404f10d96b8cebb7139cee0..544a8a2d3562c2bd6f173be3e5cee0d80f9c2696 100644 (file)
@@ -485,7 +485,7 @@ static int ch7006_encoder_init(struct i2c_client *client,
        return 0;
 }
 
-static struct i2c_device_id ch7006_ids[] = {
+static const struct i2c_device_id ch7006_ids[] = {
        { "ch7006", 0 },
        { }
 };
index db0b03fb0ff1d86d899bdaa0e70b3ed67538500e..ecaa58757529900c2aeca1698406b396239087e3 100644 (file)
@@ -415,7 +415,7 @@ sil164_encoder_init(struct i2c_client *client,
        return 0;
 }
 
-static struct i2c_device_id sil164_ids[] = {
+static const struct i2c_device_id sil164_ids[] = {
        { "sil164", 0 },
        { }
 };
index 54e3255dde13d09b27efa1ab5c5d077380d80a96..4d1f45acf2cdbb0be6b0a9a53d55f6af6fbb28c5 100644 (file)
@@ -1746,7 +1746,7 @@ static const struct of_device_id tda998x_dt_ids[] = {
 MODULE_DEVICE_TABLE(of, tda998x_dt_ids);
 #endif
 
-static struct i2c_device_id tda998x_ids[] = {
+static const struct i2c_device_id tda998x_ids[] = {
        { "tda998x", 0 },
        { }
 };
index 18d9da53282b6701c922ebfe46b40e8832d149e1..b6b175aa5d255db9b536ef68ca323655a8c739e0 100644 (file)
@@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
                            struct drm_atomic_state *old_state);
        void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
                             struct drm_atomic_state *old_state);
-       void (*update_crtcs)(struct drm_atomic_state *state,
-                            unsigned int *crtc_vblank_mask);
+       void (*update_crtcs)(struct drm_atomic_state *state);
        void (*audio_codec_enable)(struct drm_connector *connector,
                                   struct intel_encoder *encoder,
                                   const struct drm_display_mode *adjusted_mode);
index 00cd17c76fdcc3bc5427f53bf45e416d66400655..4b34fa6954f9997b01aa5953171dbd7d0a49dde2 100644 (file)
@@ -12129,73 +12129,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
        return dev->driver->get_vblank_counter(dev, crtc->pipe);
 }
 
-static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
-                                         struct drm_i915_private *dev_priv,
-                                         unsigned crtc_mask)
-{
-       unsigned last_vblank_count[I915_MAX_PIPES];
-       enum pipe pipe;
-       int ret;
-
-       if (!crtc_mask)
-               return;
-
-       for_each_pipe(dev_priv, pipe) {
-               struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
-                                                                 pipe);
-
-               if (!((1 << pipe) & crtc_mask))
-                       continue;
-
-               ret = drm_crtc_vblank_get(&crtc->base);
-               if (WARN_ON(ret != 0)) {
-                       crtc_mask &= ~(1 << pipe);
-                       continue;
-               }
-
-               last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
-       }
-
-       for_each_pipe(dev_priv, pipe) {
-               struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
-                                                                 pipe);
-               long lret;
-
-               if (!((1 << pipe) & crtc_mask))
-                       continue;
-
-               lret = wait_event_timeout(dev->vblank[pipe].queue,
-                               last_vblank_count[pipe] !=
-                                       drm_crtc_vblank_count(&crtc->base),
-                               msecs_to_jiffies(50));
-
-               WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
-
-               drm_crtc_vblank_put(&crtc->base);
-       }
-}
-
-static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
-{
-       /* fb updated, need to unpin old fb */
-       if (crtc_state->fb_changed)
-               return true;
-
-       /* wm changes, need vblank before final wm's */
-       if (crtc_state->update_wm_post)
-               return true;
-
-       if (crtc_state->wm.need_postvbl_update)
-               return true;
-
-       return false;
-}
-
 static void intel_update_crtc(struct drm_crtc *crtc,
                              struct drm_atomic_state *state,
                              struct drm_crtc_state *old_crtc_state,
-                             struct drm_crtc_state *new_crtc_state,
-                             unsigned int *crtc_vblank_mask)
+                             struct drm_crtc_state *new_crtc_state)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -12218,13 +12155,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
        }
 
        drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
-
-       if (needs_vblank_wait(pipe_config))
-               *crtc_vblank_mask |= drm_crtc_mask(crtc);
 }
 
-static void intel_update_crtcs(struct drm_atomic_state *state,
-                              unsigned int *crtc_vblank_mask)
+static void intel_update_crtcs(struct drm_atomic_state *state)
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *old_crtc_state, *new_crtc_state;
@@ -12235,12 +12168,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
                        continue;
 
                intel_update_crtc(crtc, state, old_crtc_state,
-                                 new_crtc_state, crtc_vblank_mask);
+                                 new_crtc_state);
        }
 }
 
-static void skl_update_crtcs(struct drm_atomic_state *state,
-                            unsigned int *crtc_vblank_mask)
+static void skl_update_crtcs(struct drm_atomic_state *state)
 {
        struct drm_i915_private *dev_priv = to_i915(state->dev);
        struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
@@ -12299,7 +12231,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
                                vbl_wait = true;
 
                        intel_update_crtc(crtc, state, old_crtc_state,
-                                         new_crtc_state, crtc_vblank_mask);
+                                         new_crtc_state);
 
                        if (vbl_wait)
                                intel_wait_for_vblank(dev_priv, pipe);
@@ -12361,7 +12293,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
        struct intel_crtc_state *intel_cstate;
        bool hw_check = intel_state->modeset;
        u64 put_domains[I915_MAX_PIPES] = {};
-       unsigned crtc_vblank_mask = 0;
        int i;
 
        intel_atomic_commit_fence_wait(intel_state);
@@ -12451,7 +12382,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
        }
 
        /* Now enable the clocks, plane, pipe, and connectors that we set up. */
-       dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
+       dev_priv->display.update_crtcs(state);
 
        /* FIXME: We should call drm_atomic_helper_commit_hw_done() here
         * already, but still need the state for the delayed optimization. To
@@ -12462,8 +12393,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
         * - switch over to the vblank wait helper in the core after that since
         *   we don't need out special handling any more.
         */
-       if (!state->legacy_cursor_update)
-               intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask);
+       drm_atomic_helper_wait_for_flip_done(dev, state);
 
        /*
         * Now that the vblank has passed, we can go ahead and program the
@@ -13061,6 +12991,14 @@ intel_legacy_cursor_update(struct drm_plane *plane,
                goto slow;
 
        old_plane_state = plane->state;
+       /*
+        * Don't do an async update if there is an outstanding commit modifying
+        * the plane.  This prevents our async update's changes from getting
+        * overridden by a previous synchronous update's state.
+        */
+       if (old_plane_state->commit &&
+           !try_wait_for_completion(&old_plane_state->commit->hw_done))
+               goto slow;
 
        /*
         * If any parameters change that may affect watermarks,
@@ -13120,17 +13058,12 @@ intel_legacy_cursor_update(struct drm_plane *plane,
        }
 
        old_fb = old_plane_state->fb;
-       old_vma = to_intel_plane_state(old_plane_state)->vma;
 
        i915_gem_track_fb(intel_fb_obj(old_fb), intel_fb_obj(fb),
                          intel_plane->frontbuffer_bit);
 
        /* Swap plane state */
-       new_plane_state->fence = old_plane_state->fence;
-       *to_intel_plane_state(old_plane_state) = *to_intel_plane_state(new_plane_state);
-       new_plane_state->fence = NULL;
-       new_plane_state->fb = old_fb;
-       to_intel_plane_state(new_plane_state)->vma = NULL;
+       plane->state = new_plane_state;
 
        if (plane->state->visible) {
                trace_intel_update_plane(plane, to_intel_crtc(crtc));
@@ -13142,13 +13075,17 @@ intel_legacy_cursor_update(struct drm_plane *plane,
                intel_plane->disable_plane(intel_plane, to_intel_crtc(crtc));
        }
 
+       old_vma = fetch_and_zero(&to_intel_plane_state(old_plane_state)->vma);
        if (old_vma)
                intel_unpin_fb_vma(old_vma);
 
 out_unlock:
        mutex_unlock(&dev_priv->drm.struct_mutex);
 out_free:
-       intel_plane_destroy_state(plane, new_plane_state);
+       if (ret)
+               intel_plane_destroy_state(plane, new_plane_state);
+       else
+               intel_plane_destroy_state(plane, old_plane_state);
        return ret;
 
 slow:
index f91cb72d08305f50e437f69bb2c2e7478588cf87..93c7e3f9b4a88d776959be4bf8db22c6c6ac4d97 100644 (file)
@@ -24,6 +24,7 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_of.h>
@@ -105,7 +106,7 @@ static int imx_drm_atomic_check(struct drm_device *dev,
 }
 
 static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
-       .fb_create = drm_fb_cma_create,
+       .fb_create = drm_gem_fb_create,
        .output_poll_changed = imx_drm_output_poll_changed,
        .atomic_check = imx_drm_atomic_check,
        .atomic_commit = drm_atomic_helper_commit,
index cf98596c7ce1e7598de76ad1cf1bcaa3407c8d65..247c60e6bed27a208599b12d018c3141a3995dee 100644 (file)
@@ -18,6 +18,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
 
 #include "video/imx-ipu-v3.h"
@@ -690,7 +691,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
 }
 
 static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
-       .prepare_fb = drm_fb_cma_prepare_fb,
+       .prepare_fb = drm_gem_fb_prepare_fb,
        .atomic_check = ipu_plane_atomic_check,
        .atomic_disable = ipu_plane_atomic_disable,
        .atomic_update = ipu_plane_atomic_update,
index 690c67507cbce6cd445bdcf245b08a3d52032b65..3ff502771ba2f53c76f0098b4369aa5d0e512f2f 100644 (file)
@@ -1696,11 +1696,7 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev)
 
        hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs;
        hdmi->bridge.of_node = pdev->dev.of_node;
-       ret = drm_bridge_add(&hdmi->bridge);
-       if (ret) {
-               dev_err(dev, "failed to add bridge, ret = %d\n", ret);
-               return ret;
-       }
+       drm_bridge_add(&hdmi->bridge);
 
        ret = mtk_hdmi_clk_enable_audio(hdmi);
        if (ret) {
index d84a031fae247db1787d9143eb54767381a42c33..718d8ce15b1f7a95b84a9ea7ca96468530d27d11 100644 (file)
@@ -63,6 +63,15 @@ 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_ORISETECH_OTM8009A
+       tristate "Orise Technology otm8009a 480x800 dsi 2dl 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 Orise Technology
+         otm8009a 480x800 dsi 2dl panel.
+
 config DRM_PANEL_PANASONIC_VVX10F034N00
        tristate "Panasonic VVX10F034N00 1920x1200 video mode panel"
        depends on OF
@@ -80,12 +89,28 @@ config DRM_PANEL_SAMSUNG_S6E3HA2
        depends on BACKLIGHT_CLASS_DEVICE
        select VIDEOMODE_HELPERS
 
+config DRM_PANEL_SAMSUNG_S6E63J0X03
+       tristate "Samsung S6E63J0X03 DSI command mode panel"
+       depends on OF
+       depends on DRM_MIPI_DSI
+       depends on BACKLIGHT_CLASS_DEVICE
+       select VIDEOMODE_HELPERS
+
 config DRM_PANEL_SAMSUNG_S6E8AA0
        tristate "Samsung S6E8AA0 DSI video mode panel"
        depends on OF
        select DRM_MIPI_DSI
        select VIDEOMODE_HELPERS
 
+config DRM_PANEL_SEIKO_43WVF1G
+       tristate "Seiko 43WVF1G panel"
+       depends on OF
+       depends on BACKLIGHT_CLASS_DEVICE
+       select VIDEOMODE_HELPERS
+       help
+         Say Y here if you want to enable support for the Seiko
+         43WVF1G controller for 800x480 LCD panels
+
 config DRM_PANEL_SHARP_LQ101R1SX01
        tristate "Sharp LQ101R1SX01 panel"
        depends on OF
index 9f6610d08b009e99b18034a3bdd4656832c1f695..c8483fdd5b9bd02c2289be13bed747170185ef5a 100644 (file)
@@ -3,10 +3,13 @@ obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
 obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
 obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
+obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.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_S6E3HA2) += panel-samsung-s6e3ha2.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
+obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
new file mode 100644 (file)
index 0000000..c189cd6
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <video/mipi_display.h>
+
+#define DRV_NAME "orisetech_otm8009a"
+
+#define OTM8009A_BACKLIGHT_DEFAULT     240
+#define OTM8009A_BACKLIGHT_MAX         255
+
+/* Manufacturer Command Set */
+#define MCS_ADRSFT     0x0000  /* Address Shift Function */
+#define MCS_PANSET     0xB3A6  /* Panel Type Setting */
+#define MCS_SD_CTRL    0xC0A2  /* Source Driver Timing Setting */
+#define MCS_P_DRV_M    0xC0B4  /* Panel Driving Mode */
+#define MCS_OSC_ADJ    0xC181  /* Oscillator Adjustment for Idle/Normal mode */
+#define MCS_RGB_VID_SET        0xC1A1  /* RGB Video Mode Setting */
+#define MCS_SD_PCH_CTRL        0xC480  /* Source Driver Precharge Control */
+#define MCS_NO_DOC1    0xC48A  /* Command not documented */
+#define MCS_PWR_CTRL1  0xC580  /* Power Control Setting 1 */
+#define MCS_PWR_CTRL2  0xC590  /* Power Control Setting 2 for Normal Mode */
+#define MCS_PWR_CTRL4  0xC5B0  /* Power Control Setting 4 for DC Voltage */
+#define MCS_PANCTRLSET1        0xCB80  /* Panel Control Setting 1 */
+#define MCS_PANCTRLSET2        0xCB90  /* Panel Control Setting 2 */
+#define MCS_PANCTRLSET3        0xCBA0  /* Panel Control Setting 3 */
+#define MCS_PANCTRLSET4        0xCBB0  /* Panel Control Setting 4 */
+#define MCS_PANCTRLSET5        0xCBC0  /* Panel Control Setting 5 */
+#define MCS_PANCTRLSET6        0xCBD0  /* Panel Control Setting 6 */
+#define MCS_PANCTRLSET7        0xCBE0  /* Panel Control Setting 7 */
+#define MCS_PANCTRLSET8        0xCBF0  /* Panel Control Setting 8 */
+#define MCS_PANU2D1    0xCC80  /* Panel U2D Setting 1 */
+#define MCS_PANU2D2    0xCC90  /* Panel U2D Setting 2 */
+#define MCS_PANU2D3    0xCCA0  /* Panel U2D Setting 3 */
+#define MCS_PAND2U1    0xCCB0  /* Panel D2U Setting 1 */
+#define MCS_PAND2U2    0xCCC0  /* Panel D2U Setting 2 */
+#define MCS_PAND2U3    0xCCD0  /* Panel D2U Setting 3 */
+#define MCS_GOAVST     0xCE80  /* GOA VST Setting */
+#define MCS_GOACLKA1   0xCEA0  /* GOA CLKA1 Setting */
+#define MCS_GOACLKA3   0xCEB0  /* GOA CLKA3 Setting */
+#define MCS_GOAECLK    0xCFC0  /* GOA ECLK Setting */
+#define MCS_NO_DOC2    0xCFD0  /* Command not documented */
+#define MCS_GVDDSET    0xD800  /* GVDD/NGVDD */
+#define MCS_VCOMDC     0xD900  /* VCOM Voltage Setting */
+#define MCS_GMCT2_2P   0xE100  /* Gamma Correction 2.2+ Setting */
+#define MCS_GMCT2_2N   0xE200  /* Gamma Correction 2.2- Setting */
+#define MCS_NO_DOC3    0xF5B6  /* Command not documented */
+#define MCS_CMD2_ENA1  0xFF00  /* Enable Access Command2 "CMD2" */
+#define MCS_CMD2_ENA2  0xFF80  /* Enable Access Orise Command2 */
+
+struct otm8009a {
+       struct device *dev;
+       struct drm_panel panel;
+       struct backlight_device *bl_dev;
+       struct gpio_desc *reset_gpio;
+       bool prepared;
+       bool enabled;
+};
+
+static const struct drm_display_mode default_mode = {
+       .clock = 32729,
+       .hdisplay = 480,
+       .hsync_start = 480 + 120,
+       .hsync_end = 480 + 120 + 63,
+       .htotal = 480 + 120 + 63 + 120,
+       .vdisplay = 800,
+       .vsync_start = 800 + 12,
+       .vsync_end = 800 + 12 + 12,
+       .vtotal = 800 + 12 + 12 + 12,
+       .vrefresh = 50,
+       .flags = 0,
+       .width_mm = 52,
+       .height_mm = 86,
+};
+
+static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
+{
+       return container_of(panel, struct otm8009a, panel);
+}
+
+static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
+                                  size_t len)
+{
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+
+       if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
+               DRM_WARN("mipi dsi dcs write buffer failed\n");
+}
+
+#define dcs_write_seq(ctx, seq...)                     \
+({                                                     \
+       static const u8 d[] = { seq };                  \
+       otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));  \
+})
+
+#define dcs_write_cmd_at(ctx, cmd, seq...)             \
+({                                                     \
+       dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF);   \
+       dcs_write_seq(ctx, (cmd) >> 8, seq);            \
+})
+
+static int otm8009a_init_sequence(struct otm8009a *ctx)
+{
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+       int ret;
+
+       /* Enter CMD2 */
+       dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
+
+       /* Enter Orise Command2 */
+       dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09);
+
+       dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30);
+       mdelay(10);
+
+       dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40);
+       mdelay(10);
+
+       dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9);
+       dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34);
+       dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50);
+       dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E);
+       dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */
+       dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01);
+       dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34);
+       dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33);
+       dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79);
+       dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B);
+       dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83);
+       dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83);
+       dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E);
+       dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01);
+
+       dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
+       dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
+                        0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
+       dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
+                        0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
+       dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
+                        0x01, 0x02, 0x00, 0x00);
+
+       dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00);
+
+       dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+       dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                        0, 0, 0, 0, 0);
+       dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                        0, 0, 0, 0, 0);
+       dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+       dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
+                        0, 0, 0, 0, 0);
+       dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
+                        4, 0, 0, 0, 0);
+       dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+       dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                        0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
+
+       dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
+                        0x00, 0x00, 0x00, 0x00);
+       dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                        0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
+       dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
+                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+       dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
+                        0x00, 0x00, 0x00, 0x00);
+       dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                        0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
+       dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
+                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+       dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66);
+
+       dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06);
+
+       dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
+                        0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
+                        0x01);
+       dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
+                        0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
+                        0x01);
+
+       /* Exit CMD2 */
+       dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
+
+       ret = mipi_dsi_dcs_nop(dsi);
+       if (ret)
+               return ret;
+
+       ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+       if (ret)
+               return ret;
+
+       /* Wait for sleep out exit */
+       mdelay(120);
+
+       /* Default portrait 480x800 rgb24 */
+       dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+
+       ret = mipi_dsi_dcs_set_column_address(dsi, 0,
+                                             default_mode.hdisplay - 1);
+       if (ret)
+               return ret;
+
+       ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
+       if (ret)
+               return ret;
+
+       /* See otm8009a driver documentation for pixel format descriptions */
+       ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
+                                           MIPI_DCS_PIXEL_FMT_24BIT << 4);
+       if (ret)
+               return ret;
+
+       /* Disable CABC feature */
+       dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+
+       ret = mipi_dsi_dcs_set_display_on(dsi);
+       if (ret)
+               return ret;
+
+       ret = mipi_dsi_dcs_nop(dsi);
+       if (ret)
+               return ret;
+
+       /* Send Command GRAM memory write (no parameters) */
+       dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START);
+
+       return 0;
+}
+
+static int otm8009a_disable(struct drm_panel *panel)
+{
+       struct otm8009a *ctx = panel_to_otm8009a(panel);
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+       int ret;
+
+       if (!ctx->enabled)
+               return 0; /* This is not an issue so we return 0 here */
+
+       /* Power off the backlight. Note: end-user still controls brightness */
+       ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+       ret = backlight_update_status(ctx->bl_dev);
+       if (ret)
+               return ret;
+
+       ret = mipi_dsi_dcs_set_display_off(dsi);
+       if (ret)
+               return ret;
+
+       ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+       if (ret)
+               return ret;
+
+       msleep(120);
+
+       ctx->enabled = false;
+
+       return 0;
+}
+
+static int otm8009a_unprepare(struct drm_panel *panel)
+{
+       struct otm8009a *ctx = panel_to_otm8009a(panel);
+
+       if (!ctx->prepared)
+               return 0;
+
+       if (ctx->reset_gpio) {
+               gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+               msleep(20);
+       }
+
+       ctx->prepared = false;
+
+       return 0;
+}
+
+static int otm8009a_prepare(struct drm_panel *panel)
+{
+       struct otm8009a *ctx = panel_to_otm8009a(panel);
+       int ret;
+
+       if (ctx->prepared)
+               return 0;
+
+       if (ctx->reset_gpio) {
+               gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+               gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+               msleep(20);
+               gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+               msleep(100);
+       }
+
+       ret = otm8009a_init_sequence(ctx);
+       if (ret)
+               return ret;
+
+       ctx->prepared = true;
+
+       /*
+        * Power on the backlight. Note: end-user still controls brightness
+        * Note: ctx->prepared must be true before updating the backlight.
+        */
+       ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
+       backlight_update_status(ctx->bl_dev);
+
+       return 0;
+}
+
+static int otm8009a_enable(struct drm_panel *panel)
+{
+       struct otm8009a *ctx = panel_to_otm8009a(panel);
+
+       ctx->enabled = true;
+
+       return 0;
+}
+
+static int otm8009a_get_modes(struct drm_panel *panel)
+{
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_duplicate(panel->drm, &default_mode);
+       if (!mode) {
+               DRM_ERROR("failed to add mode %ux%ux@%u\n",
+                         default_mode.hdisplay, default_mode.vdisplay,
+                         default_mode.vrefresh);
+               return -ENOMEM;
+       }
+
+       drm_mode_set_name(mode);
+
+       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+       drm_mode_probed_add(panel->connector, mode);
+
+       panel->connector->display_info.width_mm = mode->width_mm;
+       panel->connector->display_info.height_mm = mode->height_mm;
+
+       return 1;
+}
+
+static const struct drm_panel_funcs otm8009a_drm_funcs = {
+       .disable   = otm8009a_disable,
+       .unprepare = otm8009a_unprepare,
+       .prepare   = otm8009a_prepare,
+       .enable    = otm8009a_enable,
+       .get_modes = otm8009a_get_modes,
+};
+
+/*
+ * DSI-BASED BACKLIGHT
+ */
+
+static int otm8009a_backlight_update_status(struct backlight_device *bd)
+{
+       struct otm8009a *ctx = bl_get_data(bd);
+       u8 data[2];
+
+       if (!ctx->prepared) {
+               DRM_DEBUG("lcd not ready yet for setting its backlight!\n");
+               return -ENXIO;
+       }
+
+       if (bd->props.power <= FB_BLANK_NORMAL) {
+               /* Power on the backlight with the requested brightness
+                * Note We can not use mipi_dsi_dcs_set_display_brightness()
+                * as otm8009a driver support only 8-bit brightness (1 param).
+                */
+               data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
+               data[1] = bd->props.brightness;
+               otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
+
+               /* set Brightness Control & Backlight on */
+               data[1] = 0x24;
+
+       } else {
+               /* Power off the backlight: set Brightness Control & Bl off */
+               data[1] = 0;
+       }
+
+       /* Update Brightness Control & Backlight */
+       data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
+       otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
+
+       return 0;
+}
+
+static const struct backlight_ops otm8009a_backlight_ops = {
+       .update_status = otm8009a_backlight_update_status,
+};
+
+static int otm8009a_probe(struct mipi_dsi_device *dsi)
+{
+       struct device *dev = &dsi->dev;
+       struct otm8009a *ctx;
+       int ret;
+
+       ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(ctx->reset_gpio)) {
+               dev_err(dev, "cannot get reset-gpio\n");
+               return PTR_ERR(ctx->reset_gpio);
+       }
+
+       mipi_dsi_set_drvdata(dsi, ctx);
+
+       ctx->dev = dev;
+
+       dsi->lanes = 2;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+                         MIPI_DSI_MODE_LPM;
+
+       drm_panel_init(&ctx->panel);
+       ctx->panel.dev = dev;
+       ctx->panel.funcs = &otm8009a_drm_funcs;
+
+       ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx,
+                                               &otm8009a_backlight_ops, NULL);
+       if (IS_ERR(ctx->bl_dev)) {
+               dev_err(dev, "failed to register backlight device\n");
+               return PTR_ERR(ctx->bl_dev);
+       }
+
+       ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
+       ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
+       ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+       ctx->bl_dev->props.type = BACKLIGHT_RAW;
+
+       drm_panel_add(&ctx->panel);
+
+       ret = mipi_dsi_attach(dsi);
+       if (ret < 0) {
+               dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
+               drm_panel_remove(&ctx->panel);
+               backlight_device_unregister(ctx->bl_dev);
+               return ret;
+       }
+
+       DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n",
+                default_mode.hdisplay, default_mode.vdisplay,
+                default_mode.vrefresh,
+                mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
+
+       return 0;
+}
+
+static int otm8009a_remove(struct mipi_dsi_device *dsi)
+{
+       struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
+
+       mipi_dsi_detach(dsi);
+       drm_panel_remove(&ctx->panel);
+
+       backlight_device_unregister(ctx->bl_dev);
+
+       return 0;
+}
+
+static const struct of_device_id orisetech_otm8009a_of_match[] = {
+       { .compatible = "orisetech,otm8009a" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
+
+static struct mipi_dsi_driver orisetech_otm8009a_driver = {
+       .probe  = otm8009a_probe,
+       .remove = otm8009a_remove,
+       .driver = {
+               .name = DRV_NAME "_panel",
+               .of_match_table = orisetech_otm8009a_of_match,
+       },
+};
+module_mipi_dsi_driver(orisetech_otm8009a_driver);
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
new file mode 100644 (file)
index 0000000..aeb32aa
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver.
+ *
+ * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd
+ *
+ * Inki Dae <inki.dae@samsung.com>
+ * Hoegeun Kwon <hoegeun.kwon@samsung.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.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <video/mipi_display.h>
+
+#define MCS_LEVEL2_KEY         0xf0
+#define MCS_MTP_KEY            0xf1
+#define MCS_MTP_SET3           0xd4
+
+#define MAX_BRIGHTNESS         100
+#define DEFAULT_BRIGHTNESS     80
+
+#define NUM_GAMMA_STEPS                9
+#define GAMMA_CMD_CNT          28
+
+#define FIRST_COLUMN 20
+
+struct s6e63j0x03 {
+       struct device *dev;
+       struct drm_panel panel;
+       struct backlight_device *bl_dev;
+
+       struct regulator_bulk_data supplies[2];
+       struct gpio_desc *reset_gpio;
+};
+
+static const struct drm_display_mode default_mode = {
+       .clock = 4649,
+       .hdisplay = 320,
+       .hsync_start = 320 + 1,
+       .hsync_end = 320 + 1 + 1,
+       .htotal = 320 + 1 + 1 + 1,
+       .vdisplay = 320,
+       .vsync_start = 320 + 150,
+       .vsync_end = 320 + 150 + 1,
+       .vtotal = 320 + 150 + 1 + 2,
+       .vrefresh = 30,
+       .flags = 0,
+};
+
+static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = {
+       {       /* Gamma 10 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,
+               0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,
+               0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf
+       },
+       {       /* gamma 30 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,
+               0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,
+               0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc
+       },
+       {       /* gamma 60 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,
+               0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,
+               0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5
+       },
+       {       /* gamma 90 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,
+               0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,
+               0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09
+       },
+       {       /* gamma 120 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,
+               0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,
+               0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b
+       },
+       {       /* gamma 150 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,
+               0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,
+               0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29
+       },
+       {       /* gamma 200 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,
+               0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,
+               0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b
+       },
+       {       /* gamma 240 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,
+               0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,
+               0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47
+       },
+       {       /* gamma 300 */
+               MCS_MTP_SET3,
+               0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,
+               0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,
+               0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57
+       }
+};
+
+static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
+{
+       return container_of(panel, struct s6e63j0x03, panel);
+}
+
+static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,
+                                       const void *seq, size_t len)
+{
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+
+       return mipi_dsi_dcs_write_buffer(dsi, seq, len);
+}
+
+#define s6e63j0x03_dcs_write_seq_static(ctx, seq...)                   \
+       ({                                                              \
+               static const u8 d[] = { seq };                          \
+               s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d));        \
+       })
+
+static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)
+{
+       return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);
+}
+
+static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)
+{
+       if (on)
+               return s6e63j0x03_dcs_write_seq_static(ctx,
+                               MCS_MTP_KEY, 0x5a, 0x5a);
+
+       return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);
+}
+
+static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)
+{
+       int ret;
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+       if (ret < 0)
+               return ret;
+
+       msleep(30);
+
+       gpiod_set_value(ctx->reset_gpio, 1);
+       usleep_range(1000, 2000);
+       gpiod_set_value(ctx->reset_gpio, 0);
+       usleep_range(5000, 6000);
+
+       return 0;
+}
+
+static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
+{
+       return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)
+{
+       unsigned int index;
+
+       index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);
+
+       if (index >= NUM_GAMMA_STEPS)
+               index = NUM_GAMMA_STEPS - 1;
+
+       return index;
+}
+
+static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,
+                                       unsigned int brightness)
+{
+       struct backlight_device *bl_dev = ctx->bl_dev;
+       unsigned int index = s6e63j0x03_get_brightness_index(brightness);
+       int ret;
+
+       ret = s6e63j0x03_apply_mtp_key(ctx, true);
+       if (ret < 0)
+               return ret;
+
+       ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);
+       if (ret < 0)
+               return ret;
+
+       ret = s6e63j0x03_apply_mtp_key(ctx, false);
+       if (ret < 0)
+               return ret;
+
+       bl_dev->props.brightness = brightness;
+
+       return 0;
+}
+
+static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)
+{
+       struct s6e63j0x03 *ctx = bl_get_data(bl_dev);
+       unsigned int brightness = bl_dev->props.brightness;
+
+       return s6e63j0x03_update_gamma(ctx, brightness);
+}
+
+static const struct backlight_ops s6e63j0x03_bl_ops = {
+       .update_status = s6e63j0x03_set_brightness,
+};
+
+static int s6e63j0x03_disable(struct drm_panel *panel)
+{
+       struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+       int ret;
+
+       ret = mipi_dsi_dcs_set_display_off(dsi);
+       if (ret < 0)
+               return ret;
+
+       ctx->bl_dev->props.power = FB_BLANK_NORMAL;
+
+       ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+       if (ret < 0)
+               return ret;
+
+       msleep(120);
+
+       return 0;
+}
+
+static int s6e63j0x03_unprepare(struct drm_panel *panel)
+{
+       struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
+       int ret;
+
+       ret = s6e63j0x03_power_off(ctx);
+       if (ret < 0)
+               return ret;
+
+       ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+
+       return 0;
+}
+
+static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)
+{
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+       int ret;
+
+       ret = s6e63j0x03_enable_lv2_command(ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = s6e63j0x03_apply_mtp_key(ctx, true);
+       if (ret < 0)
+               return ret;
+
+       /* set porch adjustment */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);
+       if (ret < 0)
+               return ret;
+
+       /* set frame freq */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);
+       if (ret < 0)
+               return ret;
+
+       /* set caset, paset */
+       ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN,
+               default_mode.hdisplay - 1 + FIRST_COLUMN);
+       if (ret < 0)
+               return ret;
+
+       ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
+       if (ret < 0)
+               return ret;
+
+       /* set ltps timming 0, 1 */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17,
+               0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);
+       if (ret < 0)
+               return ret;
+
+       ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);
+       if (ret < 0)
+               return ret;
+
+       /* set param pos te_edge */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);
+       if (ret < 0)
+               return ret;
+
+       /* set te rising edge */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);
+       if (ret < 0)
+               return ret;
+
+       /* set param pos default */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);
+       if (ret < 0)
+               return ret;
+
+       ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+       if (ret < 0)
+               return ret;
+
+       ret = s6e63j0x03_apply_mtp_key(ctx, false);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int s6e63j0x03_prepare(struct drm_panel *panel)
+{
+       struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
+       int ret;
+
+       ret = s6e63j0x03_power_on(ctx);
+       if (ret < 0)
+               return ret;
+
+       ret = s6e63j0x03_panel_init(ctx);
+       if (ret < 0)
+               goto err;
+
+       ctx->bl_dev->props.power = FB_BLANK_NORMAL;
+
+       return 0;
+
+err:
+       s6e63j0x03_power_off(ctx);
+       return ret;
+}
+
+static int s6e63j0x03_enable(struct drm_panel *panel)
+{
+       struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+       int ret;
+
+       msleep(120);
+
+       ret = s6e63j0x03_apply_mtp_key(ctx, true);
+       if (ret < 0)
+               return ret;
+
+       /* set elvss_cond */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);
+       if (ret < 0)
+               return ret;
+
+       /* set pos */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx,
+               MIPI_DCS_SET_ADDRESS_MODE, 0x40);
+       if (ret < 0)
+               return ret;
+
+       /* set default white brightness */
+       ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
+       if (ret < 0)
+               return ret;
+
+       /* set white ctrl */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx,
+               MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
+       if (ret < 0)
+               return ret;
+
+       /* set acl off */
+       ret = s6e63j0x03_dcs_write_seq_static(ctx,
+               MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+       if (ret < 0)
+               return ret;
+
+       ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+       if (ret < 0)
+               return ret;
+
+       ret = s6e63j0x03_apply_mtp_key(ctx, false);
+       if (ret < 0)
+               return ret;
+
+       ret = mipi_dsi_dcs_set_display_on(dsi);
+       if (ret < 0)
+               return ret;
+
+       ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
+
+       return 0;
+}
+
+static int s6e63j0x03_get_modes(struct drm_panel *panel)
+{
+       struct drm_connector *connector = panel->connector;
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_duplicate(panel->drm, &default_mode);
+       if (!mode) {
+               DRM_ERROR("failed to add mode %ux%ux@%u\n",
+                       default_mode.hdisplay, default_mode.vdisplay,
+                       default_mode.vrefresh);
+               return -ENOMEM;
+       }
+
+       drm_mode_set_name(mode);
+
+       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+       drm_mode_probed_add(connector, mode);
+
+       connector->display_info.width_mm = 29;
+       connector->display_info.height_mm = 29;
+
+       return 1;
+}
+
+static const struct drm_panel_funcs s6e63j0x03_funcs = {
+       .disable = s6e63j0x03_disable,
+       .unprepare = s6e63j0x03_unprepare,
+       .prepare = s6e63j0x03_prepare,
+       .enable = s6e63j0x03_enable,
+       .get_modes = s6e63j0x03_get_modes,
+};
+
+static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
+{
+       struct device *dev = &dsi->dev;
+       struct s6e63j0x03 *ctx;
+       int ret;
+
+       ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       mipi_dsi_set_drvdata(dsi, ctx);
+
+       ctx->dev = dev;
+
+       dsi->lanes = 1;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET;
+
+       ctx->supplies[0].supply = "vdd3";
+       ctx->supplies[1].supply = "vci";
+       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
+                                     ctx->supplies);
+       if (ret < 0) {
+               dev_err(dev, "failed to get regulators: %d\n", ret);
+               return ret;
+       }
+
+       ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(ctx->reset_gpio)) {
+               dev_err(dev, "cannot get reset-gpio: %ld\n",
+                               PTR_ERR(ctx->reset_gpio));
+               return PTR_ERR(ctx->reset_gpio);
+       }
+
+       drm_panel_init(&ctx->panel);
+       ctx->panel.dev = dev;
+       ctx->panel.funcs = &s6e63j0x03_funcs;
+
+       ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
+                                               &s6e63j0x03_bl_ops, NULL);
+       if (IS_ERR(ctx->bl_dev)) {
+               dev_err(dev, "failed to register backlight device\n");
+               return PTR_ERR(ctx->bl_dev);
+       }
+
+       ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;
+       ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
+       ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+
+       ret = drm_panel_add(&ctx->panel);
+       if (ret < 0)
+               goto unregister_backlight;
+
+       ret = mipi_dsi_attach(dsi);
+       if (ret < 0)
+               goto remove_panel;
+
+       return ret;
+
+remove_panel:
+       drm_panel_remove(&ctx->panel);
+
+unregister_backlight:
+       backlight_device_unregister(ctx->bl_dev);
+
+       return ret;
+}
+
+static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
+{
+       struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
+
+       mipi_dsi_detach(dsi);
+       drm_panel_remove(&ctx->panel);
+
+       backlight_device_unregister(ctx->bl_dev);
+
+       return 0;
+}
+
+static const struct of_device_id s6e63j0x03_of_match[] = {
+       { .compatible = "samsung,s6e63j0x03" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
+
+static struct mipi_dsi_driver s6e63j0x03_driver = {
+       .probe = s6e63j0x03_probe,
+       .remove = s6e63j0x03_remove,
+       .driver = {
+               .name = "panel_samsung_s6e63j0x03",
+               .of_match_table = s6e63j0x03_of_match,
+       },
+};
+module_mipi_dsi_driver(s6e63j0x03_driver);
+
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c
new file mode 100644 (file)
index 0000000..71c09ed
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2017 NXP Semiconductors.
+ * Author: Marco Franchi <marco.franchi@nxp.com>
+ *
+ * Based on Panel Simple driver by Thierry Reding <treding@nvidia.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.
+ */
+
+#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_panel.h>
+
+#include <video/display_timing.h>
+#include <video/videomode.h>
+
+struct seiko_panel_desc {
+       const struct drm_display_mode *modes;
+       unsigned int num_modes;
+       const struct display_timing *timings;
+       unsigned int num_timings;
+
+       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;
+       } size;
+
+       u32 bus_format;
+       u32 bus_flags;
+};
+
+struct seiko_panel {
+       struct drm_panel base;
+       bool prepared;
+       bool enabled;
+       const struct seiko_panel_desc *desc;
+       struct backlight_device *backlight;
+       struct regulator *dvdd;
+       struct regulator *avdd;
+};
+
+static inline struct seiko_panel *to_seiko_panel(struct drm_panel *panel)
+{
+       return container_of(panel, struct seiko_panel, base);
+}
+
+static int seiko_panel_get_fixed_modes(struct seiko_panel *panel)
+{
+       struct drm_connector *connector = panel->base.connector;
+       struct drm_device *drm = panel->base.drm;
+       struct drm_display_mode *mode;
+       unsigned int i, num = 0;
+
+       if (!panel->desc)
+               return 0;
+
+       for (i = 0; i < panel->desc->num_timings; i++) {
+               const struct display_timing *dt = &panel->desc->timings[i];
+               struct videomode vm;
+
+               videomode_from_timing(dt, &vm);
+               mode = drm_mode_create(drm);
+               if (!mode) {
+                       dev_err(drm->dev, "failed to add mode %ux%u\n",
+                               dt->hactive.typ, dt->vactive.typ);
+                       continue;
+               }
+
+               drm_display_mode_from_videomode(&vm, mode);
+
+               mode->type |= DRM_MODE_TYPE_DRIVER;
+
+               if (panel->desc->num_timings == 1)
+                       mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+               drm_mode_probed_add(connector, mode);
+               num++;
+       }
+
+       for (i = 0; i < panel->desc->num_modes; i++) {
+               const struct drm_display_mode *m = &panel->desc->modes[i];
+
+               mode = drm_mode_duplicate(drm, m);
+               if (!mode) {
+                       dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
+                               m->hdisplay, m->vdisplay, m->vrefresh);
+                       continue;
+               }
+
+               mode->type |= DRM_MODE_TYPE_DRIVER;
+
+               if (panel->desc->num_modes == 1)
+                       mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+               drm_mode_set_name(mode);
+
+               drm_mode_probed_add(connector, mode);
+               num++;
+       }
+
+       connector->display_info.bpc = panel->desc->bpc;
+       connector->display_info.width_mm = panel->desc->size.width;
+       connector->display_info.height_mm = panel->desc->size.height;
+       if (panel->desc->bus_format)
+               drm_display_info_set_bus_formats(&connector->display_info,
+                                                &panel->desc->bus_format, 1);
+       connector->display_info.bus_flags = panel->desc->bus_flags;
+
+       return num;
+}
+
+static int seiko_panel_disable(struct drm_panel *panel)
+{
+       struct seiko_panel *p = to_seiko_panel(panel);
+
+       if (!p->enabled)
+               return 0;
+
+       if (p->backlight) {
+               p->backlight->props.power = FB_BLANK_POWERDOWN;
+               p->backlight->props.state |= BL_CORE_FBBLANK;
+               backlight_update_status(p->backlight);
+       }
+
+       p->enabled = false;
+
+       return 0;
+}
+
+static int seiko_panel_unprepare(struct drm_panel *panel)
+{
+       struct seiko_panel *p = to_seiko_panel(panel);
+
+       if (!p->prepared)
+               return 0;
+
+       regulator_disable(p->avdd);
+
+       /* Add a 100ms delay as per the panel datasheet */
+       msleep(100);
+
+       regulator_disable(p->dvdd);
+
+       p->prepared = false;
+
+       return 0;
+}
+
+static int seiko_panel_prepare(struct drm_panel *panel)
+{
+       struct seiko_panel *p = to_seiko_panel(panel);
+       int err;
+
+       if (p->prepared)
+               return 0;
+
+       err = regulator_enable(p->dvdd);
+       if (err < 0) {
+               dev_err(panel->dev, "failed to enable dvdd: %d\n", err);
+               return err;
+       }
+
+       /* Add a 100ms delay as per the panel datasheet */
+       msleep(100);
+
+       err = regulator_enable(p->avdd);
+       if (err < 0) {
+               dev_err(panel->dev, "failed to enable avdd: %d\n", err);
+               goto disable_dvdd;
+       }
+
+       p->prepared = true;
+
+       return 0;
+
+disable_dvdd:
+       regulator_disable(p->dvdd);
+       return err;
+}
+
+static int seiko_panel_enable(struct drm_panel *panel)
+{
+       struct seiko_panel *p = to_seiko_panel(panel);
+
+       if (p->enabled)
+               return 0;
+
+       if (p->backlight) {
+               p->backlight->props.state &= ~BL_CORE_FBBLANK;
+               p->backlight->props.power = FB_BLANK_UNBLANK;
+               backlight_update_status(p->backlight);
+       }
+
+       p->enabled = true;
+
+       return 0;
+}
+
+static int seiko_panel_get_modes(struct drm_panel *panel)
+{
+       struct seiko_panel *p = to_seiko_panel(panel);
+
+       /* add hard-coded panel modes */
+       return seiko_panel_get_fixed_modes(p);
+}
+
+static int seiko_panel_get_timings(struct drm_panel *panel,
+                                   unsigned int num_timings,
+                                   struct display_timing *timings)
+{
+       struct seiko_panel *p = to_seiko_panel(panel);
+       unsigned int i;
+
+       if (p->desc->num_timings < num_timings)
+               num_timings = p->desc->num_timings;
+
+       if (timings)
+               for (i = 0; i < num_timings; i++)
+                       timings[i] = p->desc->timings[i];
+
+       return p->desc->num_timings;
+}
+
+static const struct drm_panel_funcs seiko_panel_funcs = {
+       .disable = seiko_panel_disable,
+       .unprepare = seiko_panel_unprepare,
+       .prepare = seiko_panel_prepare,
+       .enable = seiko_panel_enable,
+       .get_modes = seiko_panel_get_modes,
+       .get_timings = seiko_panel_get_timings,
+};
+
+static int seiko_panel_probe(struct device *dev,
+                                       const struct seiko_panel_desc *desc)
+{
+       struct device_node *backlight;
+       struct seiko_panel *panel;
+       int err;
+
+       panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+       if (!panel)
+               return -ENOMEM;
+
+       panel->enabled = false;
+       panel->prepared = false;
+       panel->desc = desc;
+
+       panel->dvdd = devm_regulator_get(dev, "dvdd");
+       if (IS_ERR(panel->dvdd))
+               return PTR_ERR(panel->dvdd);
+
+       panel->avdd = devm_regulator_get(dev, "avdd");
+       if (IS_ERR(panel->avdd))
+               return PTR_ERR(panel->avdd);
+
+       backlight = of_parse_phandle(dev->of_node, "backlight", 0);
+       if (backlight) {
+               panel->backlight = of_find_backlight_by_node(backlight);
+               of_node_put(backlight);
+
+               if (!panel->backlight)
+                       return -EPROBE_DEFER;
+       }
+
+       drm_panel_init(&panel->base);
+       panel->base.dev = dev;
+       panel->base.funcs = &seiko_panel_funcs;
+
+       err = drm_panel_add(&panel->base);
+       if (err < 0)
+               return err;
+
+       dev_set_drvdata(dev, panel);
+
+       return 0;
+}
+
+static int seiko_panel_remove(struct platform_device *pdev)
+{
+       struct seiko_panel *panel = dev_get_drvdata(&pdev->dev);
+
+       drm_panel_detach(&panel->base);
+       drm_panel_remove(&panel->base);
+
+       seiko_panel_disable(&panel->base);
+
+       if (panel->backlight)
+               put_device(&panel->backlight->dev);
+
+       return 0;
+}
+
+static void seiko_panel_shutdown(struct platform_device *pdev)
+{
+       struct seiko_panel *panel = dev_get_drvdata(&pdev->dev);
+
+       seiko_panel_disable(&panel->base);
+}
+
+static const struct display_timing seiko_43wvf1g_timing = {
+       .pixelclock = { 33500000, 33500000, 33500000 },
+       .hactive = { 800, 800, 800 },
+       .hfront_porch = {  164, 164, 164 },
+       .hback_porch = { 89, 89, 89 },
+       .hsync_len = { 10, 10, 10 },
+       .vactive = { 480, 480, 480 },
+       .vfront_porch = { 10, 10, 10 },
+       .vback_porch = { 23, 23, 23 },
+       .vsync_len = { 10, 10, 10 },
+       .flags = DISPLAY_FLAGS_DE_LOW,
+};
+
+static const struct seiko_panel_desc seiko_43wvf1g = {
+       .timings = &seiko_43wvf1g_timing,
+       .num_timings = 1,
+       .bpc = 8,
+       .size = {
+               .width = 93,
+               .height = 57,
+       },
+       .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+       .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+};
+
+static const struct of_device_id platform_of_match[] = {
+       {
+               .compatible = "sii,43wvf1g",
+               .data = &seiko_43wvf1g,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, platform_of_match);
+
+static int seiko_panel_platform_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *id;
+
+       id = of_match_node(platform_of_match, pdev->dev.of_node);
+       if (!id)
+               return -ENODEV;
+
+       return seiko_panel_probe(&pdev->dev, id->data);
+}
+
+static struct platform_driver seiko_panel_platform_driver = {
+       .driver = {
+               .name = "seiko_panel",
+               .of_match_table = platform_of_match,
+       },
+       .probe = seiko_panel_platform_probe,
+       .remove = seiko_panel_remove,
+       .shutdown = seiko_panel_shutdown,
+};
+module_platform_driver(seiko_panel_platform_driver);
+
+MODULE_AUTHOR("Marco Franchi <marco.franchi@nxp.com");
+MODULE_DESCRIPTION("Seiko 43WVF1G panel driver");
+MODULE_LICENSE("GPL v2");
index 474fa759e06ec5510661c164860b19cdefae64a7..a3c96d2ea41c20ebb928ab46a826bcdb3ab6bc06 100644 (file)
@@ -187,8 +187,7 @@ static int panel_simple_unprepare(struct drm_panel *panel)
        if (!p->prepared)
                return 0;
 
-       if (p->enable_gpio)
-               gpiod_set_value_cansleep(p->enable_gpio, 0);
+       gpiod_set_value_cansleep(p->enable_gpio, 0);
 
        regulator_disable(p->supply);
 
@@ -214,8 +213,7 @@ static int panel_simple_prepare(struct drm_panel *panel)
                return err;
        }
 
-       if (p->enable_gpio)
-               gpiod_set_value_cansleep(p->enable_gpio, 1);
+       gpiod_set_value_cansleep(p->enable_gpio, 1);
 
        if (p->desc->delay.prepare)
                msleep(p->desc->delay.prepare);
@@ -315,7 +313,8 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
                                                     GPIOD_OUT_LOW);
        if (IS_ERR(panel->enable_gpio)) {
                err = PTR_ERR(panel->enable_gpio);
-               dev_err(dev, "failed to request GPIO: %d\n", err);
+               if (err != -EPROBE_DEFER)
+                       dev_err(dev, "failed to request GPIO: %d\n", err);
                return err;
        }
 
@@ -369,6 +368,7 @@ static int panel_simple_remove(struct device *dev)
        drm_panel_remove(&panel->base);
 
        panel_simple_disable(&panel->base);
+       panel_simple_unprepare(&panel->base);
 
        if (panel->ddc)
                put_device(&panel->ddc->dev);
@@ -384,6 +384,7 @@ static void panel_simple_shutdown(struct device *dev)
        struct panel_simple *panel = dev_get_drvdata(dev);
 
        panel_simple_disable(&panel->base);
+       panel_simple_unprepare(&panel->base);
 }
 
 static const struct drm_display_mode ampire_am_480272h3tmqw_t01h_mode = {
@@ -1522,8 +1523,8 @@ static const struct panel_desc olimex_lcd_olinuxino_43ts = {
        .modes = &olimex_lcd_olinuxino_43ts_mode,
        .num_modes = 1,
        .size = {
-               .width = 105,
-               .height = 67,
+               .width = 95,
+               .height = 54,
        },
        .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
 };
index bbfba87cd1a888af732150535f00da93184ee964..e5e2abd66491e30021b04e20790ca1272590e060 100644 (file)
@@ -6,7 +6,8 @@ config DRM_PL111
        select DRM_KMS_HELPER
        select DRM_KMS_CMA_HELPER
        select DRM_GEM_CMA_HELPER
-       select DRM_PANEL
+       select DRM_BRIDGE
+       select DRM_PANEL_BRIDGE
        select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
        help
          Choose this option for DRM support for the PL111 CLCD controller.
index 59483d610ef5cd6898b5661ee771ef3fc0c20903..fce1453a93e19e42fb146c343179d50a17f1813d 100644 (file)
@@ -1,5 +1,5 @@
-pl111_drm-y += pl111_connector.o \
-               pl111_display.o \
+pl111_drm-y += pl111_display.o \
+               pl111_versatile.o \
                pl111_drv.o
 
 pl111_drm-$(CONFIG_DEBUG_FS) += pl111_debugfs.o
diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
deleted file mode 100644 (file)
index d335f9a..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
- *
- * Parts of this file were based on sources as follows:
- *
- * Copyright (c) 2006-2008 Intel Corporation
- * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
- * Copyright (C) 2011 Texas Instruments
- *
- * This program is free software and is provided to you under the terms of the
- * GNU General Public License version 2 as published by the Free Software
- * Foundation, and any use by you of this program is subject to the terms of
- * such GNU licence.
- *
- */
-
-/**
- * pl111_drm_connector.c
- * Implementation of the connector functions for PL111 DRM
- */
-#include <linux/amba/clcd-regs.h>
-#include <linux/version.h>
-#include <linux/shmem_fs.h>
-#include <linux/dma-buf.h>
-
-#include <drm/drmP.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_of.h>
-#include <drm/drm_panel.h>
-
-#include "pl111_drm.h"
-
-static void pl111_connector_destroy(struct drm_connector *connector)
-{
-       struct pl111_drm_connector *pl111_connector =
-               to_pl111_connector(connector);
-
-       if (pl111_connector->panel)
-               drm_panel_detach(pl111_connector->panel);
-
-       drm_connector_unregister(connector);
-       drm_connector_cleanup(connector);
-}
-
-static enum drm_connector_status pl111_connector_detect(struct drm_connector
-                                                       *connector, bool force)
-{
-       struct pl111_drm_connector *pl111_connector =
-               to_pl111_connector(connector);
-
-       return (pl111_connector->panel ?
-               connector_status_connected :
-               connector_status_disconnected);
-}
-
-static int pl111_connector_helper_get_modes(struct drm_connector *connector)
-{
-       struct pl111_drm_connector *pl111_connector =
-               to_pl111_connector(connector);
-
-       if (!pl111_connector->panel)
-               return 0;
-
-       return drm_panel_get_modes(pl111_connector->panel);
-}
-
-const struct drm_connector_funcs connector_funcs = {
-       .fill_modes = drm_helper_probe_single_connector_modes,
-       .destroy = pl111_connector_destroy,
-       .detect = pl111_connector_detect,
-       .reset = drm_atomic_helper_connector_reset,
-       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-const struct drm_connector_helper_funcs connector_helper_funcs = {
-       .get_modes = pl111_connector_helper_get_modes,
-};
-
-/* Walks the OF graph to find the panel node and then asks DRM to look
- * up the panel.
- */
-static struct drm_panel *pl111_get_panel(struct device *dev)
-{
-       struct device_node *endpoint, *panel_node;
-       struct device_node *np = dev->of_node;
-       struct drm_panel *panel;
-
-       endpoint = of_graph_get_next_endpoint(np, NULL);
-       if (!endpoint) {
-               dev_err(dev, "no endpoint to fetch 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);
-       of_node_put(endpoint);
-       if (!panel_node) {
-               dev_err(dev, "no valid panel node\n");
-               return NULL;
-       }
-
-       panel = of_drm_find_panel(panel_node);
-       of_node_put(panel_node);
-
-       return panel;
-}
-
-int pl111_connector_init(struct drm_device *dev)
-{
-       struct pl111_drm_dev_private *priv = dev->dev_private;
-       struct pl111_drm_connector *pl111_connector = &priv->connector;
-       struct drm_connector *connector = &pl111_connector->connector;
-
-       drm_connector_init(dev, connector, &connector_funcs,
-                          DRM_MODE_CONNECTOR_DPI);
-       drm_connector_helper_add(connector, &connector_helper_funcs);
-
-       pl111_connector->panel = pl111_get_panel(dev->dev);
-       if (pl111_connector->panel)
-               drm_panel_attach(pl111_connector->panel, connector);
-
-       return 0;
-}
-
index 0d9dee199b2cc9792abe9c04db379323b9017267..7ddc7e3b9e7dca8191f047b035805f6f090b0c19 100644 (file)
@@ -22,8 +22,14 @@ static const struct {
        REGDEF(CLCD_TIM2),
        REGDEF(CLCD_TIM3),
        REGDEF(CLCD_UBAS),
+       REGDEF(CLCD_LBAS),
        REGDEF(CLCD_PL111_CNTL),
        REGDEF(CLCD_PL111_IENB),
+       REGDEF(CLCD_PL111_RIS),
+       REGDEF(CLCD_PL111_MIS),
+       REGDEF(CLCD_PL111_ICR),
+       REGDEF(CLCD_PL111_UCUR),
+       REGDEF(CLCD_PL111_LCUR),
 };
 
 int pl111_debugfs_regs(struct seq_file *m, void *unused)
index b58c988d9da0118b4a117f4e1bd852d9b40dfe84..06c4bf756b692232363f256cb203fc1eeeb83844 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/of_graph.h>
 
 #include <drm/drmP.h>
-#include <drm/drm_panel.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_fb_cma_helper.h>
@@ -94,7 +93,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
        struct pl111_drm_dev_private *priv = drm->dev_private;
        const struct drm_display_mode *mode = &cstate->mode;
        struct drm_framebuffer *fb = plane->state->fb;
-       struct drm_connector *connector = &priv->connector.connector;
+       struct drm_connector *connector = priv->connector;
        u32 cntl;
        u32 ppl, hsw, hfp, hbp;
        u32 lpp, vsw, vfp, vbp;
@@ -156,10 +155,8 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
 
        writel(0, priv->regs + CLCD_TIM3);
 
-       drm_panel_prepare(priv->connector.panel);
-
-       /* Enable and Power Up */
-       cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
+       /* Hard-code TFT panel */
+       cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1);
 
        /* Note that the the hardware's format reader takes 'r' from
         * the low bit, while DRM formats list channels from high bit
@@ -202,9 +199,21 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
                break;
        }
 
-       writel(cntl, priv->regs + CLCD_PL111_CNTL);
+       /* Power sequence: first enable and chill */
+       writel(cntl, priv->regs + priv->ctrl);
+
+       /*
+        * We expect this delay to stabilize the contrast
+        * voltage Vee as stipulated by the manual
+        */
+       msleep(20);
+
+       if (priv->variant_display_enable)
+               priv->variant_display_enable(drm, fb->format->format);
 
-       drm_panel_enable(priv->connector.panel);
+       /* Power Up */
+       cntl |= CNTL_LCDPWR;
+       writel(cntl, priv->regs + priv->ctrl);
 
        drm_crtc_vblank_on(crtc);
 }
@@ -214,15 +223,28 @@ void pl111_display_disable(struct drm_simple_display_pipe *pipe)
        struct drm_crtc *crtc = &pipe->crtc;
        struct drm_device *drm = crtc->dev;
        struct pl111_drm_dev_private *priv = drm->dev_private;
+       u32 cntl;
 
        drm_crtc_vblank_off(crtc);
 
-       drm_panel_disable(priv->connector.panel);
+       /* Power Down */
+       cntl = readl(priv->regs + priv->ctrl);
+       if (cntl & CNTL_LCDPWR) {
+               cntl &= ~CNTL_LCDPWR;
+               writel(cntl, priv->regs + priv->ctrl);
+       }
+
+       /*
+        * We expect this delay to stabilize the contrast voltage Vee as
+        * stipulated by the manual
+        */
+       msleep(20);
 
-       /* Disable and Power Down */
-       writel(0, priv->regs + CLCD_PL111_CNTL);
+       if (priv->variant_display_disable)
+               priv->variant_display_disable(drm);
 
-       drm_panel_unprepare(priv->connector.panel);
+       /* Disable */
+       writel(0, priv->regs + priv->ctrl);
 
        clk_disable_unprepare(priv->clk);
 }
@@ -260,7 +282,7 @@ int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc)
 {
        struct pl111_drm_dev_private *priv = drm->dev_private;
 
-       writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
+       writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + priv->ienb);
 
        return 0;
 }
@@ -269,7 +291,7 @@ void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc)
 {
        struct pl111_drm_dev_private *priv = drm->dev_private;
 
-       writel(0, priv->regs + CLCD_PL111_IENB);
+       writel(0, priv->regs + priv->ienb);
 }
 
 static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe,
@@ -413,22 +435,6 @@ int pl111_display_init(struct drm_device *drm)
        struct device_node *endpoint;
        u32 tft_r0b0g0[3];
        int ret;
-       static const u32 formats[] = {
-               DRM_FORMAT_ABGR8888,
-               DRM_FORMAT_XBGR8888,
-               DRM_FORMAT_ARGB8888,
-               DRM_FORMAT_XRGB8888,
-               DRM_FORMAT_BGR565,
-               DRM_FORMAT_RGB565,
-               DRM_FORMAT_ABGR1555,
-               DRM_FORMAT_XBGR1555,
-               DRM_FORMAT_ARGB1555,
-               DRM_FORMAT_XRGB1555,
-               DRM_FORMAT_ABGR4444,
-               DRM_FORMAT_XBGR4444,
-               DRM_FORMAT_ARGB4444,
-               DRM_FORMAT_XRGB4444,
-       };
 
        endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
        if (!endpoint)
@@ -444,21 +450,16 @@ int pl111_display_init(struct drm_device *drm)
        }
        of_node_put(endpoint);
 
-       if (tft_r0b0g0[0] != 0 ||
-           tft_r0b0g0[1] != 8 ||
-           tft_r0b0g0[2] != 16) {
-               dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
-               return -EINVAL;
-       }
-
        ret = pl111_init_clock_divider(drm);
        if (ret)
                return ret;
 
        ret = drm_simple_display_pipe_init(drm, &priv->pipe,
                                           &pl111_display_funcs,
-                                          formats, ARRAY_SIZE(formats),
-                                          NULL, &priv->connector.connector);
+                                          priv->variant->formats,
+                                          priv->variant->nformats,
+                                          NULL,
+                                          priv->connector);
        if (ret)
                return ret;
 
index 5c685bfc8fdc52927cd2dddca77aae310e7ed6ac..440f53ebee8cdf4099c864cdbc81d9569dda539b 100644 (file)
 
 #include <drm/drm_gem.h>
 #include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_bridge.h>
 #include <linux/clk-provider.h>
+#include <linux/interrupt.h>
 
 #define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
 
 struct drm_minor;
 
-struct pl111_drm_connector {
-       struct drm_connector connector;
-       struct drm_panel *panel;
+/**
+ * struct pl111_variant_data - encodes IP differences
+ * @name: the name of this variant
+ * @is_pl110: this is the early PL110 variant
+ * @formats: array of supported pixel formats on this variant
+ * @nformats: the length of the array of supported pixel formats
+ */
+struct pl111_variant_data {
+       const char *name;
+       bool is_pl110;
+       const u32 *formats;
+       unsigned int nformats;
 };
 
 struct pl111_drm_dev_private {
        struct drm_device *drm;
 
-       struct pl111_drm_connector connector;
+       struct drm_connector *connector;
+       struct drm_panel *panel;
+       struct drm_bridge *bridge;
        struct drm_simple_display_pipe pipe;
        struct drm_fbdev_cma *fbdev;
 
        void *regs;
+       u32 ienb;
+       u32 ctrl;
        /* The pixel clock (a reference to our clock divider off of CLCDCLK). */
        struct clk *clk;
        /* pl111's internal clock divider. */
@@ -48,20 +66,15 @@ struct pl111_drm_dev_private {
         * subsystem and pl111_display_enable().
         */
        spinlock_t tim2_lock;
+       const struct pl111_variant_data *variant;
+       void (*variant_display_enable) (struct drm_device *drm, u32 format);
+       void (*variant_display_disable) (struct drm_device *drm);
 };
 
-#define to_pl111_connector(x) \
-       container_of(x, struct pl111_drm_connector, connector)
-
 int pl111_display_init(struct drm_device *dev);
 int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc);
 void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc);
 irqreturn_t pl111_irq(int irq, void *data);
-int pl111_connector_init(struct drm_device *dev);
-int pl111_encoder_init(struct drm_device *dev);
-int pl111_dumb_create(struct drm_file *file_priv,
-                     struct drm_device *dev,
-                     struct drm_mode_create_dumb *args);
 int pl111_debugfs_init(struct drm_minor *minor);
 
 #endif /* _PL111_DRM_H_ */
index 581c452cede1df08822a8e0f5932062c3c52c6cc..201d57d5cb54d0ea8b85d3725719a741758e9179 100644 (file)
@@ -41,9 +41,6 @@
  * - Fix race between setting plane base address and getting IRQ for
  *   vsync firing the pageflip completion.
  *
- * - Expose the correct set of formats we can support based on the
- *   "arm,pl11x,tft-r0g0b0-pads" DT property.
- *
  * - Use the "max-memory-bandwidth" DT property to filter the
  *   supported formats.
  *
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_panel.h>
 
 #include "pl111_drm.h"
+#include "pl111_versatile.h"
 
 #define DRIVER_DESC      "DRM module for PL111"
 
@@ -83,6 +84,8 @@ static int pl111_modeset_init(struct drm_device *dev)
 {
        struct drm_mode_config *mode_config;
        struct pl111_drm_dev_private *priv = dev->dev_private;
+       struct drm_panel *panel;
+       struct drm_bridge *bridge;
        int ret = 0;
 
        drm_mode_config_init(dev);
@@ -93,34 +96,43 @@ static int pl111_modeset_init(struct drm_device *dev)
        mode_config->min_height = 1;
        mode_config->max_height = 768;
 
-       ret = pl111_connector_init(dev);
-       if (ret) {
-               dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
-               goto out_config;
-       }
-
-       /* Don't actually attach if we didn't find a drm_panel
-        * attached to us.  This will allow a kernel to include both
-        * the fbdev pl111 driver and this one, and choose between
-        * them based on which subsystem has support for the panel.
-        */
-       if (!priv->connector.panel) {
-               dev_info(dev->dev,
-                        "Disabling due to lack of DRM panel device.\n");
-               ret = -ENODEV;
-               goto out_config;
+       ret = drm_of_find_panel_or_bridge(dev->dev->of_node,
+                                         0, 0, &panel, &bridge);
+       if (ret && ret != -ENODEV)
+               return ret;
+       if (panel) {
+               bridge = drm_panel_bridge_add(panel,
+                                             DRM_MODE_CONNECTOR_Unknown);
+               if (IS_ERR(bridge)) {
+                       ret = PTR_ERR(bridge);
+                       goto out_config;
+               }
+               /*
+                * TODO: when we are using a different bridge than a panel
+                * (such as a dumb VGA connector) we need to devise a different
+                * method to get the connector out of the bridge.
+                */
        }
 
        ret = pl111_display_init(dev);
        if (ret != 0) {
                dev_err(dev->dev, "Failed to init display\n");
-               goto out_config;
+               goto out_bridge;
        }
 
+       ret = drm_simple_display_pipe_attach_bridge(&priv->pipe,
+                                                   bridge);
+       if (ret)
+               return ret;
+
+       priv->bridge = bridge;
+       priv->panel = panel;
+       priv->connector = panel->connector;
+
        ret = drm_vblank_init(dev, 1);
        if (ret != 0) {
                dev_err(dev->dev, "Failed to init vblank\n");
-               goto out_config;
+               goto out_bridge;
        }
 
        drm_mode_config_reset(dev);
@@ -132,6 +144,9 @@ static int pl111_modeset_init(struct drm_device *dev)
 
        goto finish;
 
+out_bridge:
+       if (panel)
+               drm_panel_bridge_remove(bridge);
 out_config:
        drm_mode_config_cleanup(dev);
 finish:
@@ -183,6 +198,7 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
 {
        struct device *dev = &amba_dev->dev;
        struct pl111_drm_dev_private *priv;
+       struct pl111_variant_data *variant = id->data;
        struct drm_device *drm;
        int ret;
 
@@ -196,6 +212,33 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
        amba_set_drvdata(amba_dev, drm);
        priv->drm = drm;
        drm->dev_private = priv;
+       priv->variant = variant;
+
+       /*
+        * The PL110 and PL111 variants have two registers
+        * swapped: interrupt enable and control. For this reason
+        * we use offsets that we can change per variant.
+        */
+       if (variant->is_pl110) {
+               /*
+                * The ARM Versatile boards are even more special:
+                * their PrimeCell ID say they are PL110 but the
+                * control and interrupt enable registers are anyway
+                * swapped to the PL111 order so they are not following
+                * the PL110 datasheet.
+                */
+               if (of_machine_is_compatible("arm,versatile-ab") ||
+                   of_machine_is_compatible("arm,versatile-pb")) {
+                       priv->ienb = CLCD_PL111_IENB;
+                       priv->ctrl = CLCD_PL111_CNTL;
+               } else {
+                       priv->ienb = CLCD_PL110_IENB;
+                       priv->ctrl = CLCD_PL110_CNTL;
+               }
+       } else {
+               priv->ienb = CLCD_PL111_IENB;
+               priv->ctrl = CLCD_PL111_CNTL;
+       }
 
        priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
        if (IS_ERR(priv->regs)) {
@@ -204,15 +247,19 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
        }
 
        /* turn off interrupts before requesting the irq */
-       writel(0, priv->regs + CLCD_PL111_IENB);
+       writel(0, priv->regs + priv->ienb);
 
        ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
-                              "pl111", priv);
+                              variant->name, priv);
        if (ret != 0) {
                dev_err(dev, "%s failed irq %d\n", __func__, ret);
                return ret;
        }
 
+       ret = pl111_versatile_init(dev, priv);
+       if (ret)
+               goto dev_unref;
+
        ret = pl111_modeset_init(drm);
        if (ret != 0)
                goto dev_unref;
@@ -236,16 +283,70 @@ static int pl111_amba_remove(struct amba_device *amba_dev)
        drm_dev_unregister(drm);
        if (priv->fbdev)
                drm_fbdev_cma_fini(priv->fbdev);
+       if (priv->panel)
+               drm_panel_bridge_remove(priv->bridge);
        drm_mode_config_cleanup(drm);
        drm_dev_unref(drm);
 
        return 0;
 }
 
-static struct amba_id pl111_id_table[] = {
+/*
+ * This variant exist in early versions like the ARM Integrator
+ * and this version lacks the 565 and 444 pixel formats.
+ */
+static const u32 pl110_pixel_formats[] = {
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ABGR1555,
+       DRM_FORMAT_XBGR1555,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+};
+
+static const struct pl111_variant_data pl110_variant = {
+       .name = "PL110",
+       .is_pl110 = true,
+       .formats = pl110_pixel_formats,
+       .nformats = ARRAY_SIZE(pl110_pixel_formats),
+};
+
+/* RealView, Versatile Express etc use this modern variant */
+static const u32 pl111_pixel_formats[] = {
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_BGR565,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_ABGR1555,
+       DRM_FORMAT_XBGR1555,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_ABGR4444,
+       DRM_FORMAT_XBGR4444,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB4444,
+};
+
+static const struct pl111_variant_data pl111_variant = {
+       .name = "PL111",
+       .formats = pl111_pixel_formats,
+       .nformats = ARRAY_SIZE(pl111_pixel_formats),
+};
+
+static const struct amba_id pl111_id_table[] = {
+       {
+               .id = 0x00041110,
+               .mask = 0x000fffff,
+               .data = (void*)&pl110_variant,
+       },
        {
                .id = 0x00041111,
                .mask = 0x000fffff,
+               .data = (void*)&pl111_variant,
        },
        {0, 0},
 };
diff --git a/drivers/gpu/drm/pl111/pl111_versatile.c b/drivers/gpu/drm/pl111/pl111_versatile.c
new file mode 100644 (file)
index 0000000..97d4af6
--- /dev/null
@@ -0,0 +1,270 @@
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include "pl111_versatile.h"
+#include "pl111_drm.h"
+
+static struct regmap *versatile_syscon_map;
+
+/*
+ * We detect the different syscon types from the compatible strings.
+ */
+enum versatile_clcd {
+       INTEGRATOR_CLCD_CM,
+       VERSATILE_CLCD,
+       REALVIEW_CLCD_EB,
+       REALVIEW_CLCD_PB1176,
+       REALVIEW_CLCD_PB11MP,
+       REALVIEW_CLCD_PBA8,
+       REALVIEW_CLCD_PBX,
+};
+
+static const struct of_device_id versatile_clcd_of_match[] = {
+       {
+               .compatible = "arm,core-module-integrator",
+               .data = (void *)INTEGRATOR_CLCD_CM,
+       },
+       {
+               .compatible = "arm,versatile-sysreg",
+               .data = (void *)VERSATILE_CLCD,
+       },
+       {
+               .compatible = "arm,realview-eb-syscon",
+               .data = (void *)REALVIEW_CLCD_EB,
+       },
+       {
+               .compatible = "arm,realview-pb1176-syscon",
+               .data = (void *)REALVIEW_CLCD_PB1176,
+       },
+       {
+               .compatible = "arm,realview-pb11mp-syscon",
+               .data = (void *)REALVIEW_CLCD_PB11MP,
+       },
+       {
+               .compatible = "arm,realview-pba8-syscon",
+               .data = (void *)REALVIEW_CLCD_PBA8,
+       },
+       {
+               .compatible = "arm,realview-pbx-syscon",
+               .data = (void *)REALVIEW_CLCD_PBX,
+       },
+       {},
+};
+
+/*
+ * Core module CLCD control on the Integrator/CP, bits
+ * 8 thru 19 of the CM_CONTROL register controls a bunch
+ * of CLCD settings.
+ */
+#define INTEGRATOR_HDR_CTRL_OFFSET     0x0C
+#define INTEGRATOR_CLCD_LCDBIASEN      BIT(8)
+#define INTEGRATOR_CLCD_LCDBIASUP      BIT(9)
+#define INTEGRATOR_CLCD_LCDBIASDN      BIT(10)
+/* Bits 11,12,13 controls the LCD type */
+#define INTEGRATOR_CLCD_LCDMUX_MASK    (BIT(11)|BIT(12)|BIT(13))
+#define INTEGRATOR_CLCD_LCDMUX_LCD24   BIT(11)
+#define INTEGRATOR_CLCD_LCDMUX_VGA565  BIT(12)
+#define INTEGRATOR_CLCD_LCDMUX_SHARP   (BIT(11)|BIT(12))
+#define INTEGRATOR_CLCD_LCDMUX_VGA555  BIT(13)
+#define INTEGRATOR_CLCD_LCDMUX_VGA24   (BIT(11)|BIT(12)|BIT(13))
+#define INTEGRATOR_CLCD_LCD0_EN                BIT(14)
+#define INTEGRATOR_CLCD_LCD1_EN                BIT(15)
+/* R/L flip on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC1    BIT(16)
+/* U/D flip on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC2    BIT(17)
+/* No connection on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC     BIT(18)
+/* 0 = 24bit VGA, 1 = 18bit VGA */
+#define INTEGRATOR_CLCD_LCD_N24BITEN   BIT(19)
+
+#define INTEGRATOR_CLCD_MASK           (INTEGRATOR_CLCD_LCDBIASEN | \
+                                        INTEGRATOR_CLCD_LCDBIASUP | \
+                                        INTEGRATOR_CLCD_LCDBIASDN | \
+                                        INTEGRATOR_CLCD_LCDMUX_MASK | \
+                                        INTEGRATOR_CLCD_LCD0_EN | \
+                                        INTEGRATOR_CLCD_LCD1_EN | \
+                                        INTEGRATOR_CLCD_LCD_STATIC1 | \
+                                        INTEGRATOR_CLCD_LCD_STATIC2 | \
+                                        INTEGRATOR_CLCD_LCD_STATIC | \
+                                        INTEGRATOR_CLCD_LCD_N24BITEN)
+
+static void pl111_integrator_enable(struct drm_device *drm, u32 format)
+{
+       u32 val;
+
+       dev_info(drm->dev, "enable Integrator CLCD connectors\n");
+
+       /* FIXME: really needed? */
+       val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 |
+               INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN;
+
+       switch (format) {
+       case DRM_FORMAT_XBGR8888:
+       case DRM_FORMAT_XRGB8888:
+               break;
+       case DRM_FORMAT_BGR565:
+       case DRM_FORMAT_RGB565:
+               /* truecolor RGB565 */
+               val |= INTEGRATOR_CLCD_LCDMUX_VGA565;
+               break;
+       case DRM_FORMAT_XBGR1555:
+       case DRM_FORMAT_XRGB1555:
+               /* Pseudocolor, RGB555, BGR555 */
+               val |= INTEGRATOR_CLCD_LCDMUX_VGA555;
+               break;
+       default:
+               dev_err(drm->dev, "unhandled format on Integrator 0x%08x\n",
+                       format);
+               break;
+       }
+
+       regmap_update_bits(versatile_syscon_map,
+                          INTEGRATOR_HDR_CTRL_OFFSET,
+                          INTEGRATOR_CLCD_MASK,
+                          val);
+}
+
+/*
+ * This configuration register in the Versatile and RealView
+ * family is uniformly present but appears more and more
+ * unutilized starting with the RealView series.
+ */
+#define SYS_CLCD                       0x50
+#define SYS_CLCD_MODE_MASK             (BIT(0)|BIT(1))
+#define SYS_CLCD_MODE_888              0
+#define SYS_CLCD_MODE_5551             BIT(0)
+#define SYS_CLCD_MODE_565_R_LSB                BIT(1)
+#define SYS_CLCD_MODE_565_B_LSB                (BIT(0)|BIT(1))
+#define SYS_CLCD_CONNECTOR_MASK                (BIT(2)|BIT(3)|BIT(4)|BIT(5))
+#define SYS_CLCD_NLCDIOON              BIT(2)
+#define SYS_CLCD_VDDPOSSWITCH          BIT(3)
+#define SYS_CLCD_PWR3V5SWITCH          BIT(4)
+#define SYS_CLCD_VDDNEGSWITCH          BIT(5)
+
+static void pl111_versatile_disable(struct drm_device *drm)
+{
+       dev_info(drm->dev, "disable Versatile CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          0);
+}
+
+static void pl111_versatile_enable(struct drm_device *drm, u32 format)
+{
+       u32 val = 0;
+
+       dev_info(drm->dev, "enable Versatile CLCD connectors\n");
+
+       switch (format) {
+       case DRM_FORMAT_ABGR8888:
+       case DRM_FORMAT_XBGR8888:
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_XRGB8888:
+               val |= SYS_CLCD_MODE_888;
+               break;
+       case DRM_FORMAT_BGR565:
+               val |= SYS_CLCD_MODE_565_R_LSB;
+               break;
+       case DRM_FORMAT_RGB565:
+               val |= SYS_CLCD_MODE_565_B_LSB;
+               break;
+       case DRM_FORMAT_ABGR1555:
+       case DRM_FORMAT_XBGR1555:
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB1555:
+               val |= SYS_CLCD_MODE_5551;
+               break;
+       default:
+               dev_err(drm->dev, "unhandled format on Versatile 0x%08x\n",
+                       format);
+               break;
+       }
+
+       /* Set up the MUX */
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_MODE_MASK,
+                          val);
+
+       /* Then enable the display */
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
+}
+
+static void pl111_realview_clcd_disable(struct drm_device *drm)
+{
+       dev_info(drm->dev, "disable RealView CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          0);
+}
+
+static void pl111_realview_clcd_enable(struct drm_device *drm, u32 format)
+{
+       dev_info(drm->dev, "enable RealView CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
+}
+
+int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
+{
+       const struct of_device_id *clcd_id;
+       enum versatile_clcd versatile_clcd_type;
+       struct device_node *np;
+       struct regmap *map;
+
+       np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
+                                            &clcd_id);
+       if (!np) {
+               /* Non-ARM reference designs, just bail out */
+               return 0;
+       }
+       versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
+
+       map = syscon_node_to_regmap(np);
+       if (IS_ERR(map)) {
+               dev_err(dev, "no Versatile syscon regmap\n");
+               return PTR_ERR(map);
+       }
+
+       switch (versatile_clcd_type) {
+       case INTEGRATOR_CLCD_CM:
+               versatile_syscon_map = map;
+               priv->variant_display_enable = pl111_integrator_enable;
+               dev_info(dev, "set up callbacks for Integrator PL110\n");
+               break;
+       case VERSATILE_CLCD:
+               versatile_syscon_map = map;
+               priv->variant_display_enable = pl111_versatile_enable;
+               priv->variant_display_disable = pl111_versatile_disable;
+               dev_info(dev, "set up callbacks for Versatile PL110+\n");
+               break;
+       case REALVIEW_CLCD_EB:
+       case REALVIEW_CLCD_PB1176:
+       case REALVIEW_CLCD_PB11MP:
+       case REALVIEW_CLCD_PBA8:
+       case REALVIEW_CLCD_PBX:
+               versatile_syscon_map = map;
+               priv->variant_display_enable = pl111_realview_clcd_enable;
+               priv->variant_display_disable = pl111_realview_clcd_disable;
+               dev_info(dev, "set up callbacks for RealView PL111\n");
+               break;
+       default:
+               dev_info(dev, "unknown Versatile system controller\n");
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pl111_versatile_init);
diff --git a/drivers/gpu/drm/pl111/pl111_versatile.h b/drivers/gpu/drm/pl111/pl111_versatile.h
new file mode 100644 (file)
index 0000000..41aa6d9
--- /dev/null
@@ -0,0 +1,9 @@
+#include <linux/device.h>
+#include "pl111_drm.h"
+
+#ifndef PL111_VERSATILE_H
+#define PL111_VERSATILE_H
+
+int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv);
+
+#endif
index dcc539ba85d65da734d9a94a77bfc6b09d62b05e..0c31f0a27b9cbd3348ecad9c4acfb0134a1f8302 100644 (file)
@@ -57,4 +57,12 @@ config ROCKCHIP_INNO_HDMI
          for the Innosilicon HDMI driver. If you want to enable
          HDMI on RK3036 based SoC, you should select this option.
 
+config ROCKCHIP_LVDS
+       bool "Rockchip LVDS support"
+       depends on DRM_ROCKCHIP
+       help
+         Choose this option to enable support for Rockchip LVDS controllers.
+         Rockchip rk3288 SoC has LVDS TX Controller can be used, and it
+         support LVDS, rgb, dual LVDS output mode. say Y to enable its
+         driver.
 endif
index fa8dc9d9aac28dfdf546bd84cbc0b54f723b4121..a881d2cc4f25046b0c680bfad889af6d2562c779 100644 (file)
@@ -12,5 +12,6 @@ rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
+rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
index 9606121fa185aad8c15127d46372d465409f8db7..4d3f6ad0abdde8e8dcc142d1e6c724cdb2136259 100644 (file)
@@ -88,7 +88,7 @@ static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
        if (!analogix_dp_psr_supported(dp->dev))
                return;
 
-       dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
+       DRM_DEV_DEBUG(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
 
        spin_lock_irqsave(&dp->psr_lock, flags);
        if (enabled)
@@ -110,7 +110,7 @@ static void analogix_dp_psr_work(struct work_struct *work)
        ret = rockchip_drm_wait_vact_end(dp->encoder.crtc,
                                         PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
        if (ret) {
-               dev_err(dp->dev, "line flag interrupt did not arrive\n");
+               DRM_DEV_ERROR(dp->dev, "line flag interrupt did not arrive\n");
                return;
        }
 
@@ -140,13 +140,13 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
 
        ret = clk_prepare_enable(dp->pclk);
        if (ret < 0) {
-               dev_err(dp->dev, "failed to enable pclk %d\n", ret);
+               DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret);
                return ret;
        }
 
        ret = rockchip_dp_pre_init(dp);
        if (ret < 0) {
-               dev_err(dp->dev, "failed to dp pre init %d\n", ret);
+               DRM_DEV_ERROR(dp->dev, "failed to dp pre init %d\n", ret);
                clk_disable_unprepare(dp->pclk);
                return ret;
        }
@@ -211,17 +211,17 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder)
        else
                val = dp->data->lcdsel_big;
 
-       dev_dbg(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
+       DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
 
        ret = clk_prepare_enable(dp->grfclk);
        if (ret < 0) {
-               dev_err(dp->dev, "failed to enable grfclk %d\n", ret);
+               DRM_DEV_ERROR(dp->dev, "failed to enable grfclk %d\n", ret);
                return;
        }
 
        ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val);
        if (ret != 0)
-               dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+               DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
 
        clk_disable_unprepare(dp->grfclk);
 }
@@ -277,7 +277,7 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp)
 
        dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
        if (IS_ERR(dp->grf)) {
-               dev_err(dev, "failed to get rockchip,grf property\n");
+               DRM_DEV_ERROR(dev, "failed to get rockchip,grf property\n");
                return PTR_ERR(dp->grf);
        }
 
@@ -287,31 +287,31 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp)
        } else if (PTR_ERR(dp->grfclk) == -EPROBE_DEFER) {
                return -EPROBE_DEFER;
        } else if (IS_ERR(dp->grfclk)) {
-               dev_err(dev, "failed to get grf clock\n");
+               DRM_DEV_ERROR(dev, "failed to get grf clock\n");
                return PTR_ERR(dp->grfclk);
        }
 
        dp->pclk = devm_clk_get(dev, "pclk");
        if (IS_ERR(dp->pclk)) {
-               dev_err(dev, "failed to get pclk property\n");
+               DRM_DEV_ERROR(dev, "failed to get pclk property\n");
                return PTR_ERR(dp->pclk);
        }
 
        dp->rst = devm_reset_control_get(dev, "dp");
        if (IS_ERR(dp->rst)) {
-               dev_err(dev, "failed to get dp reset control\n");
+               DRM_DEV_ERROR(dev, "failed to get dp reset control\n");
                return PTR_ERR(dp->rst);
        }
 
        ret = clk_prepare_enable(dp->pclk);
        if (ret < 0) {
-               dev_err(dp->dev, "failed to enable pclk %d\n", ret);
+               DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret);
                return ret;
        }
 
        ret = rockchip_dp_pre_init(dp);
        if (ret < 0) {
-               dev_err(dp->dev, "failed to pre init %d\n", ret);
+               DRM_DEV_ERROR(dp->dev, "failed to pre init %d\n", ret);
                clk_disable_unprepare(dp->pclk);
                return ret;
        }
index b14d211f6c21739766ed8707b8ffd2e2d30e7f18..eb3042c6d1b202bb27e2f1b55f6714d4f48b252a 100644 (file)
@@ -323,7 +323,7 @@ int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
        reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
        dp->fw_version |= reg << 24;
 
-       dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
+       DRM_DEV_DEBUG(dp->dev, "firmware version: %x\n", dp->fw_version);
 
        return 0;
 }
index 9a20b9dc27c834ab3d92a709dba64d0a4aec550e..b15755b6129c2b3aa37e016f5d11f97da6427ba8 100644 (file)
@@ -430,9 +430,9 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 
        testdin = max_mbps_to_testdin(dsi->lane_mbps);
        if (testdin < 0) {
-               dev_err(dsi->dev,
-                       "failed to get testdin for %dmbps lane clock\n",
-                       dsi->lane_mbps);
+               DRM_DEV_ERROR(dsi->dev,
+                             "failed to get testdin for %dmbps lane clock\n",
+                             dsi->lane_mbps);
                return testdin;
        }
 
@@ -443,7 +443,7 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 
        ret = clk_prepare_enable(dsi->phy_cfg_clk);
        if (ret) {
-               dev_err(dsi->dev, "Failed to enable phy_cfg_clk\n");
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n");
                return ret;
        }
 
@@ -501,7 +501,7 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
        ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
                                 val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
        if (ret < 0) {
-               dev_err(dsi->dev, "failed to wait for phy lock state\n");
+               DRM_DEV_ERROR(dsi->dev, "failed to wait for phy lock state\n");
                goto phy_init_end;
        }
 
@@ -509,8 +509,8 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
                                 val, val & STOP_STATE_CLK_LANE, 1000,
                                 PHY_STATUS_TIMEOUT_US);
        if (ret < 0)
-               dev_err(dsi->dev,
-                       "failed to wait for phy clk lane stop state\n");
+               DRM_DEV_ERROR(dsi->dev,
+                             "failed to wait for phy clk lane stop state\n");
 
 phy_init_end:
        clk_disable_unprepare(dsi->phy_cfg_clk);
@@ -529,8 +529,9 @@ static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
 
        bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
        if (bpp < 0) {
-               dev_err(dsi->dev, "failed to get bpp for pixel format %d\n",
-                       dsi->format);
+               DRM_DEV_ERROR(dsi->dev,
+                             "failed to get bpp for pixel format %d\n",
+                             dsi->format);
                return bpp;
        }
 
@@ -541,7 +542,8 @@ static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
                if (tmp < max_mbps)
                        target_mbps = tmp;
                else
-                       dev_err(dsi->dev, "DPHY clock frequency is out of range\n");
+                       DRM_DEV_ERROR(dsi->dev,
+                                     "DPHY clock frequency is out of range\n");
        }
 
        pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
@@ -582,8 +584,9 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
        struct dw_mipi_dsi *dsi = host_to_dsi(host);
 
        if (device->lanes > dsi->pdata->max_data_lanes) {
-               dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
-                       device->lanes);
+               DRM_DEV_ERROR(dsi->dev,
+                             "the number of data lanes(%u) is too many\n",
+                             device->lanes);
                return -EINVAL;
        }
 
@@ -632,7 +635,8 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
                                 val, !(val & GEN_CMD_FULL), 1000,
                                 CMD_PKT_STATUS_TIMEOUT_US);
        if (ret < 0) {
-               dev_err(dsi->dev, "failed to get available command FIFO\n");
+               DRM_DEV_ERROR(dsi->dev,
+                             "failed to get available command FIFO\n");
                return ret;
        }
 
@@ -643,7 +647,7 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
                                 val, (val & mask) == mask,
                                 1000, CMD_PKT_STATUS_TIMEOUT_US);
        if (ret < 0) {
-               dev_err(dsi->dev, "failed to write command FIFO\n");
+               DRM_DEV_ERROR(dsi->dev, "failed to write command FIFO\n");
                return ret;
        }
 
@@ -663,8 +667,9 @@ static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
                data |= tx_buf[1] << 8;
 
        if (msg->tx_len > 2) {
-               dev_err(dsi->dev, "too long tx buf length %zu for short write\n",
-                       msg->tx_len);
+               DRM_DEV_ERROR(dsi->dev,
+                             "too long tx buf length %zu for short write\n",
+                             msg->tx_len);
                return -EINVAL;
        }
 
@@ -682,8 +687,9 @@ static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
        u32 val;
 
        if (msg->tx_len < 3) {
-               dev_err(dsi->dev, "wrong tx buf length %zu for long write\n",
-                       msg->tx_len);
+               DRM_DEV_ERROR(dsi->dev,
+                             "wrong tx buf length %zu for long write\n",
+                             msg->tx_len);
                return -EINVAL;
        }
 
@@ -704,8 +710,8 @@ static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
                                         val, !(val & GEN_PLD_W_FULL), 1000,
                                         CMD_PKT_STATUS_TIMEOUT_US);
                if (ret < 0) {
-                       dev_err(dsi->dev,
-                               "failed to get available write payload FIFO\n");
+                       DRM_DEV_ERROR(dsi->dev,
+                                     "failed to get available write payload FIFO\n");
                        return ret;
                }
        }
@@ -731,8 +737,8 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
                ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
                break;
        default:
-               dev_err(dsi->dev, "unsupported message type 0x%02x\n",
-                       msg->type);
+               DRM_DEV_ERROR(dsi->dev, "unsupported message type 0x%02x\n",
+                             msg->type);
                ret = -EINVAL;
        }
 
@@ -935,7 +941,7 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
                return;
 
        if (clk_prepare_enable(dsi->pclk)) {
-               dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
                return;
        }
 
@@ -967,7 +973,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
                return;
 
        if (clk_prepare_enable(dsi->pclk)) {
-               dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
                return;
        }
 
@@ -991,7 +997,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
         */
        ret = clk_prepare_enable(dsi->grf_clk);
        if (ret) {
-               dev_err(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
+               DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
                return;
        }
 
@@ -1004,7 +1010,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
 
        dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
        if (drm_panel_prepare(dsi->panel))
-               dev_err(dsi->dev, "failed to prepare panel\n");
+               DRM_DEV_ERROR(dsi->dev, "failed to prepare panel\n");
 
        dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
        drm_panel_enable(dsi->panel);
@@ -1017,7 +1023,8 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
                val = pdata->dsi0_en_bit << 16;
 
        regmap_write(dsi->grf_regmap, pdata->grf_switch_reg, val);
-       dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
+       DRM_DEV_DEBUG(dsi->dev,
+                     "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
        dsi->dpms_mode = DRM_MODE_DPMS_ON;
 
        clk_disable_unprepare(dsi->grf_clk);
@@ -1111,7 +1118,7 @@ static int dw_mipi_dsi_register(struct drm_device *drm,
        ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
                               DRM_MODE_ENCODER_DSI, NULL);
        if (ret) {
-               dev_err(dev, "Failed to initialize encoder with drm\n");
+               DRM_DEV_ERROR(dev, "Failed to initialize encoder with drm\n");
                return ret;
        }
 
@@ -1133,7 +1140,7 @@ static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi)
 
        dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
        if (IS_ERR(dsi->grf_regmap)) {
-               dev_err(dsi->dev, "Unable to get rockchip,grf\n");
+               DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n");
                return PTR_ERR(dsi->grf_regmap);
        }
 
@@ -1205,14 +1212,15 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
        dsi->pllref_clk = devm_clk_get(dev, "ref");
        if (IS_ERR(dsi->pllref_clk)) {
                ret = PTR_ERR(dsi->pllref_clk);
-               dev_err(dev, "Unable to get pll reference clock: %d\n", ret);
+               DRM_DEV_ERROR(dev,
+                             "Unable to get pll reference clock: %d\n", ret);
                return ret;
        }
 
        dsi->pclk = devm_clk_get(dev, "pclk");
        if (IS_ERR(dsi->pclk)) {
                ret = PTR_ERR(dsi->pclk);
-               dev_err(dev, "Unable to get pclk: %d\n", ret);
+               DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret);
                return ret;
        }
 
@@ -1226,7 +1234,8 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
                if (ret == -ENOENT) {
                        apb_rst = NULL;
                } else {
-                       dev_err(dev, "Unable to get reset control: %d\n", ret);
+                       DRM_DEV_ERROR(dev,
+                                     "Unable to get reset control: %d\n", ret);
                        return ret;
                }
        }
@@ -1234,7 +1243,7 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
        if (apb_rst) {
                ret = clk_prepare_enable(dsi->pclk);
                if (ret) {
-                       dev_err(dev, "%s: Failed to enable pclk\n", __func__);
+                       DRM_DEV_ERROR(dev, "Failed to enable pclk\n");
                        return ret;
                }
 
@@ -1249,7 +1258,8 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
                dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg");
                if (IS_ERR(dsi->phy_cfg_clk)) {
                        ret = PTR_ERR(dsi->phy_cfg_clk);
-                       dev_err(dev, "Unable to get phy_cfg_clk: %d\n", ret);
+                       DRM_DEV_ERROR(dev,
+                                     "Unable to get phy_cfg_clk: %d\n", ret);
                        return ret;
                }
        }
@@ -1258,20 +1268,20 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
                dsi->grf_clk = devm_clk_get(dev, "grf");
                if (IS_ERR(dsi->grf_clk)) {
                        ret = PTR_ERR(dsi->grf_clk);
-                       dev_err(dev, "Unable to get grf_clk: %d\n", ret);
+                       DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret);
                        return ret;
                }
        }
 
        ret = clk_prepare_enable(dsi->pllref_clk);
        if (ret) {
-               dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
+               DRM_DEV_ERROR(dev, "Failed to enable pllref_clk\n");
                return ret;
        }
 
        ret = dw_mipi_dsi_register(drm, dsi);
        if (ret) {
-               dev_err(dev, "Failed to register mipi_dsi: %d\n", ret);
+               DRM_DEV_ERROR(dev, "Failed to register mipi_dsi: %d\n", ret);
                goto err_pllref;
        }
 
@@ -1281,7 +1291,7 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
        dsi->dsi_host.dev = dev;
        ret = mipi_dsi_host_register(&dsi->dsi_host);
        if (ret) {
-               dev_err(dev, "Failed to register MIPI host: %d\n", ret);
+               DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
                goto err_cleanup;
        }
 
index ccd5d595ada7ca207164582368b39753be0ed7e4..1eb02a82fd9188533bfd1debce378071b9eab540 100644 (file)
@@ -168,7 +168,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 
        hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
        if (IS_ERR(hdmi->regmap)) {
-               dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
+               DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
                return PTR_ERR(hdmi->regmap);
        }
 
@@ -178,7 +178,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
        } else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
                return -EPROBE_DEFER;
        } else if (IS_ERR(hdmi->vpll_clk)) {
-               dev_err(hdmi->dev, "failed to get grf clock\n");
+               DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
                return PTR_ERR(hdmi->vpll_clk);
        }
 
@@ -188,13 +188,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
        } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
                return -EPROBE_DEFER;
        } else if (IS_ERR(hdmi->grf_clk)) {
-               dev_err(hdmi->dev, "failed to get grf clock\n");
+               DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
                return PTR_ERR(hdmi->grf_clk);
        }
 
        ret = clk_prepare_enable(hdmi->vpll_clk);
        if (ret) {
-               dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
+               DRM_DEV_ERROR(hdmi->dev,
+                             "Failed to enable HDMI vpll: %d\n", ret);
                return ret;
        }
 
@@ -259,17 +260,17 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 
        ret = clk_prepare_enable(hdmi->grf_clk);
        if (ret < 0) {
-               dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret);
+               DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret);
                return;
        }
 
        ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
        if (ret != 0)
-               dev_err(hdmi->dev, "Could not write to GRF: %d\n", ret);
+               DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
 
        clk_disable_unprepare(hdmi->grf_clk);
-       dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
-               ret ? "LIT" : "BIG");
+       DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n",
+                     ret ? "LIT" : "BIG");
 }
 
 static int
@@ -368,7 +369,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 
        ret = rockchip_hdmi_parse_dt(hdmi);
        if (ret) {
-               dev_err(hdmi->dev, "Unable to parse OF data\n");
+               DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n");
                return ret;
        }
 
index 7a251a54e7925d5e8e0119c29e2354bc9ca339ce..ee584d87111fb2fe4ea3d74431d0945c4e1c9192 100644 (file)
@@ -224,7 +224,7 @@ static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
                break;
 
        default:
-               dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+               DRM_DEV_ERROR(hdmi->dev, "Unknown power mode %d\n", mode);
        }
 }
 
@@ -742,8 +742,9 @@ static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
        hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
 
        for (i = 0; i < num; i++) {
-               dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
-                       i + 1, num, msgs[i].len, msgs[i].flags);
+               DRM_DEV_DEBUG(hdmi->dev,
+                             "xfer: num: %d/%d, len: %d, flags: %#x\n",
+                             i + 1, num, msgs[i].len, msgs[i].flags);
 
                if (msgs[i].flags & I2C_M_RD)
                        ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
@@ -806,7 +807,7 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
 
        hdmi->i2c = i2c;
 
-       dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+       DRM_DEV_INFO(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
 
        return adap;
 }
@@ -838,13 +839,14 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
 
        hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
        if (IS_ERR(hdmi->pclk)) {
-               dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
+               DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI pclk clk\n");
                return PTR_ERR(hdmi->pclk);
        }
 
        ret = clk_prepare_enable(hdmi->pclk);
        if (ret) {
-               dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
+               DRM_DEV_ERROR(hdmi->dev,
+                             "Cannot enable HDMI pclk clock: %d\n", ret);
                return ret;
        }
 
index ff3d0f5efbb144465e802c3ab54df0e8b05b75f7..76d63de5921d1c0931eabae91ed0fd1b6ff9fcc4 100644 (file)
@@ -58,7 +58,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 
        ret = iommu_attach_device(private->domain, dev);
        if (ret) {
-               dev_err(dev, "Failed to attach iommu device\n");
+               DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
                return ret;
        }
 
@@ -373,8 +373,9 @@ static int rockchip_drm_platform_of_probe(struct device *dev)
 
                iommu = of_parse_phandle(port->parent, "iommus", 0);
                if (!iommu || !of_device_is_available(iommu->parent)) {
-                       dev_dbg(dev, "no iommu attached for %pOF, using non-iommu buffers\n",
-                               port->parent);
+                       DRM_DEV_DEBUG(dev,
+                                     "no iommu attached for %pOF, using non-iommu buffers\n",
+                                     port->parent);
                        /*
                         * if there is a crtc not support iommu, force set all
                         * crtc use non-iommu buffer.
@@ -389,12 +390,13 @@ static int rockchip_drm_platform_of_probe(struct device *dev)
        }
 
        if (i == 0) {
-               dev_err(dev, "missing 'ports' property\n");
+               DRM_DEV_ERROR(dev, "missing 'ports' property\n");
                return -ENODEV;
        }
 
        if (!found) {
-               dev_err(dev, "No available vop found for display-subsystem.\n");
+               DRM_DEV_ERROR(dev,
+                             "No available vop found for display-subsystem.\n");
                return -ENODEV;
        }
 
@@ -453,6 +455,8 @@ static int __init rockchip_drm_init(void)
 
        num_rockchip_sub_drivers = 0;
        ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
+       ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
+                               CONFIG_ROCKCHIP_LVDS);
        ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
                                CONFIG_ROCKCHIP_ANALOGIX_DP);
        ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
index c7e96b82cf6397b95191a34beee6380d39183779..498dfbc52ceca3d575886c11ba8264afcddd4502 100644 (file)
@@ -69,5 +69,6 @@ extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
 extern struct platform_driver dw_mipi_dsi_driver;
 extern struct platform_driver inno_hdmi_driver;
 extern struct platform_driver rockchip_dp_driver;
+extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
index 70773041785bf8b1a34077f1e7c2fae49f71b1a0..cd2ace0c3caa3582777b571637044232c3540c7c 100644 (file)
@@ -100,8 +100,9 @@ rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cm
        ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
                                   &rockchip_drm_fb_funcs);
        if (ret) {
-               dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
-                       ret);
+               DRM_DEV_ERROR(dev->dev,
+                             "Failed to initialize framebuffer: %d\n",
+                             ret);
                kfree(rockchip_fb);
                return ERR_PTR(ret);
        }
@@ -134,7 +135,8 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
 
                obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
                if (!obj) {
-                       dev_err(dev->dev, "Failed to lookup GEM object\n");
+                       DRM_DEV_ERROR(dev->dev,
+                                     "Failed to lookup GEM object\n");
                        ret = -ENXIO;
                        goto err_gem_object_unreference;
                }
index 724579ebf947f537ca774fb6229dbb0a653683c0..e6650553f5d6cdc93f706dbef2482efe308dac19 100644 (file)
@@ -76,7 +76,7 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
 
        fbi = drm_fb_helper_alloc_fbi(helper);
        if (IS_ERR(fbi)) {
-               dev_err(dev->dev, "Failed to create framebuffer info.\n");
+               DRM_DEV_ERROR(dev->dev, "Failed to create framebuffer info.\n");
                ret = PTR_ERR(fbi);
                goto out;
        }
@@ -84,7 +84,8 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
        helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
                                                   private->fbdev_bo);
        if (IS_ERR(helper->fb)) {
-               dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
+               DRM_DEV_ERROR(dev->dev,
+                             "Failed to allocate DRM framebuffer.\n");
                ret = PTR_ERR(helper->fb);
                goto out;
        }
@@ -138,21 +139,24 @@ int rockchip_drm_fbdev_init(struct drm_device *dev)
 
        ret = drm_fb_helper_init(dev, helper, ROCKCHIP_MAX_CONNECTOR);
        if (ret < 0) {
-               dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n",
-                       ret);
+               DRM_DEV_ERROR(dev->dev,
+                             "Failed to initialize drm fb helper - %d.\n",
+                             ret);
                return ret;
        }
 
        ret = drm_fb_helper_single_add_all_connectors(helper);
        if (ret < 0) {
-               dev_err(dev->dev, "Failed to add connectors - %d.\n", ret);
+               DRM_DEV_ERROR(dev->dev,
+                             "Failed to add connectors - %d.\n", ret);
                goto err_drm_fb_helper_fini;
        }
 
        ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
        if (ret < 0) {
-               dev_err(dev->dev, "Failed to set initial hw config - %d.\n",
-                       ret);
+               DRM_DEV_ERROR(dev->dev,
+                             "Failed to set initial hw config - %d.\n",
+                             ret);
                goto err_drm_fb_helper_fini;
        }
 
index bf9ed0e639731cf15d0847674856a7082986ac76..19128b4dea544105761fbd1fdc213da1c40ad369 100644 (file)
@@ -160,7 +160,7 @@ static void vop_reg_set(struct vop *vop, const struct vop_reg *reg,
        int offset, mask, shift;
 
        if (!reg || !reg->mask) {
-               dev_dbg(vop->dev, "Warning: not support %s\n", reg_name);
+               DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name);
                return;
        }
 
@@ -499,7 +499,7 @@ static int vop_enable(struct drm_crtc *crtc)
 
        ret = pm_runtime_get_sync(vop->dev);
        if (ret < 0) {
-               dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
+               DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
                return ret;
        }
 
@@ -523,7 +523,8 @@ static int vop_enable(struct drm_crtc *crtc)
         */
        ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev);
        if (ret) {
-               dev_err(vop->dev, "failed to attach dma mapping, %d\n", ret);
+               DRM_DEV_ERROR(vop->dev,
+                             "failed to attach dma mapping, %d\n", ret);
                goto err_disable_aclk;
        }
 
@@ -1361,42 +1362,42 @@ static int vop_initial(struct vop *vop)
 
        vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
        if (IS_ERR(vop->hclk)) {
-               dev_err(vop->dev, "failed to get hclk source\n");
+               DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n");
                return PTR_ERR(vop->hclk);
        }
        vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
        if (IS_ERR(vop->aclk)) {
-               dev_err(vop->dev, "failed to get aclk source\n");
+               DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n");
                return PTR_ERR(vop->aclk);
        }
        vop->dclk = devm_clk_get(vop->dev, "dclk_vop");
        if (IS_ERR(vop->dclk)) {
-               dev_err(vop->dev, "failed to get dclk source\n");
+               DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n");
                return PTR_ERR(vop->dclk);
        }
 
        ret = pm_runtime_get_sync(vop->dev);
        if (ret < 0) {
-               dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
+               DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
                return ret;
        }
 
        ret = clk_prepare(vop->dclk);
        if (ret < 0) {
-               dev_err(vop->dev, "failed to prepare dclk\n");
+               DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n");
                goto err_put_pm_runtime;
        }
 
        /* Enable both the hclk and aclk to setup the vop */
        ret = clk_prepare_enable(vop->hclk);
        if (ret < 0) {
-               dev_err(vop->dev, "failed to prepare/enable hclk\n");
+               DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n");
                goto err_unprepare_dclk;
        }
 
        ret = clk_prepare_enable(vop->aclk);
        if (ret < 0) {
-               dev_err(vop->dev, "failed to prepare/enable aclk\n");
+               DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n");
                goto err_disable_hclk;
        }
 
@@ -1405,7 +1406,7 @@ static int vop_initial(struct vop *vop)
         */
        ahb_rst = devm_reset_control_get(vop->dev, "ahb");
        if (IS_ERR(ahb_rst)) {
-               dev_err(vop->dev, "failed to get ahb reset\n");
+               DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n");
                ret = PTR_ERR(ahb_rst);
                goto err_disable_aclk;
        }
@@ -1434,7 +1435,7 @@ static int vop_initial(struct vop *vop)
         */
        vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk");
        if (IS_ERR(vop->dclk_rst)) {
-               dev_err(vop->dev, "failed to get dclk reset\n");
+               DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n");
                ret = PTR_ERR(vop->dclk_rst);
                goto err_disable_aclk;
        }
@@ -1511,7 +1512,7 @@ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
        vop_line_flag_irq_disable(vop);
 
        if (jiffies_left == 0) {
-               dev_err(vop->dev, "Timeout waiting for IRQ\n");
+               DRM_DEV_ERROR(vop->dev, "Timeout waiting for IRQ\n");
                return -ETIMEDOUT;
        }
 
@@ -1558,7 +1559,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
-               dev_err(dev, "cannot find irq for vop\n");
+               DRM_DEV_ERROR(dev, "cannot find irq for vop\n");
                return irq;
        }
        vop->irq = (unsigned int)irq;
@@ -1584,7 +1585,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 
        ret = vop_initial(vop);
        if (ret < 0) {
-               dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
+               DRM_DEV_ERROR(&pdev->dev,
+                             "cannot initial vop dev - err %d\n", ret);
                goto err_disable_pm_runtime;
        }
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
new file mode 100644 (file)
index 0000000..c5fbe53
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Mark Yao <mark.yao@rock-chips.com>
+ *      Sandy Huang <hjc@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_of.h>
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+#include "rockchip_lvds.h"
+
+#define DISPLAY_OUTPUT_RGB             0
+#define DISPLAY_OUTPUT_LVDS            1
+#define DISPLAY_OUTPUT_DUAL_LVDS       2
+
+#define connector_to_lvds(c) \
+               container_of(c, struct rockchip_lvds, connector)
+
+#define encoder_to_lvds(c) \
+               container_of(c, struct rockchip_lvds, encoder)
+
+/**
+ * rockchip_lvds_soc_data - rockchip lvds Soc private data
+ * @ch1_offset: lvds channel 1 registe offset
+ * grf_soc_con6: general registe offset for LVDS contrl
+ * grf_soc_con7: general registe offset for LVDS contrl
+ * has_vop_sel: to indicate whether need to choose from different VOP.
+ */
+struct rockchip_lvds_soc_data {
+       u32 ch1_offset;
+       int grf_soc_con6;
+       int grf_soc_con7;
+       bool has_vop_sel;
+};
+
+struct rockchip_lvds {
+       struct device *dev;
+       void __iomem *regs;
+       struct regmap *grf;
+       struct clk *pclk;
+       const struct rockchip_lvds_soc_data *soc_data;
+       int output; /* rgb lvds or dual lvds output */
+       int format; /* vesa or jeida format */
+       struct drm_device *drm_dev;
+       struct drm_panel *panel;
+       struct drm_bridge *bridge;
+       struct drm_connector connector;
+       struct drm_encoder encoder;
+       struct dev_pin_info *pins;
+};
+
+static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val)
+{
+       writel_relaxed(val, lvds->regs + offset);
+       if (lvds->output == DISPLAY_OUTPUT_LVDS)
+               return;
+       writel_relaxed(val, lvds->regs + offset + lvds->soc_data->ch1_offset);
+}
+
+static inline int lvds_name_to_format(const char *s)
+{
+       if (strncmp(s, "jeida-18", 8) == 0)
+               return LVDS_JEIDA_18;
+       else if (strncmp(s, "jeida-24", 8) == 0)
+               return LVDS_JEIDA_24;
+       else if (strncmp(s, "vesa-24", 7) == 0)
+               return LVDS_VESA_24;
+
+       return -EINVAL;
+}
+
+static inline int lvds_name_to_output(const char *s)
+{
+       if (strncmp(s, "rgb", 3) == 0)
+               return DISPLAY_OUTPUT_RGB;
+       else if (strncmp(s, "lvds", 4) == 0)
+               return DISPLAY_OUTPUT_LVDS;
+       else if (strncmp(s, "duallvds", 8) == 0)
+               return DISPLAY_OUTPUT_DUAL_LVDS;
+
+       return -EINVAL;
+}
+
+static int rockchip_lvds_poweron(struct rockchip_lvds *lvds)
+{
+       int ret;
+       u32 val;
+
+       ret = clk_enable(lvds->pclk);
+       if (ret < 0) {
+               DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret);
+               return ret;
+       }
+       ret = pm_runtime_get_sync(lvds->dev);
+       if (ret < 0) {
+               DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret);
+               clk_disable(lvds->pclk);
+               return ret;
+       }
+       val = RK3288_LVDS_CH0_REG0_LANE4_EN | RK3288_LVDS_CH0_REG0_LANE3_EN |
+               RK3288_LVDS_CH0_REG0_LANE2_EN | RK3288_LVDS_CH0_REG0_LANE1_EN |
+               RK3288_LVDS_CH0_REG0_LANE0_EN;
+       if (lvds->output == DISPLAY_OUTPUT_RGB) {
+               val |= RK3288_LVDS_CH0_REG0_TTL_EN |
+                       RK3288_LVDS_CH0_REG0_LANECK_EN;
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val);
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
+                           RK3288_LVDS_PLL_FBDIV_REG2(0x46));
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG4,
+                           RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
+                           RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
+                           RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
+                           RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
+                           RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
+                           RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG5,
+                           RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
+                           RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
+                           RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
+                           RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
+                           RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
+                           RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
+       } else {
+               val |= RK3288_LVDS_CH0_REG0_LVDS_EN |
+                           RK3288_LVDS_CH0_REG0_LANECK_EN;
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val);
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG1,
+                           RK3288_LVDS_CH0_REG1_LANECK_BIAS |
+                           RK3288_LVDS_CH0_REG1_LANE4_BIAS |
+                           RK3288_LVDS_CH0_REG1_LANE3_BIAS |
+                           RK3288_LVDS_CH0_REG1_LANE2_BIAS |
+                           RK3288_LVDS_CH0_REG1_LANE1_BIAS |
+                           RK3288_LVDS_CH0_REG1_LANE0_BIAS);
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
+                           RK3288_LVDS_CH0_REG2_RESERVE_ON |
+                           RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
+                           RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
+                           RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
+                           RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
+                           RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
+                           RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
+                           RK3288_LVDS_PLL_FBDIV_REG2(0x46));
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00);
+               lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00);
+       }
+       lvds_writel(lvds, RK3288_LVDS_CH0_REG3, RK3288_LVDS_PLL_FBDIV_REG3(0x46));
+       lvds_writel(lvds, RK3288_LVDS_CH0_REGD, RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
+       lvds_writel(lvds, RK3288_LVDS_CH0_REG20, RK3288_LVDS_CH0_REG20_LSB);
+
+       lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE);
+       lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE);
+
+       return 0;
+}
+
+static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds)
+{
+       int ret;
+       u32 val;
+
+       lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE);
+       lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE);
+       val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN;
+       val |= val << 16;
+       ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
+       if (ret != 0)
+               DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret);
+
+       pm_runtime_put(lvds->dev);
+       clk_disable(lvds->pclk);
+}
+
+static const struct drm_connector_funcs rockchip_lvds_connector_funcs = {
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = drm_connector_cleanup,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int rockchip_lvds_connector_get_modes(struct drm_connector *connector)
+{
+       struct rockchip_lvds *lvds = connector_to_lvds(connector);
+       struct drm_panel *panel = lvds->panel;
+
+       return drm_panel_get_modes(panel);
+}
+
+static const
+struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = {
+       .get_modes = rockchip_lvds_connector_get_modes,
+};
+
+static void rockchip_lvds_grf_config(struct drm_encoder *encoder,
+                                    struct drm_display_mode *mode)
+{
+       struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+       u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
+       u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
+       u32 val;
+       int ret;
+
+       /* iomux to LCD data/sync mode */
+       if (lvds->output == DISPLAY_OUTPUT_RGB)
+               if (lvds->pins && !IS_ERR(lvds->pins->default_state))
+                       pinctrl_select_state(lvds->pins->p,
+                                            lvds->pins->default_state);
+       val = lvds->format | LVDS_CH0_EN;
+       if (lvds->output == DISPLAY_OUTPUT_RGB)
+               val |= LVDS_TTL_EN | LVDS_CH1_EN;
+       else if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS)
+               val |= LVDS_DUAL | LVDS_CH1_EN;
+
+       if ((mode->htotal - mode->hsync_start) & 0x01)
+               val |= LVDS_START_PHASE_RST_1;
+
+       val |= (pin_dclk << 8) | (pin_hsync << 9);
+       val |= (0xffff << 16);
+       ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
+       if (ret != 0) {
+               DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret);
+               return;
+       }
+}
+
+static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds,
+                                       struct drm_encoder *encoder)
+{
+       u32 val;
+       int ret;
+
+       if (!lvds->soc_data->has_vop_sel)
+               return 0;
+
+       ret = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder);
+       if (ret < 0)
+               return ret;
+
+       val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
+       if (ret)
+               val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT;
+
+       ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int
+rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder,
+                                  struct drm_crtc_state *crtc_state,
+                                  struct drm_connector_state *conn_state)
+{
+       struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+       s->output_mode = ROCKCHIP_OUT_MODE_P888;
+       s->output_type = DRM_MODE_CONNECTOR_LVDS;
+
+       return 0;
+}
+
+static void rockchip_lvds_encoder_enable(struct drm_encoder *encoder)
+{
+       struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+       struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+       int ret;
+
+       drm_panel_prepare(lvds->panel);
+       ret = rockchip_lvds_poweron(lvds);
+       if (ret < 0) {
+               DRM_DEV_ERROR(lvds->dev, "failed to power on lvds: %d\n", ret);
+               drm_panel_unprepare(lvds->panel);
+       }
+       rockchip_lvds_grf_config(encoder, mode);
+       rockchip_lvds_set_vop_source(lvds, encoder);
+       drm_panel_enable(lvds->panel);
+}
+
+static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder)
+{
+       struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+
+       drm_panel_disable(lvds->panel);
+       rockchip_lvds_poweroff(lvds);
+       drm_panel_unprepare(lvds->panel);
+}
+
+static const
+struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = {
+       .enable = rockchip_lvds_encoder_enable,
+       .disable = rockchip_lvds_encoder_disable,
+       .atomic_check = rockchip_lvds_encoder_atomic_check,
+};
+
+static const struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+static const struct rockchip_lvds_soc_data rk3288_lvds_data = {
+       .ch1_offset = 0x100,
+       .grf_soc_con6 = 0x025c,
+       .grf_soc_con7 = 0x0260,
+       .has_vop_sel = true,
+};
+
+static const struct of_device_id rockchip_lvds_dt_ids[] = {
+       {
+               .compatible = "rockchip,rk3288-lvds",
+               .data = &rk3288_lvds_data
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
+
+static int rockchip_lvds_bind(struct device *dev, struct device *master,
+                             void *data)
+{
+       struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       struct drm_encoder *encoder;
+       struct drm_connector *connector;
+       struct device_node *remote = NULL;
+       struct device_node  *port, *endpoint;
+       int ret;
+       const char *name;
+       u32 endpoint_id;
+
+       lvds->drm_dev = drm_dev;
+       port = of_graph_get_port_by_id(dev->of_node, 1);
+       if (!port) {
+               DRM_DEV_ERROR(dev,
+                             "can't found port point, please init lvds panel port!\n");
+               return -EINVAL;
+       }
+       for_each_child_of_node(port, endpoint) {
+               of_property_read_u32(endpoint, "reg", &endpoint_id);
+               ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id,
+                                                 &lvds->panel, &lvds->bridge);
+               if (!ret)
+                       break;
+       }
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n");
+               ret  = -EPROBE_DEFER;
+               goto err_put_port;
+       }
+       if (lvds->panel)
+               remote = lvds->panel->dev->of_node;
+       else
+               remote = lvds->bridge->of_node;
+       if (of_property_read_string(dev->of_node, "rockchip,output", &name))
+               /* default set it as output rgb */
+               lvds->output = DISPLAY_OUTPUT_RGB;
+       else
+               lvds->output = lvds_name_to_output(name);
+
+       if (lvds->output < 0) {
+               DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name);
+               ret = lvds->output;
+               goto err_put_remote;
+       }
+
+       if (of_property_read_string(remote, "data-mapping", &name))
+               /* default set it as format vesa 18 */
+               lvds->format = LVDS_VESA_18;
+       else
+               lvds->format = lvds_name_to_format(name);
+
+       if (lvds->format < 0) {
+               DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name);
+               ret = lvds->format;
+               goto err_put_remote;
+       }
+
+       encoder = &lvds->encoder;
+       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+                                                            dev->of_node);
+
+       ret = drm_encoder_init(drm_dev, encoder, &rockchip_lvds_encoder_funcs,
+                              DRM_MODE_ENCODER_LVDS, NULL);
+       if (ret < 0) {
+               DRM_DEV_ERROR(drm_dev->dev,
+                             "failed to initialize encoder: %d\n", ret);
+               goto err_put_remote;
+       }
+
+       drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs);
+
+       if (lvds->panel) {
+               connector = &lvds->connector;
+               connector->dpms = DRM_MODE_DPMS_OFF;
+               ret = drm_connector_init(drm_dev, connector,
+                                        &rockchip_lvds_connector_funcs,
+                                        DRM_MODE_CONNECTOR_LVDS);
+               if (ret < 0) {
+                       DRM_DEV_ERROR(drm_dev->dev,
+                                     "failed to initialize connector: %d\n", ret);
+                       goto err_free_encoder;
+               }
+
+               drm_connector_helper_add(connector,
+                                        &rockchip_lvds_connector_helper_funcs);
+
+               ret = drm_mode_connector_attach_encoder(connector, encoder);
+               if (ret < 0) {
+                       DRM_DEV_ERROR(drm_dev->dev,
+                                     "failed to attach encoder: %d\n", ret);
+                       goto err_free_connector;
+               }
+
+               ret = drm_panel_attach(lvds->panel, connector);
+               if (ret < 0) {
+                       DRM_DEV_ERROR(drm_dev->dev,
+                                     "failed to attach panel: %d\n", ret);
+                       goto err_free_connector;
+               }
+       } else {
+               lvds->bridge->encoder = encoder;
+               ret = drm_bridge_attach(encoder, lvds->bridge, NULL);
+               if (ret) {
+                       DRM_DEV_ERROR(drm_dev->dev,
+                                     "failed to attach bridge: %d\n", ret);
+                       goto err_free_encoder;
+               }
+               encoder->bridge = lvds->bridge;
+       }
+
+       pm_runtime_enable(dev);
+       of_node_put(remote);
+       of_node_put(port);
+
+       return 0;
+
+err_free_connector:
+       drm_connector_cleanup(connector);
+err_free_encoder:
+       drm_encoder_cleanup(encoder);
+err_put_remote:
+       of_node_put(remote);
+err_put_port:
+       of_node_put(port);
+
+       return ret;
+}
+
+static void rockchip_lvds_unbind(struct device *dev, struct device *master,
+                               void *data)
+{
+       struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+
+       rockchip_lvds_encoder_disable(&lvds->encoder);
+       if (lvds->panel)
+               drm_panel_detach(lvds->panel);
+       pm_runtime_disable(dev);
+       drm_connector_cleanup(&lvds->connector);
+       drm_encoder_cleanup(&lvds->encoder);
+}
+
+static const struct component_ops rockchip_lvds_component_ops = {
+       .bind = rockchip_lvds_bind,
+       .unbind = rockchip_lvds_unbind,
+};
+
+static int rockchip_lvds_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rockchip_lvds *lvds;
+       const struct of_device_id *match;
+       struct resource *res;
+       int ret;
+
+       if (!dev->of_node)
+               return -ENODEV;
+
+       lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+       if (!lvds)
+               return -ENOMEM;
+
+       lvds->dev = dev;
+       match = of_match_node(rockchip_lvds_dt_ids, dev->of_node);
+       if (!match)
+               return -ENODEV;
+       lvds->soc_data = match->data;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       lvds->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(lvds->regs))
+               return PTR_ERR(lvds->regs);
+
+       lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
+       if (IS_ERR(lvds->pclk)) {
+               DRM_DEV_ERROR(dev, "could not get pclk_lvds\n");
+               return PTR_ERR(lvds->pclk);
+       }
+
+       lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins),
+                                 GFP_KERNEL);
+       if (!lvds->pins)
+               return -ENOMEM;
+
+       lvds->pins->p = devm_pinctrl_get(lvds->dev);
+       if (IS_ERR(lvds->pins->p)) {
+               DRM_DEV_ERROR(dev, "no pinctrl handle\n");
+               devm_kfree(lvds->dev, lvds->pins);
+               lvds->pins = NULL;
+       } else {
+               lvds->pins->default_state =
+                       pinctrl_lookup_state(lvds->pins->p, "lcdc");
+               if (IS_ERR(lvds->pins->default_state)) {
+                       DRM_DEV_ERROR(dev, "no default pinctrl state\n");
+                       devm_kfree(lvds->dev, lvds->pins);
+                       lvds->pins = NULL;
+               }
+       }
+
+       lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                                   "rockchip,grf");
+       if (IS_ERR(lvds->grf)) {
+               DRM_DEV_ERROR(dev, "missing rockchip,grf property\n");
+               return PTR_ERR(lvds->grf);
+       }
+
+       dev_set_drvdata(dev, lvds);
+
+       ret = clk_prepare(lvds->pclk);
+       if (ret < 0) {
+               DRM_DEV_ERROR(dev, "failed to prepare pclk_lvds\n");
+               return ret;
+       }
+       ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
+       if (ret < 0) {
+               DRM_DEV_ERROR(dev, "failed to add component\n");
+               clk_unprepare(lvds->pclk);
+       }
+
+       return ret;
+}
+
+static int rockchip_lvds_remove(struct platform_device *pdev)
+{
+       struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
+
+       component_del(&pdev->dev, &rockchip_lvds_component_ops);
+       clk_unprepare(lvds->pclk);
+
+       return 0;
+}
+
+struct platform_driver rockchip_lvds_driver = {
+       .probe = rockchip_lvds_probe,
+       .remove = rockchip_lvds_remove,
+       .driver = {
+                  .name = "rockchip-lvds",
+                  .of_match_table = of_match_ptr(rockchip_lvds_dt_ids),
+       },
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h
new file mode 100644 (file)
index 0000000..15810b7
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Sandy Huang <hjc@rock-chips.com>
+ *      Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _ROCKCHIP_LVDS_
+#define _ROCKCHIP_LVDS_
+
+#define RK3288_LVDS_CH0_REG0                   0x00
+#define RK3288_LVDS_CH0_REG0_LVDS_EN           BIT(7)
+#define RK3288_LVDS_CH0_REG0_TTL_EN            BIT(6)
+#define RK3288_LVDS_CH0_REG0_LANECK_EN         BIT(5)
+#define RK3288_LVDS_CH0_REG0_LANE4_EN          BIT(4)
+#define RK3288_LVDS_CH0_REG0_LANE3_EN          BIT(3)
+#define RK3288_LVDS_CH0_REG0_LANE2_EN          BIT(2)
+#define RK3288_LVDS_CH0_REG0_LANE1_EN          BIT(1)
+#define RK3288_LVDS_CH0_REG0_LANE0_EN          BIT(0)
+
+#define RK3288_LVDS_CH0_REG1                   0x04
+#define RK3288_LVDS_CH0_REG1_LANECK_BIAS       BIT(5)
+#define RK3288_LVDS_CH0_REG1_LANE4_BIAS                BIT(4)
+#define RK3288_LVDS_CH0_REG1_LANE3_BIAS                BIT(3)
+#define RK3288_LVDS_CH0_REG1_LANE2_BIAS                BIT(2)
+#define RK3288_LVDS_CH0_REG1_LANE1_BIAS                BIT(1)
+#define RK3288_LVDS_CH0_REG1_LANE0_BIAS                BIT(0)
+
+#define RK3288_LVDS_CH0_REG2                   0x08
+#define RK3288_LVDS_CH0_REG2_RESERVE_ON                BIT(7)
+#define RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE  BIT(6)
+#define RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE   BIT(5)
+#define RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE   BIT(4)
+#define RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE   BIT(3)
+#define RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE   BIT(2)
+#define RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE   BIT(1)
+#define RK3288_LVDS_CH0_REG2_PLL_FBDIV8                BIT(0)
+
+#define RK3288_LVDS_CH0_REG3                   0x0c
+#define RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK    0xff
+
+#define RK3288_LVDS_CH0_REG4                   0x10
+#define RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE   BIT(5)
+#define RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE    BIT(4)
+#define RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE    BIT(3)
+#define RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE    BIT(2)
+#define RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE    BIT(1)
+#define RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE    BIT(0)
+
+#define RK3288_LVDS_CH0_REG5                   0x14
+#define RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA   BIT(5)
+#define RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA    BIT(4)
+#define RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA    BIT(3)
+#define RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA    BIT(2)
+#define RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA    BIT(1)
+#define RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA    BIT(0)
+
+#define RK3288_LVDS_CFG_REGC                   0x30
+#define RK3288_LVDS_CFG_REGC_PLL_ENABLE                0x00
+#define RK3288_LVDS_CFG_REGC_PLL_DISABLE       0xff
+
+#define RK3288_LVDS_CH0_REGD                   0x34
+#define RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK   0x1f
+
+#define RK3288_LVDS_CH0_REG20                  0x80
+#define RK3288_LVDS_CH0_REG20_MSB              0x45
+#define RK3288_LVDS_CH0_REG20_LSB              0x44
+
+#define RK3288_LVDS_CFG_REG21                  0x84
+#define RK3288_LVDS_CFG_REG21_TX_ENABLE                0x92
+#define RK3288_LVDS_CFG_REG21_TX_DISABLE       0x00
+#define RK3288_LVDS_CH1_OFFSET                 0x100
+
+/* fbdiv value is split over 2 registers, with bit8 in reg2 */
+#define RK3288_LVDS_PLL_FBDIV_REG2(_fbd) \
+               (_fbd & BIT(8) ? RK3288_LVDS_CH0_REG2_PLL_FBDIV8 : 0)
+#define RK3288_LVDS_PLL_FBDIV_REG3(_fbd) \
+               (_fbd & RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK)
+#define RK3288_LVDS_PLL_PREDIV_REGD(_pd) \
+               (_pd & RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK)
+
+#define RK3288_LVDS_SOC_CON6_SEL_VOP_LIT       BIT(3)
+
+#define LVDS_FMT_MASK                          (0x07 << 16)
+#define LVDS_MSB                               BIT(3)
+#define LVDS_DUAL                              BIT(4)
+#define LVDS_FMT_1                             BIT(5)
+#define LVDS_TTL_EN                            BIT(6)
+#define LVDS_START_PHASE_RST_1                 BIT(7)
+#define LVDS_DCLK_INV                          BIT(8)
+#define LVDS_CH0_EN                            BIT(11)
+#define LVDS_CH1_EN                            BIT(12)
+#define LVDS_PWRDN                             BIT(15)
+
+#define LVDS_24BIT                             (0 << 1)
+#define LVDS_18BIT                             (1 << 1)
+#define LVDS_FORMAT_VESA                       (0 << 0)
+#define LVDS_FORMAT_JEIDA                      (1 << 0)
+
+#define LVDS_VESA_24                           0
+#define LVDS_JEIDA_24                          1
+#define LVDS_VESA_18                           2
+#define LVDS_JEIDA_18                          3
+
+#endif /* _ROCKCHIP_LVDS_ */
index 94de7b9f6fde598d90bb19f00bbf9dc68bdfaf49..4a39049e901a95113baffa4e19c5c820481d2a8b 100644 (file)
@@ -533,7 +533,7 @@ static int vop_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
 
        if (!dev->of_node) {
-               dev_err(dev, "can't find vop devices\n");
+               DRM_DEV_ERROR(dev, "can't find vop devices\n");
                return -ENODEV;
        }
 
index 1700c542cd93fb449076f3d135c977bc59068e94..9e934310173864165534e1d4e1a09184e7e4b0dd 100644 (file)
@@ -16,6 +16,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_of.h>
 
@@ -145,7 +146,7 @@ static void sti_output_poll_changed(struct drm_device *ddev)
 }
 
 static const struct drm_mode_config_funcs sti_mode_config_funcs = {
-       .fb_create = drm_fb_cma_create,
+       .fb_create = drm_gem_fb_create,
        .output_poll_changed = sti_output_poll_changed,
        .atomic_check = sti_atomic_check,
        .atomic_commit = drm_atomic_helper_commit,
index 852bf2293b05888919aee4a5e48c396ce3d9765e..83314aee65cb6c9423f735e9d2aab4a05acea5c3 100644 (file)
@@ -463,11 +463,7 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
        bridge->driver_private = dvo;
        bridge->funcs = &sti_dvo_bridge_funcs;
        bridge->of_node = dvo->dev.of_node;
-       err = drm_bridge_add(bridge);
-       if (err) {
-               DRM_ERROR("Failed to add bridge\n");
-               return err;
-       }
+       drm_bridge_add(bridge);
 
        err = drm_bridge_attach(encoder, bridge, NULL);
        if (err) {
index b333b37f3f898a9a666e730f1999272b67518498..c857663eafc275faff536fd9a79c4bfcac62238d 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 
 #include "ltdc.h"
 
@@ -31,7 +32,7 @@ static void drv_output_poll_changed(struct drm_device *ddev)
 }
 
 static const struct drm_mode_config_funcs drv_mode_config_funcs = {
-       .fb_create = drm_fb_cma_create,
+       .fb_create = drm_gem_fb_create,
        .output_poll_changed = drv_output_poll_changed,
        .atomic_check = drm_atomic_helper_check,
        .atomic_commit = drm_atomic_helper_commit,
index 568c5d0461eab411171d4386c241cbb46e02991d..e5b6310240fe6b7e19a8bc216fdde2bc6a06dd67 100644 (file)
@@ -113,11 +113,13 @@ static enum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt)
 
 static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
 {
+       int divisor = idf * odf;
+
        /* prevent from division by 0 */
-       if (idf * odf)
-               return DIV_ROUND_CLOSEST(clkin_khz * ndiv, idf * odf);
+       if (!divisor)
+               return 0;
 
-       return 0;
+       return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor);
 }
 
 static int dsi_pll_get_params(int clkin_khz, int clkout_khz,
index ace59651892fb636400088e2ac9136a0917d45dd..a2012638d5f77a8d7c23cf32db2d8c3370904e99 100644 (file)
@@ -106,11 +106,6 @@ static int sun4i_drv_bind(struct device *dev)
                goto free_drm;
        }
 
-       /* drm_vblank_init calls kcalloc, which can fail */
-       ret = drm_vblank_init(drm, 1);
-       if (ret)
-               goto free_mem_region;
-
        drm_mode_config_init(drm);
 
        ret = component_bind_all(drm->dev, drm);
@@ -119,6 +114,11 @@ static int sun4i_drv_bind(struct device *dev)
                goto cleanup_mode_config;
        }
 
+       /* drm_vblank_init calls kcalloc, which can fail */
+       ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+       if (ret)
+               goto free_mem_region;
+
        drm->irq_enabled = true;
 
        /* Remove early framebuffers (ie. simplefb) */
@@ -200,11 +200,39 @@ static int compare_of(struct device *dev, void *data)
        return dev->of_node == data;
 }
 
+/*
+ * The encoder drivers use drm_of_find_possible_crtcs to get upstream
+ * crtcs from the device tree using of_graph. For the results to be
+ * correct, encoders must be probed/bound after _all_ crtcs have been
+ * created. The existing code uses a depth first recursive traversal
+ * of the of_graph, which means the encoders downstream of the TCON
+ * get add right after the first TCON. The second TCON or CRTC will
+ * never be properly associated with encoders connected to it.
+ *
+ * Also, in a dual display pipeline setup, both frontends can feed
+ * either backend, and both backends can feed either TCON, we want
+ * all components of the same type to be added before the next type
+ * in the pipeline. Fortunately, the pipelines are perfectly symmetric,
+ * i.e. components of the same type are at the same depth when counted
+ * from the frontend. The only exception is the third pipeline in
+ * the A80 SoC, which we do not support anyway.
+ *
+ * Hence we can use a breadth first search traversal order to add
+ * components. We do not need to check for duplicates. The component
+ * matching system handles this for us.
+ */
+struct endpoint_list {
+       struct device_node *node;
+       struct list_head list;
+};
+
 static int sun4i_drv_add_endpoints(struct device *dev,
+                                  struct list_head *endpoints,
                                   struct component_match **match,
                                   struct device_node *node)
 {
        struct device_node *port, *ep, *remote;
+       struct endpoint_list *endpoint;
        int count = 0;
 
        /*
@@ -264,10 +292,15 @@ static int sun4i_drv_add_endpoints(struct device *dev,
                        }
                }
 
-               /* Walk down our tree */
-               count += sun4i_drv_add_endpoints(dev, match, remote);
+               /* Add downstream nodes to the queue */
+               endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL);
+               if (!endpoint) {
+                       of_node_put(remote);
+                       return -ENOMEM;
+               }
 
-               of_node_put(remote);
+               endpoint->node = remote;
+               list_add_tail(&endpoint->list, endpoints);
        }
 
        return count;
@@ -277,7 +310,9 @@ static int sun4i_drv_probe(struct platform_device *pdev)
 {
        struct component_match *match = NULL;
        struct device_node *np = pdev->dev.of_node;
-       int i, count = 0;
+       struct endpoint_list *endpoint, *endpoint_temp;
+       int i, ret, count = 0;
+       LIST_HEAD(endpoints);
 
        for (i = 0;; i++) {
                struct device_node *pipeline = of_parse_phandle(np,
@@ -286,12 +321,31 @@ static int sun4i_drv_probe(struct platform_device *pdev)
                if (!pipeline)
                        break;
 
-               count += sun4i_drv_add_endpoints(&pdev->dev, &match,
-                                               pipeline);
-               of_node_put(pipeline);
+               endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL);
+               if (!endpoint) {
+                       ret = -ENOMEM;
+                       goto err_free_endpoints;
+               }
 
-               DRM_DEBUG_DRIVER("Queued %d outputs on pipeline %d\n",
-                                count, i);
+               endpoint->node = pipeline;
+               list_add_tail(&endpoint->list, &endpoints);
+       }
+
+       list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) {
+               /* process this endpoint */
+               ret = sun4i_drv_add_endpoints(&pdev->dev, &endpoints, &match,
+                                             endpoint->node);
+
+               /* sun4i_drv_add_endpoints can fail to allocate memory */
+               if (ret < 0)
+                       goto err_free_endpoints;
+
+               count += ret;
+
+               /* delete and cleanup the current entry */
+               list_del(&endpoint->list);
+               of_node_put(endpoint->node);
+               kfree(endpoint);
        }
 
        if (count)
@@ -300,6 +354,15 @@ static int sun4i_drv_probe(struct platform_device *pdev)
                                                       match);
        else
                return 0;
+
+err_free_endpoints:
+       list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) {
+               list_del(&endpoint->list);
+               of_node_put(endpoint->node);
+               kfree(endpoint);
+       }
+
+       return ret;
 }
 
 static int sun4i_drv_remove(struct platform_device *pdev)
index d9791292553ef364ad62eb8497d57ec185e53bc7..e853dfe5138922a7ee1f86a76f223fca50b9d0a6 100644 (file)
@@ -463,42 +463,170 @@ static int sun4i_tcon_init_regmap(struct device *dev,
  * function in fact searches the corresponding engine, and the ID is
  * requested via the get_id function of the engine.
  */
-static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv,
-                                                  struct device_node *node)
+static struct sunxi_engine *
+sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv,
+                               struct device_node *node)
 {
        struct device_node *port, *ep, *remote;
-       struct sunxi_engine *engine;
+       struct sunxi_engine *engine = ERR_PTR(-EINVAL);
 
        port = of_graph_get_port_by_id(node, 0);
        if (!port)
                return ERR_PTR(-EINVAL);
 
+       /*
+        * This only works if there is only one path from the TCON
+        * to any display engine. Otherwise the probe order of the
+        * TCONs and display engines is not guaranteed. They may
+        * either bind to the wrong one, or worse, bind to the same
+        * one if additional checks are not done.
+        *
+        * Bail out if there are multiple input connections.
+        */
+       if (of_get_available_child_count(port) != 1)
+               goto out_put_port;
+
+       /* Get the first connection without specifying an ID */
+       ep = of_get_next_available_child(port, NULL);
+       if (!ep)
+               goto out_put_port;
+
+       remote = of_graph_get_remote_port_parent(ep);
+       if (!remote)
+               goto out_put_ep;
+
+       /* does this node match any registered engines? */
+       list_for_each_entry(engine, &drv->engine_list, list)
+               if (remote == engine->node)
+                       goto out_put_remote;
+
+       /* keep looking through upstream ports */
+       engine = sun4i_tcon_find_engine_traverse(drv, remote);
+
+out_put_remote:
+       of_node_put(remote);
+out_put_ep:
+       of_node_put(ep);
+out_put_port:
+       of_node_put(port);
+
+       return engine;
+}
+
+/*
+ * The device tree binding says that the remote endpoint ID of any
+ * connection between components, up to and including the TCON, of
+ * the display pipeline should be equal to the actual ID of the local
+ * component. Thus we can look at any one of the input connections of
+ * the TCONs, and use that connection's remote endpoint ID as our own.
+ *
+ * Since the user of this function already finds the input port,
+ * the port is passed in directly without further checks.
+ */
+static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
+{
+       struct device_node *ep;
+       int ret = -EINVAL;
+
+       /* try finding an upstream endpoint */
        for_each_available_child_of_node(port, ep) {
-               remote = of_graph_get_remote_port_parent(ep);
+               struct device_node *remote;
+               u32 reg;
+
+               remote = of_graph_get_remote_endpoint(ep);
                if (!remote)
                        continue;
 
-               /* does this node match any registered engines? */
-               list_for_each_entry(engine, &drv->engine_list, list) {
-                       if (remote == engine->node) {
-                               of_node_put(remote);
-                               of_node_put(port);
-                               return engine;
-                       }
-               }
+               ret = of_property_read_u32(remote, "reg", &reg);
+               if (ret)
+                       continue;
 
-               /* keep looking through upstream ports */
-               engine = sun4i_tcon_find_engine(drv, remote);
-               if (!IS_ERR(engine)) {
-                       of_node_put(remote);
-                       of_node_put(port);
-                       return engine;
-               }
+               ret = reg;
        }
 
+       return ret;
+}
+
+/*
+ * Once we know the TCON's id, we can look through the list of
+ * engines to find a matching one. We assume all engines have
+ * been probed and added to the list.
+ */
+static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv,
+                                                       int id)
+{
+       struct sunxi_engine *engine;
+
+       list_for_each_entry(engine, &drv->engine_list, list)
+               if (engine->id == id)
+                       return engine;
+
        return ERR_PTR(-EINVAL);
 }
 
+/*
+ * On SoCs with the old display pipeline design (Display Engine 1.0),
+ * we assumed the TCON was always tied to just one backend. However
+ * this proved not to be the case. On the A31, the TCON can select
+ * either backend as its source. On the A20 (and likely on the A10),
+ * the backend can choose which TCON to output to.
+ *
+ * The device tree binding says that the remote endpoint ID of any
+ * connection between components, up to and including the TCON, of
+ * the display pipeline should be equal to the actual ID of the local
+ * component. Thus we should be able to look at any one of the input
+ * connections of the TCONs, and use that connection's remote endpoint
+ * ID as our own.
+ *
+ * However  the connections between the backend and TCON were assumed
+ * to be always singular, and their endpoit IDs were all incorrectly
+ * set to 0. This means for these old device trees, we cannot just look
+ * up the remote endpoint ID of a TCON input endpoint. TCON1 would be
+ * incorrectly identified as TCON0.
+ *
+ * This function first checks if the TCON node has 2 input endpoints.
+ * If so, then the device tree is a corrected version, and it will use
+ * sun4i_tcon_of_get_id() and sun4i_tcon_get_engine_by_id() from above
+ * to fetch the ID and engine directly. If not, then it is likely an
+ * old device trees, where the endpoint IDs were incorrect, but did not
+ * have endpoint connections between the backend and TCON across
+ * different display pipelines. It will fall back to the old method of
+ * traversing the  of_graph to try and find a matching engine by device
+ * node.
+ *
+ * In the case of single display pipeline device trees, either method
+ * works.
+ */
+static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv,
+                                                  struct device_node *node)
+{
+       struct device_node *port;
+       struct sunxi_engine *engine;
+
+       port = of_graph_get_port_by_id(node, 0);
+       if (!port)
+               return ERR_PTR(-EINVAL);
+
+       /*
+        * Is this a corrected device tree with cross pipeline
+        * connections between the backend and TCON?
+        */
+       if (of_get_child_count(port) > 1) {
+               /* Get our ID directly from an upstream endpoint */
+               int id = sun4i_tcon_of_get_id_from_port(port);
+
+               /* Get our engine by matching our ID */
+               engine = sun4i_tcon_get_engine_by_id(drv, id);
+
+               of_node_put(port);
+               return engine;
+       }
+
+       /* Fallback to old method by traversing input endpoints */
+       of_node_put(port);
+       return sun4i_tcon_find_engine_traverse(drv, node);
+}
+
 static int sun4i_tcon_bind(struct device *dev, struct device *master,
                           void *data)
 {
@@ -530,10 +658,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
        }
 
        /* Make sure our TCON is reset */
-       if (!reset_control_status(tcon->lcd_rst))
-               reset_control_assert(tcon->lcd_rst);
-
-       ret = reset_control_deassert(tcon->lcd_rst);
+       ret = reset_control_reset(tcon->lcd_rst);
        if (ret) {
                dev_err(dev, "Couldn't deassert our reset line\n");
                return ret;
@@ -574,6 +699,25 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
        if (ret < 0)
                goto err_free_clocks;
 
+       if (tcon->quirks->needs_de_be_mux) {
+               /*
+                * We assume there is no dynamic muxing of backends
+                * and TCONs, so we select the backend with same ID.
+                *
+                * While dynamic selection might be interesting, since
+                * the CRTC is tied to the TCON, while the layers are
+                * tied to the backends, this means, we will need to
+                * switch between groups of layers. There might not be
+                * a way to represent this constraint in DRM.
+                */
+               regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
+                                  SUN4I_TCON0_CTL_SRC_SEL_MASK,
+                                  tcon->id);
+               regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+                                  SUN4I_TCON1_CTL_SRC_SEL_MASK,
+                                  tcon->id);
+       }
+
        list_add_tail(&tcon->list, &drv->tcon_list);
 
        return 0;
@@ -629,11 +773,13 @@ static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
 };
 
 static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
-       .has_channel_1  = true,
+       .has_channel_1          = true,
+       .needs_de_be_mux        = true,
 };
 
 static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
-       .has_channel_1  = true,
+       .has_channel_1          = true,
+       .needs_de_be_mux        = true,
 };
 
 static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
index 552c88ec16be38d82cf3041a73e9dd27d4aafd18..5a219d1ccc261a17ec7e796282dc9935f2da648c 100644 (file)
@@ -37,6 +37,7 @@
 #define SUN4I_TCON0_CTL_TCON_ENABLE                    BIT(31)
 #define SUN4I_TCON0_CTL_CLK_DELAY_MASK                 GENMASK(8, 4)
 #define SUN4I_TCON0_CTL_CLK_DELAY(delay)               ((delay << 4) & SUN4I_TCON0_CTL_CLK_DELAY_MASK)
+#define SUN4I_TCON0_CTL_SRC_SEL_MASK                   GENMASK(2, 0)
 
 #define SUN4I_TCON0_DCLK_REG                   0x44
 #define SUN4I_TCON0_DCLK_GATE_BIT                      (31)
@@ -85,6 +86,7 @@
 #define SUN4I_TCON1_CTL_INTERLACE_ENABLE               BIT(20)
 #define SUN4I_TCON1_CTL_CLK_DELAY_MASK                 GENMASK(8, 4)
 #define SUN4I_TCON1_CTL_CLK_DELAY(delay)               ((delay << 4) & SUN4I_TCON1_CTL_CLK_DELAY_MASK)
+#define SUN4I_TCON1_CTL_SRC_SEL_MASK                   GENMASK(1, 0)
 
 #define SUN4I_TCON1_BASIC0_REG                 0x94
 #define SUN4I_TCON1_BASIC0_X(width)                    ((((width) - 1) & 0xfff) << 16)
 struct sun4i_tcon_quirks {
        bool    has_unknown_mux; /* sun5i has undocumented mux */
        bool    has_channel_1;  /* a33 does not have channel 1 */
+       bool    needs_de_be_mux; /* sun6i needs mux to select backend */
 };
 
 struct sun4i_tcon {
index b0d70f943cec56116085f7c3b4740ea30e11e750..146ac9a5a2fd07ab4e7b9ae6b2f1968f9e9072cd 100644 (file)
@@ -23,6 +23,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 
 #include "tilcdc_drv.h"
 #include "tilcdc_regs.h"
@@ -65,7 +66,7 @@ static struct of_device_id tilcdc_of_match[];
 static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev,
                struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd)
 {
-       return drm_fb_cma_create(dev, file_priv, mode_cmd);
+       return drm_gem_fb_create(dev, file_priv, mode_cmd);
 }
 
 static void tilcdc_fb_output_poll_changed(struct drm_device *dev)
index 7e5bb7d6f655835e39a2fb91db54b960e199213d..7fd26912f2bae2b6599676396e11679de22ba2c5 100644 (file)
@@ -163,7 +163,6 @@ MODULE_DEVICE_TABLE(spi, mi0283qt_id);
 static int mi0283qt_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
-       struct tinydrm_device *tdev;
        struct mipi_dbi *mipi;
        struct gpio_desc *dc;
        u32 rotation = 0;
@@ -215,20 +214,9 @@ static int mi0283qt_probe(struct spi_device *spi)
                return ret;
        }
 
-       tdev = &mipi->tinydrm;
-
-       ret = devm_tinydrm_register(tdev);
-       if (ret)
-               return ret;
-
        spi_set_drvdata(spi, mipi);
 
-       DRM_DEBUG_DRIVER("Initialized %s:%s @%uMHz on minor %d\n",
-                        tdev->drm->driver->name, dev_name(dev),
-                        spi->max_speed_hz / 1000000,
-                        tdev->drm->primary->index);
-
-       return 0;
+       return devm_tinydrm_register(&mipi->tinydrm);
 }
 
 static void mi0283qt_shutdown(struct spi_device *spi)
index 2caeabcd34588ee202de9f439673a9d591868f96..f0dedc24494461d09ec599c1dab132c66edd6f6f 100644 (file)
@@ -842,6 +842,8 @@ int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi,
                        return -ENOMEM;
        }
 
+       DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
+
        return 0;
 }
 EXPORT_SYMBOL(mipi_dbi_spi_init);
index 30dc97b3ff218881d55cf3e15c5ff87f28e597c9..5fbe14715c839045b482cc102e77f4930699856e 100644 (file)
@@ -1078,19 +1078,11 @@ static int repaper_probe(struct spi_device *spi)
                return ret;
 
        drm_mode_config_reset(tdev->drm);
-
-       ret = devm_tinydrm_register(tdev);
-       if (ret)
-               return ret;
-
        spi_set_drvdata(spi, tdev);
 
-       DRM_DEBUG_DRIVER("Initialized %s:%s @%uMHz on minor %d\n",
-                        tdev->drm->driver->name, dev_name(dev),
-                        spi->max_speed_hz / 1000000,
-                        tdev->drm->primary->index);
+       DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
 
-       return 0;
+       return devm_tinydrm_register(tdev);
 }
 
 static void repaper_shutdown(struct spi_device *spi)
index b439956a07f41798a1dd408c3ddad49956c9ceb8..07b4d312784c78740d3724e8ffcf2435f694e3a4 100644 (file)
@@ -343,7 +343,6 @@ MODULE_DEVICE_TABLE(spi, st7586_id);
 static int st7586_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
-       struct tinydrm_device *tdev;
        struct mipi_dbi *mipi;
        struct gpio_desc *a0;
        u32 rotation = 0;
@@ -388,20 +387,9 @@ static int st7586_probe(struct spi_device *spi)
        if (ret)
                return ret;
 
-       tdev = &mipi->tinydrm;
-
-       ret = devm_tinydrm_register(tdev);
-       if (ret)
-               return ret;
-
        spi_set_drvdata(spi, mipi);
 
-       DRM_DEBUG_DRIVER("Initialized %s:%s @%uMHz on minor %d\n",
-                        tdev->drm->driver->name, dev_name(dev),
-                        spi->max_speed_hz / 1000000,
-                        tdev->drm->primary->index);
-
-       return 0;
+       return devm_tinydrm_register(&mipi->tinydrm);
 }
 
 static void st7586_shutdown(struct spi_device *spi)
diff --git a/drivers/gpu/drm/tve200/Kconfig b/drivers/gpu/drm/tve200/Kconfig
new file mode 100644 (file)
index 0000000..c5f03bf
--- /dev/null
@@ -0,0 +1,16 @@
+config DRM_TVE200
+       tristate "DRM Support for Faraday TV Encoder TVE200"
+       depends on DRM
+       depends on CMA
+       depends on ARM || COMPILE_TEST
+       depends on OF
+       select DRM_BRIDGE
+       select DRM_PANEL_BRIDGE
+       select DRM_KMS_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_GEM_CMA_HELPER
+       select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+       help
+         Choose this option for DRM support for the Faraday TV Encoder
+         TVE200 Controller.
+         If M is selected the module will be called tve200_drm.
diff --git a/drivers/gpu/drm/tve200/Makefile b/drivers/gpu/drm/tve200/Makefile
new file mode 100644 (file)
index 0000000..6b7a6a1
--- /dev/null
@@ -0,0 +1,4 @@
+tve200_drm-y +=        tve200_display.o \
+               tve200_drv.o
+
+obj-$(CONFIG_DRM_TVE200) += tve200_drm.o
diff --git a/drivers/gpu/drm/tve200/tve200_display.c b/drivers/gpu/drm/tve200/tve200_display.c
new file mode 100644 (file)
index 0000000..18457de
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2006-2008 Intel Corporation
+ * Copyright (C) 2007 Amos Lee <amos_lee@storlinksemi.com>
+ * Copyright (C) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ * Copyright (C) 2017 Eric Anholt
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ */
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/dma-buf.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "tve200_drm.h"
+
+irqreturn_t tve200_irq(int irq, void *data)
+{
+       struct tve200_drm_dev_private *priv = data;
+       u32 stat;
+       u32 val;
+
+       stat = readl(priv->regs + TVE200_INT_STAT);
+
+       if (!stat)
+               return IRQ_NONE;
+
+       /*
+        * Vblank IRQ
+        *
+        * The hardware is a bit tilted: the line stays high after clearing
+        * the vblank IRQ, firing many more interrupts. We counter this
+        * by toggling the IRQ back and forth from firing at vblank and
+        * firing at start of active image, which works around the problem
+        * since those occur strictly in sequence, and we get two IRQs for each
+        * frame, one at start of Vblank (that we make call into the CRTC) and
+        * another one at the start of the image (that we discard).
+        */
+       if (stat & TVE200_INT_V_STATUS) {
+               val = readl(priv->regs + TVE200_CTRL);
+               /* We have an actual start of vsync */
+               if (!(val & TVE200_VSTSTYPE_BITS)) {
+                       drm_crtc_handle_vblank(&priv->pipe.crtc);
+                       /* Toggle trigger to start of active image */
+                       val |= TVE200_VSTSTYPE_VAI;
+               } else {
+                       /* Toggle trigger back to start of vsync */
+                       val &= ~TVE200_VSTSTYPE_BITS;
+               }
+               writel(val, priv->regs + TVE200_CTRL);
+       } else
+               dev_err(priv->drm->dev, "stray IRQ %08x\n", stat);
+
+       /* Clear the interrupt once done */
+       writel(stat, priv->regs + TVE200_INT_CLR);
+
+       return IRQ_HANDLED;
+}
+
+static int tve200_display_check(struct drm_simple_display_pipe *pipe,
+                              struct drm_plane_state *pstate,
+                              struct drm_crtc_state *cstate)
+{
+       const struct drm_display_mode *mode = &cstate->mode;
+       struct drm_framebuffer *old_fb = pipe->plane.state->fb;
+       struct drm_framebuffer *fb = pstate->fb;
+
+       /*
+        * We support these specific resolutions and nothing else.
+        */
+       if (!(mode->hdisplay == 352 && mode->vdisplay == 240) && /* SIF(525) */
+           !(mode->hdisplay == 352 && mode->vdisplay == 288) && /* CIF(625) */
+           !(mode->hdisplay == 640 && mode->vdisplay == 480) && /* VGA */
+           !(mode->hdisplay == 720 && mode->vdisplay == 480) && /* D1 */
+           !(mode->hdisplay == 720 && mode->vdisplay == 576)) { /* D1 */
+               DRM_DEBUG_KMS("unsupported display mode (%u x %u)\n",
+                       mode->hdisplay, mode->vdisplay);
+               return -EINVAL;
+       }
+
+       if (fb) {
+               u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0);
+
+               /* FB base address must be dword aligned. */
+               if (offset & 3) {
+                       DRM_DEBUG_KMS("FB not 32-bit aligned\n");
+                       return -EINVAL;
+               }
+
+               /*
+                * There's no pitch register, the mode's hdisplay
+                * controls this.
+                */
+               if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) {
+                       DRM_DEBUG_KMS("can't handle pitches\n");
+                       return -EINVAL;
+               }
+
+               /*
+                * We can't change the FB format in a flicker-free
+                * manner (and only update it during CRTC enable).
+                */
+               if (old_fb && old_fb->format != fb->format)
+                       cstate->mode_changed = true;
+       }
+
+       return 0;
+}
+
+static void tve200_display_enable(struct drm_simple_display_pipe *pipe,
+                                struct drm_crtc_state *cstate)
+{
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_plane *plane = &pipe->plane;
+       struct drm_device *drm = crtc->dev;
+       struct tve200_drm_dev_private *priv = drm->dev_private;
+       const struct drm_display_mode *mode = &cstate->mode;
+       struct drm_framebuffer *fb = plane->state->fb;
+       struct drm_connector *connector = priv->connector;
+       u32 format = fb->format->format;
+       u32 ctrl1 = 0;
+
+       clk_prepare_enable(priv->clk);
+
+       /* Function 1 */
+       ctrl1 |= TVE200_CTRL_CSMODE;
+       /* Interlace mode for CCIR656: parameterize? */
+       ctrl1 |= TVE200_CTRL_NONINTERLACE;
+       /* 32 words per burst */
+       ctrl1 |= TVE200_CTRL_BURST_32_WORDS;
+       /* 16 retries */
+       ctrl1 |= TVE200_CTRL_RETRYCNT_16;
+       /* NTSC mode: parametrize? */
+       ctrl1 |= TVE200_CTRL_NTSC;
+
+       /* Vsync IRQ at start of Vsync at first */
+       ctrl1 |= TVE200_VSTSTYPE_VSYNC;
+
+       if (connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE)
+               ctrl1 |= TVE200_CTRL_TVCLKP;
+
+       if ((mode->hdisplay == 352 && mode->vdisplay == 240) || /* SIF(525) */
+           (mode->hdisplay == 352 && mode->vdisplay == 288)) { /* CIF(625) */
+               ctrl1 |= TVE200_CTRL_IPRESOL_CIF;
+               dev_info(drm->dev, "CIF mode\n");
+       } else if (mode->hdisplay == 640 && mode->vdisplay == 480) {
+               ctrl1 |= TVE200_CTRL_IPRESOL_VGA;
+               dev_info(drm->dev, "VGA mode\n");
+       } else if ((mode->hdisplay == 720 && mode->vdisplay == 480) ||
+                  (mode->hdisplay == 720 && mode->vdisplay == 576)) {
+               ctrl1 |= TVE200_CTRL_IPRESOL_D1;
+               dev_info(drm->dev, "D1 mode\n");
+       }
+
+       if (format & DRM_FORMAT_BIG_ENDIAN) {
+               ctrl1 |= TVE200_CTRL_BBBP;
+               format &= ~DRM_FORMAT_BIG_ENDIAN;
+       }
+
+       switch (format) {
+       case DRM_FORMAT_XRGB8888:
+               ctrl1 |= TVE200_IPDMOD_RGB888;
+               break;
+       case DRM_FORMAT_RGB565:
+               ctrl1 |= TVE200_IPDMOD_RGB565;
+               break;
+       case DRM_FORMAT_XRGB1555:
+               ctrl1 |= TVE200_IPDMOD_RGB555;
+               break;
+       case DRM_FORMAT_XBGR8888:
+               ctrl1 |= TVE200_IPDMOD_RGB888 | TVE200_BGR;
+               break;
+       case DRM_FORMAT_BGR565:
+               ctrl1 |= TVE200_IPDMOD_RGB565 | TVE200_BGR;
+               break;
+       case DRM_FORMAT_XBGR1555:
+               ctrl1 |= TVE200_IPDMOD_RGB555 | TVE200_BGR;
+               break;
+       case DRM_FORMAT_YUYV:
+               ctrl1 |= TVE200_IPDMOD_YUV422;
+               ctrl1 |= TVE200_CTRL_YCBCRODR_CR0Y1CB0Y0;
+               break;
+       case DRM_FORMAT_YVYU:
+               ctrl1 |= TVE200_IPDMOD_YUV422;
+               ctrl1 |= TVE200_CTRL_YCBCRODR_CB0Y1CR0Y0;
+               break;
+       case DRM_FORMAT_UYVY:
+               ctrl1 |= TVE200_IPDMOD_YUV422;
+               ctrl1 |= TVE200_CTRL_YCBCRODR_Y1CR0Y0CB0;
+               break;
+       case DRM_FORMAT_VYUY:
+               ctrl1 |= TVE200_IPDMOD_YUV422;
+               ctrl1 |= TVE200_CTRL_YCBCRODR_Y1CB0Y0CR0;
+               break;
+       case DRM_FORMAT_YUV420:
+               ctrl1 |= TVE200_CTRL_YUV420;
+               ctrl1 |= TVE200_IPDMOD_YUV420;
+               break;
+       default:
+               dev_err(drm->dev, "Unknown FB format 0x%08x\n",
+                       fb->format->format);
+               break;
+       }
+
+       ctrl1 |= TVE200_TVEEN;
+
+       /* Turn it on */
+       writel(ctrl1, priv->regs + TVE200_CTRL);
+
+       drm_crtc_vblank_on(crtc);
+}
+
+void tve200_display_disable(struct drm_simple_display_pipe *pipe)
+{
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_device *drm = crtc->dev;
+       struct tve200_drm_dev_private *priv = drm->dev_private;
+
+       drm_crtc_vblank_off(crtc);
+
+       /* Disable and Power Down */
+       writel(0, priv->regs + TVE200_CTRL);
+
+       clk_disable_unprepare(priv->clk);
+}
+
+static void tve200_display_update(struct drm_simple_display_pipe *pipe,
+                                struct drm_plane_state *old_pstate)
+{
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_device *drm = crtc->dev;
+       struct tve200_drm_dev_private *priv = drm->dev_private;
+       struct drm_pending_vblank_event *event = crtc->state->event;
+       struct drm_plane *plane = &pipe->plane;
+       struct drm_plane_state *pstate = plane->state;
+       struct drm_framebuffer *fb = pstate->fb;
+
+       if (fb) {
+               /* For RGB, the Y component is used as base address */
+               writel(drm_fb_cma_get_gem_addr(fb, pstate, 0),
+                      priv->regs + TVE200_Y_FRAME_BASE_ADDR);
+
+               /* For three plane YUV we need two more addresses */
+               if (fb->format->format == DRM_FORMAT_YUV420) {
+                       writel(drm_fb_cma_get_gem_addr(fb, pstate, 1),
+                              priv->regs + TVE200_U_FRAME_BASE_ADDR);
+                       writel(drm_fb_cma_get_gem_addr(fb, pstate, 2),
+                              priv->regs + TVE200_V_FRAME_BASE_ADDR);
+               }
+       }
+
+       if (event) {
+               crtc->state->event = NULL;
+
+               spin_lock_irq(&crtc->dev->event_lock);
+               if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
+                       drm_crtc_arm_vblank_event(crtc, event);
+               else
+                       drm_crtc_send_vblank_event(crtc, event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+       }
+}
+
+int tve200_enable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+       struct tve200_drm_dev_private *priv = drm->dev_private;
+
+       writel(TVE200_INT_V_STATUS, priv->regs + TVE200_INT_EN);
+       return 0;
+}
+
+void tve200_disable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+       struct tve200_drm_dev_private *priv = drm->dev_private;
+
+       writel(0, priv->regs + TVE200_INT_EN);
+}
+
+static int tve200_display_prepare_fb(struct drm_simple_display_pipe *pipe,
+                                   struct drm_plane_state *plane_state)
+{
+       return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
+}
+
+const struct drm_simple_display_pipe_funcs tve200_display_funcs = {
+       .check = tve200_display_check,
+       .enable = tve200_display_enable,
+       .disable = tve200_display_disable,
+       .update = tve200_display_update,
+       .prepare_fb = tve200_display_prepare_fb,
+};
+
+int tve200_display_init(struct drm_device *drm)
+{
+       struct tve200_drm_dev_private *priv = drm->dev_private;
+       int ret;
+       static const u32 formats[] = {
+               DRM_FORMAT_XRGB8888,
+               DRM_FORMAT_XBGR8888,
+               DRM_FORMAT_RGB565,
+               DRM_FORMAT_BGR565,
+               DRM_FORMAT_XRGB1555,
+               DRM_FORMAT_XBGR1555,
+               /*
+                * The controller actually supports any YCbCr ordering,
+                * for packed YCbCr. This just lists the orderings that
+                * DRM supports.
+                */
+               DRM_FORMAT_YUYV,
+               DRM_FORMAT_YVYU,
+               DRM_FORMAT_UYVY,
+               DRM_FORMAT_VYUY,
+               /* This uses three planes */
+               DRM_FORMAT_YUV420,
+       };
+
+       ret = drm_simple_display_pipe_init(drm, &priv->pipe,
+                                          &tve200_display_funcs,
+                                          formats, ARRAY_SIZE(formats),
+                                          NULL,
+                                          priv->connector);
+       if (ret)
+               return ret;
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/tve200/tve200_drm.h b/drivers/gpu/drm/tve200/tve200_drm.h
new file mode 100644 (file)
index 0000000..628b793
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2006-2008 Intel Corporation
+ * Copyright (C) 2007 Amos Lee <amos_lee@storlinksemi.com>
+ * Copyright (C) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ * Copyright (C) 2017 Eric Anholt
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ */
+
+#ifndef _TVE200_DRM_H_
+#define _TVE200_DRM_H_
+
+/* Bits 2-31 are valid physical base addresses */
+#define TVE200_Y_FRAME_BASE_ADDR       0x00
+#define TVE200_U_FRAME_BASE_ADDR       0x04
+#define TVE200_V_FRAME_BASE_ADDR       0x08
+
+#define TVE200_INT_EN                  0x0C
+#define TVE200_INT_CLR                 0x10
+#define TVE200_INT_STAT                        0x14
+#define TVE200_INT_BUS_ERR             BIT(7)
+#define TVE200_INT_V_STATUS            BIT(6) /* vertical blank */
+#define TVE200_INT_V_NEXT_FRAME                BIT(5)
+#define TVE200_INT_U_NEXT_FRAME                BIT(4)
+#define TVE200_INT_Y_NEXT_FRAME                BIT(3)
+#define TVE200_INT_V_FIFO_UNDERRUN     BIT(2)
+#define TVE200_INT_U_FIFO_UNDERRUN     BIT(1)
+#define TVE200_INT_Y_FIFO_UNDERRUN     BIT(0)
+#define TVE200_FIFO_UNDERRUNS          (TVE200_INT_V_FIFO_UNDERRUN | \
+                                        TVE200_INT_U_FIFO_UNDERRUN | \
+                                        TVE200_INT_Y_FIFO_UNDERRUN)
+
+#define TVE200_CTRL                    0x18
+#define TVE200_CTRL_YUV420             BIT(31)
+#define TVE200_CTRL_CSMODE             BIT(30)
+#define TVE200_CTRL_NONINTERLACE       BIT(28) /* 0 = non-interlace CCIR656 */
+#define TVE200_CTRL_TVCLKP             BIT(27) /* Inverted clock phase */
+/* Bits 24..26 define the burst size after arbitration on the bus */
+#define TVE200_CTRL_BURST_4_WORDS      (0 << 24)
+#define TVE200_CTRL_BURST_8_WORDS      (1 << 24)
+#define TVE200_CTRL_BURST_16_WORDS     (2 << 24)
+#define TVE200_CTRL_BURST_32_WORDS     (3 << 24)
+#define TVE200_CTRL_BURST_64_WORDS     (4 << 24)
+#define TVE200_CTRL_BURST_128_WORDS    (5 << 24)
+#define TVE200_CTRL_BURST_256_WORDS    (6 << 24)
+#define TVE200_CTRL_BURST_0_WORDS      (7 << 24) /* ? */
+/*
+ * Bits 16..23 is the retry count*16 before issueing a new AHB transfer
+ * on the AHB bus.
+ */
+#define TVE200_CTRL_RETRYCNT_MASK      GENMASK(23, 16)
+#define TVE200_CTRL_RETRYCNT_16                (1 << 16)
+#define TVE200_CTRL_BBBP               BIT(15) /* 0 = little-endian */
+/* Bits 12..14 define the YCbCr ordering */
+#define TVE200_CTRL_YCBCRODR_CB0Y0CR0Y1        (0 << 12)
+#define TVE200_CTRL_YCBCRODR_Y0CB0Y1CR0        (1 << 12)
+#define TVE200_CTRL_YCBCRODR_CR0Y0CB0Y1        (2 << 12)
+#define TVE200_CTRL_YCBCRODR_Y1CB0Y0CR0        (3 << 12)
+#define TVE200_CTRL_YCBCRODR_CR0Y1CB0Y0        (4 << 12)
+#define TVE200_CTRL_YCBCRODR_Y1CR0Y0CB0        (5 << 12)
+#define TVE200_CTRL_YCBCRODR_CB0Y1CR0Y0        (6 << 12)
+#define TVE200_CTRL_YCBCRODR_Y0CR0Y1CB0        (7 << 12)
+/* Bits 10..11 define the input resolution (framebuffer size) */
+#define TVE200_CTRL_IPRESOL_CIF                (0 << 10)
+#define TVE200_CTRL_IPRESOL_VGA                (1 << 10)
+#define TVE200_CTRL_IPRESOL_D1         (2 << 10)
+#define TVE200_CTRL_NTSC               BIT(9) /* 0 = PAL, 1 = NTSC */
+#define TVE200_CTRL_INTERLACE          BIT(8) /* 1 = interlace, only for D1 */
+#define TVE200_IPDMOD_RGB555           (0 << 6) /* TVE200_CTRL_YUV420 = 0 */
+#define TVE200_IPDMOD_RGB565           (1 << 6)
+#define TVE200_IPDMOD_RGB888           (2 << 6)
+#define TVE200_IPDMOD_YUV420           (2 << 6) /* TVE200_CTRL_YUV420 = 1 */
+#define TVE200_IPDMOD_YUV422           (3 << 6)
+/* Bits 4 & 5 define when to fire the vblank IRQ */
+#define TVE200_VSTSTYPE_VSYNC          (0 << 4) /* start of vsync */
+#define TVE200_VSTSTYPE_VBP            (1 << 4) /* start of v back porch */
+#define TVE200_VSTSTYPE_VAI            (2 << 4) /* start of v active image */
+#define TVE200_VSTSTYPE_VFP            (3 << 4) /* start of v front porch */
+#define TVE200_VSTSTYPE_BITS           (BIT(4) | BIT(5))
+#define TVE200_BGR                     BIT(1) /* 0 = RGB, 1 = BGR */
+#define TVE200_TVEEN                   BIT(0) /* Enable TVE block */
+
+#define TVE200_CTRL_2                  0x1c
+#define TVE200_CTRL_3                  0x20
+
+#define TVE200_CTRL_4                  0x24
+#define TVE200_CTRL_4_RESET            BIT(0) /* triggers reset of TVE200 */
+
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+
+struct tve200_drm_dev_private {
+       struct drm_device *drm;
+
+       struct drm_connector *connector;
+       struct drm_panel *panel;
+       struct drm_bridge *bridge;
+       struct drm_simple_display_pipe pipe;
+       struct drm_fbdev_cma *fbdev;
+
+       void *regs;
+       struct clk *pclk;
+       struct clk *clk;
+};
+
+#define to_tve200_connector(x) \
+       container_of(x, struct tve200_drm_connector, connector)
+
+int tve200_display_init(struct drm_device *dev);
+int tve200_enable_vblank(struct drm_device *drm, unsigned int crtc);
+void tve200_disable_vblank(struct drm_device *drm, unsigned int crtc);
+irqreturn_t tve200_irq(int irq, void *data);
+int tve200_connector_init(struct drm_device *dev);
+int tve200_encoder_init(struct drm_device *dev);
+int tve200_dumb_create(struct drm_file *file_priv,
+                     struct drm_device *dev,
+                     struct drm_mode_create_dumb *args);
+
+#endif /* _TVE200_DRM_H_ */
diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c
new file mode 100644 (file)
index 0000000..eae38b6
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2006-2008 Intel Corporation
+ * Copyright (C) 2007 Amos Lee <amos_lee@storlinksemi.com>
+ * Copyright (C) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ * Copyright (C) 2017 Eric Anholt
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ */
+
+/**
+ * DOC: Faraday TV Encoder TVE200 DRM Driver
+ *
+ * The Faraday TV Encoder TVE200 is also known as the Gemini TV Interface
+ * Controller (TVC) and is found in the Gemini Chipset from Storlink
+ * Semiconductor (later Storm Semiconductor, later Cortina Systems)
+ * but also in the Grain Media GM8180 chipset. On the Gemini the module
+ * is connected to 8 data lines and a single clock line, comprising an
+ * 8-bit BT.656 interface.
+ *
+ * This is a very basic YUV display driver. The datasheet specifies that
+ * it supports the ITU BT.656 standard. It requires a 27 MHz clock which is
+ * the hallmark of any TV encoder supporting both PAL and NTSC.
+ *
+ * This driver exposes a standard KMS interface for this TV encoder.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-buf.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/shmem_fs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_of.h>
+#include <drm/drm_bridge.h>
+
+#include "tve200_drm.h"
+
+#define DRIVER_DESC      "DRM module for Faraday TVE200"
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+       .fb_create = drm_fb_cma_create,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static int tve200_modeset_init(struct drm_device *dev)
+{
+       struct drm_mode_config *mode_config;
+       struct tve200_drm_dev_private *priv = dev->dev_private;
+       struct drm_panel *panel;
+       struct drm_bridge *bridge;
+       int ret = 0;
+
+       drm_mode_config_init(dev);
+       mode_config = &dev->mode_config;
+       mode_config->funcs = &mode_config_funcs;
+       mode_config->min_width = 352;
+       mode_config->max_width = 720;
+       mode_config->min_height = 240;
+       mode_config->max_height = 576;
+
+       ret = drm_of_find_panel_or_bridge(dev->dev->of_node,
+                                         0, 0, &panel, &bridge);
+       if (ret && ret != -ENODEV)
+               return ret;
+       if (panel) {
+               bridge = drm_panel_bridge_add(panel,
+                                             DRM_MODE_CONNECTOR_Unknown);
+               if (IS_ERR(bridge)) {
+                       ret = PTR_ERR(bridge);
+                       goto out_bridge;
+               }
+       } else {
+               /*
+                * TODO: when we are using a different bridge than a panel
+                * (such as a dumb VGA connector) we need to devise a different
+                * method to get the connector out of the bridge.
+                */
+               dev_err(dev->dev, "the bridge is not a panel\n");
+               goto out_bridge;
+       }
+
+       ret = tve200_display_init(dev);
+       if (ret) {
+               dev_err(dev->dev, "failed to init display\n");
+               goto out_bridge;
+       }
+
+       ret = drm_simple_display_pipe_attach_bridge(&priv->pipe,
+                                                   bridge);
+       if (ret) {
+               dev_err(dev->dev, "failed to attach bridge\n");
+               goto out_bridge;
+       }
+
+       priv->panel = panel;
+       priv->connector = panel->connector;
+       priv->bridge = bridge;
+
+       dev_info(dev->dev, "attached to panel %s\n",
+                dev_name(panel->dev));
+
+       ret = drm_vblank_init(dev, 1);
+       if (ret) {
+               dev_err(dev->dev, "failed to init vblank\n");
+               goto out_bridge;
+       }
+
+       drm_mode_config_reset(dev);
+
+       /*
+        * Passing in 16 here will make the RGB656 mode the default
+        * Passing in 32 will use XRGB8888 mode
+        */
+       priv->fbdev = drm_fbdev_cma_init(dev, 16,
+                                        dev->mode_config.num_connector);
+       drm_kms_helper_poll_init(dev);
+
+       goto finish;
+
+out_bridge:
+       if (panel)
+               drm_panel_bridge_remove(bridge);
+       drm_mode_config_cleanup(dev);
+finish:
+       return ret;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drm_fops);
+
+static void tve200_lastclose(struct drm_device *dev)
+{
+       struct tve200_drm_dev_private *priv = dev->dev_private;
+
+       drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static struct drm_driver tve200_drm_driver = {
+       .driver_features =
+               DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
+       .lastclose = tve200_lastclose,
+       .ioctls = NULL,
+       .fops = &drm_fops,
+       .name = "tve200",
+       .desc = DRIVER_DESC,
+       .date = "20170703",
+       .major = 1,
+       .minor = 0,
+       .patchlevel = 0,
+       .dumb_create = drm_gem_cma_dumb_create,
+       .gem_free_object_unlocked = drm_gem_cma_free_object,
+       .gem_vm_ops = &drm_gem_cma_vm_ops,
+
+       .enable_vblank = tve200_enable_vblank,
+       .disable_vblank = tve200_disable_vblank,
+
+       .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_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_vunmap = drm_gem_cma_prime_vunmap,
+       .gem_prime_mmap = drm_gem_cma_prime_mmap,
+};
+
+static int tve200_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct tve200_drm_dev_private *priv;
+       struct drm_device *drm;
+       struct resource *res;
+       int irq;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       drm = drm_dev_alloc(&tve200_drm_driver, dev);
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
+       platform_set_drvdata(pdev, drm);
+       priv->drm = drm;
+       drm->dev_private = priv;
+
+       /* Clock the silicon so we can access the registers */
+       priv->pclk = devm_clk_get(dev, "PCLK");
+       if (IS_ERR(priv->pclk)) {
+               dev_err(dev, "unable to get PCLK\n");
+               ret = PTR_ERR(priv->pclk);
+               goto dev_unref;
+       }
+       ret = clk_prepare_enable(priv->pclk);
+       if (ret) {
+               dev_err(dev, "failed to enable PCLK\n");
+               goto dev_unref;
+       }
+
+       /* This clock is for the pixels (27MHz) */
+       priv->clk = devm_clk_get(dev, "TVE");
+       if (IS_ERR(priv->clk)) {
+               dev_err(dev, "unable to get TVE clock\n");
+               ret = PTR_ERR(priv->clk);
+               goto clk_disable;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->regs = devm_ioremap_resource(dev, res);
+       if (!priv->regs) {
+               dev_err(dev, "%s failed mmio\n", __func__);
+               ret = -EINVAL;
+               goto clk_disable;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (!irq) {
+               ret = -EINVAL;
+               goto clk_disable;
+       }
+
+       /* turn off interrupts before requesting the irq */
+       writel(0, priv->regs + TVE200_INT_EN);
+
+       ret = devm_request_irq(dev, irq, tve200_irq, 0, "tve200", priv);
+       if (ret) {
+               dev_err(dev, "failed to request irq %d\n", ret);
+               goto clk_disable;
+       }
+
+       ret = tve200_modeset_init(drm);
+       if (ret)
+               goto clk_disable;
+
+       ret = drm_dev_register(drm, 0);
+       if (ret < 0)
+               goto clk_disable;
+
+       return 0;
+
+clk_disable:
+       clk_disable_unprepare(priv->pclk);
+dev_unref:
+       drm_dev_unref(drm);
+       return ret;
+}
+
+static int tve200_remove(struct platform_device *pdev)
+{
+       struct drm_device *drm = platform_get_drvdata(pdev);
+       struct tve200_drm_dev_private *priv = drm->dev_private;
+
+       drm_dev_unregister(drm);
+       if (priv->fbdev)
+               drm_fbdev_cma_fini(priv->fbdev);
+       if (priv->panel)
+               drm_panel_bridge_remove(priv->bridge);
+       drm_mode_config_cleanup(drm);
+       clk_disable_unprepare(priv->pclk);
+       drm_dev_unref(drm);
+
+       return 0;
+}
+
+static const struct of_device_id tve200_of_match[] = {
+       {
+               .compatible = "faraday,tve200",
+       },
+       {},
+};
+
+static struct platform_driver tve200_driver = {
+       .driver = {
+               .name           = "tve200",
+               .of_match_table = of_match_ptr(tve200_of_match),
+       },
+       .probe = tve200_probe,
+       .remove = tve200_remove,
+};
+module_platform_driver(tve200_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_LICENSE("GPL");
index 25bd5d30415df55e4715334a597ca1e7b3012bd7..719a771f3d5c615398012b3b188387693cb9f031 100644 (file)
@@ -24,5 +24,3 @@ vc4-y := \
 vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
 
 obj-$(CONFIG_DRM_VC4)  += vc4.o
-
-CFLAGS_vc4_trace_points.o := -I$(src)
index 937da8dd65b8b02168aeee602225c8a3e3fa171e..fa37a1c07cf695900b0cb5b681f727169af51a62 100644 (file)
@@ -309,16 +309,13 @@ static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs =
 static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
                                                     struct drm_encoder *encoder)
 {
-       struct drm_connector *connector = NULL;
+       struct drm_connector *connector;
        struct vc4_hdmi_connector *hdmi_connector;
-       int ret = 0;
 
        hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector),
                                      GFP_KERNEL);
-       if (!hdmi_connector) {
-               ret = -ENOMEM;
-               goto fail;
-       }
+       if (!hdmi_connector)
+               return ERR_PTR(-ENOMEM);
        connector = &hdmi_connector->base;
 
        hdmi_connector->encoder = encoder;
@@ -336,12 +333,6 @@ static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
        drm_mode_connector_attach_encoder(connector, encoder);
 
        return connector;
-
- fail:
-       if (connector)
-               vc4_hdmi_connector_destroy(connector);
-
-       return ERR_PTR(ret);
 }
 
 static void vc4_hdmi_encoder_destroy(struct drm_encoder *encoder)
index ad7b1ea720c288eb587d16e5f8e10eace5de93f3..deafb32923e147f4bb9ad93ce441a6aecf32188c 100644 (file)
@@ -59,5 +59,5 @@ TRACE_EVENT(vc4_wait_for_seqno_end,
 
 /* This part must be outside protection */
 #undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/vc4
 #include <trace/define_trace.h>
index 45244828fc1f29fce54009045fd34f377ba71a3d..e8b8266c0cde6fa66ebe15423071630dd41ed1a0 100644 (file)
@@ -22,6 +22,7 @@
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drmP.h>
 
@@ -40,7 +41,7 @@ static void zx_drm_fb_output_poll_changed(struct drm_device *drm)
 }
 
 static const struct drm_mode_config_funcs zx_drm_mode_config_funcs = {
-       .fb_create = drm_fb_cma_create,
+       .fb_create = drm_gem_fb_create,
        .output_poll_changed = zx_drm_fb_output_poll_changed,
        .atomic_check = drm_atomic_helper_check,
        .atomic_commit = drm_atomic_helper_commit,
index 8a5808eb562876cd35f2d3f098db5c9156757f3b..5834580d75bca03689ff327fcbcdf611fce839c3 100644 (file)
@@ -144,7 +144,6 @@ struct __drm_planes_state {
 struct __drm_crtcs_state {
        struct drm_crtc *ptr;
        struct drm_crtc_state *state, *old_state, *new_state;
-       struct drm_crtc_commit *commit;
        s32 __user *out_fence_ptr;
        unsigned last_vblank_count;
 };
@@ -236,6 +235,18 @@ struct drm_atomic_state {
 
        struct drm_modeset_acquire_ctx *acquire_ctx;
 
+       /**
+        * @fake_commit:
+        *
+        * Used for signaling unbound planes/connectors.
+        * When a connector or plane is not bound to any CRTC, it's still important
+        * to preserve linearity to prevent the atomic states from being freed to early.
+        *
+        * This commit (if set) is not bound to any crtc, but will be completed when
+        * drm_atomic_helper_commit_hw_done() is called.
+        */
+       struct drm_crtc_commit *fake_commit;
+
        /**
         * @commit_work:
         *
@@ -252,10 +263,14 @@ void __drm_crtc_commit_free(struct kref *kref);
  * @commit: CRTC commit
  *
  * Increases the reference of @commit.
+ *
+ * Returns:
+ * The pointer to @commit, with reference increased.
  */
-static inline void drm_crtc_commit_get(struct drm_crtc_commit *commit)
+static inline struct drm_crtc_commit *drm_crtc_commit_get(struct drm_crtc_commit *commit)
 {
        kref_get(&commit->ref);
+       return commit;
 }
 
 /**
@@ -554,31 +569,6 @@ int __must_check drm_atomic_nonblocking_commit(struct drm_atomic_state *state);
 
 void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
 
-/**
- * for_each_connector_in_state - iterate over all connectors in an atomic update
- * @__state: &struct drm_atomic_state pointer
- * @connector: &struct drm_connector iteration cursor
- * @connector_state: &struct drm_connector_state iteration cursor
- * @__i: int iteration cursor, for macro-internal use
- *
- * This iterates over all connectors in an atomic update. Note that before the
- * software state is committed (by calling drm_atomic_helper_swap_state(), this
- * points to the new state, while afterwards it points to the old state. Due to
- * this tricky confusion this macro is deprecated.
- *
- * FIXME:
- *
- * Replace all usage of this with one of the explicit iterators below and then
- * remove this macro.
- */
-#define for_each_connector_in_state(__state, connector, connector_state, __i) \
-       for ((__i) = 0;                                                 \
-            (__i) < (__state)->num_connector &&                                \
-            ((connector) = (__state)->connectors[__i].ptr,                     \
-            (connector_state) = (__state)->connectors[__i].state, 1);  \
-            (__i)++)                                                   \
-               for_each_if (connector)
-
 /**
  * for_each_oldnew_connector_in_state - iterate over all connectors in an atomic update
  * @__state: &struct drm_atomic_state pointer
@@ -642,31 +632,6 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
             (__i)++)                                                   \
                for_each_if (connector)
 
-/**
- * for_each_crtc_in_state - iterate over all connectors in an atomic update
- * @__state: &struct drm_atomic_state pointer
- * @crtc: &struct drm_crtc iteration cursor
- * @crtc_state: &struct drm_crtc_state iteration cursor
- * @__i: int iteration cursor, for macro-internal use
- *
- * This iterates over all CRTCs in an atomic update. Note that before the
- * software state is committed (by calling drm_atomic_helper_swap_state(), this
- * points to the new state, while afterwards it points to the old state. Due to
- * this tricky confusion this macro is deprecated.
- *
- * FIXME:
- *
- * Replace all usage of this with one of the explicit iterators below and then
- * remove this macro.
- */
-#define for_each_crtc_in_state(__state, crtc, crtc_state, __i) \
-       for ((__i) = 0;                                         \
-            (__i) < (__state)->dev->mode_config.num_crtc &&    \
-            ((crtc) = (__state)->crtcs[__i].ptr,                       \
-            (crtc_state) = (__state)->crtcs[__i].state, 1);    \
-            (__i)++)                                           \
-               for_each_if (crtc_state)
-
 /**
  * for_each_oldnew_crtc_in_state - iterate over all CRTCs in an atomic update
  * @__state: &struct drm_atomic_state pointer
@@ -726,31 +691,6 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
             (__i)++)                                                   \
                for_each_if (crtc)
 
-/**
- * for_each_plane_in_state - iterate over all planes in an atomic update
- * @__state: &struct drm_atomic_state pointer
- * @plane: &struct drm_plane iteration cursor
- * @plane_state: &struct drm_plane_state iteration cursor
- * @__i: int iteration cursor, for macro-internal use
- *
- * This iterates over all planes in an atomic update. Note that before the
- * software state is committed (by calling drm_atomic_helper_swap_state(), this
- * points to the new state, while afterwards it points to the old state. Due to
- * this tricky confusion this macro is deprecated.
- *
- * FIXME:
- *
- * Replace all usage of this with one of the explicit iterators below and then
- * remove this macro.
- */
-#define for_each_plane_in_state(__state, plane, plane_state, __i)              \
-       for ((__i) = 0;                                                 \
-            (__i) < (__state)->dev->mode_config.num_total_plane &&     \
-            ((plane) = (__state)->planes[__i].ptr,                             \
-            (plane_state) = (__state)->planes[__i].state, 1);          \
-            (__i)++)                                                   \
-               for_each_if (plane_state)
-
 /**
  * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update
  * @__state: &struct drm_atomic_state pointer
index 6522d4cbc9d9d7426728198024ce44aa622f2b32..682d01ba920c0fd262087fc2fe164b1679a603e3 100644 (file)
@@ -245,7 +245,7 @@ struct drm_bridge {
        void *driver_private;
 };
 
-int drm_bridge_add(struct drm_bridge *bridge);
+void drm_bridge_add(struct drm_bridge *bridge);
 void drm_bridge_remove(struct drm_bridge *bridge);
 struct drm_bridge *of_drm_find_bridge(struct device_node *np);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
index ea8da401c93c14248374ffaabb1675c2e1bf45d4..b34904dc8b9beb5ca068b6ebd63ae4cb03a97665 100644 (file)
@@ -347,6 +347,13 @@ struct drm_connector_state {
 
        struct drm_atomic_state *state;
 
+       /**
+        * @commit: Tracks the pending commit to prevent use-after-free conditions.
+        *
+        * Is only set when @crtc is NULL.
+        */
+       struct drm_crtc_commit *commit;
+
        struct drm_tv_connector_state tv;
 
        /**
@@ -888,8 +895,7 @@ struct drm_connector {
         * This is protected by @drm_mode_config.connection_mutex. Note that
         * nonblocking atomic commits access the current connector state without
         * taking locks. Either by going through the &struct drm_atomic_state
-        * pointers, see for_each_connector_in_state(),
-        * for_each_oldnew_connector_in_state(),
+        * pointers, see for_each_oldnew_connector_in_state(),
         * for_each_old_connector_in_state() and
         * for_each_new_connector_in_state(). Or through careful ordering of
         * atomic commit operations as implemented in the atomic helpers, see
index 1a642020e306778e55ee65e8a535c0746eb07372..80c97210eda55c040617378e3937ab4fa6522099 100644 (file)
@@ -253,6 +253,15 @@ struct drm_crtc_state {
         */
        struct drm_pending_vblank_event *event;
 
+       /**
+        * @commit:
+        *
+        * This tracks how the commit for this update proceeds through the
+        * various phases. This is never cleared, except when we destroy the
+        * state, so that subsequent commits can synchronize with previous ones.
+        */
+       struct drm_crtc_commit *commit;
+
        struct drm_atomic_state *state;
 };
 
@@ -797,10 +806,10 @@ struct drm_crtc {
         * This is protected by @mutex. Note that nonblocking atomic commits
         * access the current CRTC state without taking locks. Either by going
         * through the &struct drm_atomic_state pointers, see
-        * for_each_crtc_in_state(), for_each_oldnew_crtc_in_state(),
-        * for_each_old_crtc_in_state() and for_each_new_crtc_in_state(). Or
-        * through careful ordering of atomic commit operations as implemented
-        * in the atomic helpers, see &struct drm_crtc_commit.
+        * for_each_oldnew_crtc_in_state(), for_each_old_crtc_in_state() and
+        * for_each_new_crtc_in_state(). Or through careful ordering of atomic
+        * commit operations as implemented in the atomic helpers, see
+        * &struct drm_crtc_commit.
         */
        struct drm_crtc_state *state;
 
@@ -808,10 +817,16 @@ struct drm_crtc {
         * @commit_list:
         *
         * List of &drm_crtc_commit structures tracking pending commits.
-        * Protected by @commit_lock. This list doesn't hold its own full
-        * reference, but burrows it from the ongoing commit. Commit entries
-        * must be removed from this list once the commit is fully completed,
-        * but before it's correspoding &drm_atomic_state gets destroyed.
+        * Protected by @commit_lock. This list holds its own full reference,
+        * as does the ongoing commit.
+        *
+        * "Note that the commit for a state change is also tracked in
+        * &drm_crtc_state.commit. For accessing the immediately preceding
+        * commit in an atomic update it is recommended to just use that
+        * pointer in the old CRTC state, since accessing that doesn't need
+        * any locking or list-walking. @commit_list should only be used to
+        * stall for framebuffer cleanup that's signalled through
+        * &drm_crtc_commit.cleanup_done."
         */
        struct list_head commit_list;
 
index b17476a6909c0b7a1cfeb8ae058ddf21a2e49bfe..11c39f15f1b32b2eb61a21fda7573008d4d54d5a 100644 (file)
 #define DP_RECEIVER_ALPM_STATUS                    0x200b  /* eDP 1.4 */
 # define DP_ALPM_LOCK_TIMEOUT_ERROR        (1 << 0)
 
+#define DP_LANE0_1_STATUS_ESI                  0x200c /* status same as 0x202 */
+#define DP_LANE2_3_STATUS_ESI                  0x200d /* status same as 0x203 */
+#define DP_LANE_ALIGN_STATUS_UPDATED_ESI       0x200e /* status same as 0x204 */
+#define DP_SINK_STATUS_ESI                     0x200f /* status same as 0x205 */
+
 #define DP_DPRX_FEATURE_ENUMERATION_LIST    0x2210  /* DP 1.3 */
 # define DP_GTC_CAP                                    (1 << 0)  /* DP 1.3 */
 # define DP_SST_SPLIT_SDP_CAP                          (1 << 1)  /* DP 1.4 */
@@ -871,6 +876,18 @@ void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
 u8 drm_dp_link_rate_to_bw_code(int link_rate);
 int drm_dp_bw_code_to_link_rate(u8 link_bw);
 
+#define DP_SDP_AUDIO_TIMESTAMP         0x01
+#define DP_SDP_AUDIO_STREAM            0x02
+#define DP_SDP_EXTENSION               0x04 /* DP 1.1 */
+#define DP_SDP_AUDIO_COPYMANAGEMENT    0x05 /* DP 1.2 */
+#define DP_SDP_ISRC                    0x06 /* DP 1.2 */
+#define DP_SDP_VSC                     0x07 /* DP 1.2 */
+#define DP_SDP_CAMERA_GENERIC(i)       (0x08 + (i)) /* 0-7, DP 1.3 */
+#define DP_SDP_PPS                     0x10 /* DP 1.4 */
+#define DP_SDP_VSC_EXT_VESA            0x20 /* DP 1.4 */
+#define DP_SDP_VSC_EXT_CEA             0x21 /* DP 1.4 */
+/* 0x80+ CEA-861 infoframe types */
+
 struct edp_sdp_header {
        u8 HB0; /* Secondary Data Packet ID */
        u8 HB1; /* Secondary Data Packet Type */
index d55abb75f29ae1f998a8cc3f0124bacfceb10170..7f78d26a076694ce5dcd42fe99da555fe092daa1 100644 (file)
@@ -631,5 +631,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
 int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
                                     struct drm_dp_mst_topology_mgr *mgr,
                                     int slots);
+int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_dp_mst_port *port, bool power_up);
 
 #endif
index 1e1908a6b1d66eddede740c60ccc5084e2f49deb..6f35909b8add3221fe8ad4b4c108e5a1498c7f08 100644 (file)
@@ -341,6 +341,8 @@ int drm_av_sync_delay(struct drm_connector *connector,
 
 #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
 struct edid *drm_load_edid_firmware(struct drm_connector *connector);
+int __drm_set_edid_firmware_path(const char *path);
+int __drm_get_edid_firmware_path(char *buf, size_t bufsize);
 #else
 static inline struct edid *
 drm_load_edid_firmware(struct drm_connector *connector)
index db9cfa07235edaaba88f610cfd93bfd03b33b7ae..5ca7cdc3f5276cb9ab85e13e6151ea808664bb0c 100644 (file)
@@ -2,8 +2,8 @@
 #define __DRM_GEM_FB_HELPER_H__
 
 struct drm_device;
-struct drm_file;
 struct drm_fb_helper_surface_size;
+struct drm_file;
 struct drm_framebuffer;
 struct drm_framebuffer_funcs;
 struct drm_gem_object;
index c55cf3ff68477b8b6e0009a8c54e5b33238df9d5..16646c44b7df715c93a1e59cbc3aacc70c25d9c9 100644 (file)
@@ -314,7 +314,7 @@ struct drm_crtc_helper_funcs {
         * implementation in drm_atomic_helper_check().
         *
         * When using drm_atomic_helper_check_planes() this hook is called
-        * after the &drm_plane_helper_funcs.atomc_check hook for planes, which
+        * after the &drm_plane_helper_funcs.atomic_check hook for planes, which
         * allows drivers to assign shared resources requested by planes in this
         * callback here. For more complicated dependencies the driver can call
         * the provided check helpers multiple times until the computed state
index 4b27c2bb955c4687c9f43bad8d8cfa5b608f7189..a685d1bb21f262a256e3b5f6115d915e9d1d04e2 100644 (file)
@@ -34,6 +34,7 @@ struct drm_modeset_lock;
  * @contended: used internally for -EDEADLK handling
  * @locked: list of held locks
  * @trylock_only: trylock mode used in atomic contexts/panic notifiers
+ * @interruptible: whether interruptible locking should be used.
  *
  * Each thread competing for a set of locks must use one acquire
  * ctx.  And if any lock fxn returns -EDEADLK, it must backoff and
@@ -59,6 +60,9 @@ struct drm_modeset_acquire_ctx {
         * Trylock mode, use only for panic handlers!
         */
        bool trylock_only;
+
+       /* Perform interruptible waits on this context. */
+       bool interruptible;
 };
 
 /**
@@ -82,12 +86,13 @@ struct drm_modeset_lock {
        struct list_head head;
 };
 
+#define DRM_MODESET_ACQUIRE_INTERRUPTIBLE BIT(0)
+
 void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
                uint32_t flags);
 void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
 void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
-void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
-int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx);
+int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
 
 void drm_modeset_lock_init(struct drm_modeset_lock *lock);
 
@@ -111,8 +116,7 @@ static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock)
 
 int drm_modeset_lock(struct drm_modeset_lock *lock,
                struct drm_modeset_acquire_ctx *ctx);
-int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
-               struct drm_modeset_acquire_ctx *ctx);
+int __must_check drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock);
 void drm_modeset_unlock(struct drm_modeset_lock *lock);
 
 struct drm_device;
index 73f90f9d057f984a34ff541c3f110e7d61403726..82a217bd77f05a8014f364b0e434ea486e665b61 100644 (file)
@@ -123,6 +123,14 @@ struct drm_plane_state {
         */
        bool visible;
 
+       /**
+        * @commit: Tracks the pending commit to prevent use-after-free conditions,
+        * and for async plane updates.
+        *
+        * May be NULL.
+        */
+       struct drm_crtc_commit *commit;
+
        struct drm_atomic_state *state;
 };
 
@@ -531,10 +539,10 @@ struct drm_plane {
         * This is protected by @mutex. Note that nonblocking atomic commits
         * access the current plane state without taking locks. Either by going
         * through the &struct drm_atomic_state pointers, see
-        * for_each_plane_in_state(), for_each_oldnew_plane_in_state(),
-        * for_each_old_plane_in_state() and for_each_new_plane_in_state(). Or
-        * through careful ordering of atomic commit operations as implemented
-        * in the atomic helpers, see &struct drm_crtc_commit.
+        * for_each_oldnew_plane_in_state(), for_each_old_plane_in_state() and
+        * for_each_new_plane_in_state(). Or through careful ordering of atomic
+        * commit operations as implemented in the atomic helpers, see
+        * &struct drm_crtc_commit.
         */
        struct drm_plane_state *state;
 
index 0ad87c434ae6a344984837e8dd053fe7705d21f1..790ca021203a75e6d173e43a161e930926424725 100644 (file)
  * @file:              file representing this fence
  * @sync_file_list:    membership in global file list
  * @wq:                        wait queue for fence signaling
+ * @flags:             flags for the sync_file
  * @fence:             fence with the fences in the sync_file
  * @cb:                        fence callback information
+ *
+ * flags:
+ * POLL_ENABLED: whether userspace is currently poll()'ing or not
  */
 struct sync_file {
        struct file             *file;
index 54fc38c3c3f1dbd4cd65f188e5ce8f0d29cc194a..34b6bb34b0026715bedd2743c4c15efa71bd19cf 100644 (file)
@@ -749,9 +749,9 @@ struct drm_format_modifier {
         * If the number formats grew to 128, and formats 98-102 are
         * supported with the modifier:
         *
-        * 0x0000003c00000000 0000000000000000
+        * 0x0000007c00000000 0000000000000000
         *                ^
-        *                |__offset = 64, formats = 0x3c00000000
+        *                |__offset = 64, formats = 0x7c00000000
         *
         */
        __u64 formats;