diff options
author | Dave Airlie <airlied@redhat.com> | 2018-11-19 10:40:00 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-11-19 10:40:33 +1000 |
commit | d7563c55ef9fc1fd2301b8708b3c1f53509d6745 (patch) | |
tree | d7c8ba37972ecab71b056356366e136d5f2527ec | |
parent | 9ff01193a20d391e8dbce4403dd5ef87c7eaaca6 (diff) | |
parent | e7afb623b4fb82089c9a50c733c740522b8220bc (diff) |
Merge tag 'drm-misc-next-2018-11-07' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v4.21, part 1:
UAPI Changes:
- Add syncobj timeline support to drm.
Cross-subsystem Changes:
- Remove shared fence staging in dma-buf's fence object, and allow
reserving more than 1 fence and add more paranoia when debugging.
- Constify infoframe functions in video/hdmi.
Core Changes:
- Add vkms todo, and a lot of assorted doc fixes.
- Drop transitional helpers and convert drivers to use drm_atomic_helper_shutdown().
- Move atomic state helper functions to drm_atomic_state_helper.[ch]
- Refactor drm selftests, and add new tests.
- DP MST atomic state cleanups.
- Drop EXPORT_SYMBOL from drm leases.
- Lease cleanups and fixes.
- Create render node for vgem.
Driver Changes:
- Fix build failure in imx without fbdev emulation.
- Add rotation quirk for GPD win2 panel.
- Add support for various CDTech panels, Banana Pi Panel, DLC1010GIG,
Olimex LCD-O-LinuXino, Samsung S6D16D0, Truly NT35597 WQXGA,
Himax HX8357D, simulated RTSM AEMv8.
- Add dw_hdmi support to rockchip driver.
- Fix YUV support in vc4.
- Fix resource id handling in virtio.
- Make rockchip use dw-mipi-dsi bridge driver, and add dual dsi support.
- Advertise that tinydrm only supports DRM_FORMAT_MOD_LINEAR.
- Convert many drivers to use atomic helpers, and drm_fbdev_generic_setup().
- Add Mali linear tiled formats, and enable them in the Mali-DP driver.
- Add support for H6 DE3 mixer 0, DW HDMI, HDMI PHY and TCON TOP.
- Assorted driver cleanups and fixes.
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/be7ebd91-edd9-8fa4-4286-1c57e3165113@linux.intel.com
221 files changed, 7461 insertions, 3693 deletions
diff --git a/Documentation/devicetree/bindings/bus/sun50i-de2-bus.txt b/Documentation/devicetree/bindings/bus/sun50i-de2-bus.txt index 87dfb33fb3be..b9d533717dff 100644 --- a/Documentation/devicetree/bindings/bus/sun50i-de2-bus.txt +++ b/Documentation/devicetree/bindings/bus/sun50i-de2-bus.txt @@ -1,11 +1,14 @@ -Device tree bindings for Allwinner A64 DE2 bus +Device tree bindings for Allwinner DE2/3 bus The Allwinner A64 DE2 is on a special bus, which needs a SRAM region (SRAM C) -to be claimed for enabling the access. +to be claimed for enabling the access. The DE3 on Allwinner H6 is at the same +situation, and the binding also applies. Required properties: - - compatible: Should contain "allwinner,sun50i-a64-de2" + - compatible: Should be one of: + - "allwinner,sun50i-a64-de2" + - "allwinner,sun50i-h6-de3", "allwinner,sun50i-a64-de2" - reg: A resource specifier for the register space - #address-cells: Must be set to 1 - #size-cells: Must be set to 1 diff --git a/Documentation/devicetree/bindings/display/himax,hx8357d.txt b/Documentation/devicetree/bindings/display/himax,hx8357d.txt new file mode 100644 index 000000000000..e641f664763d --- /dev/null +++ b/Documentation/devicetree/bindings/display/himax,hx8357d.txt @@ -0,0 +1,26 @@ +Himax HX8357D display panels + +This binding is for display panels using a Himax HX8357D controller in SPI +mode, such as the Adafruit 3.5" TFT for Raspberry Pi. + +Required properties: +- compatible: "adafruit,yx350hv15", "himax,hx8357d" +- dc-gpios: D/C pin +- reg: address of the panel on the SPI bus + +The node for this driver must be a child node of a SPI controller, hence +all mandatory properties described in ../spi/spi-bus.txt must be specified. + +Optional properties: +- rotation: panel rotation in degrees counter clockwise (0,90,180,270) +- backlight: phandle of the backlight device attached to the panel + +Example: + display@0{ + compatible = "adafruit,yx350hv15", "himax,hx8357d"; + reg = <0>; + spi-max-frequency = <32000000>; + dc-gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>; + rotation = <90>; + backlight = <&backlight>; + }; diff --git a/Documentation/devicetree/bindings/display/panel/bananapi,s070wv20-ct16.txt b/Documentation/devicetree/bindings/display/panel/bananapi,s070wv20-ct16.txt new file mode 100644 index 000000000000..35bc0c839f49 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/bananapi,s070wv20-ct16.txt @@ -0,0 +1,12 @@ +Banana Pi 7" (S070WV20-CT16) TFT LCD Panel + +Required properties: +- compatible: should be "bananapi,s070wv20-ct16" +- power-supply: see ./panel-common.txt + +Optional properties: +- enable-gpios: see ./simple-panel.txt +- backlight: see ./simple-panel.txt + +This binding is compatible with the simple-panel binding, which is specified +in ./simple-panel.txt. diff --git a/Documentation/devicetree/bindings/display/panel/cdtech,s043wq26h-ct7.txt b/Documentation/devicetree/bindings/display/panel/cdtech,s043wq26h-ct7.txt new file mode 100644 index 000000000000..057f7f3f6dbe --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/cdtech,s043wq26h-ct7.txt @@ -0,0 +1,12 @@ +CDTech(H.K.) Electronics Limited 4.3" 480x272 color TFT-LCD panel + +Required properties: +- compatible: should be "cdtech,s043wq26h-ct7" +- power-supply: as specified in the base binding + +Optional properties: +- backlight: as specified in the base binding +- enable-gpios: as specified in the base binding + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/cdtech,s070wv95-ct16.txt b/Documentation/devicetree/bindings/display/panel/cdtech,s070wv95-ct16.txt new file mode 100644 index 000000000000..505615dfa0df --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/cdtech,s070wv95-ct16.txt @@ -0,0 +1,12 @@ +CDTech(H.K.) Electronics Limited 7" 800x480 color TFT-LCD panel + +Required properties: +- compatible: should be "cdtech,s070wv95-ct16" +- power-supply: as specified in the base binding + +Optional properties: +- backlight: as specified in the base binding +- enable-gpios: as specified in the base binding + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/dlc,dlc1010gig.txt b/Documentation/devicetree/bindings/display/panel/dlc,dlc1010gig.txt new file mode 100644 index 000000000000..fbf5dcd15661 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/dlc,dlc1010gig.txt @@ -0,0 +1,12 @@ +DLC Display Co. DLC1010GIG 10.1" WXGA TFT LCD Panel + +Required properties: +- compatible: should be "dlc,dlc1010gig" +- power-supply: See simple-panel.txt + +Optional properties: +- enable-gpios: See simple-panel.txt +- backlight: See simple-panel.txt + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino.txt b/Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino.txt new file mode 100644 index 000000000000..a89f9c830a85 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino.txt @@ -0,0 +1,42 @@ +Binding for Olimex Ltd. LCD-OLinuXino bridge panel. + +This device can be used as bridge between a host controller and LCD panels. +Currently supported LCDs are: + - LCD-OLinuXino-4.3TS + - LCD-OLinuXino-5 + - LCD-OLinuXino-7 + - LCD-OLinuXino-10 + +The panel itself contains: + - AT24C16C EEPROM holding panel identification and timing requirements + - AR1021 resistive touch screen controller (optional) + - FT5x6 capacitive touch screnn controller (optional) + - GT911/GT928 capacitive touch screen controller (optional) + +The above chips share same I2C bus. The EEPROM is factory preprogrammed with +device information (id, serial, etc.) and timing requirements. + +Touchscreen bingings can be found in these files: + - input/touchscreen/goodix.txt + - input/touchscreen/edt-ft5x06.txt + - input/touchscreen/ar1021.txt + +Required properties: + - compatible: should be "olimex,lcd-olinuxino" + - reg: address of the configuration EEPROM, should be <0x50> + - power-supply: phandle of the regulator that provides the supply voltage + +Optional properties: + - enable-gpios: GPIO pin to enable or disable the panel + - backlight: phandle of the backlight device attacked to the panel + +Example: +&i2c2 { + panel@50 { + compatible = "olimex,lcd-olinuxino"; + reg = <0x50>; + power-supply = <®_vcc5v0>; + enable-gpios = <&pio 7 8 GPIO_ACTIVE_HIGH>; + backlight = <&backlight>; + }; +}; diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6d16d0.txt b/Documentation/devicetree/bindings/display/panel/samsung,s6d16d0.txt new file mode 100644 index 000000000000..b94e366f451b --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/samsung,s6d16d0.txt @@ -0,0 +1,30 @@ +Samsung S6D16D0 4" 864x480 AMOLED panel + +Required properties: + - compatible: should be: + "samsung,s6d16d0", + - reg: the virtual channel number of a DSI peripheral + - vdd1-supply: I/O voltage supply + - reset-gpios: a GPIO spec for the reset pin (active low) + +The device node can contain one 'port' child node with one child +'endpoint' node, according to the bindings defined in +media/video-interfaces.txt. This node should describe panel's video bus. + +Example: +&dsi { + ... + + panel@0 { + compatible = "samsung,s6d16d0"; + reg = <0>; + vdd1-supply = <&foo>; + reset-gpios = <&foo_gpio 0 GPIO_ACTIVE_LOW>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt index adc94fc3c9f8..39143424a474 100644 --- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt @@ -13,6 +13,7 @@ Required properties: - compatible: should be one of the following: "rockchip,rk3288-dw-hdmi" + "rockchip,rk3328-dw-hdmi" "rockchip,rk3399-dw-hdmi" - reg: See dw_hdmi.txt. - reg-io-width: See dw_hdmi.txt. Shall be 4. @@ -34,6 +35,8 @@ Optional properties - clock-names: May contain "cec" as defined in dw_hdmi.txt. - clock-names: May contain "grf", power for grf io. - clock-names: May contain "vpll", external clock for some hdmi phy. +- phys: from general PHY binding: the phandle for the PHY device. +- phy-names: Should be "hdmi" if phys references an external phy. Example: diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index 7854fff4fc16..f426bdb42f18 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -79,6 +79,7 @@ Required properties: - compatible: value must be one of: * "allwinner,sun8i-a83t-dw-hdmi" * "allwinner,sun50i-a64-dw-hdmi", "allwinner,sun8i-a83t-dw-hdmi" + * "allwinner,sun50i-h6-dw-hdmi" - reg: base address and size of memory-mapped region - reg-io-width: See dw_hdmi.txt. Shall be 1. - interrupts: HDMI interrupt number @@ -86,9 +87,14 @@ Required properties: * iahb: the HDMI bus clock * isfr: the HDMI register clock * tmds: TMDS clock + * cec: HDMI CEC clock (H6 only) + * hdcp: HDCP clock (H6 only) + * hdcp-bus: HDCP bus clock (H6 only) - clock-names: the clock names mentioned above - - resets: phandle to the reset controller - - reset-names: must be "ctrl" + - resets: + * ctrl: HDMI controller reset + * hdcp: HDCP reset (H6 only) + - reset-names: reset names mentioned above - phys: phandle to the DWC HDMI PHY - phy-names: must be "phy" @@ -109,6 +115,7 @@ Required properties: * allwinner,sun8i-h3-hdmi-phy * allwinner,sun8i-r40-hdmi-phy * allwinner,sun50i-a64-hdmi-phy + * allwinner,sun50i-h6-hdmi-phy - reg: base address and size of memory-mapped region - clocks: phandles to the clocks feeding the HDMI PHY * bus: the HDMI PHY interface clock @@ -158,6 +165,7 @@ Required properties: * allwinner,sun9i-a80-tcon-tv * "allwinner,sun50i-a64-tcon-lcd", "allwinner,sun8i-a83t-tcon-lcd" * "allwinner,sun50i-a64-tcon-tv", "allwinner,sun8i-a83t-tcon-tv" + * allwinner,sun50i-h6-tcon-tv, allwinner,sun8i-r40-tcon-tv - reg: base address and size of memory-mapped region - interrupts: interrupt associated to this IP - clocks: phandles to the clocks feeding the TCON. @@ -220,24 +228,26 @@ It allows display pipeline to be configured in very different ways: \ [3] TCON-TV1 [1] - TVE1/RGB Note that both TCON TOP references same physical unit. Both mixers can be -connected to any TCON. +connected to any TCON. Not all TCON TOP variants support all features. Required properties: - compatible: value must be one of: * allwinner,sun8i-r40-tcon-top + * allwinner,sun50i-h6-tcon-top - reg: base address and size of the memory-mapped region. - clocks: phandle to the clocks feeding the TCON TOP * bus: TCON TOP interface clock * tcon-tv0: TCON TV0 clock - * tve0: TVE0 clock - * tcon-tv1: TCON TV1 clock - * tve1: TVE0 clock - * dsi: MIPI DSI clock + * tve0: TVE0 clock (R40 only) + * tcon-tv1: TCON TV1 clock (R40 only) + * tve1: TVE0 clock (R40 only) + * dsi: MIPI DSI clock (R40 only) - clock-names: clock name mentioned above - resets: phandle to the reset line driving the TCON TOP - #clock-cells : must contain 1 - clock-output-names: Names of clocks created for TCON TV0 channel clock, - TCON TV1 channel clock and DSI channel clock, in that order. + TCON TV1 channel clock (R40 only) and DSI channel clock (R40 only), in + that order. - ports: A ports node with endpoint definitions as defined in Documentation/devicetree/bindings/media/video-interfaces.txt. 6 ports should @@ -381,6 +391,7 @@ Required properties: * allwinner,sun8i-v3s-de2-mixer * allwinner,sun50i-a64-de2-mixer-0 * allwinner,sun50i-a64-de2-mixer-1 + * allwinner,sun50i-h6-de3-mixer-0 - reg: base address and size of the memory-mapped region. - clocks: phandles to the clocks feeding the mixer * bus: the mixer interface clock @@ -415,9 +426,10 @@ Required properties: * allwinner,sun8i-v3s-display-engine * allwinner,sun9i-a80-display-engine * allwinner,sun50i-a64-display-engine + * allwinner,sun50i-h6-display-engine - allwinner,pipelines: list of phandle to the display engine - frontends (DE 1.0) or mixers (DE 2.0) available. + frontends (DE 1.0) or mixers (DE 2.0/3.0) available. Example: diff --git a/Documentation/devicetree/bindings/display/truly,nt35597.txt b/Documentation/devicetree/bindings/display/truly,nt35597.txt new file mode 100644 index 000000000000..f39c77ee36ea --- /dev/null +++ b/Documentation/devicetree/bindings/display/truly,nt35597.txt @@ -0,0 +1,59 @@ +Truly model NT35597 DSI display driver + +The Truly NT35597 is a generic display driver, currently only configured +for use in the 2K display on the Qualcomm SDM845 MTP board. + +Required properties: +- compatible: should be "truly,nt35597-2K-display" +- vdda-supply: phandle of the regulator that provides the supply voltage + Power IC supply +- vdispp-supply: phandle of the regulator that provides the supply voltage + for positive LCD bias +- vdispn-supply: phandle of the regulator that provides the supply voltage + for negative LCD bias +- reset-gpios: phandle of gpio for reset line + This should be 8mA, gpio can be configured using mux, pinctrl, pinctrl-names + (active low) +- mode-gpios: phandle of the gpio for choosing the mode of the display + for single DSI or Dual DSI + This should be low for dual DSI and high for single DSI mode +- ports: This device has two video ports driven by two DSIs. Their connections + are modeled using the OF graph bindings specified in + Documentation/devicetree/bindings/graph.txt. + - port@0: DSI input port driven by master DSI + - port@1: DSI input port driven by secondary DSI + +Example: + + dsi@ae94000 { + panel@0 { + compatible = "truly,nt35597-2K-display"; + reg = <0>; + vdda-supply = <&pm8998_l14>; + vdispp-supply = <&lab_regulator>; + vdispn-supply = <&ibb_regulator>; + pinctrl-names = "default", "suspend"; + pinctrl-0 = <&dpu_dsi_active>; + pinctrl-1 = <&dpu_dsi_suspend>; + + reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; + mode-gpios = <&tlmm 52 GPIO_ACTIVE_HIGH>; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + panel0_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + panel1_in: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 4b1a2a8fcc16..a2f4451cf3f9 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -67,6 +67,7 @@ capella Capella Microsystems, Inc cascoda Cascoda, Ltd. cavium Cavium, Inc. cdns Cadence Design Systems Inc. +cdtech CDTech(H.K.) Electronics Limited ceva Ceva, Inc. chipidea Chipidea, Inc chipone ChipOne diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index f9cfcdcdf024..4b4dc236ef6f 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -59,19 +59,28 @@ Implementing Asynchronous Atomic Commit .. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c :doc: implementing nonblocking commit +Helper Functions Reference +-------------------------- + +.. kernel-doc:: include/drm/drm_atomic_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c + :export: + Atomic State Reset and Initialization ------------------------------------- -.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c +.. kernel-doc:: drivers/gpu/drm/drm_atomic_state_helper.c :doc: atomic state reset and initialization -Helper Functions Reference --------------------------- +Atomic State Helper Reference +----------------------------- -.. kernel-doc:: include/drm/drm_atomic_helper.h +.. kernel-doc:: include/drm/drm_atomic_state_helper.h :internal: -.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c +.. kernel-doc:: drivers/gpu/drm/drm_atomic_state_helper.c :export: Simple KMS Helper Reference diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index a2214cc1f821..4b4bf2c5eac5 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -197,6 +197,9 @@ EPERM/EACCESS: difference between EACCESS and EPERM. ENODEV: + The device is not (yet) present or fully initialized. + +EOPNOTSUPP: Feature (like PRIME, modesetting, GEM) is not supported by the driver. ENXIO: diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 77c2b3c25565..5c9d86c962af 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -339,6 +339,16 @@ Some of these date from the very introduction of KMS in 2008 ... leftovers from older (never merged into upstream) KMS designs where modes where set using their ID, including support to add/remove modes. +- Make ->funcs and ->helper_private vtables optional. There's a bunch of empty + function tables in drivers, but before we can remove them we need to make sure + that all the users in helpers and drivers do correctly check for a NULL + vtable. + +- Cleanup up the various ->destroy callbacks. A lot of them just wrapt the + drm_*_cleanup implementations and can be removed. Some tack a kfree() at the + end, for which we could add drm_*_cleanup_kfree(). And then there's the (for + historical reasons) misnamed drm_primary_helper_destroy() function. + Better Testing ============== diff --git a/Documentation/gpu/vkms.rst b/Documentation/gpu/vkms.rst index 0a6ea6216e41..7dfc349a4508 100644 --- a/Documentation/gpu/vkms.rst +++ b/Documentation/gpu/vkms.rst @@ -10,8 +10,8 @@ TODO ==== -CRC API -------- +CRC API Improvements +-------------------- - Optimize CRC computation ``compute_crc()`` and plane blending ``blend()`` @@ -22,3 +22,100 @@ CRC API - Add igt test to check extreme alpha values i.e. fully opaque and fully transparent (intermediate values are affected by hw-specific rounding modes). + +Vblank issues +------------- + +Some IGT test cases are failing. Need to analyze why and fix the issues: + +- plain-flip-fb-recreate +- plain-flip-ts-check +- flip-vs-blocking-wf-vblank +- plain-flip-fb-recreate-interruptible +- flip-vs-wf_vblank-interruptible + +Runtime Configuration +--------------------- + +We want to be able to reconfigure vkms instance without having to reload the +module. Use/Test-cases: + +- Hotplug/hotremove connectors on the fly (to be able to test DP MST handling of + compositors). + +- Configure planes/crtcs/connectors (we'd need some code to have more than 1 of + them first). + +- Change output configuration: Plug/unplug screens, change EDID, allow changing + the refresh rate. + +The currently proposed solution is to expose vkms configuration through +configfs. All existing module options should be supported through configfs too. + +Add Plane Features +------------------ + +There's lots of plane features we could add support for: + +- Real overlay planes, not just cursor. + +- Full alpha blending on all planes. + +- Rotation, scaling. + +- Additional buffer formats, especially YUV formats for video like NV12. + Low/high bpp RGB formats would also be interesting. + +- Async updates (currently only possible on cursor plane using the legacy cursor + api). + +For all of these, we also want to review the igt test coverage and make sure all +relevant igt testcases work on vkms. + +Writeback support +----------------- + +Currently vkms only computes a CRC for each frame. Once we have additional plane +features, we could write back the entire composited frame, and expose it as: + +- Writeback connector. This is useful for testing compositors if you don't have + hardware with writeback support. + +- As a v4l device. This is useful for debugging compositors on special vkms + configurations, so that developers see what's really going on. + +Prime Buffer Sharing +-------------------- + +We already have vgem, which is a gem driver for testing rendering, similar to +how vkms is for testing the modeset side. Adding buffer sharing support to vkms +allows us to test them together, to test synchronization and lots of other +features. Also, this allows compositors to test whether they work correctly on +SoC chips, where the display and rendering is very often split between 2 +drivers. + +Output Features +--------------- + +- Variable refresh rate/freesync support. This probably needs prime buffer + sharing support, so that we can use vgem fences to simulate rendering in + testing. Also needs support to specify the EDID. + +- Add support for link status, so that compositors can validate their runtime + fallbacks when e.g. a Display Port link goes bad. + +- All the hotplug handling describe under "Runtime Configuration". + +Atomic Check using eBPF +----------------------- + +Atomic drivers have lots of restrictions which are not exposed to userspace in +any explicit form through e.g. possible property values. Userspace can only +inquiry about these limits through the atomic IOCTL, possibly using the +TEST_ONLY flag. Trying to add configurable code for all these limits, to allow +compositors to be tested against them, would be rather futile exercise. Instead +we could add support for eBPF to validate any kind of atomic state, and +implement a library of different restrictions. + +This needs a bunch of features (plane compositing, multiple outputs, ...) +enabled already to make sense. diff --git a/MAINTAINERS b/MAINTAINERS index b755a89fa325..1026150ae90f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4655,6 +4655,13 @@ S: Maintained F: drivers/gpu/drm/tinydrm/ili9225.c F: Documentation/devicetree/bindings/display/ilitek,ili9225.txt +DRM DRIVER FOR HX8357D PANELS +M: Eric Anholt <eric@anholt.net> +T: git git://anongit.freedesktop.org/drm/drm-misc +S: Maintained +F: drivers/gpu/drm/tinydrm/hx8357d.c +F: Documentation/devicetree/bindings/display/himax,hx8357d.txt + DRM DRIVER FOR INTEL I810 VIDEO CARDS S: Orphan / Obsolete F: drivers/gpu/drm/i810/ @@ -4696,6 +4703,12 @@ S: Supported F: drivers/gpu/drm/nouveau/ F: include/uapi/drm/nouveau_drm.h +DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS +M: Stefan Mavrodiev <stefan@olimex.com> +S: Maintained +F: drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c +F: Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino.txt + DRM DRIVER FOR PERVASIVE DISPLAYS REPAPER PANELS M: Noralf Trønnes <noralf@tronnes.org> S: Maintained diff --git a/drivers/dma-buf/reservation.c b/drivers/dma-buf/reservation.c index 6c95f61a32e7..c1618335ca99 100644 --- a/drivers/dma-buf/reservation.c +++ b/drivers/dma-buf/reservation.c @@ -56,9 +56,10 @@ const char reservation_seqcount_string[] = "reservation_seqcount"; EXPORT_SYMBOL(reservation_seqcount_string); /** - * reservation_object_reserve_shared - Reserve space to add a shared - * fence to a reservation_object. + * reservation_object_reserve_shared - Reserve space to add shared fences to + * a reservation_object. * @obj: reservation object + * @num_fences: number of fences we want to add * * Should be called before reservation_object_add_shared_fence(). Must * be called with obj->lock held. @@ -66,107 +67,27 @@ EXPORT_SYMBOL(reservation_seqcount_string); * RETURNS * Zero for success, or -errno */ -int reservation_object_reserve_shared(struct reservation_object *obj) +int reservation_object_reserve_shared(struct reservation_object *obj, + unsigned int num_fences) { - struct reservation_object_list *fobj, *old; - u32 max; + struct reservation_object_list *old, *new; + unsigned int i, j, k, max; old = reservation_object_get_list(obj); if (old && old->shared_max) { - if (old->shared_count < old->shared_max) { - /* perform an in-place update */ - kfree(obj->staged); - obj->staged = NULL; + if ((old->shared_count + num_fences) <= old->shared_max) return 0; - } else - max = old->shared_max * 2; - } else - max = 4; - - /* - * resize obj->staged or allocate if it doesn't exist, - * noop if already correct size - */ - fobj = krealloc(obj->staged, offsetof(typeof(*fobj), shared[max]), - GFP_KERNEL); - if (!fobj) - return -ENOMEM; - - obj->staged = fobj; - fobj->shared_max = max; - return 0; -} -EXPORT_SYMBOL(reservation_object_reserve_shared); - -static void -reservation_object_add_shared_inplace(struct reservation_object *obj, - struct reservation_object_list *fobj, - struct dma_fence *fence) -{ - struct dma_fence *signaled = NULL; - u32 i, signaled_idx; - - dma_fence_get(fence); - - preempt_disable(); - write_seqcount_begin(&obj->seq); - - for (i = 0; i < fobj->shared_count; ++i) { - struct dma_fence *old_fence; - - old_fence = rcu_dereference_protected(fobj->shared[i], - reservation_object_held(obj)); - - if (old_fence->context == fence->context) { - /* memory barrier is added by write_seqcount_begin */ - RCU_INIT_POINTER(fobj->shared[i], fence); - write_seqcount_end(&obj->seq); - preempt_enable(); - - dma_fence_put(old_fence); - return; - } - - if (!signaled && dma_fence_is_signaled(old_fence)) { - signaled = old_fence; - signaled_idx = i; - } - } - - /* - * memory barrier is added by write_seqcount_begin, - * fobj->shared_count is protected by this lock too - */ - if (signaled) { - RCU_INIT_POINTER(fobj->shared[signaled_idx], fence); + else + max = max(old->shared_count + num_fences, + old->shared_max * 2); } else { - BUG_ON(fobj->shared_count >= fobj->shared_max); - RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence); - fobj->shared_count++; + max = 4; } - write_seqcount_end(&obj->seq); - preempt_enable(); - - dma_fence_put(signaled); -} - -static void -reservation_object_add_shared_replace(struct reservation_object *obj, - struct reservation_object_list *old, - struct reservation_object_list *fobj, - struct dma_fence *fence) -{ - unsigned i, j, k; - - dma_fence_get(fence); - - if (!old) { - RCU_INIT_POINTER(fobj->shared[0], fence); - fobj->shared_count = 1; - goto done; - } + new = kmalloc(offsetof(typeof(*new), shared[max]), GFP_KERNEL); + if (!new) + return -ENOMEM; /* * no need to bump fence refcounts, rcu_read access @@ -174,46 +95,45 @@ reservation_object_add_shared_replace(struct reservation_object *obj, * references from the old struct are carried over to * the new. */ - for (i = 0, j = 0, k = fobj->shared_max; i < old->shared_count; ++i) { - struct dma_fence *check; - - check = rcu_dereference_protected(old->shared[i], - reservation_object_held(obj)); + for (i = 0, j = 0, k = max; i < (old ? old->shared_count : 0); ++i) { + struct dma_fence *fence; - if (check->context == fence->context || - dma_fence_is_signaled(check)) - RCU_INIT_POINTER(fobj->shared[--k], check); + fence = rcu_dereference_protected(old->shared[i], + reservation_object_held(obj)); + if (dma_fence_is_signaled(fence)) + RCU_INIT_POINTER(new->shared[--k], fence); else - RCU_INIT_POINTER(fobj->shared[j++], check); + RCU_INIT_POINTER(new->shared[j++], fence); } - fobj->shared_count = j; - RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence); - fobj->shared_count++; + new->shared_count = j; + new->shared_max = max; -done: preempt_disable(); write_seqcount_begin(&obj->seq); /* * RCU_INIT_POINTER can be used here, * seqcount provides the necessary barriers */ - RCU_INIT_POINTER(obj->fence, fobj); + RCU_INIT_POINTER(obj->fence, new); write_seqcount_end(&obj->seq); preempt_enable(); if (!old) - return; + return 0; /* Drop the references to the signaled fences */ - for (i = k; i < fobj->shared_max; ++i) { - struct dma_fence *f; + for (i = k; i < new->shared_max; ++i) { + struct dma_fence *fence; - f = rcu_dereference_protected(fobj->shared[i], - reservation_object_held(obj)); - dma_fence_put(f); + fence = rcu_dereference_protected(new->shared[i], + reservation_object_held(obj)); + dma_fence_put(fence); } kfree_rcu(old, rcu); + + return 0; } +EXPORT_SYMBOL(reservation_object_reserve_shared); /** * reservation_object_add_shared_fence - Add a fence to a shared slot @@ -226,15 +146,39 @@ done: void reservation_object_add_shared_fence(struct reservation_object *obj, struct dma_fence *fence) { - struct reservation_object_list *old, *fobj = obj->staged; + struct reservation_object_list *fobj; + unsigned int i, count; - old = reservation_object_get_list(obj); - obj->staged = NULL; + dma_fence_get(fence); - if (!fobj) - reservation_object_add_shared_inplace(obj, old, fence); - else - reservation_object_add_shared_replace(obj, old, fobj, fence); + fobj = reservation_object_get_list(obj); + count = fobj->shared_count; + + preempt_disable(); + write_seqcount_begin(&obj->seq); + + for (i = 0; i < count; ++i) { + struct dma_fence *old_fence; + + old_fence = rcu_dereference_protected(fobj->shared[i], + reservation_object_held(obj)); + if (old_fence->context == fence->context || + dma_fence_is_signaled(old_fence)) { + dma_fence_put(old_fence); + goto replace; + } + } + + BUG_ON(fobj->shared_count >= fobj->shared_max); + count++; + +replace: + RCU_INIT_POINTER(fobj->shared[i], fence); + /* pointer update must be visible before we extend the shared_count */ + smp_store_mb(fobj->shared_count, count); + + write_seqcount_end(&obj->seq); + preempt_enable(); } EXPORT_SYMBOL(reservation_object_add_shared_fence); @@ -343,9 +287,6 @@ retry: new = dma_fence_get_rcu_safe(&src->fence_excl); rcu_read_unlock(); - kfree(dst->staged); - dst->staged = NULL; - src_list = reservation_object_get_list(dst); old = reservation_object_get_excl(dst); diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index bc6a16a3c36e..576ba985e138 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -36,7 +36,8 @@ 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 \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ drm_simple_kms_helper.o drm_modeset_helper.o \ - drm_scdc_helper.o drm_gem_framebuffer_helper.o + drm_scdc_helper.o drm_gem_framebuffer_helper.o \ + drm_atomic_state_helper.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 663043c8f0f5..35bc8fc3bc70 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -955,7 +955,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) if (r) return r; - r = reservation_object_reserve_shared(vm->root.base.bo->tbo.resv); + r = reservation_object_reserve_shared(vm->root.base.bo->tbo.resv, 1); if (r) return r; @@ -1104,7 +1104,7 @@ static int amdgpu_syncobj_lookup_and_add_to_sync(struct amdgpu_cs_parser *p, { int r; struct dma_fence *fence; - r = drm_syncobj_find_fence(p->filp, handle, 0, &fence); + r = drm_syncobj_find_fence(p->filp, handle, 0, 0, &fence); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 904014dc5915..cf768acb51dc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -640,7 +640,7 @@ int amdgpu_bo_backup_to_shadow(struct amdgpu_device *adev, bo_addr = amdgpu_bo_gpu_offset(bo); shadow_addr = amdgpu_bo_gpu_offset(bo->shadow); - r = reservation_object_reserve_shared(bo->tbo.resv); + r = reservation_object_reserve_shared(bo->tbo.resv, 1); if (r) goto err; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c index e45e929aaab5..3e44d889f7af 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c @@ -339,8 +339,6 @@ static const struct dma_buf_ops amdgpu_dmabuf_ops = { .unmap_dma_buf = drm_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release, .begin_cpu_access = amdgpu_gem_begin_cpu_access, - .map = drm_gem_dmabuf_kmap, - .unmap = drm_gem_dmabuf_kunmap, .mmap = drm_gem_dmabuf_mmap, .vmap = drm_gem_dmabuf_vmap, .vunmap = drm_gem_dmabuf_vunmap, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index dad0e2342df9..58a2363040dd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -773,7 +773,7 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev, ring = container_of(vm->entity.rq->sched, struct amdgpu_ring, sched); - r = reservation_object_reserve_shared(bo->tbo.resv); + r = reservation_object_reserve_shared(bo->tbo.resv, 1); if (r) return r; @@ -1842,7 +1842,7 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev, if (r) goto error_free; - r = reservation_object_reserve_shared(vm->root.base.bo->tbo.resv); + r = reservation_object_reserve_shared(vm->root.base.bo->tbo.resv, 1); if (r) goto error_free; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index c1262f62cd9f..5064768642f3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3185,7 +3185,6 @@ amdgpu_dm_connector_helper_funcs = { */ .get_modes = get_modes, .mode_valid = amdgpu_dm_connector_mode_valid, - .best_encoder = drm_atomic_helper_best_encoder }; static void dm_crtc_helper_disable(struct drm_crtc *crtc) @@ -3588,14 +3587,17 @@ static int to_drm_connector_type(enum signal_type st) } } +static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector) +{ + return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); +} + static void amdgpu_dm_get_native_mode(struct drm_connector *connector) { - const struct drm_connector_helper_funcs *helper = - connector->helper_private; struct drm_encoder *encoder; struct amdgpu_encoder *amdgpu_encoder; - encoder = helper->best_encoder(connector); + encoder = amdgpu_dm_connector_to_encoder(connector); if (encoder == NULL) return; @@ -3722,14 +3724,12 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) { - const struct drm_connector_helper_funcs *helper = - connector->helper_private; struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); struct drm_encoder *encoder; struct edid *edid = amdgpu_dm_connector->edid; - encoder = helper->best_encoder(connector); + encoder = amdgpu_dm_connector_to_encoder(connector); if (!edid || !drm_edid_is_valid(edid)) { amdgpu_dm_connector->num_modes = diff --git a/drivers/gpu/drm/arc/arcpgu.h b/drivers/gpu/drm/arc/arcpgu.h index e8fcf3ab1d9a..90ef76b19f8a 100644 --- a/drivers/gpu/drm/arc/arcpgu.h +++ b/drivers/gpu/drm/arc/arcpgu.h @@ -20,7 +20,6 @@ struct arcpgu_drm_private { void __iomem *regs; struct clk *clk; - struct drm_fbdev_cma *fbdev; struct drm_framebuffer *fb; struct drm_crtc crtc; struct drm_plane *plane; @@ -43,8 +42,5 @@ static inline u32 arc_pgu_read(struct arcpgu_drm_private *arcpgu, int arc_pgu_setup_crtc(struct drm_device *dev); int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np); int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np); -struct drm_fbdev_cma *arcpgu_fbdev_cma_init(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int num_crtc, - unsigned int max_conn_count); #endif diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c index 965cda48dc13..62f51f70606d 100644 --- a/drivers/gpu/drm/arc/arcpgu_crtc.c +++ b/drivers/gpu/drm/arc/arcpgu_crtc.c @@ -158,8 +158,6 @@ static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = { .mode_valid = arc_pgu_crtc_mode_valid, - .mode_set = drm_helper_crtc_mode_set, - .mode_set_base = drm_helper_crtc_mode_set_base, .mode_set_nofb = arc_pgu_crtc_mode_set_nofb, .atomic_begin = arc_pgu_crtc_atomic_begin, .atomic_enable = arc_pgu_crtc_atomic_enable, @@ -186,7 +184,6 @@ static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = { static void arc_pgu_plane_destroy(struct drm_plane *plane) { - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); } diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index f067de4e1e82..2af847ebca34 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -17,6 +17,7 @@ #include <linux/clk.h> #include <drm/drm_crtc_helper.h> #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_atomic_helper.h> @@ -25,16 +26,8 @@ #include "arcpgu.h" #include "arcpgu_regs.h" -static void arcpgu_fb_output_poll_changed(struct drm_device *dev) -{ - struct arcpgu_drm_private *arcpgu = dev->dev_private; - - drm_fbdev_cma_hotplug_event(arcpgu->fbdev); -} - static const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = { .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, }; @@ -51,13 +44,6 @@ static void arcpgu_setup_mode_config(struct drm_device *drm) DEFINE_DRM_GEM_CMA_FOPS(arcpgu_drm_ops); -static void arcpgu_lastclose(struct drm_device *drm) -{ - struct arcpgu_drm_private *arcpgu = drm->dev_private; - - drm_fbdev_cma_restore_mode(arcpgu->fbdev); -} - static int arcpgu_load(struct drm_device *drm) { struct platform_device *pdev = to_platform_device(drm->dev); @@ -113,27 +99,14 @@ static int arcpgu_load(struct drm_device *drm) drm_mode_config_reset(drm); drm_kms_helper_poll_init(drm); - arcpgu->fbdev = drm_fbdev_cma_init(drm, 16, - drm->mode_config.num_connector); - if (IS_ERR(arcpgu->fbdev)) { - ret = PTR_ERR(arcpgu->fbdev); - arcpgu->fbdev = NULL; - return -ENODEV; - } - platform_set_drvdata(pdev, drm); return 0; } static int arcpgu_unload(struct drm_device *drm) { - struct arcpgu_drm_private *arcpgu = drm->dev_private; - - if (arcpgu->fbdev) { - drm_fbdev_cma_fini(arcpgu->fbdev); - arcpgu->fbdev = NULL; - } drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); drm_mode_config_cleanup(drm); return 0; @@ -167,7 +140,6 @@ static int arcpgu_debugfs_init(struct drm_minor *minor) static struct drm_driver arcpgu_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = arcpgu_lastclose, .name = "arcpgu", .desc = "ARC PGU Controller", .date = "20160219", @@ -210,6 +182,8 @@ static int arcpgu_probe(struct platform_device *pdev) if (ret) goto err_unload; + drm_fbdev_generic_setup(drm, 16); + return 0; err_unload: diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c index 7aad7dd80d8c..b9bed1138fa3 100644 --- a/drivers/gpu/drm/arm/malidp_hw.c +++ b/drivers/gpu/drm/arm/malidp_hw.c @@ -77,12 +77,18 @@ static const struct malidp_format_id malidp500_de_formats[] = { { DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) }, \ { DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) }, \ { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(5, 6) }, \ - { DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) } + { DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) }, \ + { DRM_FORMAT_X0L2, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 6)} static const struct malidp_format_id malidp550_de_formats[] = { MALIDP_COMMON_FORMATS, }; +static const struct malidp_format_id malidp650_de_formats[] = { + MALIDP_COMMON_FORMATS, + { DRM_FORMAT_X0L0, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 4)}, +}; + static const struct malidp_layer malidp500_layers[] = { /* id, base address, fb pointer address base, stride offset, * yuv2rgb matrix offset, mmu control register offset, rotation_features @@ -630,6 +636,8 @@ static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 case DRM_FORMAT_BGR565: case DRM_FORMAT_UYVY: case DRM_FORMAT_YUYV: + case DRM_FORMAT_X0L0: + case DRM_FORMAT_X0L2: bytes_per_col = 32; break; /* 16 lines at 1.5 bytes per pixel */ @@ -905,8 +913,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { MALIDP550_DC_IRQ_SE, .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID, }, - .pixel_formats = malidp550_de_formats, - .n_pixel_formats = ARRAY_SIZE(malidp550_de_formats), + .pixel_formats = malidp650_de_formats, + .n_pixel_formats = ARRAY_SIZE(malidp650_de_formats), .bus_align_bytes = 16, }, .query_hw = malidp650_query_hw, diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 837a24d56675..c9a6d3e0cada 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -398,6 +398,7 @@ static int malidp_de_plane_check(struct drm_plane *plane, struct drm_framebuffer *fb; u16 pixel_alpha = state->pixel_blend_mode; int i, ret; + unsigned int block_w, block_h; if (!state->crtc || !state->fb) return 0; @@ -413,13 +414,26 @@ static int malidp_de_plane_check(struct drm_plane *plane, ms->n_planes = fb->format->num_planes; for (i = 0; i < ms->n_planes; i++) { u8 alignment = malidp_hw_get_pitch_align(mp->hwdev, rotated); - if (fb->pitches[i] & (alignment - 1)) { + + if ((fb->pitches[i] * drm_format_info_block_height(fb->format, i)) + & (alignment - 1)) { DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n", fb->pitches[i], i); return -EINVAL; } } + block_w = drm_format_info_block_width(fb->format, 0); + block_h = drm_format_info_block_height(fb->format, 0); + if (fb->width % block_w || fb->height % block_h) { + DRM_DEBUG_KMS("Buffer width/height needs to be a multiple of tile sizes"); + return -EINVAL; + } + if ((state->src_x >> 16) % block_w || (state->src_y >> 16) % block_h) { + DRM_DEBUG_KMS("Plane src_x/src_y needs to be a multiple of tile sizes"); + return -EINVAL; + } + if ((state->crtc_w > mp->hwdev->max_line_size) || (state->crtc_h > mp->hwdev->max_line_size) || (state->crtc_w < mp->hwdev->min_line_size) || @@ -492,10 +506,18 @@ static void malidp_de_set_plane_pitches(struct malidp_plane *mp, num_strides = (mp->hwdev->hw->features & MALIDP_DEVICE_LV_HAS_3_STRIDES) ? 3 : 2; - for (i = 0; i < num_strides; ++i) - malidp_hw_write(mp->hwdev, pitches[i], + /* + * The drm convention for pitch is that it needs to cover width * cpp, + * but our hardware wants the pitch/stride to cover all rows included + * in a tile. + */ + for (i = 0; i < num_strides; ++i) { + unsigned int block_h = drm_format_info_block_height(mp->base.state->fb->format, i); + + malidp_hw_write(mp->hwdev, pitches[i] * block_h, mp->layer->base + mp->layer->stride_offset + i * 4); + } } static const s16 diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 9e34bce089d0..96f4082671fe 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -364,9 +364,7 @@ static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { .mode_valid = atmel_hlcdc_crtc_mode_valid, - .mode_set = drm_helper_crtc_mode_set, .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb, - .mode_set_base = drm_helper_crtc_mode_set_base, .atomic_check = atmel_hlcdc_crtc_atomic_check, .atomic_begin = atmel_hlcdc_crtc_atomic_begin, .atomic_flush = atmel_hlcdc_crtc_atomic_flush, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 843cac222e60..034a91112098 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -556,7 +556,6 @@ error: static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = atmel_hlcdc_fb_create, - .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = atmel_hlcdc_dc_atomic_commit, }; @@ -658,8 +657,6 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) platform_set_drvdata(pdev, dev); - drm_fb_cma_fbdev_init(dev, 24, 0); - drm_kms_helper_poll_init(dev); return 0; @@ -678,7 +675,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev) { struct atmel_hlcdc_dc *dc = dev->dev_private; - drm_fb_cma_fbdev_fini(dev); flush_workqueue(dc->wq); drm_kms_helper_poll_fini(dev); drm_atomic_helper_shutdown(dev); @@ -727,7 +723,6 @@ static struct drm_driver atmel_hlcdc_dc_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = drm_fb_helper_lastclose, .irq_handler = atmel_hlcdc_dc_irq_handler, .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, @@ -763,19 +758,21 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) ret = atmel_hlcdc_dc_load(ddev); if (ret) - goto err_unref; + goto err_put; ret = drm_dev_register(ddev, 0); if (ret) goto err_unload; + drm_fbdev_generic_setup(ddev, 24); + return 0; err_unload: atmel_hlcdc_dc_unload(ddev); -err_unref: - drm_dev_unref(ddev); +err_put: + drm_dev_put(ddev); return ret; } @@ -786,7 +783,7 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) drm_dev_unregister(ddev); atmel_hlcdc_dc_unload(ddev); - drm_dev_unref(ddev); + drm_dev_put(ddev); return 0; } diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h index e7a69077e45a..577a8b917cb9 100644 --- a/drivers/gpu/drm/bochs/bochs.h +++ b/drivers/gpu/drm/bochs/bochs.h @@ -66,6 +66,7 @@ struct bochs_device { u16 yres_virtual; u32 stride; u32 bpp; + struct edid *edid; /* drm */ struct drm_device *dev; @@ -126,6 +127,7 @@ void bochs_hw_setmode(struct bochs_device *bochs, const struct drm_format_info *format); void bochs_hw_setbase(struct bochs_device *bochs, int x, int y, u64 addr); +int bochs_hw_load_edid(struct bochs_device *bochs); /* bochs_mm.c */ int bochs_mm_init(struct bochs_device *bochs); diff --git a/drivers/gpu/drm/bochs/bochs_hw.c b/drivers/gpu/drm/bochs/bochs_hw.c index cacff73a64ab..c90a0d492fd5 100644 --- a/drivers/gpu/drm/bochs/bochs_hw.c +++ b/drivers/gpu/drm/bochs/bochs_hw.c @@ -69,6 +69,35 @@ static void bochs_hw_set_little_endian(struct bochs_device *bochs) #define bochs_hw_set_native_endian(_b) bochs_hw_set_little_endian(_b) #endif +static int bochs_get_edid_block(void *data, u8 *buf, + unsigned int block, size_t len) +{ + struct bochs_device *bochs = data; + size_t i, start = block * EDID_LENGTH; + + if (start + len > 0x400 /* vga register offset */) + return -1; + + for (i = 0; i < len; i++) { + buf[i] = readb(bochs->mmio + start + i); + } + return 0; +} + +int bochs_hw_load_edid(struct bochs_device *bochs) +{ + if (!bochs->mmio) + return -1; + + kfree(bochs->edid); + bochs->edid = drm_do_get_edid(&bochs->connector, + bochs_get_edid_block, bochs); + if (bochs->edid == NULL) + return -1; + + return 0; +} + int bochs_hw_init(struct drm_device *dev) { struct bochs_device *bochs = dev->dev_private; @@ -164,6 +193,7 @@ void bochs_hw_fini(struct drm_device *dev) if (bochs->fb_map) iounmap(bochs->fb_map); pci_release_regions(dev->pdev); + kfree(bochs->edid); } void bochs_hw_setmode(struct bochs_device *bochs, diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index 9bc5b438aefd..f87c284dd93d 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -213,10 +213,17 @@ static void bochs_encoder_init(struct drm_device *dev) static int bochs_connector_get_modes(struct drm_connector *connector) { - int count; + struct bochs_device *bochs = + container_of(connector, struct bochs_device, connector); + int count = 0; + + if (bochs->edid) + count = drm_add_edid_modes(connector, bochs->edid); - count = drm_add_modes_noedid(connector, 8192, 8192); - drm_set_preferred_mode(connector, defx, defy); + if (!count) { + count = drm_add_modes_noedid(connector, 8192, 8192); + drm_set_preferred_mode(connector, defx, defy); + } return count; } @@ -271,6 +278,13 @@ static void bochs_connector_init(struct drm_device *dev) drm_connector_helper_add(connector, &bochs_connector_connector_helper_funcs); drm_connector_register(connector); + + bochs_hw_load_edid(bochs); + if (bochs->edid) { + DRM_INFO("Found EDID data blob.\n"); + drm_connector_attach_edid_property(connector); + drm_connector_update_edid_property(connector, bochs->edid); + } } diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c index a61c1ecb2bdc..e6ccf7fa92d4 100644 --- a/drivers/gpu/drm/bochs/bochs_mm.c +++ b/drivers/gpu/drm/bochs/bochs_mm.c @@ -414,7 +414,7 @@ int bochs_dumb_create(struct drm_file *file, struct drm_device *dev, return ret; ret = drm_gem_handle_create(file, gobj, &handle); - drm_gem_object_unreference_unlocked(gobj); + drm_gem_object_put_unlocked(gobj); if (ret) return ret; @@ -454,6 +454,6 @@ int bochs_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, bo = gem_to_bochs_bo(obj); *offset = bochs_bo_mmap_offset(bo); - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_put_unlocked(obj); return 0; } diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 2f21d3b6850b..753e96129ab7 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1219,12 +1219,12 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge) * plat_data->attch return, that's why we record the connector * point after plat attached. */ - if (dp->plat_data->attach) { - ret = dp->plat_data->attach(dp->plat_data, bridge, connector); - if (ret) { - DRM_ERROR("Failed at platform attch func\n"); - return ret; - } + if (dp->plat_data->attach) { + ret = dp->plat_data->attach(dp->plat_data, bridge, connector); + if (ret) { + DRM_ERROR("Failed at platform attach func\n"); + return ret; + } } if (dp->plat_data->panel) { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 5971976284bf..64c3cf027518 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -1664,6 +1664,7 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) case 0x131a: case 0x132a: case 0x201a: + case 0x212a: count = 1; break; default: @@ -1957,7 +1958,6 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = { static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { .get_modes = dw_hdmi_connector_get_modes, - .best_encoder = drm_atomic_helper_best_encoder, }; static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) @@ -2205,7 +2205,9 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) unsigned int i; u8 phy_type; - phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); + phy_type = hdmi->plat_data->phy_force_vendor ? + DW_HDMI_PHY_VENDOR_PHY : + hdmi_readb(hdmi, HDMI_CONFIG2_ID); if (phy_type == DW_HDMI_PHY_VENDOR_PHY) { /* Vendor PHYs require support from the glue layer. */ diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index fd7999642cf8..2f4b145b73af 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -230,10 +230,21 @@ struct dw_mipi_dsi { u32 format; unsigned long mode_flags; + struct dw_mipi_dsi *master; /* dual-dsi master ptr */ + struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */ + const struct dw_mipi_dsi_plat_data *plat_data; }; /* + * Check if either a link to a master or slave is present + */ +static inline bool dw_mipi_is_dual_mode(struct dw_mipi_dsi *dsi) +{ + return dsi->slave || dsi->master; +} + +/* * The controller should generate 2 frames before * preparing the peripheral. */ @@ -270,6 +281,7 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct dw_mipi_dsi *dsi = host_to_dsi(host); + const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data; struct drm_bridge *bridge; struct drm_panel *panel; int ret; @@ -300,6 +312,12 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, drm_bridge_add(&dsi->bridge); + if (pdata->host_ops && pdata->host_ops->attach) { + ret = pdata->host_ops->attach(pdata->priv_data, device); + if (ret < 0) + return ret; + } + return 0; } @@ -307,6 +325,14 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct dw_mipi_dsi *dsi = host_to_dsi(host); + const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data; + int ret; + + if (pdata->host_ops && pdata->host_ops->detach) { + ret = pdata->host_ops->detach(pdata->priv_data, device); + if (ret < 0) + return ret; + } drm_of_panel_bridge_remove(host->dev->of_node, 1, 0); @@ -441,10 +467,17 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host, } dw_mipi_message_config(dsi, msg); + if (dsi->slave) + dw_mipi_message_config(dsi->slave, msg); ret = dw_mipi_dsi_write(dsi, &packet); if (ret) return ret; + if (dsi->slave) { + ret = dw_mipi_dsi_write(dsi->slave, &packet); + if (ret) + return ret; + } if (msg->rx_buf && msg->rx_len) { ret = dw_mipi_dsi_read(dsi, msg); @@ -583,7 +616,11 @@ static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi, * DSI_VNPCR.NPSIZE... especially because this driver supports * non-burst video modes, see dw_mipi_dsi_video_mode_config()... */ - dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay)); + + dsi_write(dsi, DSI_VID_PKT_SIZE, + dw_mipi_is_dual_mode(dsi) ? + VID_PKT_SIZE(mode->hdisplay / 2) : + VID_PKT_SIZE(mode->hdisplay)); } static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi) @@ -755,24 +792,43 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge) */ dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge); + if (dsi->slave) { + dw_mipi_dsi_disable(dsi->slave); + clk_disable_unprepare(dsi->slave->pclk); + pm_runtime_put(dsi->slave->dev); + } dw_mipi_dsi_disable(dsi); + clk_disable_unprepare(dsi->pclk); pm_runtime_put(dsi->dev); } -static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi) +{ + /* this instance is the slave, so add the master's lanes */ + if (dsi->master) + return dsi->master->lanes + dsi->lanes; + + /* this instance is the master, so add the slave's lanes */ + if (dsi->slave) + return dsi->lanes + dsi->slave->lanes; + + /* single-dsi, so no other instance to consider */ + return dsi->lanes; +} + +static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi, + struct drm_display_mode *adjusted_mode) { - struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; void *priv_data = dsi->plat_data->priv_data; int ret; + u32 lanes = dw_mipi_dsi_get_lanes(dsi); clk_prepare_enable(dsi->pclk); ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi->mode_flags, - dsi->lanes, dsi->format, &dsi->lane_mbps); + lanes, dsi->format, &dsi->lane_mbps); if (ret) DRM_DEBUG_DRIVER("Phy get_lane_mbps() failed\n"); @@ -804,12 +860,25 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, dw_mipi_dsi_set_mode(dsi, 0); } +static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + + dw_mipi_dsi_mode_set(dsi, adjusted_mode); + if (dsi->slave) + dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode); +} + static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); /* Switch to video mode for panel-bridge enable & panel enable */ dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO); + if (dsi->slave) + dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO); } static enum drm_mode_status @@ -941,9 +1010,25 @@ __dw_mipi_dsi_probe(struct platform_device *pdev, static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi) { + mipi_dsi_host_unregister(&dsi->dsi_host); + pm_runtime_disable(dsi->dev); } +void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave) +{ + /* introduce controllers to each other */ + dsi->slave = slave; + dsi->slave->master = dsi; + + /* migrate settings for already attached displays */ + dsi->slave->lanes = dsi->lanes; + dsi->slave->channel = dsi->channel; + dsi->slave->format = dsi->format; + dsi->slave->mode_flags = dsi->mode_flags; +} +EXPORT_SYMBOL_GPL(dw_mipi_dsi_set_slave); + /* * Probe/remove API, used from platforms based on the DRM bridge API. */ @@ -957,8 +1042,6 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe); void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi) { - mipi_dsi_host_unregister(&dsi->dsi_host); - __dw_mipi_dsi_remove(dsi); } EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove); @@ -966,31 +1049,22 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove); /* * Bind/unbind API, used from platforms based on the component framework. */ -struct dw_mipi_dsi * -dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder, - const struct dw_mipi_dsi_plat_data *plat_data) +int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder) { - struct dw_mipi_dsi *dsi; int ret; - dsi = __dw_mipi_dsi_probe(pdev, plat_data); - if (IS_ERR(dsi)) - return dsi; - ret = drm_bridge_attach(encoder, &dsi->bridge, NULL); if (ret) { - dw_mipi_dsi_remove(dsi); DRM_ERROR("Failed to initialize bridge with drm\n"); - return ERR_PTR(ret); + return ret; } - return dsi; + return ret; } EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind); void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi) { - __dw_mipi_dsi_remove(dsi); } EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index d8b526b7932c..474b503a73a1 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -92,6 +92,17 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, } } +/* + * For connectors that support multiple encoders, either the + * .atomic_best_encoder() or .best_encoder() operation must be implemented. + */ +static struct drm_encoder * +pick_single_encoder_for_connector(struct drm_connector *connector) +{ + WARN_ON(connector->encoder_ids[1]); + return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); +} + static int handle_conflicting_encoders(struct drm_atomic_state *state, bool disable_conflicting_encoders) { @@ -119,7 +130,7 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, else if (funcs->best_encoder) new_encoder = funcs->best_encoder(connector); else - new_encoder = drm_atomic_helper_best_encoder(connector); + new_encoder = pick_single_encoder_for_connector(connector); if (new_encoder) { if (encoder_mask & drm_encoder_mask(new_encoder)) { @@ -336,7 +347,7 @@ update_connector_routing(struct drm_atomic_state *state, else if (funcs->best_encoder) new_encoder = funcs->best_encoder(connector); else - new_encoder = drm_atomic_helper_best_encoder(connector); + new_encoder = pick_single_encoder_for_connector(connector); if (!new_encoder) { DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n", @@ -3411,586 +3422,3 @@ fail: return ret; } EXPORT_SYMBOL(drm_atomic_helper_page_flip_target); - -/** - * drm_atomic_helper_best_encoder - Helper for - * &drm_connector_helper_funcs.best_encoder callback - * @connector: Connector control structure - * - * This is a &drm_connector_helper_funcs.best_encoder callback helper for - * connectors that support exactly 1 encoder, statically determined at driver - * init time. - */ -struct drm_encoder * -drm_atomic_helper_best_encoder(struct drm_connector *connector) -{ - WARN_ON(connector->encoder_ids[1]); - return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); -} -EXPORT_SYMBOL(drm_atomic_helper_best_encoder); - -/** - * DOC: atomic state reset and initialization - * - * Both the drm core and the atomic helpers assume that there is always the full - * and correct atomic software state for all connectors, CRTCs and planes - * available. Which is a bit a problem on driver load and also after system - * suspend. One way to solve this is to have a hardware state read-out - * infrastructure which reconstructs the full software state (e.g. the i915 - * driver). - * - * The simpler solution is to just reset the software state to everything off, - * which is easiest to do by calling drm_mode_config_reset(). To facilitate this - * the atomic helpers provide default reset implementations for all hooks. - * - * On the upside the precise state tracking of atomic simplifies system suspend - * and resume a lot. For drivers using drm_mode_config_reset() a complete recipe - * is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume(). - * For other drivers the building blocks are split out, see the documentation - * for these functions. - */ - -/** - * drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs - * @crtc: drm CRTC - * - * Resets the atomic state for @crtc by freeing the state pointer (which might - * be NULL, e.g. at driver load time) and allocating a new empty state object. - */ -void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) -{ - if (crtc->state) - __drm_atomic_helper_crtc_destroy_state(crtc->state); - - kfree(crtc->state); - crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); - - if (crtc->state) - crtc->state->crtc = crtc; -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); - -/** - * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state - * @crtc: CRTC object - * @state: atomic CRTC state - * - * Copies atomic state from a CRTC's current state and resets inferred values. - * This is useful for drivers that subclass the CRTC state. - */ -void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - memcpy(state, crtc->state, sizeof(*state)); - - if (state->mode_blob) - drm_property_blob_get(state->mode_blob); - if (state->degamma_lut) - drm_property_blob_get(state->degamma_lut); - if (state->ctm) - drm_property_blob_get(state->ctm); - if (state->gamma_lut) - drm_property_blob_get(state->gamma_lut); - state->mode_changed = false; - state->active_changed = false; - state->planes_changed = false; - state->connectors_changed = false; - state->color_mgmt_changed = false; - state->zpos_changed = false; - state->commit = NULL; - state->event = NULL; - state->pageflip_flags = 0; -} -EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); - -/** - * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook - * @crtc: drm CRTC - * - * Default CRTC state duplicate hook for drivers which don't have their own - * subclassed CRTC state structure. - */ -struct drm_crtc_state * -drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) -{ - struct drm_crtc_state *state; - - if (WARN_ON(!crtc->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_crtc_duplicate_state(crtc, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); - -/** - * __drm_atomic_helper_crtc_destroy_state - release CRTC state - * @state: CRTC state object to release - * - * Releases all resources stored in the CRTC state without actually freeing - * the memory of the CRTC state. This is useful for drivers that subclass the - * CRTC state. - */ -void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) -{ - if (state->commit) { - /* - * In the event that a non-blocking commit returns - * -ERESTARTSYS before the commit_tail work is queued, we will - * have an extra reference to the commit object. Release it, if - * the event has not been consumed by the worker. - * - * state->event may be freed, so we can't directly look at - * state->event->base.completion. - */ - if (state->event && state->commit->abort_completion) - drm_crtc_commit_put(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); - drm_property_blob_put(state->gamma_lut); -} -EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); - -/** - * drm_atomic_helper_crtc_destroy_state - default state destroy hook - * @crtc: drm CRTC - * @state: CRTC state object to release - * - * Default CRTC state destroy hook for drivers which don't have their own - * subclassed CRTC state structure. - */ -void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - __drm_atomic_helper_crtc_destroy_state(state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); - -/** - * __drm_atomic_helper_plane_reset - resets planes state to default values - * @plane: plane object, must not be NULL - * @state: atomic plane state, must not be NULL - * - * Initializes plane state to default. This is useful for drivers that subclass - * the plane state. - */ -void __drm_atomic_helper_plane_reset(struct drm_plane *plane, - struct drm_plane_state *state) -{ - state->plane = plane; - state->rotation = DRM_MODE_ROTATE_0; - - state->alpha = DRM_BLEND_ALPHA_OPAQUE; - state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; - - plane->state = state; -} -EXPORT_SYMBOL(__drm_atomic_helper_plane_reset); - -/** - * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes - * @plane: drm plane - * - * Resets the atomic state for @plane by freeing the state pointer (which might - * be NULL, e.g. at driver load time) and allocating a new empty state object. - */ -void drm_atomic_helper_plane_reset(struct drm_plane *plane) -{ - if (plane->state) - __drm_atomic_helper_plane_destroy_state(plane->state); - - kfree(plane->state); - plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); - if (plane->state) - __drm_atomic_helper_plane_reset(plane, plane->state); -} -EXPORT_SYMBOL(drm_atomic_helper_plane_reset); - -/** - * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state - * @plane: plane object - * @state: atomic plane state - * - * Copies atomic state from a plane's current state. This is useful for - * drivers that subclass the plane state. - */ -void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - memcpy(state, plane->state, sizeof(*state)); - - if (state->fb) - drm_framebuffer_get(state->fb); - - state->fence = NULL; - state->commit = NULL; -} -EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); - -/** - * drm_atomic_helper_plane_duplicate_state - default state duplicate hook - * @plane: drm plane - * - * Default plane state duplicate hook for drivers which don't have their own - * subclassed plane state structure. - */ -struct drm_plane_state * -drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) -{ - struct drm_plane_state *state; - - if (WARN_ON(!plane->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_plane_duplicate_state(plane, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); - -/** - * __drm_atomic_helper_plane_destroy_state - release plane state - * @state: plane state object to release - * - * Releases all resources stored in the plane state without actually freeing - * the memory of the plane state. This is useful for drivers that subclass the - * plane state. - */ -void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state) -{ - if (state->fb) - drm_framebuffer_put(state->fb); - - 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); - -/** - * drm_atomic_helper_plane_destroy_state - default state destroy hook - * @plane: drm plane - * @state: plane state object to release - * - * Default plane state destroy hook for drivers which don't have their own - * subclassed plane state structure. - */ -void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - __drm_atomic_helper_plane_destroy_state(state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); - -/** - * __drm_atomic_helper_connector_reset - reset state on connector - * @connector: drm connector - * @conn_state: connector state to assign - * - * Initializes the newly allocated @conn_state and assigns it to - * the &drm_conector->state pointer of @connector, usually required when - * initializing the drivers or when called from the &drm_connector_funcs.reset - * hook. - * - * This is useful for drivers that subclass the connector state. - */ -void -__drm_atomic_helper_connector_reset(struct drm_connector *connector, - struct drm_connector_state *conn_state) -{ - if (conn_state) - conn_state->connector = connector; - - connector->state = conn_state; -} -EXPORT_SYMBOL(__drm_atomic_helper_connector_reset); - -/** - * drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors - * @connector: drm connector - * - * Resets the atomic state for @connector by freeing the state pointer (which - * might be NULL, e.g. at driver load time) and allocating a new empty state - * object. - */ -void drm_atomic_helper_connector_reset(struct drm_connector *connector) -{ - struct drm_connector_state *conn_state = - kzalloc(sizeof(*conn_state), GFP_KERNEL); - - if (connector->state) - __drm_atomic_helper_connector_destroy_state(connector->state); - - kfree(connector->state); - __drm_atomic_helper_connector_reset(connector, conn_state); -} -EXPORT_SYMBOL(drm_atomic_helper_connector_reset); - -/** - * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state - * @connector: connector object - * @state: atomic connector state - * - * Copies atomic state from a connector's current state. This is useful for - * drivers that subclass the connector state. - */ -void -__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - memcpy(state, connector->state, sizeof(*state)); - if (state->crtc) - drm_connector_get(connector); - state->commit = NULL; - - /* Don't copy over a writeback job, they are used only once */ - state->writeback_job = NULL; -} -EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); - -/** - * drm_atomic_helper_connector_duplicate_state - default state duplicate hook - * @connector: drm connector - * - * Default connector state duplicate hook for drivers which don't have their own - * subclassed connector state structure. - */ -struct drm_connector_state * -drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) -{ - struct drm_connector_state *state; - - if (WARN_ON(!connector->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_connector_duplicate_state(connector, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); - -/** - * drm_atomic_helper_duplicate_state - duplicate an atomic state object - * @dev: DRM device - * @ctx: lock acquisition context - * - * Makes a copy of the current atomic state by looping over all objects and - * duplicating their respective states. This is used for example by suspend/ - * resume support code to save the state prior to suspend such that it can - * be restored upon resume. - * - * Note that this treats atomic state as persistent between save and restore. - * Drivers must make sure that this is possible and won't result in confusion - * or erroneous behaviour. - * - * Note that if callers haven't already acquired all modeset locks this might - * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). - * - * Returns: - * A pointer to the copy of the atomic state object on success or an - * ERR_PTR()-encoded error code on failure. - * - * See also: - * drm_atomic_helper_suspend(), drm_atomic_helper_resume() - */ -struct drm_atomic_state * -drm_atomic_helper_duplicate_state(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_atomic_state *state; - struct drm_connector *conn; - struct drm_connector_list_iter conn_iter; - struct drm_plane *plane; - struct drm_crtc *crtc; - int err = 0; - - state = drm_atomic_state_alloc(dev); - if (!state) - return ERR_PTR(-ENOMEM); - - state->acquire_ctx = ctx; - - drm_for_each_crtc(crtc, dev) { - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - err = PTR_ERR(crtc_state); - goto free; - } - } - - drm_for_each_plane(plane, dev) { - struct drm_plane_state *plane_state; - - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - err = PTR_ERR(plane_state); - goto free; - } - } - - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(conn, &conn_iter) { - struct drm_connector_state *conn_state; - - conn_state = drm_atomic_get_connector_state(state, conn); - if (IS_ERR(conn_state)) { - err = PTR_ERR(conn_state); - drm_connector_list_iter_end(&conn_iter); - goto free; - } - } - drm_connector_list_iter_end(&conn_iter); - - /* clear the acquire context so that it isn't accidentally reused */ - state->acquire_ctx = NULL; - -free: - if (err < 0) { - drm_atomic_state_put(state); - state = ERR_PTR(err); - } - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_duplicate_state); - -/** - * __drm_atomic_helper_connector_destroy_state - release connector state - * @state: connector state object to release - * - * Releases all resources stored in the connector state without actually - * freeing the memory of the connector state. This is useful for drivers that - * subclass the connector state. - */ -void -__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); - -/** - * drm_atomic_helper_connector_destroy_state - default state destroy hook - * @connector: drm connector - * @state: connector state object to release - * - * Default connector state destroy hook for drivers which don't have their own - * subclassed connector state structure. - */ -void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - __drm_atomic_helper_connector_destroy_state(state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); - -/** - * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table - * @crtc: CRTC object - * @red: red correction table - * @green: green correction table - * @blue: green correction table - * @size: size of the tables - * @ctx: lock acquire context - * - * Implements support for legacy gamma correction table for drivers - * that support color management through the DEGAMMA_LUT/GAMMA_LUT - * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for - * how the atomic color management and gamma tables work. - */ -int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, - u16 *red, u16 *green, u16 *blue, - uint32_t size, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_device *dev = crtc->dev; - struct drm_atomic_state *state; - struct drm_crtc_state *crtc_state; - struct drm_property_blob *blob = NULL; - struct drm_color_lut *blob_data; - int i, ret = 0; - bool replaced; - - state = drm_atomic_state_alloc(crtc->dev); - if (!state) - return -ENOMEM; - - blob = drm_property_create_blob(dev, - sizeof(struct drm_color_lut) * size, - NULL); - if (IS_ERR(blob)) { - ret = PTR_ERR(blob); - blob = NULL; - goto fail; - } - - /* Prepare GAMMA_LUT with the legacy values. */ - blob_data = blob->data; - for (i = 0; i < size; i++) { - blob_data[i].red = red[i]; - blob_data[i].green = green[i]; - blob_data[i].blue = blue[i]; - } - - state->acquire_ctx = ctx; - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto fail; - } - - /* Reset DEGAMMA_LUT and CTM properties. */ - replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL); - replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); - replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob); - crtc_state->color_mgmt_changed |= replaced; - - ret = drm_atomic_commit(state); - -fail: - drm_atomic_state_put(state); - drm_property_blob_put(blob); - return ret; -} -EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); - -/** - * __drm_atomic_helper_private_duplicate_state - copy atomic private state - * @obj: CRTC object - * @state: new private object state - * - * Copies atomic state from a private objects's current state and resets inferred values. - * This is useful for drivers that subclass the private state. - */ -void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, - struct drm_private_state *state) -{ - memcpy(state, obj->state, sizeof(*state)); -} -EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c new file mode 100644 index 000000000000..3ba996069d69 --- /dev/null +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2018 Intel Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rob Clark <robdclark@gmail.com> + * Daniel Vetter <daniel.vetter@ffwll.ch> + */ + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_plane.h> +#include <drm/drm_connector.h> +#include <drm/drm_atomic.h> +#include <drm/drm_device.h> + +#include <linux/slab.h> +#include <linux/dma-fence.h> + +/** + * DOC: atomic state reset and initialization + * + * Both the drm core and the atomic helpers assume that there is always the full + * and correct atomic software state for all connectors, CRTCs and planes + * available. Which is a bit a problem on driver load and also after system + * suspend. One way to solve this is to have a hardware state read-out + * infrastructure which reconstructs the full software state (e.g. the i915 + * driver). + * + * The simpler solution is to just reset the software state to everything off, + * which is easiest to do by calling drm_mode_config_reset(). To facilitate this + * the atomic helpers provide default reset implementations for all hooks. + * + * On the upside the precise state tracking of atomic simplifies system suspend + * and resume a lot. For drivers using drm_mode_config_reset() a complete recipe + * is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume(). + * For other drivers the building blocks are split out, see the documentation + * for these functions. + */ + +/** + * drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs + * @crtc: drm CRTC + * + * Resets the atomic state for @crtc by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) +{ + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(crtc->state); + crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); + + if (crtc->state) + crtc->state->crtc = crtc; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); + +/** + * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state + * @crtc: CRTC object + * @state: atomic CRTC state + * + * Copies atomic state from a CRTC's current state and resets inferred values. + * This is useful for drivers that subclass the CRTC state. + */ +void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + memcpy(state, crtc->state, sizeof(*state)); + + if (state->mode_blob) + drm_property_blob_get(state->mode_blob); + if (state->degamma_lut) + drm_property_blob_get(state->degamma_lut); + if (state->ctm) + drm_property_blob_get(state->ctm); + if (state->gamma_lut) + drm_property_blob_get(state->gamma_lut); + state->mode_changed = false; + state->active_changed = false; + state->planes_changed = false; + state->connectors_changed = false; + state->color_mgmt_changed = false; + state->zpos_changed = false; + state->commit = NULL; + state->event = NULL; + state->pageflip_flags = 0; +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); + +/** + * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook + * @crtc: drm CRTC + * + * Default CRTC state duplicate hook for drivers which don't have their own + * subclassed CRTC state structure. + */ +struct drm_crtc_state * +drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + + if (WARN_ON(!crtc->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_duplicate_state(crtc, state); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); + +/** + * __drm_atomic_helper_crtc_destroy_state - release CRTC state + * @state: CRTC state object to release + * + * Releases all resources stored in the CRTC state without actually freeing + * the memory of the CRTC state. This is useful for drivers that subclass the + * CRTC state. + */ +void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) +{ + if (state->commit) { + /* + * In the event that a non-blocking commit returns + * -ERESTARTSYS before the commit_tail work is queued, we will + * have an extra reference to the commit object. Release it, if + * the event has not been consumed by the worker. + * + * state->event may be freed, so we can't directly look at + * state->event->base.completion. + */ + if (state->event && state->commit->abort_completion) + drm_crtc_commit_put(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); + drm_property_blob_put(state->gamma_lut); +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); + +/** + * drm_atomic_helper_crtc_destroy_state - default state destroy hook + * @crtc: drm CRTC + * @state: CRTC state object to release + * + * Default CRTC state destroy hook for drivers which don't have their own + * subclassed CRTC state structure. + */ +void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + __drm_atomic_helper_crtc_destroy_state(state); + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); + +/** + * __drm_atomic_helper_plane_reset - resets planes state to default values + * @plane: plane object, must not be NULL + * @state: atomic plane state, must not be NULL + * + * Initializes plane state to default. This is useful for drivers that subclass + * the plane state. + */ +void __drm_atomic_helper_plane_reset(struct drm_plane *plane, + struct drm_plane_state *state) +{ + state->plane = plane; + state->rotation = DRM_MODE_ROTATE_0; + + state->alpha = DRM_BLEND_ALPHA_OPAQUE; + state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; + + plane->state = state; +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_reset); + +/** + * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes + * @plane: drm plane + * + * Resets the atomic state for @plane by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void drm_atomic_helper_plane_reset(struct drm_plane *plane) +{ + if (plane->state) + __drm_atomic_helper_plane_destroy_state(plane->state); + + kfree(plane->state); + plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); + if (plane->state) + __drm_atomic_helper_plane_reset(plane, plane->state); +} +EXPORT_SYMBOL(drm_atomic_helper_plane_reset); + +/** + * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state + * @plane: plane object + * @state: atomic plane state + * + * Copies atomic state from a plane's current state. This is useful for + * drivers that subclass the plane state. + */ +void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + memcpy(state, plane->state, sizeof(*state)); + + if (state->fb) + drm_framebuffer_get(state->fb); + + state->fence = NULL; + state->commit = NULL; +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); + +/** + * drm_atomic_helper_plane_duplicate_state - default state duplicate hook + * @plane: drm plane + * + * Default plane state duplicate hook for drivers which don't have their own + * subclassed plane state structure. + */ +struct drm_plane_state * +drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + + if (WARN_ON(!plane->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_plane_duplicate_state(plane, state); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); + +/** + * __drm_atomic_helper_plane_destroy_state - release plane state + * @state: plane state object to release + * + * Releases all resources stored in the plane state without actually freeing + * the memory of the plane state. This is useful for drivers that subclass the + * plane state. + */ +void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state) +{ + if (state->fb) + drm_framebuffer_put(state->fb); + + 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); + +/** + * drm_atomic_helper_plane_destroy_state - default state destroy hook + * @plane: drm plane + * @state: plane state object to release + * + * Default plane state destroy hook for drivers which don't have their own + * subclassed plane state structure. + */ +void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + __drm_atomic_helper_plane_destroy_state(state); + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); + +/** + * __drm_atomic_helper_connector_reset - reset state on connector + * @connector: drm connector + * @conn_state: connector state to assign + * + * Initializes the newly allocated @conn_state and assigns it to + * the &drm_conector->state pointer of @connector, usually required when + * initializing the drivers or when called from the &drm_connector_funcs.reset + * hook. + * + * This is useful for drivers that subclass the connector state. + */ +void +__drm_atomic_helper_connector_reset(struct drm_connector *connector, + struct drm_connector_state *conn_state) +{ + if (conn_state) + conn_state->connector = connector; + + connector->state = conn_state; +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_reset); + +/** + * drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors + * @connector: drm connector + * + * Resets the atomic state for @connector by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void drm_atomic_helper_connector_reset(struct drm_connector *connector) +{ + struct drm_connector_state *conn_state = + kzalloc(sizeof(*conn_state), GFP_KERNEL); + + if (connector->state) + __drm_atomic_helper_connector_destroy_state(connector->state); + + kfree(connector->state); + __drm_atomic_helper_connector_reset(connector, conn_state); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_reset); + +/** + * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state + * @connector: connector object + * @state: atomic connector state + * + * Copies atomic state from a connector's current state. This is useful for + * drivers that subclass the connector state. + */ +void +__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + memcpy(state, connector->state, sizeof(*state)); + if (state->crtc) + drm_connector_get(connector); + state->commit = NULL; + + /* Don't copy over a writeback job, they are used only once */ + state->writeback_job = NULL; +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); + +/** + * drm_atomic_helper_connector_duplicate_state - default state duplicate hook + * @connector: drm connector + * + * Default connector state duplicate hook for drivers which don't have their own + * subclassed connector state structure. + */ +struct drm_connector_state * +drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) +{ + struct drm_connector_state *state; + + if (WARN_ON(!connector->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_connector_duplicate_state(connector, state); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); + +/** + * drm_atomic_helper_duplicate_state - duplicate an atomic state object + * @dev: DRM device + * @ctx: lock acquisition context + * + * Makes a copy of the current atomic state by looping over all objects and + * duplicating their respective states. This is used for example by suspend/ + * resume support code to save the state prior to suspend such that it can + * be restored upon resume. + * + * Note that this treats atomic state as persistent between save and restore. + * Drivers must make sure that this is possible and won't result in confusion + * or erroneous behaviour. + * + * Note that if callers haven't already acquired all modeset locks this might + * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). + * + * Returns: + * A pointer to the copy of the atomic state object on success or an + * ERR_PTR()-encoded error code on failure. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() + */ +struct drm_atomic_state * +drm_atomic_helper_duplicate_state(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_connector *conn; + struct drm_connector_list_iter conn_iter; + struct drm_plane *plane; + struct drm_crtc *crtc; + int err = 0; + + state = drm_atomic_state_alloc(dev); + if (!state) + return ERR_PTR(-ENOMEM); + + state->acquire_ctx = ctx; + + drm_for_each_crtc(crtc, dev) { + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + err = PTR_ERR(crtc_state); + goto free; + } + } + + drm_for_each_plane(plane, dev) { + struct drm_plane_state *plane_state; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + err = PTR_ERR(plane_state); + goto free; + } + } + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(conn, &conn_iter) { + struct drm_connector_state *conn_state; + + conn_state = drm_atomic_get_connector_state(state, conn); + if (IS_ERR(conn_state)) { + err = PTR_ERR(conn_state); + drm_connector_list_iter_end(&conn_iter); + goto free; + } + } + drm_connector_list_iter_end(&conn_iter); + + /* clear the acquire context so that it isn't accidentally reused */ + state->acquire_ctx = NULL; + +free: + if (err < 0) { + drm_atomic_state_put(state); + state = ERR_PTR(err); + } + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_duplicate_state); + +/** + * __drm_atomic_helper_connector_destroy_state - release connector state + * @state: connector state object to release + * + * Releases all resources stored in the connector state without actually + * freeing the memory of the connector state. This is useful for drivers that + * subclass the connector state. + */ +void +__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); + +/** + * drm_atomic_helper_connector_destroy_state - default state destroy hook + * @connector: drm connector + * @state: connector state object to release + * + * Default connector state destroy hook for drivers which don't have their own + * subclassed connector state structure. + */ +void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + __drm_atomic_helper_connector_destroy_state(state); + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); + +/** + * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table + * @crtc: CRTC object + * @red: red correction table + * @green: green correction table + * @blue: green correction table + * @size: size of the tables + * @ctx: lock acquire context + * + * Implements support for legacy gamma correction table for drivers + * that support color management through the DEGAMMA_LUT/GAMMA_LUT + * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for + * how the atomic color management and gamma tables work. + */ +int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_device *dev = crtc->dev; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_property_blob *blob = NULL; + struct drm_color_lut *blob_data; + int i, ret = 0; + bool replaced; + + state = drm_atomic_state_alloc(crtc->dev); + if (!state) + return -ENOMEM; + + blob = drm_property_create_blob(dev, + sizeof(struct drm_color_lut) * size, + NULL); + if (IS_ERR(blob)) { + ret = PTR_ERR(blob); + blob = NULL; + goto fail; + } + + /* Prepare GAMMA_LUT with the legacy values. */ + blob_data = blob->data; + for (i = 0; i < size; i++) { + blob_data[i].red = red[i]; + blob_data[i].green = green[i]; + blob_data[i].blue = blue[i]; + } + + state->acquire_ctx = ctx; + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto fail; + } + + /* Reset DEGAMMA_LUT and CTM properties. */ + replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL); + replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); + replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob); + crtc_state->color_mgmt_changed |= replaced; + + ret = drm_atomic_commit(state); + +fail: + drm_atomic_state_put(state); + drm_property_blob_put(blob); + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); + +/** + * __drm_atomic_helper_private_duplicate_state - copy atomic private state + * @obj: CRTC object + * @state: new private object state + * + * Copies atomic state from a private objects's current state and resets inferred values. + * This is useful for drivers that subclass the private state. + */ +void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + memcpy(state, obj->state, sizeof(*state)); +} +EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state); diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 7412acaf3cde..d7d10cabb9bb 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -36,6 +36,8 @@ #include <drm/drmP.h> #include "drm_legacy.h" +#include <linux/nospec.h> + static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, struct drm_local_map *map) { @@ -1417,6 +1419,7 @@ int drm_legacy_freebufs(struct drm_device *dev, void *data, idx, dma->buf_count - 1); return -EINVAL; } + idx = array_index_nospec(idx, dma->buf_count); buf = dma->buflist[idx]; if (buf->file_priv != file_priv) { DRM_ERROR("Process %d freeing buffer not owned\n", diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 4943cef178be..aa18b1d7d3e4 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -260,9 +260,7 @@ int drm_connector_init(struct drm_device *dev, if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && connector_type != DRM_MODE_CONNECTOR_WRITEBACK) - drm_object_attach_property(&connector->base, - config->edid_property, - 0); + drm_connector_attach_edid_property(connector); drm_object_attach_property(&connector->base, config->dpms_property, 0); @@ -295,6 +293,24 @@ out_put: EXPORT_SYMBOL(drm_connector_init); /** + * drm_connector_attach_edid_property - attach edid property. + * @connector: the connector + * + * Some connector types like DRM_MODE_CONNECTOR_VIRTUAL do not get a + * edid property attached by default. This function can be used to + * explicitly enable the edid property in these cases. + */ +void drm_connector_attach_edid_property(struct drm_connector *connector) +{ + struct drm_mode_config *config = &connector->dev->mode_config; + + drm_object_attach_property(&connector->base, + config->edid_property, + 0); +} +EXPORT_SYMBOL(drm_connector_attach_edid_property); + +/** * drm_connector_attach_encoder - attach a connector to an encoder * @connector: connector to attach * @encoder: encoder to attach @connector to diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index ce75e9506e85..a3c81850e755 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -984,118 +984,3 @@ void drm_helper_resume_force_mode(struct drm_device *dev) drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_helper_resume_force_mode); - -/** - * drm_helper_crtc_mode_set - mode_set implementation for atomic plane helpers - * @crtc: DRM CRTC - * @mode: DRM display mode which userspace requested - * @adjusted_mode: DRM display mode adjusted by ->mode_fixup callbacks - * @x: x offset of the CRTC scanout area on the underlying framebuffer - * @y: y offset of the CRTC scanout area on the underlying framebuffer - * @old_fb: previous framebuffer - * - * This function implements a callback useable as the ->mode_set callback - * required by the CRTC helpers. Besides the atomic plane helper functions for - * the primary plane the driver must also provide the ->mode_set_nofb callback - * to set up the CRTC. - * - * This is a transitional helper useful for converting drivers to the atomic - * interfaces. - */ -int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_crtc_state *crtc_state; - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - int ret; - - if (crtc->funcs->atomic_duplicate_state) - crtc_state = crtc->funcs->atomic_duplicate_state(crtc); - else { - if (!crtc->state) - drm_atomic_helper_crtc_reset(crtc); - - crtc_state = drm_atomic_helper_crtc_duplicate_state(crtc); - } - - if (!crtc_state) - return -ENOMEM; - - crtc_state->planes_changed = true; - crtc_state->mode_changed = true; - ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); - if (ret) - goto out; - drm_mode_copy(&crtc_state->adjusted_mode, adjusted_mode); - - if (crtc_funcs->atomic_check) { - ret = crtc_funcs->atomic_check(crtc, crtc_state); - if (ret) - goto out; - } - - swap(crtc->state, crtc_state); - - crtc_funcs->mode_set_nofb(crtc); - - ret = drm_helper_crtc_mode_set_base(crtc, x, y, old_fb); - -out: - if (crtc_state) { - if (crtc->funcs->atomic_destroy_state) - crtc->funcs->atomic_destroy_state(crtc, crtc_state); - else - drm_atomic_helper_crtc_destroy_state(crtc, crtc_state); - } - - return ret; -} -EXPORT_SYMBOL(drm_helper_crtc_mode_set); - -/** - * drm_helper_crtc_mode_set_base - mode_set_base implementation for atomic plane helpers - * @crtc: DRM CRTC - * @x: x offset of the CRTC scanout area on the underlying framebuffer - * @y: y offset of the CRTC scanout area on the underlying framebuffer - * @old_fb: previous framebuffer - * - * This function implements a callback useable as the ->mode_set_base used - * required by the CRTC helpers. The driver must provide the atomic plane helper - * functions for the primary plane. - * - * This is a transitional helper useful for converting drivers to the atomic - * interfaces. - */ -int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_plane_state *plane_state; - struct drm_plane *plane = crtc->primary; - - if (plane->funcs->atomic_duplicate_state) - plane_state = plane->funcs->atomic_duplicate_state(plane); - else { - if (!plane->state) - drm_atomic_helper_plane_reset(plane); - - plane_state = drm_atomic_helper_plane_duplicate_state(plane); - } - if (!plane_state) - return -ENOMEM; - plane_state->plane = plane; - - plane_state->crtc = crtc; - drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb); - plane_state->crtc_x = 0; - plane_state->crtc_y = 0; - plane_state->crtc_h = crtc->mode.vdisplay; - plane_state->crtc_w = crtc->mode.hdisplay; - plane_state->src_x = x << 16; - plane_state->src_y = y << 16; - plane_state->src_h = crtc->mode.vdisplay << 16; - plane_state->src_w = crtc->mode.hdisplay << 16; - - return drm_plane_helper_commit(plane, plane_state, old_fb); -} -EXPORT_SYMBOL(drm_helper_crtc_mode_set_base); diff --git a/drivers/gpu/drm/drm_dp_cec.c b/drivers/gpu/drm/drm_dp_cec.c index 8a718f85079a..b15cee85b702 100644 --- a/drivers/gpu/drm/drm_dp_cec.c +++ b/drivers/gpu/drm/drm_dp_cec.c @@ -424,8 +424,6 @@ void drm_dp_cec_register_connector(struct drm_dp_aux *aux, const char *name, aux->cec.parent = parent; INIT_DELAYED_WORK(&aux->cec.unregister_work, drm_dp_cec_unregister_work); - - drm_dp_cec_set_edid(aux, NULL); } EXPORT_SYMBOL(drm_dp_cec_register_connector); diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 0e0df398222d..529414556962 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -2572,9 +2572,16 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_ EXPORT_SYMBOL(drm_dp_mst_get_edid); /** - * drm_dp_find_vcpi_slots() - find slots for this PBN value + * drm_dp_find_vcpi_slots() - Find VCPI slots for this PBN value * @mgr: manager to use * @pbn: payload bandwidth to convert into slots. + * + * Calculate the number of VCPI slots that will be required for the given PBN + * value. This function is deprecated, and should not be used in atomic + * drivers. + * + * RETURNS: + * The total slots required for this port, or error. */ int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int pbn) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 36e8e9cbec52..aa1cef794f9a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -476,8 +476,6 @@ static void drm_fs_inode_free(struct inode *inode) * The initial ref-count of the object is 1. Use drm_dev_get() and * drm_dev_put() to take and drop further ref-counts. * - * Note that for purely virtual devices @parent can be NULL. - * * Drivers that do not want to allocate their own device struct * embedding &struct drm_device can call drm_dev_alloc() instead. For drivers * that do embed &struct drm_device it must be placed first in the overall @@ -502,6 +500,8 @@ int drm_dev_init(struct drm_device *dev, return -ENODEV; } + BUG_ON(!parent); + kref_init(&dev->ref); dev->dev = parent; dev->driver = driver; @@ -556,9 +556,7 @@ int drm_dev_init(struct drm_device *dev, } } - /* Use the parent device name as DRM device unique identifier, but fall - * back to the driver name for virtual devices like vgem. */ - ret = drm_dev_set_unique(dev, parent ? dev_name(parent) : driver->name); + ret = drm_dev_set_unique(dev, dev_name(parent)); if (ret) goto err_setunique; diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index fb0dfc62b1b6..5b516615881a 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -72,7 +72,9 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); /** - * drm_fb_cma_get_gem_addr() - Get physical address for framebuffer + * drm_fb_cma_get_gem_addr() - Get physical address for framebuffer, for pixel + * formats where values are grouped in blocks this will get you the beginning of + * the block * @fb: The framebuffer * @state: Which state of drm plane * @plane: Which plane @@ -87,6 +89,13 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, struct drm_gem_cma_object *obj; dma_addr_t paddr; u8 h_div = 1, v_div = 1; + u32 block_w = drm_format_info_block_width(fb->format, plane); + u32 block_h = drm_format_info_block_height(fb->format, plane); + u32 block_size = fb->format->char_per_block[plane]; + u32 sample_x; + u32 sample_y; + u32 block_start_y; + u32 num_hblocks; obj = drm_fb_cma_get_gem_obj(fb, plane); if (!obj) @@ -99,8 +108,13 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, v_div = fb->format->vsub; } - paddr += (fb->format->cpp[plane] * (state->src_x >> 16)) / h_div; - paddr += (fb->pitches[plane] * (state->src_y >> 16)) / v_div; + sample_x = (state->src_x >> 16) / h_div; + sample_y = (state->src_y >> 16) / v_div; + block_start_y = (sample_y / block_h) * block_h; + num_hblocks = sample_x / block_w; + + paddr += fb->pitches[plane] * block_start_y; + paddr += block_size * num_hblocks; return paddr; } @@ -124,10 +138,7 @@ int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp, /* dev->fb_helper will indirectly point to fbdev_cma after this call */ fbdev_cma = drm_fbdev_cma_init(dev, preferred_bpp, max_conn_count); - if (IS_ERR(fbdev_cma)) - return PTR_ERR(fbdev_cma); - - return 0; + return PTR_ERR_OR_ZERO(fbdev_cma); } EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init); @@ -226,21 +237,3 @@ void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma) drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event); - -/** - * drm_fbdev_cma_set_suspend_unlocked - wrapper around - * drm_fb_helper_set_suspend_unlocked - * @fbdev_cma: The drm_fbdev_cma struct, may be NULL - * @state: desired state, zero to resume, non-zero to suspend - * - * Calls drm_fb_helper_set_suspend, which is a wrapper around - * fb_set_suspend implemented by fbdev core. - */ -void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma *fbdev_cma, - bool state) -{ - if (fbdev_cma) - drm_fb_helper_set_suspend_unlocked(&fbdev_cma->fb_helper, - state); -} -EXPORT_SYMBOL(drm_fbdev_cma_set_suspend_unlocked); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index a502f3e519fd..9a69ad7e9f3b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1632,6 +1632,10 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, if (var->pixclock != 0 || in_dbg_master()) return -EINVAL; + if ((drm_format_info_block_width(fb->format, 0) > 1) || + (drm_format_info_block_height(fb->format, 0) > 1)) + return -EINVAL; + /* * Changes struct fb_var_screeninfo are currently not pushed back * to KMS, hence fail if different settings are requested. @@ -1949,6 +1953,8 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe { struct drm_framebuffer *fb = fb_helper->fb; + WARN_ON((drm_format_info_block_width(fb->format, 0) > 1) || + (drm_format_info_block_height(fb->format, 0) > 1)); info->pseudo_palette = fb_helper->pseudo_palette; info->var.xres_virtual = fb->width; info->var.yres_virtual = fb->height; diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 8aaa5e86a979..f523948c82b1 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -103,8 +103,8 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); * * Computes a drm fourcc pixel format code for the given @bpp/@depth values. * Unlike drm_mode_legacy_fb_format() this looks at the drivers mode_config, - * and depending on the quirk_addfb_prefer_host_byte_order flag it returns - * little endian byte order or host byte order framebuffer formats. + * and depending on the &drm_mode_config.quirk_addfb_prefer_host_byte_order flag + * it returns little endian byte order or host byte order framebuffer formats. */ uint32_t drm_driver_legacy_fb_format(struct drm_device *dev, uint32_t bpp, uint32_t depth) @@ -225,6 +225,18 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_UYVY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true }, + { .format = DRM_FORMAT_Y0L0, .depth = 0, .num_planes = 1, + .char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 }, + .hsub = 2, .vsub = 2, .has_alpha = true, .is_yuv = true }, + { .format = DRM_FORMAT_X0L0, .depth = 0, .num_planes = 1, + .char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 }, + .hsub = 2, .vsub = 2, .is_yuv = true }, + { .format = DRM_FORMAT_Y0L2, .depth = 0, .num_planes = 1, + .char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 }, + .hsub = 2, .vsub = 2, .has_alpha = true, .is_yuv = true }, + { .format = DRM_FORMAT_X0L2, .depth = 0, .num_planes = 1, + .char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 }, + .hsub = 2, .vsub = 2, .is_yuv = true }, }; unsigned int i; @@ -400,3 +412,65 @@ int drm_format_plane_height(int height, uint32_t format, int plane) return height / info->vsub; } EXPORT_SYMBOL(drm_format_plane_height); + +/** + * drm_format_info_block_width - width in pixels of block. + * @info: pixel format info + * @plane: plane index + * + * Returns: + * The width in pixels of a block, depending on the plane index. + */ +unsigned int drm_format_info_block_width(const struct drm_format_info *info, + int plane) +{ + if (!info || plane < 0 || plane >= info->num_planes) + return 0; + + if (!info->block_w[plane]) + return 1; + return info->block_w[plane]; +} +EXPORT_SYMBOL(drm_format_info_block_width); + +/** + * drm_format_info_block_height - height in pixels of a block + * @info: pixel format info + * @plane: plane index + * + * Returns: + * The height in pixels of a block, depending on the plane index. + */ +unsigned int drm_format_info_block_height(const struct drm_format_info *info, + int plane) +{ + if (!info || plane < 0 || plane >= info->num_planes) + return 0; + + if (!info->block_h[plane]) + return 1; + return info->block_h[plane]; +} +EXPORT_SYMBOL(drm_format_info_block_height); + +/** + * drm_format_info_min_pitch - computes the minimum required pitch in bytes + * @info: pixel format info + * @plane: plane index + * @buffer_width: buffer width in pixels + * + * Returns: + * The minimum required pitch in bytes for a buffer by taking into consideration + * the pixel format information and the buffer width. + */ +uint64_t drm_format_info_min_pitch(const struct drm_format_info *info, + int plane, unsigned int buffer_width) +{ + if (!info || plane < 0 || plane >= info->num_planes) + return 0; + + return DIV_ROUND_UP_ULL((u64)buffer_width * info->char_per_block[plane], + drm_format_info_block_width(info, plane) * + drm_format_info_block_height(info, plane)); +} +EXPORT_SYMBOL(drm_format_info_min_pitch); diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 3bf729d0aae5..fcaea8f50513 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -195,20 +195,26 @@ static int framebuffer_check(struct drm_device *dev, for (i = 0; i < info->num_planes; i++) { unsigned int width = fb_plane_width(r->width, info, i); unsigned int height = fb_plane_height(r->height, info, i); - unsigned int cpp = info->cpp[i]; + unsigned int block_size = info->char_per_block[i]; + u64 min_pitch = drm_format_info_min_pitch(info, i, width); + + if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) { + DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i); + return -EINVAL; + } if (!r->handles[i]) { DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); return -EINVAL; } - if ((uint64_t) width * cpp > UINT_MAX) + if (min_pitch > UINT_MAX) return -ERANGE; if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) return -ERANGE; - if (r->pitches[i] < width * cpp) { + if (block_size && r->pitches[i] < min_pitch) { DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); return -EINVAL; } @@ -317,6 +323,7 @@ drm_internal_framebuffer_create(struct drm_device *dev, return fb; } +EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create); /** * drm_mode_addfb2 - add an FB to the graphics configuration diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index ded7a379ac35..acb466d25afc 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -171,7 +171,7 @@ drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file, } min_size = (height - 1) * mode_cmd->pitches[i] - + width * info->cpp[i] + + drm_format_info_min_pitch(info, i, width) + mode_cmd->offsets[i]; if (objs[i]->size < min_size) { diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c index 24a177ea5417..3650d3c46718 100644 --- a/drivers/gpu/drm/drm_lease.c +++ b/drivers/gpu/drm/drm_lease.c @@ -39,7 +39,6 @@ struct drm_master *drm_lease_owner(struct drm_master *master) master = master->lessor; return master; } -EXPORT_SYMBOL(drm_lease_owner); /** * _drm_find_lessee - find lessee by id (idr_mutex held) @@ -117,7 +116,6 @@ bool _drm_lease_held(struct drm_file *file_priv, int id) return _drm_lease_held_master(file_priv->master, id); } -EXPORT_SYMBOL(_drm_lease_held); /** * drm_lease_held - check drm_mode_object lease status (idr_mutex not held) @@ -144,7 +142,6 @@ bool drm_lease_held(struct drm_file *file_priv, int id) mutex_unlock(&master->dev->mode_config.idr_mutex); return ret; } -EXPORT_SYMBOL(drm_lease_held); /** * drm_lease_filter_crtcs - restricted crtc set to leased values (idr_mutex not held) @@ -184,7 +181,6 @@ uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs_in) mutex_unlock(&master->dev->mode_config.idr_mutex); return crtcs_out; } -EXPORT_SYMBOL(drm_lease_filter_crtcs); /* * drm_lease_create - create a new drm_master with leased objects (idr_mutex not held) @@ -195,7 +191,7 @@ EXPORT_SYMBOL(drm_lease_filter_crtcs); * make sure all of the desired objects can be leased, atomically * leasing them to the new drmmaster. * - * ERR_PTR(-EACCESS) some other master holds the title to any object + * ERR_PTR(-EACCES) some other master holds the title to any object * ERR_PTR(-ENOENT) some object is not a valid DRM object for this device * ERR_PTR(-EBUSY) some other lessee holds title to this object * ERR_PTR(-EEXIST) same object specified more than once in the provided list @@ -357,9 +353,9 @@ void drm_lease_revoke(struct drm_master *top) } static int validate_lease(struct drm_device *dev, - struct drm_file *lessor_priv, int object_count, - struct drm_mode_object **objects) + struct drm_mode_object **objects, + bool universal_planes) { int o; int has_crtc = -1; @@ -376,14 +372,14 @@ static int validate_lease(struct drm_device *dev, if (objects[o]->type == DRM_MODE_OBJECT_CONNECTOR && has_connector == -1) has_connector = o; - if (lessor_priv->universal_planes) { + if (universal_planes) { if (objects[o]->type == DRM_MODE_OBJECT_PLANE && has_plane == -1) has_plane = o; } } if (has_crtc == -1 || has_connector == -1) return -EINVAL; - if (lessor_priv->universal_planes && has_plane == -1) + if (universal_planes && has_plane == -1) return -EINVAL; return 0; } @@ -397,6 +393,8 @@ static int fill_object_idr(struct drm_device *dev, struct drm_mode_object **objects; u32 o; int ret; + bool universal_planes = READ_ONCE(lessor_priv->universal_planes); + objects = kcalloc(object_count, sizeof(struct drm_mode_object *), GFP_KERNEL); if (!objects) @@ -419,14 +417,17 @@ static int fill_object_idr(struct drm_device *dev, } if (!drm_mode_object_lease_required(objects[o]->type)) { + DRM_DEBUG_KMS("invalid object for lease\n"); ret = -EINVAL; goto out_free_objects; } } - ret = validate_lease(dev, lessor_priv, object_count, objects); - if (ret) + ret = validate_lease(dev, object_count, objects, universal_planes); + if (ret) { + DRM_DEBUG_LEASE("lease validation failed\n"); goto out_free_objects; + } /* add their IDs to the lease request - taking into account universal planes */ @@ -449,7 +450,7 @@ static int fill_object_idr(struct drm_device *dev, object_id, ret); goto out_free_objects; } - if (obj->type == DRM_MODE_OBJECT_CRTC && !lessor_priv->universal_planes) { + if (obj->type == DRM_MODE_OBJECT_CRTC && !universal_planes) { struct drm_crtc *crtc = obj_to_crtc(obj); ret = idr_alloc(leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL); if (ret < 0) { @@ -509,15 +510,21 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev, return -EOPNOTSUPP; /* Do not allow sub-leases */ - if (lessor->lessor) + if (lessor->lessor) { + DRM_DEBUG_LEASE("recursive leasing not allowed\n"); return -EINVAL; + } /* need some objects */ - if (cl->object_count == 0) + if (cl->object_count == 0) { + DRM_DEBUG_LEASE("no objects in lease\n"); return -EINVAL; + } - if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) + if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) { + DRM_DEBUG_LEASE("invalid flags\n"); return -EINVAL; + } object_count = cl->object_count; @@ -532,6 +539,7 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev, object_count, object_ids); kfree(object_ids); if (ret) { + DRM_DEBUG_LEASE("lease object lookup failed: %i\n", ret); idr_destroy(&leases); return ret; } diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c index d69e4fc1ee77..40c4349cb939 100644 --- a/drivers/gpu/drm/drm_memory.c +++ b/drivers/gpu/drm/drm_memory.c @@ -51,7 +51,7 @@ #endif static void *agp_remap(unsigned long offset, unsigned long size, - struct drm_device * dev) + struct drm_device *dev) { unsigned long i, num_pages = PAGE_ALIGN(size) / PAGE_SIZE; @@ -94,26 +94,26 @@ static void *agp_remap(unsigned long offset, unsigned long size, } /** Wrapper around agp_free_memory() */ -void drm_free_agp(struct agp_memory * handle, int pages) +void drm_free_agp(struct agp_memory *handle, int pages) { agp_free_memory(handle); } /** Wrapper around agp_bind_memory() */ -int drm_bind_agp(struct agp_memory * handle, unsigned int start) +int drm_bind_agp(struct agp_memory *handle, unsigned int start) { return agp_bind_memory(handle, start); } /** Wrapper around agp_unbind_memory() */ -int drm_unbind_agp(struct agp_memory * handle) +int drm_unbind_agp(struct agp_memory *handle) { return agp_unbind_memory(handle); } #else /* CONFIG_AGP */ static inline void *agp_remap(unsigned long offset, unsigned long size, - struct drm_device * dev) + struct drm_device *dev) { return NULL; } diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index be8b754eaf60..cd9bc0ce9be0 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -38,7 +38,8 @@ int __drm_mode_object_add(struct drm_device *dev, struct drm_mode_object *obj, int ret; mutex_lock(&dev->mode_config.idr_mutex); - ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); + ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, + 1, 0, GFP_KERNEL); if (ret >= 0) { /* * Set up the object linking under the protection of the idr diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 02db9ac82d7a..24a750436559 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -716,8 +716,8 @@ int of_get_drm_display_mode(struct device_node *np, if (bus_flags) drm_bus_flags_from_videomode(&vm, bus_flags); - pr_debug("%pOF: got %dx%d display mode from %s\n", - np, vm.hactive, vm.vactive, np->name); + pr_debug("%pOF: got %dx%d display mode\n", + np, vm.hactive, vm.vactive); drm_mode_debug_printmodeline(dmode); return 0; diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c index f1c24ab0ef09..9150fa385bba 100644 --- a/drivers/gpu/drm/drm_modeset_helper.c +++ b/drivers/gpu/drm/drm_modeset_helper.c @@ -146,6 +146,21 @@ static struct drm_plane *create_primary_plane(struct drm_device *dev) * Initialize a CRTC object with a default helper-provided primary plane and no * cursor plane. * + * Note that we make some assumptions about hardware limitations that may not be + * true for all hardware: + * + * 1. Primary plane cannot be repositioned. + * 2. Primary plane cannot be scaled. + * 3. Primary plane must cover the entire CRTC. + * 4. Subpixel positioning is not supported. + * 5. The primary plane must always be on if the CRTC is enabled. + * + * This is purely a backwards compatibility helper for old drivers. Drivers + * should instead implement their own primary plane. Atomic drivers must do so. + * Drivers with the above hardware restriction can look into using &struct + * drm_simple_display_pipe, which encapsulates the above limitations into a nice + * interface. + * * Returns: * Zero on success, error code on failure. */ diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index ee4a5e1221f1..ab4e70e63f6e 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -59,6 +59,14 @@ static const struct drm_dmi_panel_orientation_data gpd_win = { .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, }; +static const struct drm_dmi_panel_orientation_data gpd_win2 = { + .width = 720, + .height = 1280, + .bios_dates = (const char * const []){ + "12/07/2017", "05/24/2018", NULL }, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + static const struct drm_dmi_panel_orientation_data itworks_tw891 = { .width = 800, .height = 1280, @@ -106,6 +114,14 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), }, .driver_data = (void *)&gpd_win, + }, { /* GPD Win 2 (too generic strings, also match on bios date) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), + }, + .driver_data = (void *)&gpd_win2, }, { /* I.T.Works TW891 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 48f615d38931..a9d9df6c85ad 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -61,15 +61,14 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali return NULL; dmah->size = size; - dmah->vaddr = dma_alloc_coherent(&dev->pdev->dev, size, &dmah->busaddr, GFP_KERNEL | __GFP_COMP); + dmah->vaddr = dma_zalloc_coherent(&dev->pdev->dev, size, &dmah->busaddr, + GFP_KERNEL | __GFP_COMP); if (dmah->vaddr == NULL) { kfree(dmah); return NULL; } - memset(dmah->vaddr, 0, size); - /* XXX - Is virt_to_page() legal for consistent mem? */ /* Reserve */ for (addr = (unsigned long)dmah->vaddr, sz = size; diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 1fa98bd12003..679455e36829 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -636,6 +636,29 @@ static int __setplane_check(struct drm_plane *plane, return 0; } +/** + * drm_any_plane_has_format - Check whether any plane supports this format and modifier combination + * @dev: DRM device + * @format: pixel format (DRM_FORMAT_*) + * @modifier: data layout modifier + * + * Returns: + * Whether at least one plane supports the specified format and modifier combination. + */ +bool drm_any_plane_has_format(struct drm_device *dev, + u32 format, u64 modifier) +{ + struct drm_plane *plane; + + drm_for_each_plane(plane, dev) { + if (drm_plane_check_pixel_format(plane, format, modifier) == 0) + return true; + } + + return false; +} +EXPORT_SYMBOL(drm_any_plane_has_format); + /* * __setplane_internal - setplane handler for internal callers * diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index a393756b664e..0fff72dcd06d 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -42,11 +42,8 @@ * primary plane support on top of the normal CRTC configuration interface. * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary * plane together with the CRTC state this does not allow userspace to disable - * the primary plane itself. To avoid too much duplicated code use - * drm_plane_helper_check_update() which can be used to enforce the same - * restrictions as primary planes had thus. The default primary plane only - * expose XRBG8888 and ARGB8888 as valid pixel formats for the attached - * framebuffer. + * the primary plane itself. The default primary plane only expose XRBG8888 and + * ARGB8888 as valid pixel formats for the attached framebuffer. * * Drivers are highly recommended to implement proper support for primary * planes, and newly merged drivers must not rely upon these transitional @@ -100,43 +97,17 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, return count; } -/** - * drm_plane_helper_check_update() - Check plane update for validity - * @plane: plane object to update - * @crtc: owning CRTC of owning plane - * @fb: framebuffer to flip onto plane - * @src: source coordinates in 16.16 fixed point - * @dst: integer destination coordinates - * @rotation: plane rotation - * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point - * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point - * @can_position: is it legal to position the plane such that it - * doesn't cover the entire crtc? This will generally - * only be false for primary planes. - * @can_update_disabled: can the plane be updated while the crtc - * is disabled? - * @visible: output parameter indicating whether plane is still visible after - * clipping - * - * Checks that a desired plane update is valid. Drivers that provide - * their own plane handling rather than helper-provided implementations may - * still wish to call this function to avoid duplication of error checking - * code. - * - * RETURNS: - * Zero if update appears valid, error code on failure - */ -int drm_plane_helper_check_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_rect *src, - struct drm_rect *dst, - unsigned int rotation, - int min_scale, - int max_scale, - bool can_position, - bool can_update_disabled, - bool *visible) +static int drm_plane_helper_check_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_rect *src, + struct drm_rect *dst, + unsigned int rotation, + int min_scale, + int max_scale, + bool can_position, + bool can_update_disabled, + bool *visible) { struct drm_plane_state plane_state = { .plane = plane, @@ -173,52 +144,14 @@ int drm_plane_helper_check_update(struct drm_plane *plane, return 0; } -EXPORT_SYMBOL(drm_plane_helper_check_update); -/** - * drm_primary_helper_update() - Helper for primary plane update - * @plane: plane object to update - * @crtc: owning CRTC of owning plane - * @fb: framebuffer to flip onto plane - * @crtc_x: x offset of primary plane on crtc - * @crtc_y: y offset of primary plane on crtc - * @crtc_w: width of primary plane rectangle on crtc - * @crtc_h: height of primary plane rectangle on crtc - * @src_x: x offset of @fb for panning - * @src_y: y offset of @fb for panning - * @src_w: width of source rectangle in @fb - * @src_h: height of source rectangle in @fb - * @ctx: lock acquire context, not used here - * - * Provides a default plane update handler for primary planes. This is handler - * is called in response to a userspace SetPlane operation on the plane with a - * non-NULL framebuffer. We call the driver's modeset handler to update the - * framebuffer. - * - * SetPlane() on a primary plane of a disabled CRTC is not supported, and will - * return an error. - * - * Note that we make some assumptions about hardware limitations that may not be - * true for all hardware -- - * - * 1. Primary plane cannot be repositioned. - * 2. Primary plane cannot be scaled. - * 3. Primary plane must cover the entire CRTC. - * 4. Subpixel positioning is not supported. - * - * Drivers for hardware that don't have these restrictions can provide their - * own implementation rather than using this helper. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - struct drm_modeset_acquire_ctx *ctx) +static int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + struct drm_modeset_acquire_ctx *ctx) { struct drm_mode_set set = { .crtc = crtc, @@ -285,35 +218,12 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, kfree(connector_list); return ret; } -EXPORT_SYMBOL(drm_primary_helper_update); -/** - * drm_primary_helper_disable() - Helper for primary plane disable - * @plane: plane to disable - * @ctx: lock acquire context, not used here - * - * Provides a default plane disable handler for primary planes. This is handler - * is called in response to a userspace SetPlane operation on the plane with a - * NULL framebuffer parameter. It unconditionally fails the disable call with - * -EINVAL the only way to disable the primary plane without driver support is - * to disable the entire CRTC. Which does not match the plane - * &drm_plane_funcs.disable_plane hook. - * - * Note that some hardware may be able to disable the primary plane without - * disabling the whole CRTC. Drivers for such hardware should provide their - * own disable handler that disables just the primary plane (and they'll likely - * need to provide their own update handler as well to properly re-enable a - * disabled primary plane). - * - * RETURNS: - * Unconditionally returns -EINVAL. - */ -int drm_primary_helper_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx) +static int drm_primary_helper_disable(struct drm_plane *plane, + struct drm_modeset_acquire_ctx *ctx) { return -EINVAL; } -EXPORT_SYMBOL(drm_primary_helper_disable); /** * drm_primary_helper_destroy() - Helper for primary plane destruction @@ -336,200 +246,3 @@ const struct drm_plane_funcs drm_primary_helper_funcs = { .destroy = drm_primary_helper_destroy, }; EXPORT_SYMBOL(drm_primary_helper_funcs); - -int drm_plane_helper_commit(struct drm_plane *plane, - struct drm_plane_state *plane_state, - struct drm_framebuffer *old_fb) -{ - const struct drm_plane_helper_funcs *plane_funcs; - struct drm_crtc *crtc[2]; - const struct drm_crtc_helper_funcs *crtc_funcs[2]; - int i, ret = 0; - - plane_funcs = plane->helper_private; - - /* Since this is a transitional helper we can't assume that plane->state - * is always valid. Hence we need to use plane->crtc instead of - * plane->state->crtc as the old crtc. */ - crtc[0] = plane->crtc; - crtc[1] = crtc[0] != plane_state->crtc ? plane_state->crtc : NULL; - - for (i = 0; i < 2; i++) - crtc_funcs[i] = crtc[i] ? crtc[i]->helper_private : NULL; - - if (plane_funcs->atomic_check) { - ret = plane_funcs->atomic_check(plane, plane_state); - if (ret) - goto out; - } - - if (plane_funcs->prepare_fb && plane_state->fb != old_fb) { - ret = plane_funcs->prepare_fb(plane, - plane_state); - if (ret) - goto out; - } - - /* Point of no return, commit sw state. */ - swap(plane->state, plane_state); - - for (i = 0; i < 2; i++) { - if (crtc_funcs[i] && crtc_funcs[i]->atomic_begin) - crtc_funcs[i]->atomic_begin(crtc[i], crtc[i]->state); - } - - /* - * Drivers may optionally implement the ->atomic_disable callback, so - * special-case that here. - */ - if (drm_atomic_plane_disabling(plane_state, plane->state) && - plane_funcs->atomic_disable) - plane_funcs->atomic_disable(plane, plane_state); - else - plane_funcs->atomic_update(plane, plane_state); - - for (i = 0; i < 2; i++) { - if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush) - crtc_funcs[i]->atomic_flush(crtc[i], crtc[i]->state); - } - - /* - * If we only moved the plane and didn't change fb's, there's no need to - * wait for vblank. - */ - if (plane->state->fb == old_fb) - goto out; - - for (i = 0; i < 2; i++) { - if (!crtc[i]) - continue; - - if (crtc[i]->cursor == plane) - continue; - - /* There's no other way to figure out whether the crtc is running. */ - ret = drm_crtc_vblank_get(crtc[i]); - if (ret == 0) { - drm_crtc_wait_one_vblank(crtc[i]); - drm_crtc_vblank_put(crtc[i]); - } - - ret = 0; - } - - if (plane_funcs->cleanup_fb) - plane_funcs->cleanup_fb(plane, plane_state); -out: - if (plane->funcs->atomic_destroy_state) - plane->funcs->atomic_destroy_state(plane, plane_state); - else - drm_atomic_helper_plane_destroy_state(plane, plane_state); - - return ret; -} - -/** - * drm_plane_helper_update() - Transitional helper for plane update - * @plane: plane object to update - * @crtc: owning CRTC of owning plane - * @fb: framebuffer to flip onto plane - * @crtc_x: x offset of primary plane on crtc - * @crtc_y: y offset of primary plane on crtc - * @crtc_w: width of primary plane rectangle on crtc - * @crtc_h: height of primary plane rectangle on crtc - * @src_x: x offset of @fb for panning - * @src_y: y offset of @fb for panning - * @src_w: width of source rectangle in @fb - * @src_h: height of source rectangle in @fb - * @ctx: lock acquire context, not used here - * - * Provides a default plane update handler using the atomic plane update - * functions. It is fully left to the driver to check plane constraints and - * handle corner-cases like a fully occluded or otherwise invisible plane. - * - * This is useful for piecewise transitioning of a driver to the atomic helpers. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_plane_state *plane_state; - - if (plane->funcs->atomic_duplicate_state) - plane_state = plane->funcs->atomic_duplicate_state(plane); - else { - if (!plane->state) - drm_atomic_helper_plane_reset(plane); - - plane_state = drm_atomic_helper_plane_duplicate_state(plane); - } - if (!plane_state) - return -ENOMEM; - plane_state->plane = plane; - - plane_state->crtc = crtc; - drm_atomic_set_fb_for_plane(plane_state, fb); - plane_state->crtc_x = crtc_x; - plane_state->crtc_y = crtc_y; - plane_state->crtc_h = crtc_h; - plane_state->crtc_w = crtc_w; - plane_state->src_x = src_x; - plane_state->src_y = src_y; - plane_state->src_h = src_h; - plane_state->src_w = src_w; - - return drm_plane_helper_commit(plane, plane_state, plane->fb); -} -EXPORT_SYMBOL(drm_plane_helper_update); - -/** - * drm_plane_helper_disable() - Transitional helper for plane disable - * @plane: plane to disable - * @ctx: lock acquire context, not used here - * - * Provides a default plane disable handler using the atomic plane update - * functions. It is fully left to the driver to check plane constraints and - * handle corner-cases like a fully occluded or otherwise invisible plane. - * - * This is useful for piecewise transitioning of a driver to the atomic helpers. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_plane_helper_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_plane_state *plane_state; - struct drm_framebuffer *old_fb; - - /* crtc helpers love to call disable functions for already disabled hw - * functions. So cope with that. */ - if (!plane->crtc) - return 0; - - if (plane->funcs->atomic_duplicate_state) - plane_state = plane->funcs->atomic_duplicate_state(plane); - else { - if (!plane->state) - drm_atomic_helper_plane_reset(plane); - - plane_state = drm_atomic_helper_plane_duplicate_state(plane); - } - if (!plane_state) - return -ENOMEM; - plane_state->plane = plane; - - plane_state->crtc = NULL; - old_fb = plane_state->fb; - drm_atomic_set_fb_for_plane(plane_state, NULL); - - return drm_plane_helper_commit(plane, plane_state, old_fb); -} -EXPORT_SYMBOL(drm_plane_helper_disable); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 3f0205fc0a1a..8d54d51a6b6b 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -434,34 +434,6 @@ void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr) EXPORT_SYMBOL(drm_gem_dmabuf_vunmap); /** - * drm_gem_dmabuf_kmap - map implementation for GEM - * @dma_buf: buffer to be mapped - * @page_num: page number within the buffer - * - * Not implemented. This can be used as the &dma_buf_ops.map callback. - */ -void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} -EXPORT_SYMBOL(drm_gem_dmabuf_kmap); - -/** - * drm_gem_dmabuf_kunmap - unmap implementation for GEM - * @dma_buf: buffer to be unmapped - * @page_num: page number within the buffer - * @addr: virtual address of the buffer - * - * Not implemented. This can be used as the &dma_buf_ops.unmap callback. - */ -void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_num, - void *addr) -{ - -} -EXPORT_SYMBOL(drm_gem_dmabuf_kunmap); - -/** * drm_gem_dmabuf_mmap - dma_buf mmap implementation for GEM * @dma_buf: buffer to be mapped * @vma: virtual address range @@ -489,8 +461,6 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { .map_dma_buf = drm_gem_map_dma_buf, .unmap_dma_buf = drm_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release, - .map = drm_gem_dmabuf_kmap, - .unmap = drm_gem_dmabuf_kunmap, .mmap = drm_gem_dmabuf_mmap, .vmap = drm_gem_dmabuf_vmap, .vunmap = drm_gem_dmabuf_vunmap, diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 51fa978f0d23..917812448d1b 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -190,6 +190,13 @@ static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane, pipe->funcs->cleanup_fb(pipe, state); } +static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane, + uint32_t format, + uint64_t modifier) +{ + return modifier == DRM_FORMAT_MOD_LINEAR; +} + static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = { .prepare_fb = drm_simple_kms_plane_prepare_fb, .cleanup_fb = drm_simple_kms_plane_cleanup_fb, @@ -204,6 +211,7 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .format_mod_supported = drm_simple_kms_format_mod_supported, }; /** diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 5c2091dbd230..da8175d9c6ff 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -56,6 +56,9 @@ #include "drm_internal.h" #include <drm/drm_syncobj.h> +/* merge normal syncobj to timeline syncobj, the point interval is 1 */ +#define DRM_SYNCOBJ_BINARY_POINT 1 + struct drm_syncobj_stub_fence { struct dma_fence base; spinlock_t lock; @@ -71,7 +74,29 @@ static const struct dma_fence_ops drm_syncobj_stub_fence_ops = { .get_timeline_name = drm_syncobj_stub_fence_get_name, }; +struct drm_syncobj_signal_pt { + struct dma_fence_array *fence_array; + u64 value; + struct list_head list; +}; + +static DEFINE_SPINLOCK(signaled_fence_lock); +static struct dma_fence signaled_fence; +static struct dma_fence *drm_syncobj_get_stub_fence(void) +{ + spin_lock(&signaled_fence_lock); + if (!signaled_fence.ops) { + dma_fence_init(&signaled_fence, + &drm_syncobj_stub_fence_ops, + &signaled_fence_lock, + 0, 0); + dma_fence_signal_locked(&signaled_fence); + } + spin_unlock(&signaled_fence_lock); + + return dma_fence_get(&signaled_fence); +} /** * drm_syncobj_find - lookup and reference a sync object. * @file_private: drm file private pointer @@ -98,6 +123,27 @@ struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, } EXPORT_SYMBOL(drm_syncobj_find); +static struct dma_fence * +drm_syncobj_find_signal_pt_for_point(struct drm_syncobj *syncobj, + uint64_t point) +{ + struct drm_syncobj_signal_pt *signal_pt; + + if ((syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) && + (point <= syncobj->timeline)) + return drm_syncobj_get_stub_fence(); + + list_for_each_entry(signal_pt, &syncobj->signal_pt_list, list) { + if (point > signal_pt->value) + continue; + if ((syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) && + (point != signal_pt->value)) + continue; + return dma_fence_get(&signal_pt->fence_array->base); + } + return NULL; +} + static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj, struct drm_syncobj_cb *cb, drm_syncobj_func_t func) @@ -106,55 +152,158 @@ static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj, list_add_tail(&cb->node, &syncobj->cb_list); } -static int drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, - struct dma_fence **fence, - struct drm_syncobj_cb *cb, - drm_syncobj_func_t func) +static void drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, + struct dma_fence **fence, + struct drm_syncobj_cb *cb, + drm_syncobj_func_t func) { - int ret; + u64 pt_value = 0; WARN_ON(*fence); - *fence = drm_syncobj_fence_get(syncobj); - if (*fence) - return 1; + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { + /*BINARY syncobj always wait on last pt */ + pt_value = syncobj->signal_point; - spin_lock(&syncobj->lock); - /* We've already tried once to get a fence and failed. Now that we - * have the lock, try one more time just to be sure we don't add a - * callback when a fence has already been set. - */ - if (syncobj->fence) { - *fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, - lockdep_is_held(&syncobj->lock))); - ret = 1; - } else { - *fence = NULL; - drm_syncobj_add_callback_locked(syncobj, cb, func); - ret = 0; + if (pt_value == 0) + pt_value += DRM_SYNCOBJ_BINARY_POINT; } - spin_unlock(&syncobj->lock); - return ret; + mutex_lock(&syncobj->cb_mutex); + spin_lock(&syncobj->pt_lock); + *fence = drm_syncobj_find_signal_pt_for_point(syncobj, pt_value); + spin_unlock(&syncobj->pt_lock); + if (!*fence) + drm_syncobj_add_callback_locked(syncobj, cb, func); + mutex_unlock(&syncobj->cb_mutex); } -void drm_syncobj_add_callback(struct drm_syncobj *syncobj, - struct drm_syncobj_cb *cb, - drm_syncobj_func_t func) +static void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, + struct drm_syncobj_cb *cb) { - spin_lock(&syncobj->lock); - drm_syncobj_add_callback_locked(syncobj, cb, func); - spin_unlock(&syncobj->lock); + mutex_lock(&syncobj->cb_mutex); + list_del_init(&cb->node); + mutex_unlock(&syncobj->cb_mutex); } -void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, - struct drm_syncobj_cb *cb) +static void drm_syncobj_init(struct drm_syncobj *syncobj) { - spin_lock(&syncobj->lock); - list_del_init(&cb->node); - spin_unlock(&syncobj->lock); + spin_lock(&syncobj->pt_lock); + syncobj->timeline_context = dma_fence_context_alloc(1); + syncobj->timeline = 0; + syncobj->signal_point = 0; + init_waitqueue_head(&syncobj->wq); + + INIT_LIST_HEAD(&syncobj->signal_pt_list); + spin_unlock(&syncobj->pt_lock); +} + +static void drm_syncobj_fini(struct drm_syncobj *syncobj) +{ + struct drm_syncobj_signal_pt *signal_pt = NULL, *tmp; + + spin_lock(&syncobj->pt_lock); + list_for_each_entry_safe(signal_pt, tmp, + &syncobj->signal_pt_list, list) { + list_del(&signal_pt->list); + dma_fence_put(&signal_pt->fence_array->base); + kfree(signal_pt); + } + spin_unlock(&syncobj->pt_lock); +} + +static int drm_syncobj_create_signal_pt(struct drm_syncobj *syncobj, + struct dma_fence *fence, + u64 point) +{ + struct drm_syncobj_signal_pt *signal_pt = + kzalloc(sizeof(struct drm_syncobj_signal_pt), GFP_KERNEL); + struct drm_syncobj_signal_pt *tail_pt; + struct dma_fence **fences; + int num_fences = 0; + int ret = 0, i; + + if (!signal_pt) + return -ENOMEM; + if (!fence) + goto out; + + fences = kmalloc_array(sizeof(void *), 2, GFP_KERNEL); + if (!fences) { + ret = -ENOMEM; + goto out; + } + fences[num_fences++] = dma_fence_get(fence); + /* timeline syncobj must take this dependency */ + if (syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) { + spin_lock(&syncobj->pt_lock); + if (!list_empty(&syncobj->signal_pt_list)) { + tail_pt = list_last_entry(&syncobj->signal_pt_list, + struct drm_syncobj_signal_pt, list); + fences[num_fences++] = + dma_fence_get(&tail_pt->fence_array->base); + } + spin_unlock(&syncobj->pt_lock); + } + signal_pt->fence_array = dma_fence_array_create(num_fences, fences, + syncobj->timeline_context, + point, false); + if (!signal_pt->fence_array) { + ret = -ENOMEM; + goto fail; + } + + spin_lock(&syncobj->pt_lock); + if (syncobj->signal_point >= point) { + DRM_WARN("A later signal is ready!"); + spin_unlock(&syncobj->pt_lock); + goto exist; + } + signal_pt->value = point; + list_add_tail(&signal_pt->list, &syncobj->signal_pt_list); + syncobj->signal_point = point; + spin_unlock(&syncobj->pt_lock); + wake_up_all(&syncobj->wq); + + return 0; +exist: + dma_fence_put(&signal_pt->fence_array->base); +fail: + for (i = 0; i < num_fences; i++) + dma_fence_put(fences[i]); + kfree(fences); +out: + kfree(signal_pt); + return ret; } +static void drm_syncobj_garbage_collection(struct drm_syncobj *syncobj) +{ + struct drm_syncobj_signal_pt *signal_pt, *tmp, *tail_pt; + + spin_lock(&syncobj->pt_lock); + tail_pt = list_last_entry(&syncobj->signal_pt_list, + struct drm_syncobj_signal_pt, + list); + list_for_each_entry_safe(signal_pt, tmp, + &syncobj->signal_pt_list, list) { + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY && + signal_pt == tail_pt) + continue; + if (dma_fence_is_signaled(&signal_pt->fence_array->base)) { + syncobj->timeline = signal_pt->value; + list_del(&signal_pt->list); + dma_fence_put(&signal_pt->fence_array->base); + kfree(signal_pt); + } else { + /*signal_pt is in order in list, from small to big, so + * the later must not be signal either */ + break; + } + } + + spin_unlock(&syncobj->pt_lock); +} /** * drm_syncobj_replace_fence - replace fence in a sync object. * @syncobj: Sync object to replace fence in @@ -167,28 +316,30 @@ void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, u64 point, struct dma_fence *fence) { - struct dma_fence *old_fence; - struct drm_syncobj_cb *cur, *tmp; - - if (fence) - dma_fence_get(fence); - - spin_lock(&syncobj->lock); - - old_fence = rcu_dereference_protected(syncobj->fence, - lockdep_is_held(&syncobj->lock)); - rcu_assign_pointer(syncobj->fence, fence); + u64 pt_value = point; + + drm_syncobj_garbage_collection(syncobj); + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { + if (!fence) { + drm_syncobj_fini(syncobj); + drm_syncobj_init(syncobj); + return; + } + pt_value = syncobj->signal_point + + DRM_SYNCOBJ_BINARY_POINT; + } + drm_syncobj_create_signal_pt(syncobj, fence, pt_value); + if (fence) { + struct drm_syncobj_cb *cur, *tmp; + LIST_HEAD(cb_list); - if (fence != old_fence) { + mutex_lock(&syncobj->cb_mutex); list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) { list_del_init(&cur->node); cur->func(syncobj, cur); } + mutex_unlock(&syncobj->cb_mutex); } - - spin_unlock(&syncobj->lock); - - dma_fence_put(old_fence); } EXPORT_SYMBOL(drm_syncobj_replace_fence); @@ -211,35 +362,89 @@ static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj) return 0; } +static int +drm_syncobj_point_get(struct drm_syncobj *syncobj, u64 point, u64 flags, + struct dma_fence **fence) +{ + int ret = 0; + + if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { + ret = wait_event_interruptible(syncobj->wq, + point <= syncobj->signal_point); + if (ret < 0) + return ret; + } + spin_lock(&syncobj->pt_lock); + *fence = drm_syncobj_find_signal_pt_for_point(syncobj, point); + if (!*fence) + ret = -EINVAL; + spin_unlock(&syncobj->pt_lock); + return ret; +} + +/** + * drm_syncobj_search_fence - lookup and reference the fence in a sync object or + * in a timeline point + * @syncobj: sync object pointer + * @point: timeline point + * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not + * @fence: out parameter for the fence + * + * if flags is DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, the function will block + * here until specific timeline points is reached. + * if not, you need a submit thread and block in userspace until all future + * timeline points have materialized, only then you can submit to the kernel, + * otherwise, function will fail to return fence. + * + * Returns 0 on success or a negative error value on failure. On success @fence + * contains a reference to the fence, which must be released by calling + * dma_fence_put(). + */ +int drm_syncobj_search_fence(struct drm_syncobj *syncobj, u64 point, + u64 flags, struct dma_fence **fence) +{ + u64 pt_value = point; + + if (!syncobj) + return -ENOENT; + + drm_syncobj_garbage_collection(syncobj); + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { + /*BINARY syncobj always wait on last pt */ + pt_value = syncobj->signal_point; + + if (pt_value == 0) + pt_value += DRM_SYNCOBJ_BINARY_POINT; + } + return drm_syncobj_point_get(syncobj, pt_value, flags, fence); +} +EXPORT_SYMBOL(drm_syncobj_search_fence); + /** * drm_syncobj_find_fence - lookup and reference the fence in a sync object * @file_private: drm file private pointer * @handle: sync object handle to lookup. * @point: timeline point + * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not * @fence: out parameter for the fence * * This is just a convenience function that combines drm_syncobj_find() and - * drm_syncobj_fence_get(). + * drm_syncobj_lookup_fence(). * * Returns 0 on success or a negative error value on failure. On success @fence * contains a reference to the fence, which must be released by calling * dma_fence_put(). */ int drm_syncobj_find_fence(struct drm_file *file_private, - u32 handle, u64 point, + u32 handle, u64 point, u64 flags, struct dma_fence **fence) { struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle); - int ret = 0; - - if (!syncobj) - return -ENOENT; + int ret; - *fence = drm_syncobj_fence_get(syncobj); - if (!*fence) { - ret = -EINVAL; - } - drm_syncobj_put(syncobj); + ret = drm_syncobj_search_fence(syncobj, point, flags, fence); + if (syncobj) + drm_syncobj_put(syncobj); return ret; } EXPORT_SYMBOL(drm_syncobj_find_fence); @@ -255,7 +460,7 @@ void drm_syncobj_free(struct kref *kref) struct drm_syncobj *syncobj = container_of(kref, struct drm_syncobj, refcount); - drm_syncobj_replace_fence(syncobj, 0, NULL); + drm_syncobj_fini(syncobj); kfree(syncobj); } EXPORT_SYMBOL(drm_syncobj_free); @@ -284,7 +489,13 @@ int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, kref_init(&syncobj->refcount); INIT_LIST_HEAD(&syncobj->cb_list); - spin_lock_init(&syncobj->lock); + spin_lock_init(&syncobj->pt_lock); + mutex_init(&syncobj->cb_mutex); + if (flags & DRM_SYNCOBJ_CREATE_TYPE_TIMELINE) + syncobj->type = DRM_SYNCOBJ_TYPE_TIMELINE; + else + syncobj->type = DRM_SYNCOBJ_TYPE_BINARY; + drm_syncobj_init(syncobj); if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) { ret = drm_syncobj_assign_null_handle(syncobj); @@ -497,7 +708,7 @@ static int drm_syncobj_export_sync_file(struct drm_file *file_private, if (fd < 0) return fd; - ret = drm_syncobj_find_fence(file_private, handle, 0, &fence); + ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence); if (ret) goto err_put_fd; @@ -567,7 +778,8 @@ drm_syncobj_create_ioctl(struct drm_device *dev, void *data, return -EOPNOTSUPP; /* no valid flags yet */ - if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED) + if (args->flags & ~(DRM_SYNCOBJ_CREATE_SIGNALED | + DRM_SYNCOBJ_CREATE_TYPE_TIMELINE)) return -EINVAL; return drm_syncobj_create_as_handle(file_private, @@ -660,9 +872,8 @@ static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj, struct syncobj_wait_entry *wait = container_of(cb, struct syncobj_wait_entry, syncobj_cb); - /* This happens inside the syncobj lock */ - wait->fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, - lockdep_is_held(&syncobj->lock))); + drm_syncobj_search_fence(syncobj, 0, 0, &wait->fence); + wake_up_process(wait->task); } @@ -688,7 +899,8 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs, signaled_count = 0; for (i = 0; i < count; ++i) { entries[i].task = current; - entries[i].fence = drm_syncobj_fence_get(syncobjs[i]); + drm_syncobj_search_fence(syncobjs[i], 0, 0, + &entries[i].fence); if (!entries[i].fence) { if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { continue; @@ -953,12 +1165,13 @@ drm_syncobj_reset_ioctl(struct drm_device *dev, void *data, if (ret < 0) return ret; - for (i = 0; i < args->count_handles; i++) - drm_syncobj_replace_fence(syncobjs[i], 0, NULL); - + for (i = 0; i < args->count_handles; i++) { + drm_syncobj_fini(syncobjs[i]); + drm_syncobj_init(syncobjs[i]); + } drm_syncobj_array_free(syncobjs, args->count_handles); - return 0; + return ret; } int diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 983e67f19e45..30875f8f2933 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -179,7 +179,7 @@ static int submit_fence_sync(struct etnaviv_gem_submit *submit) struct reservation_object *robj = bo->obj->resv; if (!(bo->flags & ETNA_SUBMIT_BO_WRITE)) { - ret = reservation_object_reserve_shared(robj); + ret = reservation_object_reserve_shared(robj, 1); if (ret) return ret; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c index 0e3752437e44..18afc94e4dff 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c @@ -17,6 +17,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <video/videomode.h> #include "fsl_dcu_drm_crtc.h" #include "fsl_dcu_drm_drv.h" @@ -85,40 +86,34 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; struct drm_connector *con = &fsl_dev->connector.base; struct drm_display_mode *mode = &crtc->state->mode; - unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0; + unsigned int pol = 0; + struct videomode vm; - index = drm_crtc_index(crtc); clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000); - /* Configure timings: */ - hbp = mode->htotal - mode->hsync_end; - hfp = mode->hsync_start - mode->hdisplay; - hsw = mode->hsync_end - mode->hsync_start; - vbp = mode->vtotal - mode->vsync_end; - vfp = mode->vsync_start - mode->vdisplay; - vsw = mode->vsync_end - mode->vsync_start; + drm_display_mode_to_videomode(mode, &vm); /* INV_PXCK as default (most display sample data on rising edge) */ if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE)) pol |= DCU_SYN_POL_INV_PXCK; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) + if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW) pol |= DCU_SYN_POL_INV_HS_LOW; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) + if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW) pol |= DCU_SYN_POL_INV_VS_LOW; regmap_write(fsl_dev->regmap, DCU_HSYN_PARA, - DCU_HSYN_PARA_BP(hbp) | - DCU_HSYN_PARA_PW(hsw) | - DCU_HSYN_PARA_FP(hfp)); + DCU_HSYN_PARA_BP(vm.hback_porch) | + DCU_HSYN_PARA_PW(vm.hsync_len) | + DCU_HSYN_PARA_FP(vm.hfront_porch)); regmap_write(fsl_dev->regmap, DCU_VSYN_PARA, - DCU_VSYN_PARA_BP(vbp) | - DCU_VSYN_PARA_PW(vsw) | - DCU_VSYN_PARA_FP(vfp)); + DCU_VSYN_PARA_BP(vm.vback_porch) | + DCU_VSYN_PARA_PW(vm.vsync_len) | + DCU_VSYN_PARA_FP(vm.vfront_porch)); regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, - DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | - DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); + DCU_DISP_SIZE_DELTA_Y(vm.vactive) | + DCU_DISP_SIZE_DELTA_X(vm.hactive)); regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol); regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | DCU_BGND_G(0) | DCU_BGND_B(0)); diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 0496be5212e1..ceddc3e29258 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -26,6 +26,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_modeset_helper.h> @@ -89,20 +90,11 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags) "Invalid legacyfb_depth. Defaulting to 24bpp\n"); legacyfb_depth = 24; } - fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1); - if (IS_ERR(fsl_dev->fbdev)) { - ret = PTR_ERR(fsl_dev->fbdev); - fsl_dev->fbdev = NULL; - goto done; - } return 0; done: drm_kms_helper_poll_fini(dev); - if (fsl_dev->fbdev) - drm_fbdev_cma_fini(fsl_dev->fbdev); - drm_mode_config_cleanup(dev); drm_irq_uninstall(dev); dev->dev_private = NULL; @@ -112,14 +104,9 @@ done: static void fsl_dcu_unload(struct drm_device *dev) { - struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; - drm_atomic_helper_shutdown(dev); drm_kms_helper_poll_fini(dev); - if (fsl_dev->fbdev) - drm_fbdev_cma_fini(fsl_dev->fbdev); - drm_mode_config_cleanup(dev); drm_irq_uninstall(dev); @@ -147,19 +134,11 @@ static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg) return IRQ_HANDLED; } -static void fsl_dcu_drm_lastclose(struct drm_device *dev) -{ - struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; - - drm_fbdev_cma_restore_mode(fsl_dev->fbdev); -} - DEFINE_DRM_GEM_CMA_FOPS(fsl_dcu_drm_fops); static struct drm_driver fsl_dcu_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = fsl_dcu_drm_lastclose, .load = fsl_dcu_load, .unload = fsl_dcu_unload, .irq_handler = fsl_dcu_drm_irq, @@ -355,6 +334,8 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) if (ret < 0) goto put; + drm_fbdev_generic_setup(drm, legacyfb_depth); + return 0; put: diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h index 93bfb98012d4..cb87bb74cb87 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h @@ -191,7 +191,6 @@ struct fsl_dcu_drm_device { /*protects hardware register*/ spinlock_t irq_lock; struct drm_device *drm; - struct drm_fbdev_cma *fbdev; struct drm_crtc crtc; struct drm_encoder encoder; struct fsl_dcu_drm_connector connector; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 1aaccbe7e1de..1a1c04db6c80 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -2157,7 +2157,7 @@ await_fence_array(struct i915_execbuffer *eb, if (!(flags & I915_EXEC_FENCE_WAIT)) continue; - fence = drm_syncobj_fence_get(syncobj); + drm_syncobj_search_fence(syncobj, 0, 0, &fence); if (!fence) return -EINVAL; diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c index 31efc971a3a8..35fce4c88629 100644 --- a/drivers/gpu/drm/i915/i915_vma.c +++ b/drivers/gpu/drm/i915/i915_vma.c @@ -892,7 +892,7 @@ static void export_fence(struct i915_vma *vma, reservation_object_lock(resv, NULL); if (flags & EXEC_OBJECT_WRITE) reservation_object_add_excl_fence(resv, &rq->fence); - else if (reservation_object_reserve_shared(resv) == 0) + else if (reservation_object_reserve_shared(resv, 1) == 0) reservation_object_add_shared_fence(resv, &rq->fence); reservation_object_unlock(resv); } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index a2dab0b6bde6..d7234e03fdb0 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -474,7 +474,8 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, const struct drm_display_mode *adjusted_mode = &crtc_state->base.adjusted_mode; struct drm_connector *connector = &intel_hdmi->attached_connector->base; - bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported; + bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported || + connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB420; union hdmi_infoframe frame; int ret; diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index d3443125e661..348b5a198b9d 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -68,15 +68,7 @@ * - Powering Up HDMI controller and PHY */ -static void meson_fb_output_poll_changed(struct drm_device *dev) -{ - struct meson_drm *priv = dev->dev_private; - - drm_fbdev_cma_hotplug_event(priv->fbdev); -} - static const struct drm_mode_config_funcs meson_mode_config_funcs = { - .output_poll_changed = meson_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, .fb_create = drm_gem_fb_create, @@ -282,13 +274,6 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) drm_mode_config_reset(drm); - priv->fbdev = drm_fbdev_cma_init(drm, 32, - drm->mode_config.num_connector); - if (IS_ERR(priv->fbdev)) { - ret = PTR_ERR(priv->fbdev); - goto free_drm; - } - drm_kms_helper_poll_init(drm); platform_set_drvdata(pdev, priv); @@ -297,6 +282,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) if (ret) goto free_drm; + drm_fbdev_generic_setup(drm, 32); + return 0; free_drm: @@ -313,11 +300,9 @@ static int meson_drv_bind(struct device *dev) static void meson_drv_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct meson_drm *priv = drm->dev_private; drm_dev_unregister(drm); drm_kms_helper_poll_fini(drm); - drm_fbdev_cma_fini(priv->fbdev); drm_mode_config_cleanup(drm); drm_dev_put(drm); diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h index 8450d6ac8c9b..aab96260da9f 100644 --- a/drivers/gpu/drm/meson/meson_drv.h +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -33,7 +33,6 @@ struct meson_drm { struct drm_device *drm; struct drm_crtc *crtc; - struct drm_fbdev_cma *fbdev; struct drm_plane *primary_plane; /* Components Data */ diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 48b5304f460c..8edd80bb0428 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1222,10 +1222,7 @@ static int a5xx_crashdumper_init(struct msm_gpu *gpu, SZ_1M, MSM_BO_UNCACHED, gpu->aspace, &dumper->bo, &dumper->iova); - if (IS_ERR(dumper->ptr)) - return PTR_ERR(dumper->ptr); - - return 0; + return PTR_ERR_OR_ZERO(dumper->ptr); } static void a5xx_crashdumper_free(struct msm_gpu *gpu, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index f549daf30fe6..d77a8cb15404 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -1179,8 +1179,6 @@ static void dpu_plane_destroy(struct drm_plane *plane) mutex_destroy(&pdpu->lock); - drm_plane_helper_disable(plane, NULL); - /* this will destroy the states as well */ drm_plane_cleanup(plane); diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c index 79ff653d8081..7a499731ce93 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c @@ -68,7 +68,6 @@ static void mdp4_plane_destroy(struct drm_plane *plane) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); kfree(mdp4_plane); diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c index 7f42c3e68a53..310459541e48 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c @@ -46,7 +46,6 @@ static void mdp5_plane_destroy(struct drm_plane *plane) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); kfree(mdp5_plane); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index c79659ca5706..23670907a29d 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -579,7 +579,7 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) hdmi_cfg = (struct hdmi_platform_config *) of_device_get_match_data(dev); if (!hdmi_cfg) { - dev_err(dev, "unknown hdmi_cfg: %s\n", of_node->name); + dev_err(dev, "unknown hdmi_cfg: %pOFn\n", of_node); return -ENXIO; } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 4904d0d41409..5e758d95751a 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -312,6 +312,7 @@ static int msm_drm_uninit(struct device *dev) if (fbdev && priv->fbdev) msm_fbdev_free(ddev); #endif + drm_atomic_helper_shutdown(ddev); drm_mode_config_cleanup(ddev); pm_runtime_get_sync(dev); diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 7a7923e6220d..a90aedd6883a 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -241,7 +241,8 @@ static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit) * strange place to call it. OTOH this is a * convenient can-fail point to hook it in. */ - ret = reservation_object_reserve_shared(msm_obj->resv); + ret = reservation_object_reserve_shared(msm_obj->resv, + 1); if (ret) return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 99be61ddeb75..d4964f3397a1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -341,7 +341,7 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e int ret = 0, i; if (!exclusive) { - ret = reservation_object_reserve_shared(resv); + ret = reservation_object_reserve_shared(resv, 1); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 6020c30a33b3..3f3537719beb 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -90,6 +90,18 @@ 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_OLIMEX_LCD_OLINUXINO + tristate "Olimex LCD-OLinuXino panel" + depends on OF + depends on I2C + depends on BACKLIGHT_CLASS_DEVICE + help + The panel is used with different sizes LCDs, from 480x272 to + 1280x800, and 24 bit per pixel. + + Say Y here if you want to enable support for Olimex Ltd. + LCD-OLinuXino panel. + config DRM_PANEL_ORISETECH_OTM8009A tristate "Orise Technology otm8009a 480x800 dsi 2dl panel" depends on OF @@ -126,6 +138,12 @@ config DRM_PANEL_RAYDIUM_RM68200 Say Y here if you want to enable support for Raydium RM68200 720x1280 DSI video mode panel. +config DRM_PANEL_SAMSUNG_S6D16D0 + tristate "Samsung S6D16D0 DSI video mode panel" + depends on OF + depends on DRM_MIPI_DSI + select VIDEOMODE_HELPERS + config DRM_PANEL_SAMSUNG_S6E3HA2 tristate "Samsung S6E3HA2 DSI video mode panel" depends on OF @@ -186,4 +204,11 @@ config DRM_PANEL_SITRONIX_ST7789V Say Y here if you want to enable support for the Sitronix ST7789V controller for 240x320 LCD panels +config DRM_PANEL_TRULY_NT35597_WQXGA + tristate "Truly WQXGA" + depends on OF + depends on DRM_MIPI_DSI + help + Say Y here if you want to enable support for Truly NT35597 WQXGA Dual DSI + Video Mode panel endmenu diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 5ccaaa9d13af..4396658a7996 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -7,11 +7,13 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.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_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.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_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.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 @@ -19,3 +21,4 @@ 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 +obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 72edb334d997..ca4ae45dd307 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -506,8 +506,7 @@ static int innolux_panel_add(struct mipi_dsi_device *dsi, static void innolux_panel_del(struct innolux_panel *innolux) { - if (innolux->base.dev) - drm_panel_remove(&innolux->base); + drm_panel_remove(&innolux->base); } static int innolux_panel_probe(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c new file mode 100644 index 000000000000..5e8d4523e9ed --- /dev/null +++ b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * LCD-OLinuXino support for panel driver + * + * Copyright (C) 2018 Olimex Ltd. + * Author: Stefan Mavrodiev <stefan@olimex.com> + */ + +#include <linux/backlight.h> +#include <linux/crc32.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drmP.h> + +#include <video/videomode.h> +#include <video/display_timing.h> + +#define LCD_OLINUXINO_HEADER_MAGIC 0x4F4CB727 +#define LCD_OLINUXINO_DATA_LEN 256 + +struct lcd_olinuxino_mode { + u32 pixelclock; + u32 hactive; + u32 hfp; + u32 hbp; + u32 hpw; + u32 vactive; + u32 vfp; + u32 vbp; + u32 vpw; + u32 refresh; + u32 flags; +}; + +struct lcd_olinuxino_info { + char name[32]; + u32 width_mm; + u32 height_mm; + u32 bpc; + u32 bus_format; + u32 bus_flag; +} __attribute__((__packed__)); + +struct lcd_olinuxino_eeprom { + u32 header; + u32 id; + char revision[4]; + u32 serial; + struct lcd_olinuxino_info info; + u32 num_modes; + u8 reserved[180]; + u32 checksum; +} __attribute__((__packed__)); + +struct lcd_olinuxino { + struct drm_panel panel; + struct device *dev; + struct i2c_client *client; + struct mutex mutex; + + bool prepared; + bool enabled; + + struct backlight_device *backlight; + struct regulator *supply; + struct gpio_desc *enable_gpio; + + struct lcd_olinuxino_eeprom eeprom; +}; + +static inline struct lcd_olinuxino *to_lcd_olinuxino(struct drm_panel *panel) +{ + return container_of(panel, struct lcd_olinuxino, panel); +} + +static int lcd_olinuxino_disable(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + + if (!lcd->enabled) + return 0; + + backlight_disable(lcd->backlight); + + lcd->enabled = false; + + return 0; +} + +static int lcd_olinuxino_unprepare(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + + if (!lcd->prepared) + return 0; + + gpiod_set_value_cansleep(lcd->enable_gpio, 0); + regulator_disable(lcd->supply); + + lcd->prepared = false; + + return 0; +} + +static int lcd_olinuxino_prepare(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + int ret; + + if (lcd->prepared) + return 0; + + ret = regulator_enable(lcd->supply); + if (ret < 0) + return ret; + + gpiod_set_value_cansleep(lcd->enable_gpio, 1); + lcd->prepared = true; + + return 0; +} + +static int lcd_olinuxino_enable(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + + if (lcd->enabled) + return 0; + + backlight_enable(lcd->backlight); + + lcd->enabled = true; + + return 0; +} + +static int lcd_olinuxino_get_modes(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + struct drm_connector *connector = lcd->panel.connector; + struct lcd_olinuxino_info *lcd_info = &lcd->eeprom.info; + struct drm_device *drm = lcd->panel.drm; + struct lcd_olinuxino_mode *lcd_mode; + struct drm_display_mode *mode; + u32 i, num = 0; + + for (i = 0; i < lcd->eeprom.num_modes; i++) { + lcd_mode = (struct lcd_olinuxino_mode *) + &lcd->eeprom.reserved[i * sizeof(*lcd_mode)]; + + mode = drm_mode_create(drm); + if (!mode) { + dev_err(drm->dev, "failed to add mode %ux%u@%u\n", + lcd_mode->hactive, + lcd_mode->vactive, + lcd_mode->refresh); + continue; + } + + mode->clock = lcd_mode->pixelclock; + mode->hdisplay = lcd_mode->hactive; + mode->hsync_start = lcd_mode->hactive + lcd_mode->hfp; + mode->hsync_end = lcd_mode->hactive + lcd_mode->hfp + + lcd_mode->hpw; + mode->htotal = lcd_mode->hactive + lcd_mode->hfp + + lcd_mode->hpw + lcd_mode->hbp; + mode->vdisplay = lcd_mode->vactive; + mode->vsync_start = lcd_mode->vactive + lcd_mode->vfp; + mode->vsync_end = lcd_mode->vactive + lcd_mode->vfp + + lcd_mode->vpw; + mode->vtotal = lcd_mode->vactive + lcd_mode->vfp + + lcd_mode->vpw + lcd_mode->vbp; + mode->vrefresh = lcd_mode->refresh; + + /* Always make the first mode preferred */ + if (i == 0) + mode->type |= DRM_MODE_TYPE_PREFERRED; + mode->type |= DRM_MODE_TYPE_DRIVER; + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + num++; + } + + memcpy(connector->display_info.name, lcd_info->name, 32); + connector->display_info.width_mm = lcd_info->width_mm; + connector->display_info.height_mm = lcd_info->height_mm; + connector->display_info.bpc = lcd_info->bpc; + + if (lcd_info->bus_format) + drm_display_info_set_bus_formats(&connector->display_info, + &lcd_info->bus_format, 1); + connector->display_info.bus_flags = lcd_info->bus_flag; + + return num; +} + +static const struct drm_panel_funcs lcd_olinuxino_funcs = { + .disable = lcd_olinuxino_disable, + .unprepare = lcd_olinuxino_unprepare, + .prepare = lcd_olinuxino_prepare, + .enable = lcd_olinuxino_enable, + .get_modes = lcd_olinuxino_get_modes, +}; + +static int lcd_olinuxino_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct lcd_olinuxino *lcd; + u32 checksum, i; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -ENODEV; + + lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + i2c_set_clientdata(client, lcd); + lcd->dev = dev; + lcd->client = client; + + mutex_init(&lcd->mutex); + + /* Copy data into buffer */ + for (i = 0; i < LCD_OLINUXINO_DATA_LEN; i += I2C_SMBUS_BLOCK_MAX) { + mutex_lock(&lcd->mutex); + ret = i2c_smbus_read_i2c_block_data(client, + i, + I2C_SMBUS_BLOCK_MAX, + (u8 *)&lcd->eeprom + i); + mutex_unlock(&lcd->mutex); + if (ret < 0) { + dev_err(dev, "error reading from device at %02x\n", i); + return ret; + } + } + + /* Check configuration checksum */ + checksum = ~crc32(~0, (u8 *)&lcd->eeprom, 252); + if (checksum != lcd->eeprom.checksum) { + dev_err(dev, "configuration checksum does not match!\n"); + return -EINVAL; + } + + /* Check magic header */ + if (lcd->eeprom.header != LCD_OLINUXINO_HEADER_MAGIC) { + dev_err(dev, "magic header does not match\n"); + return -EINVAL; + } + + dev_info(dev, "Detected %s, Rev. %s, Serial: %08x\n", + lcd->eeprom.info.name, + lcd->eeprom.revision, + lcd->eeprom.serial); + + /* + * The eeprom can hold up to 4 modes. + * If the stored value is bigger, overwrite it. + */ + if (lcd->eeprom.num_modes > 4) { + dev_warn(dev, "invalid number of modes, falling back to 4\n"); + lcd->eeprom.num_modes = 4; + } + + lcd->enabled = false; + lcd->prepared = false; + + lcd->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(lcd->supply)) + return PTR_ERR(lcd->supply); + + lcd->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(lcd->enable_gpio)) + return PTR_ERR(lcd->enable_gpio); + + lcd->backlight = devm_of_find_backlight(dev); + if (IS_ERR(lcd->backlight)) + return PTR_ERR(lcd->backlight); + + drm_panel_init(&lcd->panel); + lcd->panel.dev = dev; + lcd->panel.funcs = &lcd_olinuxino_funcs; + + return drm_panel_add(&lcd->panel); +} + +static int lcd_olinuxino_remove(struct i2c_client *client) +{ + struct lcd_olinuxino *panel = i2c_get_clientdata(client); + + drm_panel_remove(&panel->panel); + + lcd_olinuxino_disable(&panel->panel); + lcd_olinuxino_unprepare(&panel->panel); + + return 0; +} + +static const struct of_device_id lcd_olinuxino_of_ids[] = { + { .compatible = "olimex,lcd-olinuxino" }, + { } +}; +MODULE_DEVICE_TABLE(of, lcd_olinuxino_of_ids); + +static struct i2c_driver lcd_olinuxino_driver = { + .driver = { + .name = "lcd_olinuxino", + .of_match_table = lcd_olinuxino_of_ids, + }, + .probe = lcd_olinuxino_probe, + .remove = lcd_olinuxino_remove, +}; + +module_i2c_driver(lcd_olinuxino_driver); + +MODULE_AUTHOR("Stefan Mavrodiev <stefan@olimex.com>"); +MODULE_DESCRIPTION("LCD-OLinuXino driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c new file mode 100644 index 000000000000..fa8bfa7c492d --- /dev/null +++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MIPI-DSI Samsung s6d16d0 panel driver. This is a 864x480 + * AMOLED panel with a command-only DSI interface. + */ + +#include <drm/drm_modes.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> + +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> +#include <linux/of_device.h> +#include <linux/module.h> + +struct s6d16d0 { + struct device *dev; + struct drm_panel panel; + struct regulator *supply; + struct gpio_desc *reset_gpio; +}; + +/* + * The timings are not very helpful as the display is used in + * command mode. + */ +static const struct drm_display_mode samsung_s6d16d0_mode = { + /* HS clock, (htotal*vtotal*vrefresh)/1000 */ + .clock = 420160, + .hdisplay = 864, + .hsync_start = 864 + 154, + .hsync_end = 864 + 154 + 16, + .htotal = 864 + 154 + 16 + 32, + .vdisplay = 480, + .vsync_start = 480 + 1, + .vsync_end = 480 + 1 + 1, + .vtotal = 480 + 1 + 1 + 1, + /* + * This depends on the clocking HS vs LP rate, this value + * is calculated as: + * vrefresh = (clock * 1000) / (htotal*vtotal) + */ + .vrefresh = 816, + .width_mm = 84, + .height_mm = 48, +}; + +static inline struct s6d16d0 *panel_to_s6d16d0(struct drm_panel *panel) +{ + return container_of(panel, struct s6d16d0, panel); +} + +static int s6d16d0_unprepare(struct drm_panel *panel) +{ + struct s6d16d0 *s6 = panel_to_s6d16d0(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + int ret; + + /* Enter sleep mode */ + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to enter sleep mode (%d)\n", + ret); + return ret; + } + + /* Assert RESET */ + gpiod_set_value_cansleep(s6->reset_gpio, 1); + regulator_disable(s6->supply); + + return 0; +} + +static int s6d16d0_prepare(struct drm_panel *panel) +{ + struct s6d16d0 *s6 = panel_to_s6d16d0(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + int ret; + + ret = regulator_enable(s6->supply); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to enable supply (%d)\n", ret); + return ret; + } + + /* Assert RESET */ + gpiod_set_value_cansleep(s6->reset_gpio, 1); + udelay(10); + /* De-assert RESET */ + gpiod_set_value_cansleep(s6->reset_gpio, 0); + msleep(120); + + /* Enabe tearing mode: send TE (tearing effect) at VBLANK */ + ret = mipi_dsi_dcs_set_tear_on(dsi, + MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to enble vblank TE (%d)\n", + ret); + return ret; + } + /* Exit sleep mode and power on */ + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to exit sleep mode (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int s6d16d0_enable(struct drm_panel *panel) +{ + struct s6d16d0 *s6 = panel_to_s6d16d0(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + int ret; + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to turn display on (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int s6d16d0_disable(struct drm_panel *panel) +{ + struct s6d16d0 *s6 = panel_to_s6d16d0(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + int ret; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to turn display off (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int s6d16d0_get_modes(struct drm_panel *panel) +{ + struct drm_connector *connector = panel->connector; + struct drm_display_mode *mode; + + strncpy(connector->display_info.name, "Samsung S6D16D0\0", + DRM_DISPLAY_INFO_LEN); + + mode = drm_mode_duplicate(panel->drm, &samsung_s6d16d0_mode); + if (!mode) { + DRM_ERROR("bad mode or failed to add mode\n"); + return -EINVAL; + } + drm_mode_set_name(mode); + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + + drm_mode_probed_add(connector, mode); + + return 1; /* Number of modes */ +} + +static const struct drm_panel_funcs s6d16d0_drm_funcs = { + .disable = s6d16d0_disable, + .unprepare = s6d16d0_unprepare, + .prepare = s6d16d0_prepare, + .enable = s6d16d0_enable, + .get_modes = s6d16d0_get_modes, +}; + +static int s6d16d0_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct s6d16d0 *s6; + int ret; + + s6 = devm_kzalloc(dev, sizeof(struct s6d16d0), GFP_KERNEL); + if (!s6) + return -ENOMEM; + + mipi_dsi_set_drvdata(dsi, s6); + s6->dev = dev; + + dsi->lanes = 2; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->hs_rate = 420160000; + dsi->lp_rate = 19200000; + /* + * This display uses command mode so no MIPI_DSI_MODE_VIDEO + * or MIPI_DSI_MODE_VIDEO_SYNC_PULSE + * + * As we only send commands we do not need to be continuously + * clocked. + */ + dsi->mode_flags = + MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_EOT_PACKET; + + s6->supply = devm_regulator_get(dev, "vdd1"); + if (IS_ERR(s6->supply)) + return PTR_ERR(s6->supply); + + /* This asserts RESET by default */ + s6->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(s6->reset_gpio)) { + ret = PTR_ERR(s6->reset_gpio); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, "failed to request GPIO (%d)\n", + ret); + return ret; + } + + drm_panel_init(&s6->panel); + s6->panel.dev = dev; + s6->panel.funcs = &s6d16d0_drm_funcs; + + ret = drm_panel_add(&s6->panel); + if (ret < 0) + return ret; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) + drm_panel_remove(&s6->panel); + + return ret; +} + +static int s6d16d0_remove(struct mipi_dsi_device *dsi) +{ + struct s6d16d0 *s6 = mipi_dsi_get_drvdata(dsi); + + mipi_dsi_detach(dsi); + drm_panel_remove(&s6->panel); + + return 0; +} + +static const struct of_device_id s6d16d0_of_match[] = { + { .compatible = "samsung,s6d16d0" }, + { } +}; +MODULE_DEVICE_TABLE(of, s6d16d0_of_match); + +static struct mipi_dsi_driver s6d16d0_driver = { + .probe = s6d16d0_probe, + .remove = s6d16d0_remove, + .driver = { + .name = "panel-samsung-s6d16d0", + .of_match_table = s6d16d0_of_match, + }, +}; +module_mipi_dsi_driver(s6d16d0_driver); + +MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("MIPI-DSI s6d16d0 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 index 75f925390551..2d99e28ff117 100644 --- a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c +++ b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * 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> @@ -366,6 +363,6 @@ static struct platform_driver seiko_panel_platform_driver = { }; module_platform_driver(seiko_panel_platform_driver); -MODULE_AUTHOR("Marco Franchi <marco.franchi@nxp.com"); +MODULE_AUTHOR("Marco Franchi <marco.franchi@nxp.com>"); MODULE_DESCRIPTION("Seiko 43WVF1G panel driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index a04ffb3b2174..5fbee837b0db 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -782,16 +782,38 @@ static const struct panel_desc avic_tm070ddh03 = { }, }; +static const struct drm_display_mode bananapi_s070wv20_ct16_mode = { + .clock = 30000, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 48, + .htotal = 800 + 40 + 48 + 40, + .vdisplay = 480, + .vsync_start = 480 + 13, + .vsync_end = 480 + 13 + 3, + .vtotal = 480 + 13 + 3 + 29, +}; + +static const struct panel_desc bananapi_s070wv20_ct16 = { + .modes = &bananapi_s070wv20_ct16_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 154, + .height = 86, + }, +}; + static const struct drm_display_mode boe_hv070wsa_mode = { - .clock = 40800, + .clock = 42105, .hdisplay = 1024, - .hsync_start = 1024 + 90, - .hsync_end = 1024 + 90 + 90, - .htotal = 1024 + 90 + 90 + 90, + .hsync_start = 1024 + 30, + .hsync_end = 1024 + 30 + 30, + .htotal = 1024 + 30 + 30 + 30, .vdisplay = 600, - .vsync_start = 600 + 3, - .vsync_end = 600 + 3 + 4, - .vtotal = 600 + 3 + 4 + 3, + .vsync_start = 600 + 10, + .vsync_end = 600 + 10 + 10, + .vtotal = 600 + 10 + 10 + 10, .vrefresh = 60, }; @@ -846,6 +868,55 @@ static const struct panel_desc boe_nv101wxmn51 = { }, }; +static const struct drm_display_mode cdtech_s043wq26h_ct7_mode = { + .clock = 9000, + .hdisplay = 480, + .hsync_start = 480 + 5, + .hsync_end = 480 + 5 + 5, + .htotal = 480 + 5 + 5 + 40, + .vdisplay = 272, + .vsync_start = 272 + 8, + .vsync_end = 272 + 8 + 8, + .vtotal = 272 + 8 + 8 + 8, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct panel_desc cdtech_s043wq26h_ct7 = { + .modes = &cdtech_s043wq26h_ct7_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 95, + .height = 54, + }, + .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, +}; + +static const struct drm_display_mode cdtech_s070wv95_ct16_mode = { + .clock = 35000, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 40, + .htotal = 800 + 40 + 40 + 48, + .vdisplay = 480, + .vsync_start = 480 + 29, + .vsync_end = 480 + 29 + 13, + .vtotal = 480 + 29 + 13 + 3, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct panel_desc cdtech_s070wv95_ct16 = { + .modes = &cdtech_s070wv95_ct16_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 154, + .height = 85, + }, +}; + static const struct drm_display_mode chunghwa_claa070wp03xg_mode = { .clock = 66770, .hdisplay = 800, @@ -971,6 +1042,36 @@ static const struct panel_desc dlc_dlc0700yzg_1 = { .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, }; +static const struct display_timing dlc_dlc1010gig_timing = { + .pixelclock = { 68900000, 71100000, 73400000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 43, 53, 63 }, + .hback_porch = { 43, 53, 63 }, + .hsync_len = { 44, 54, 64 }, + .vactive = { 800, 800, 800 }, + .vfront_porch = { 5, 8, 11 }, + .vback_porch = { 5, 8, 11 }, + .vsync_len = { 5, 7, 11 }, + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc dlc_dlc1010gig = { + .timings = &dlc_dlc1010gig_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 216, + .height = 135, + }, + .delay = { + .prepare = 60, + .enable = 150, + .disable = 100, + .unprepare = 60, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, +}; + static const struct drm_display_mode edt_et057090dhu_mode = { .clock = 25175, .hdisplay = 640, @@ -2334,6 +2435,33 @@ static const struct panel_desc winstar_wf35ltiacd = { .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; +static const struct drm_display_mode arm_rtsm_mode[] = { + { + .clock = 65000, + .hdisplay = 1024, + .hsync_start = 1024 + 24, + .hsync_end = 1024 + 24 + 136, + .htotal = 1024 + 24 + 136 + 160, + .vdisplay = 768, + .vsync_start = 768 + 3, + .vsync_end = 768 + 3 + 6, + .vtotal = 768 + 3 + 6 + 29, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, + }, +}; + +static const struct panel_desc arm_rtsm = { + .modes = arm_rtsm_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 400, + .height = 300, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, +}; + static const struct of_device_id platform_of_match[] = { { .compatible = "ampire,am-480272h3tmqw-t01h", @@ -2342,6 +2470,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "ampire,am800480r3tmqwa1h", .data = &ire_am800480r3tmqwa1h, }, { + .compatible = "arm,rtsm-display", + .data = &arm_rtsm, + }, { .compatible = "auo,b101aw03", .data = &auo_b101aw03, }, { @@ -2381,12 +2512,21 @@ static const struct of_device_id platform_of_match[] = { .compatible = "avic,tm070ddh03", .data = &avic_tm070ddh03, }, { + .compatible = "bananapi,s070wv20-ct16", + .data = &bananapi_s070wv20_ct16, + }, { .compatible = "boe,hv070wsa-100", .data = &boe_hv070wsa }, { .compatible = "boe,nv101wxmn51", .data = &boe_nv101wxmn51, }, { + .compatible = "cdtech,s043wq26h-ct7", + .data = &cdtech_s043wq26h_ct7, + }, { + .compatible = "cdtech,s070wv95-ct16", + .data = &cdtech_s070wv95_ct16, + }, { .compatible = "chunghwa,claa070wp03xg", .data = &chunghwa_claa070wp03xg, }, { @@ -2402,6 +2542,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "dlc,dlc0700yzg-1", .data = &dlc_dlc0700yzg_1, }, { + .compatible = "dlc,dlc1010gig", + .data = &dlc_dlc1010gig, + }, { .compatible = "edt,et057090dhu", .data = &edt_et057090dhu, }, { diff --git a/drivers/gpu/drm/panel/panel-truly-nt35597.c b/drivers/gpu/drm/panel/panel-truly-nt35597.c new file mode 100644 index 000000000000..fc2a66c53db4 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-truly-nt35597.c @@ -0,0 +1,675 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include <drm/drmP.h> +#include <drm/drm_panel.h> +#include <drm/drm_mipi_dsi.h> + +#include <linux/gpio/consumer.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/pinctrl/consumer.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +static const char * const regulator_names[] = { + "vdda", + "vdispp", + "vdispn", +}; + +static unsigned long const regulator_enable_loads[] = { + 62000, + 100000, + 100000, +}; + +static unsigned long const regulator_disable_loads[] = { + 80, + 100, + 100, +}; + +struct cmd_set { + u8 commands[4]; + u8 size; +}; + +struct nt35597_config { + u32 width_mm; + u32 height_mm; + const char *panel_name; + const struct cmd_set *panel_on_cmds; + u32 num_on_cmds; + const struct drm_display_mode *dm; +}; + +struct truly_nt35597 { + struct device *dev; + struct drm_panel panel; + + struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)]; + + struct gpio_desc *reset_gpio; + struct gpio_desc *mode_gpio; + + struct backlight_device *backlight; + + struct mipi_dsi_device *dsi[2]; + + const struct nt35597_config *config; + bool prepared; + bool enabled; +}; + +static inline struct truly_nt35597 *panel_to_ctx(struct drm_panel *panel) +{ + return container_of(panel, struct truly_nt35597, panel); +} + +static const struct cmd_set qcom_2k_panel_magic_cmds[] = { + /* CMD2_P0 */ + { { 0xff, 0x20 }, 2 }, + { { 0xfb, 0x01 }, 2 }, + { { 0x00, 0x01 }, 2 }, + { { 0x01, 0x55 }, 2 }, + { { 0x02, 0x45 }, 2 }, + { { 0x05, 0x40 }, 2 }, + { { 0x06, 0x19 }, 2 }, + { { 0x07, 0x1e }, 2 }, + { { 0x0b, 0x73 }, 2 }, + { { 0x0c, 0x73 }, 2 }, + { { 0x0e, 0xb0 }, 2 }, + { { 0x0f, 0xae }, 2 }, + { { 0x11, 0xb8 }, 2 }, + { { 0x13, 0x00 }, 2 }, + { { 0x58, 0x80 }, 2 }, + { { 0x59, 0x01 }, 2 }, + { { 0x5a, 0x00 }, 2 }, + { { 0x5b, 0x01 }, 2 }, + { { 0x5c, 0x80 }, 2 }, + { { 0x5d, 0x81 }, 2 }, + { { 0x5e, 0x00 }, 2 }, + { { 0x5f, 0x01 }, 2 }, + { { 0x72, 0x11 }, 2 }, + { { 0x68, 0x03 }, 2 }, + /* CMD2_P4 */ + { { 0xFF, 0x24 }, 2 }, + { { 0xFB, 0x01 }, 2 }, + { { 0x00, 0x1C }, 2 }, + { { 0x01, 0x0B }, 2 }, + { { 0x02, 0x0C }, 2 }, + { { 0x03, 0x01 }, 2 }, + { { 0x04, 0x0F }, 2 }, + { { 0x05, 0x10 }, 2 }, + { { 0x06, 0x10 }, 2 }, + { { 0x07, 0x10 }, 2 }, + { { 0x08, 0x89 }, 2 }, + { { 0x09, 0x8A }, 2 }, + { { 0x0A, 0x13 }, 2 }, + { { 0x0B, 0x13 }, 2 }, + { { 0x0C, 0x15 }, 2 }, + { { 0x0D, 0x15 }, 2 }, + { { 0x0E, 0x17 }, 2 }, + { { 0x0F, 0x17 }, 2 }, + { { 0x10, 0x1C }, 2 }, + { { 0x11, 0x0B }, 2 }, + { { 0x12, 0x0C }, 2 }, + { { 0x13, 0x01 }, 2 }, + { { 0x14, 0x0F }, 2 }, + { { 0x15, 0x10 }, 2 }, + { { 0x16, 0x10 }, 2 }, + { { 0x17, 0x10 }, 2 }, + { { 0x18, 0x89 }, 2 }, + { { 0x19, 0x8A }, 2 }, + { { 0x1A, 0x13 }, 2 }, + { { 0x1B, 0x13 }, 2 }, + { { 0x1C, 0x15 }, 2 }, + { { 0x1D, 0x15 }, 2 }, + { { 0x1E, 0x17 }, 2 }, + { { 0x1F, 0x17 }, 2 }, + /* STV */ + { { 0x20, 0x40 }, 2 }, + { { 0x21, 0x01 }, 2 }, + { { 0x22, 0x00 }, 2 }, + { { 0x23, 0x40 }, 2 }, + { { 0x24, 0x40 }, 2 }, + { { 0x25, 0x6D }, 2 }, + { { 0x26, 0x40 }, 2 }, + { { 0x27, 0x40 }, 2 }, + /* Vend */ + { { 0xE0, 0x00 }, 2 }, + { { 0xDC, 0x21 }, 2 }, + { { 0xDD, 0x22 }, 2 }, + { { 0xDE, 0x07 }, 2 }, + { { 0xDF, 0x07 }, 2 }, + { { 0xE3, 0x6D }, 2 }, + { { 0xE1, 0x07 }, 2 }, + { { 0xE2, 0x07 }, 2 }, + /* UD */ + { { 0x29, 0xD8 }, 2 }, + { { 0x2A, 0x2A }, 2 }, + /* CLK */ + { { 0x4B, 0x03 }, 2 }, + { { 0x4C, 0x11 }, 2 }, + { { 0x4D, 0x10 }, 2 }, + { { 0x4E, 0x01 }, 2 }, + { { 0x4F, 0x01 }, 2 }, + { { 0x50, 0x10 }, 2 }, + { { 0x51, 0x00 }, 2 }, + { { 0x52, 0x80 }, 2 }, + { { 0x53, 0x00 }, 2 }, + { { 0x56, 0x00 }, 2 }, + { { 0x54, 0x07 }, 2 }, + { { 0x58, 0x07 }, 2 }, + { { 0x55, 0x25 }, 2 }, + /* Reset XDONB */ + { { 0x5B, 0x43 }, 2 }, + { { 0x5C, 0x00 }, 2 }, + { { 0x5F, 0x73 }, 2 }, + { { 0x60, 0x73 }, 2 }, + { { 0x63, 0x22 }, 2 }, + { { 0x64, 0x00 }, 2 }, + { { 0x67, 0x08 }, 2 }, + { { 0x68, 0x04 }, 2 }, + /* Resolution:1440x2560 */ + { { 0x72, 0x02 }, 2 }, + /* mux */ + { { 0x7A, 0x80 }, 2 }, + { { 0x7B, 0x91 }, 2 }, + { { 0x7C, 0xD8 }, 2 }, + { { 0x7D, 0x60 }, 2 }, + { { 0x7F, 0x15 }, 2 }, + { { 0x75, 0x15 }, 2 }, + /* ABOFF */ + { { 0xB3, 0xC0 }, 2 }, + { { 0xB4, 0x00 }, 2 }, + { { 0xB5, 0x00 }, 2 }, + /* Source EQ */ + { { 0x78, 0x00 }, 2 }, + { { 0x79, 0x00 }, 2 }, + { { 0x80, 0x00 }, 2 }, + { { 0x83, 0x00 }, 2 }, + /* FP BP */ + { { 0x93, 0x0A }, 2 }, + { { 0x94, 0x0A }, 2 }, + /* Inversion Type */ + { { 0x8A, 0x00 }, 2 }, + { { 0x9B, 0xFF }, 2 }, + /* IMGSWAP =1 @PortSwap=1 */ + { { 0x9D, 0xB0 }, 2 }, + { { 0x9F, 0x63 }, 2 }, + { { 0x98, 0x10 }, 2 }, + /* FRM */ + { { 0xEC, 0x00 }, 2 }, + /* CMD1 */ + { { 0xFF, 0x10 }, 2 }, + /* VBP+VSA=,VFP = 10H */ + { { 0x3B, 0x03, 0x0A, 0x0A }, 4 }, + /* FTE on */ + { { 0x35, 0x00 }, 2 }, + /* EN_BK =1(auto black) */ + { { 0xE5, 0x01 }, 2 }, + /* CMD mode(10) VDO mode(03) */ + { { 0xBB, 0x03 }, 2 }, + /* Non Reload MTP */ + { { 0xFB, 0x01 }, 2 }, +}; + +static int truly_dcs_write(struct drm_panel *panel, u32 command) +{ + struct truly_nt35597 *ctx = panel_to_ctx(panel); + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) { + ret = mipi_dsi_dcs_write(ctx->dsi[i], command, NULL, 0); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "cmd 0x%x failed for dsi = %d\n", + command, i); + } + } + + return ret; +} + +static int truly_dcs_write_buf(struct drm_panel *panel, + u32 size, const u8 *buf) +{ + struct truly_nt35597 *ctx = panel_to_ctx(panel); + int ret = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) { + ret = mipi_dsi_dcs_write_buffer(ctx->dsi[i], buf, size); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "failed to tx cmd [%d], err: %d\n", i, ret); + return ret; + } + } + + return ret; +} + +static int truly_35597_power_on(struct truly_nt35597 *ctx) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) { + ret = regulator_set_load(ctx->supplies[i].consumer, + regulator_enable_loads[i]); + if (ret) + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret < 0) + return ret; + + /* + * Reset sequence of truly panel requires the panel to be + * out of reset for 10ms, followed by being held in reset + * for 10ms and then out again + */ + gpiod_set_value(ctx->reset_gpio, 0); + usleep_range(10000, 20000); + gpiod_set_value(ctx->reset_gpio, 1); + usleep_range(10000, 20000); + gpiod_set_value(ctx->reset_gpio, 0); + + return 0; +} + +static int truly_nt35597_power_off(struct truly_nt35597 *ctx) +{ + int ret = 0; + int i; + + gpiod_set_value(ctx->reset_gpio, 1); + + for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) { + ret = regulator_set_load(ctx->supplies[i].consumer, + regulator_disable_loads[i]); + if (ret) { + DRM_DEV_ERROR(ctx->dev, + "regulator_set_load failed %d\n", ret); + return ret; + } + } + + ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret) { + DRM_DEV_ERROR(ctx->dev, + "regulator_bulk_disable failed %d\n", ret); + } + return ret; +} + +static int truly_nt35597_disable(struct drm_panel *panel) +{ + struct truly_nt35597 *ctx = panel_to_ctx(panel); + int ret; + + if (!ctx->enabled) + return 0; + + if (ctx->backlight) { + ret = backlight_disable(ctx->backlight); + if (ret < 0) + DRM_DEV_ERROR(ctx->dev, "backlight disable failed %d\n", + ret); + } + + ctx->enabled = false; + return 0; +} + +static int truly_nt35597_unprepare(struct drm_panel *panel) +{ + struct truly_nt35597 *ctx = panel_to_ctx(panel); + int ret = 0; + + if (!ctx->prepared) + return 0; + + ctx->dsi[0]->mode_flags = 0; + ctx->dsi[1]->mode_flags = 0; + + ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_OFF); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "set_display_off cmd failed ret = %d\n", + ret); + } + + /* 120ms delay required here as per DCS spec */ + msleep(120); + + ret = truly_dcs_write(panel, MIPI_DCS_ENTER_SLEEP_MODE); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "enter_sleep cmd failed ret = %d\n", ret); + } + + ret = truly_nt35597_power_off(ctx); + if (ret < 0) + DRM_DEV_ERROR(ctx->dev, "power_off failed ret = %d\n", ret); + + ctx->prepared = false; + return ret; +} + +static int truly_nt35597_prepare(struct drm_panel *panel) +{ + struct truly_nt35597 *ctx = panel_to_ctx(panel); + int ret; + int i; + const struct cmd_set *panel_on_cmds; + const struct nt35597_config *config; + u32 num_cmds; + + if (ctx->prepared) + return 0; + + ret = truly_35597_power_on(ctx); + if (ret < 0) + return ret; + + ctx->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM; + ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM; + + config = ctx->config; + panel_on_cmds = config->panel_on_cmds; + num_cmds = config->num_on_cmds; + + for (i = 0; i < num_cmds; i++) { + ret = truly_dcs_write_buf(panel, + panel_on_cmds[i].size, + panel_on_cmds[i].commands); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "cmd set tx failed i = %d ret = %d\n", + i, ret); + goto power_off; + } + } + + ret = truly_dcs_write(panel, MIPI_DCS_EXIT_SLEEP_MODE); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "exit_sleep_mode cmd failed ret = %d\n", + ret); + goto power_off; + } + + /* Per DSI spec wait 120ms after sending exit sleep DCS command */ + msleep(120); + + ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_ON); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "set_display_on cmd failed ret = %d\n", ret); + goto power_off; + } + + /* Per DSI spec wait 120ms after sending set_display_on DCS command */ + msleep(120); + + ctx->prepared = true; + + return 0; + +power_off: + if (truly_nt35597_power_off(ctx)) + DRM_DEV_ERROR(ctx->dev, "power_off failed\n"); + return ret; +} + +static int truly_nt35597_enable(struct drm_panel *panel) +{ + struct truly_nt35597 *ctx = panel_to_ctx(panel); + int ret; + + if (ctx->enabled) + return 0; + + if (ctx->backlight) { + ret = backlight_enable(ctx->backlight); + if (ret < 0) + DRM_DEV_ERROR(ctx->dev, "backlight enable failed %d\n", + ret); + } + + ctx->enabled = true; + + return 0; +} + +static int truly_nt35597_get_modes(struct drm_panel *panel) +{ + struct drm_connector *connector = panel->connector; + struct truly_nt35597 *ctx = panel_to_ctx(panel); + struct drm_display_mode *mode; + const struct nt35597_config *config; + + config = ctx->config; + mode = drm_mode_create(connector->dev); + if (!mode) { + DRM_DEV_ERROR(ctx->dev, + "failed to create a new display mode\n"); + return 0; + } + + connector->display_info.width_mm = config->width_mm; + connector->display_info.height_mm = config->height_mm; + drm_mode_copy(mode, config->dm); + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs truly_nt35597_drm_funcs = { + .disable = truly_nt35597_disable, + .unprepare = truly_nt35597_unprepare, + .prepare = truly_nt35597_prepare, + .enable = truly_nt35597_enable, + .get_modes = truly_nt35597_get_modes, +}; + +static int truly_nt35597_panel_add(struct truly_nt35597 *ctx) +{ + struct device *dev = ctx->dev; + int ret, i; + const struct nt35597_config *config; + + config = ctx->config; + for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) + ctx->supplies[i].supply = regulator_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret < 0) + return ret; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) { + DRM_DEV_ERROR(dev, "cannot get reset gpio %ld\n", + PTR_ERR(ctx->reset_gpio)); + return PTR_ERR(ctx->reset_gpio); + } + + ctx->mode_gpio = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW); + if (IS_ERR(ctx->mode_gpio)) { + DRM_DEV_ERROR(dev, "cannot get mode gpio %ld\n", + PTR_ERR(ctx->mode_gpio)); + return PTR_ERR(ctx->mode_gpio); + } + + /* dual port */ + gpiod_set_value(ctx->mode_gpio, 0); + + drm_panel_init(&ctx->panel); + ctx->panel.dev = dev; + ctx->panel.funcs = &truly_nt35597_drm_funcs; + drm_panel_add(&ctx->panel); + + return 0; +} + +static const struct drm_display_mode qcom_sdm845_mtp_2k_mode = { + .name = "1440x2560", + .clock = 268316, + .hdisplay = 1440, + .hsync_start = 1440 + 200, + .hsync_end = 1440 + 200 + 32, + .htotal = 1440 + 200 + 32 + 64, + .vdisplay = 2560, + .vsync_start = 2560 + 8, + .vsync_end = 2560 + 8 + 1, + .vtotal = 2560 + 8 + 1 + 7, + .vrefresh = 60, + .flags = 0, +}; + +static const struct nt35597_config nt35597_dir = { + .width_mm = 74, + .height_mm = 131, + .panel_name = "qcom_sdm845_mtp_2k_panel", + .dm = &qcom_sdm845_mtp_2k_mode, + .panel_on_cmds = qcom_2k_panel_magic_cmds, + .num_on_cmds = ARRAY_SIZE(qcom_2k_panel_magic_cmds), +}; + +static int truly_nt35597_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct truly_nt35597 *ctx; + struct mipi_dsi_device *dsi1_device; + struct device_node *dsi1; + struct mipi_dsi_host *dsi1_host; + struct mipi_dsi_device *dsi_dev; + int ret = 0; + int i; + + const struct mipi_dsi_device_info info = { + .type = "trulynt35597", + .channel = 0, + .node = NULL, + }; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + + if (!ctx) + return -ENOMEM; + + /* + * This device represents itself as one with two input ports which are + * fed by the output ports of the two DSI controllers . The DSI0 is + * the master controller and has most of the panel related info in its + * child node. + */ + + ctx->config = of_device_get_match_data(dev); + + if (!ctx->config) { + dev_err(dev, "missing device configuration\n"); + return -ENODEV; + } + + dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); + if (!dsi1) { + DRM_DEV_ERROR(dev, + "failed to get remote node for dsi1_device\n"); + return -ENODEV; + } + + dsi1_host = of_find_mipi_dsi_host_by_node(dsi1); + of_node_put(dsi1); + if (!dsi1_host) { + DRM_DEV_ERROR(dev, "failed to find dsi host\n"); + return -EPROBE_DEFER; + } + + /* register the second DSI device */ + dsi1_device = mipi_dsi_device_register_full(dsi1_host, &info); + if (IS_ERR(dsi1_device)) { + DRM_DEV_ERROR(dev, "failed to create dsi device\n"); + return PTR_ERR(dsi1_device); + } + + mipi_dsi_set_drvdata(dsi, ctx); + + ctx->dev = dev; + ctx->dsi[0] = dsi; + ctx->dsi[1] = dsi1_device; + + ret = truly_nt35597_panel_add(ctx); + if (ret) { + DRM_DEV_ERROR(dev, "failed to add panel\n"); + goto err_panel_add; + } + + for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) { + dsi_dev = ctx->dsi[i]; + dsi_dev->lanes = 4; + dsi_dev->format = MIPI_DSI_FMT_RGB888; + dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + ret = mipi_dsi_attach(dsi_dev); + if (ret < 0) { + DRM_DEV_ERROR(dev, + "dsi attach failed i = %d\n", i); + goto err_dsi_attach; + } + } + + return 0; + +err_dsi_attach: + drm_panel_remove(&ctx->panel); +err_panel_add: + mipi_dsi_device_unregister(dsi1_device); + return ret; +} + +static int truly_nt35597_remove(struct mipi_dsi_device *dsi) +{ + struct truly_nt35597 *ctx = mipi_dsi_get_drvdata(dsi); + + if (ctx->dsi[0]) + mipi_dsi_detach(ctx->dsi[0]); + if (ctx->dsi[1]) { + mipi_dsi_detach(ctx->dsi[1]); + mipi_dsi_device_unregister(ctx->dsi[1]); + } + + drm_panel_remove(&ctx->panel); + return 0; +} + +static const struct of_device_id truly_nt35597_of_match[] = { + { + .compatible = "truly,nt35597-2K-display", + .data = &nt35597_dir, + }, + { } +}; +MODULE_DEVICE_TABLE(of, truly_nt35597_of_match); + +static struct mipi_dsi_driver truly_nt35597_driver = { + .driver = { + .name = "panel-truly-nt35597", + .of_match_table = truly_nt35597_of_match, + }, + .probe = truly_nt35597_probe, + .remove = truly_nt35597_remove, +}; +module_mipi_dsi_driver(truly_nt35597_driver); + +MODULE_DESCRIPTION("Truly NT35597 DSI Panel Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c index 208af9f37914..dffc5093ff16 100644 --- a/drivers/gpu/drm/qxl/qxl_cmd.c +++ b/drivers/gpu/drm/qxl/qxl_cmd.c @@ -84,6 +84,7 @@ static int qxl_check_header(struct qxl_ring *ring) int ret; struct qxl_ring_header *header = &(ring->ring->header); unsigned long flags; + spin_lock_irqsave(&ring->lock, flags); ret = header->prod - header->cons < header->num_items; if (ret == 0) @@ -97,6 +98,7 @@ int qxl_check_idle(struct qxl_ring *ring) int ret; struct qxl_ring_header *header = &(ring->ring->header); unsigned long flags; + spin_lock_irqsave(&ring->lock, flags); ret = header->prod == header->cons; spin_unlock_irqrestore(&ring->lock, flags); @@ -110,6 +112,7 @@ int qxl_ring_push(struct qxl_ring *ring, uint8_t *elt; int idx, ret; unsigned long flags; + spin_lock_irqsave(&ring->lock, flags); if (header->prod - header->cons == header->num_items) { header->notify_on_cons = header->cons + 1; @@ -156,6 +159,7 @@ static bool qxl_ring_pop(struct qxl_ring *ring, volatile uint8_t *ring_elt; int idx; unsigned long flags; + spin_lock_irqsave(&ring->lock, flags); if (header->cons == header->prod) { header->notify_on_prod = header->cons + 1; @@ -365,7 +369,6 @@ void qxl_io_flush_surfaces(struct qxl_device *qdev) wait_for_io_cmd(qdev, 0, QXL_IO_FLUSH_SURFACES_ASYNC); } - void qxl_io_destroy_primary(struct qxl_device *qdev) { wait_for_io_cmd(qdev, 0, QXL_IO_DESTROY_PRIMARY_ASYNC); @@ -373,7 +376,7 @@ void qxl_io_destroy_primary(struct qxl_device *qdev) } void qxl_io_create_primary(struct qxl_device *qdev, - unsigned offset, struct qxl_bo *bo) + unsigned int offset, struct qxl_bo *bo) { struct qxl_surface_create *create; diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c index 15c84068d3fb..118422549828 100644 --- a/drivers/gpu/drm/qxl/qxl_debugfs.c +++ b/drivers/gpu/drm/qxl/qxl_debugfs.c @@ -34,7 +34,6 @@ #include "qxl_drv.h" #include "qxl_object.h" - #if defined(CONFIG_DEBUG_FS) static int qxl_debugfs_irq_received(struct seq_file *m, void *data) @@ -102,9 +101,9 @@ qxl_debugfs_init(struct drm_minor *minor) int qxl_debugfs_add_files(struct qxl_device *qdev, struct drm_info_list *files, - unsigned nfiles) + unsigned int nfiles) { - unsigned i; + unsigned int i; for (i = 0; i < qdev->debugfs_count; i++) { if (qdev->debugfs[i].files == files) { diff --git a/drivers/gpu/drm/qxl/qxl_dev.h b/drivers/gpu/drm/qxl/qxl_dev.h index 94c5aec71920..a0ee41632d7e 100644 --- a/drivers/gpu/drm/qxl/qxl_dev.h +++ b/drivers/gpu/drm/qxl/qxl_dev.h @@ -28,7 +28,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #ifndef H_QXL_DEV #define H_QXL_DEV diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 87d16a0ce01e..2ce9a8dcec84 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -253,12 +253,13 @@ static struct mode_size { }; static int qxl_add_common_modes(struct drm_connector *connector, - unsigned pwidth, - unsigned pheight) + unsigned int pwidth, + unsigned int pheight) { struct drm_device *dev = connector->dev; struct drm_display_mode *mode = NULL; int i; + for (i = 0; i < ARRAY_SIZE(common_modes); i++) { mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false); @@ -315,6 +316,7 @@ static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc, oldcount = qdev->monitors_config->count; if (crtc->state->active) { struct drm_display_mode *mode = &crtc->mode; + head.width = mode->hdisplay; head.height = mode->vdisplay; head.x = crtc->x; @@ -391,9 +393,9 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = { static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb, struct drm_file *file_priv, - unsigned flags, unsigned color, + unsigned int flags, unsigned int color, struct drm_clip_rect *clips, - unsigned num_clips) + unsigned int num_clips) { /* TODO: vmwgfx where this was cribbed from had locking. Why? */ struct qxl_device *qdev = fb->dev->dev_private; @@ -917,8 +919,8 @@ free_mem: static int qxl_conn_get_modes(struct drm_connector *connector) { - unsigned pwidth = 1024; - unsigned pheight = 768; + unsigned int pwidth = 1024; + unsigned int pheight = 768; int ret = 0; ret = qxl_add_monitors_config_modes(connector, &pwidth, &pheight); @@ -938,8 +940,8 @@ static enum drm_mode_status qxl_conn_mode_valid(struct drm_connector *connector, /* TODO: is this called for user defined modes? (xrandr --add-mode) * TODO: check that the mode fits in the framebuffer */ - if(qdev->monitors_config_width == mode->hdisplay && - qdev->monitors_config_height == mode->vdisplay) + if (qdev->monitors_config_width == mode->hdisplay && + qdev->monitors_config_height == mode->vdisplay) return MODE_OK; for (i = 0; i < ARRAY_SIZE(common_modes); i++) { @@ -958,7 +960,6 @@ static struct drm_encoder *qxl_best_encoder(struct drm_connector *connector) return &qxl_output->enc; } - static const struct drm_encoder_helper_funcs qxl_enc_helper_funcs = { }; diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c index cc5b32e749ce..c34e45662965 100644 --- a/drivers/gpu/drm/qxl/qxl_draw.c +++ b/drivers/gpu/drm/qxl/qxl_draw.c @@ -25,7 +25,7 @@ static int alloc_clips(struct qxl_device *qdev, struct qxl_release *release, - unsigned num_clips, + unsigned int num_clips, struct qxl_bo **clips_bo) { int size = sizeof(struct qxl_clip_rects) + sizeof(struct qxl_rect) * num_clips; @@ -37,7 +37,7 @@ static int alloc_clips(struct qxl_device *qdev, * the qxl_clip_rects. This is *not* the same as the memory allocated * on the device, it is offset to qxl_clip_rects.chunk.data */ static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev, - unsigned num_clips, + unsigned int num_clips, struct qxl_bo *clips_bo) { struct qxl_clip_rects *dev_clips; @@ -168,6 +168,7 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image, int ret; struct qxl_drm_image *dimage; struct qxl_bo *palette_bo = NULL; + if (stride == 0) stride = depth * width / 8; @@ -214,6 +215,7 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image, if (depth == 1) { void *ptr; + ret = qxl_palette_create_1bit(palette_bo, release, qxl_fb_image); ptr = qxl_bo_kmap_atomic_page(qdev, dimage->bo, 0); @@ -264,9 +266,9 @@ out_free_drawable: void qxl_draw_dirty_fb(struct qxl_device *qdev, struct drm_framebuffer *fb, struct qxl_bo *bo, - unsigned flags, unsigned color, + unsigned int flags, unsigned int color, struct drm_clip_rect *clips, - unsigned num_clips, int inc) + unsigned int num_clips, int inc) { /* * TODO: if flags & DRM_MODE_FB_DIRTY_ANNOTATE_FILL then we should @@ -340,7 +342,6 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, if (ret) goto out_release_backoff; - ret = qxl_image_init(qdev, release, dimage, surface_base, left, top, width, height, depth, stride); qxl_bo_kunmap(bo); diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 8ff70a7281a7..14d3fa855708 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -23,7 +23,6 @@ * Alon Levy */ - #ifndef QXL_DRV_H #define QXL_DRV_H @@ -83,16 +82,16 @@ struct qxl_bo { struct ttm_placement placement; struct ttm_buffer_object tbo; struct ttm_bo_kmap_obj kmap; - unsigned pin_count; + unsigned int pin_count; void *kptr; int type; /* Constant after initialization */ struct drm_gem_object gem_base; - bool is_primary; /* is this now a primary surface */ - bool is_dumb; + unsigned int is_primary:1; /* is this now a primary surface */ + unsigned int is_dumb:1; struct qxl_bo *shadow; - bool hw_surf_alloc; + unsigned int hw_surf_alloc:1; struct qxl_surface surf; uint32_t surface_id; struct qxl_release *surf_create; @@ -129,11 +128,10 @@ struct qxl_output { struct qxl_mman { struct ttm_bo_global_ref bo_global_ref; struct drm_global_reference mem_global_ref; - bool mem_global_referenced; + unsigned int mem_global_referenced:1; struct ttm_bo_device bdev; }; - struct qxl_memslot { uint8_t generation; uint64_t start_phys_addr; @@ -191,12 +189,12 @@ struct qxl_draw_fill { */ struct qxl_debugfs { struct drm_info_list *files; - unsigned num_files; + unsigned int num_files; }; int qxl_debugfs_add_files(struct qxl_device *rdev, struct drm_info_list *files, - unsigned nfiles); + unsigned int nfiles); int qxl_debugfs_fence_init(struct qxl_device *rdev); struct qxl_device; @@ -231,7 +229,7 @@ struct qxl_device { struct qxl_ram_header *ram_header; - bool primary_created; + unsigned int primary_created:1; struct qxl_memslot *mem_slots; uint8_t n_mem_slots; @@ -254,7 +252,7 @@ struct qxl_device { atomic_t irq_received_display; atomic_t irq_received_cursor; atomic_t irq_received_io_cmd; - unsigned irq_received_error; + unsigned int irq_received_error; wait_queue_head_t display_event; wait_queue_head_t cursor_event; wait_queue_head_t io_cmd_event; @@ -262,7 +260,7 @@ struct qxl_device { /* debugfs */ struct qxl_debugfs debugfs[QXL_DEBUGFS_MAX_COMPONENTS]; - unsigned debugfs_count; + unsigned int debugfs_count; struct mutex update_area_mutex; @@ -372,7 +370,6 @@ int qxl_mode_dumb_mmap(struct drm_file *filp, struct drm_device *dev, uint32_t handle, uint64_t *offset_p); - /* qxl ttm */ int qxl_ttm_init(struct qxl_device *qdev); void qxl_ttm_fini(struct qxl_device *qdev); @@ -398,7 +395,7 @@ void qxl_update_screen(struct qxl_device *qxl); /* qxl io operations (qxl_cmd.c) */ void qxl_io_create_primary(struct qxl_device *qdev, - unsigned offset, + unsigned int offset, struct qxl_bo *bo); void qxl_io_destroy_primary(struct qxl_device *qdev); void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id); @@ -449,9 +446,9 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image, void qxl_draw_dirty_fb(struct qxl_device *qdev, struct drm_framebuffer *fb, struct qxl_bo *bo, - unsigned flags, unsigned color, + unsigned int flags, unsigned int color, struct drm_clip_rect *clips, - unsigned num_clips, int inc); + unsigned int num_clips, int inc); void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec); @@ -496,7 +493,7 @@ bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj); int qxl_debugfs_add_files(struct qxl_device *qdev, struct drm_info_list *files, - unsigned nfiles); + unsigned int nfiles); int qxl_surface_id_alloc(struct qxl_device *qdev, struct qxl_bo *surf); diff --git a/drivers/gpu/drm/qxl/qxl_dumb.c b/drivers/gpu/drm/qxl/qxl_dumb.c index c666b89eed5d..e3765739c396 100644 --- a/drivers/gpu/drm/qxl/qxl_dumb.c +++ b/drivers/gpu/drm/qxl/qxl_dumb.c @@ -38,6 +38,7 @@ int qxl_mode_dumb_create(struct drm_file *file_priv, int r; struct qxl_surface surf; uint32_t pitch, format; + pitch = args->width * ((args->bpp + 1) / 8); args->size = pitch * args->height; args->size = ALIGN(args->size, PAGE_SIZE); @@ -52,7 +53,7 @@ int qxl_mode_dumb_create(struct drm_file *file_priv, default: return -EINVAL; } - + surf.width = args->width; surf.height = args->height; surf.stride = pitch; diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index 2294b7f14fdf..7e047c985ea6 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -134,9 +134,9 @@ out_unref: */ static int qxlfb_framebuffer_dirty(struct drm_framebuffer *fb, struct drm_file *file_priv, - unsigned flags, unsigned color, + unsigned int flags, unsigned int color, struct drm_clip_rect *clips, - unsigned num_clips) + unsigned int num_clips) { struct qxl_device *qdev = fb->dev->dev_private; struct fb_info *info = qdev->fb_helper.fbdev; diff --git a/drivers/gpu/drm/qxl/qxl_image.c b/drivers/gpu/drm/qxl/qxl_image.c index 7fbcc35e8ad3..43688ecdd8a0 100644 --- a/drivers/gpu/drm/qxl/qxl_image.c +++ b/drivers/gpu/drm/qxl/qxl_image.c @@ -136,6 +136,7 @@ qxl_image_init_helper(struct qxl_device *qdev, int remain; int page; int size; + if (stride == linesize && chunk_stride == stride) { remain = linesize * height; page = 0; @@ -162,7 +163,8 @@ qxl_image_init_helper(struct qxl_device *qdev, page++; } } else { - unsigned page_base, page_offset, out_offset; + unsigned int page_base, page_offset, out_offset; + for (i = 0 ; i < height ; ++i) { i_data = (void *)data + i * stride; remain = linesize; diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 6cc9f3367fa0..6e828158bcb0 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -85,6 +85,7 @@ static void apply_reloc(struct qxl_device *qdev, struct qxl_reloc_info *info) { void *reloc_page; + reloc_page = qxl_bo_kmap_atomic_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK); *(uint64_t *)(reloc_page + (info->dst_offset & ~PAGE_MASK)) = qxl_bo_physical_address(qdev, info->src_bo, @@ -189,6 +190,7 @@ static int qxl_process_single_command(struct qxl_device *qdev, { struct qxl_drawable *draw = fb_cmd; + draw->mm_time = qdev->rom->mm_clock; } diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index e25c589d5f50..f6975d7c7d10 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -92,6 +92,7 @@ void qxl_reinit_memslots(struct qxl_device *qdev) static void qxl_gc_work(struct work_struct *work) { struct qxl_device *qdev = container_of(work, struct qxl_device, gc_work); + qxl_garbage_collect(qdev); } @@ -284,7 +285,6 @@ int qxl_device_init(struct qxl_device *qdev, (unsigned long)qdev->surfaceram_base, (unsigned long)qdev->surfaceram_size); - INIT_WORK(&qdev->gc_work, qxl_gc_work); return 0; diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index 6a30196e9d6c..f67a3c535afb 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -54,7 +54,7 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned) { u32 c = 0; u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0; - unsigned i; + unsigned int i; qbo->placement.placement = qbo->placements; qbo->placement.busy_placement = qbo->placements; @@ -74,7 +74,6 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned) } } - int qxl_bo_create(struct qxl_device *qdev, unsigned long size, bool kernel, bool pinned, u32 domain, struct qxl_surface *surf, @@ -266,7 +265,6 @@ static int __qxl_bo_unpin(struct qxl_bo *bo) return r; } - /* * Reserve the BO before pinning the object. If the BO was reserved * beforehand, use the internal version directly __qxl_bo_pin. @@ -335,6 +333,7 @@ void qxl_bo_fini(struct qxl_device *qdev) int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo) { int ret; + if (bo->type == QXL_GEM_DOMAIN_SURFACE && bo->surface_id == 0) { /* allocate a surface id for this surface now */ ret = qxl_surface_id_alloc(qdev, bo); diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h index 0374fd93f4d6..b40fc9a10406 100644 --- a/drivers/gpu/drm/qxl/qxl_object.h +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -35,6 +35,7 @@ static inline int qxl_bo_reserve(struct qxl_bo *bo, bool no_wait) if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { struct drm_device *ddev = bo->gem_base.dev; + dev_err(ddev->dev, "%p reserve failed\n", bo); } return r; @@ -71,6 +72,7 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type, if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { struct drm_device *ddev = bo->gem_base.dev; + dev_err(ddev->dev, "%p reserve failed for wait\n", bo); } diff --git a/drivers/gpu/drm/qxl/qxl_prime.c b/drivers/gpu/drm/qxl/qxl_prime.c index 9f029dda1f07..a55dece118b2 100644 --- a/drivers/gpu/drm/qxl/qxl_prime.c +++ b/drivers/gpu/drm/qxl/qxl_prime.c @@ -38,7 +38,6 @@ void qxl_gem_prime_unpin(struct drm_gem_object *obj) WARN_ONCE(1, "not implemented"); } - struct sg_table *qxl_gem_prime_get_sg_table(struct drm_gem_object *obj) { WARN_ONCE(1, "not implemented"); diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c index e37f0097f744..3813ec198900 100644 --- a/drivers/gpu/drm/qxl/qxl_release.c +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -234,7 +234,7 @@ static int qxl_release_validate_bo(struct qxl_bo *bo) return ret; } - ret = reservation_object_reserve_shared(bo->tbo.resv); + ret = reservation_object_reserve_shared(bo->tbo.resv, 1); if (ret) return ret; @@ -282,7 +282,6 @@ void qxl_release_backoff_reserve_list(struct qxl_release *release) ttm_eu_backoff_reservation(&release->ticket, &release->bos); } - int qxl_alloc_surface_release_reserved(struct qxl_device *qdev, enum qxl_surface_cmd_type surface_cmd_type, struct qxl_release *create_rel, diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 86a1fb32f6db..559a10113837 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -174,7 +174,7 @@ static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->default_caching = TTM_PL_FLAG_CACHED; break; default: - DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + DRM_ERROR("Unsupported memory type %u\n", (unsigned int)type); return -EINVAL; } return 0; @@ -331,7 +331,6 @@ static int qxl_bo_move(struct ttm_buffer_object *bo, bool evict, if (ret) return ret; - if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { qxl_move_null(bo, new_mem); return 0; @@ -401,11 +400,11 @@ int qxl_ttm_init(struct qxl_device *qdev) return r; } DRM_INFO("qxl: %uM of VRAM memory size\n", - (unsigned)qdev->vram_size / (1024 * 1024)); + (unsigned int)qdev->vram_size / (1024 * 1024)); DRM_INFO("qxl: %luM of IO pages memory ready (VRAM domain)\n", - ((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024)); + ((unsigned int)num_io_pages * PAGE_SIZE) / (1024 * 1024)); DRM_INFO("qxl: %uM of Surface memory size\n", - (unsigned)qdev->surfaceram_size / (1024 * 1024)); + (unsigned int)qdev->surfaceram_size / (1024 * 1024)); return 0; } @@ -418,7 +417,6 @@ void qxl_ttm_fini(struct qxl_device *qdev) DRM_INFO("qxl: ttm finalized\n"); } - #define QXL_DEBUGFS_MEM_TYPES 2 #if defined(CONFIG_DEBUG_FS) @@ -443,7 +441,7 @@ int qxl_ttm_debugfs_init(struct qxl_device *qdev) #if defined(CONFIG_DEBUG_FS) static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES]; static char qxl_mem_types_names[QXL_DEBUGFS_MEM_TYPES][32]; - unsigned i; + unsigned int i; for (i = 0; i < QXL_DEBUGFS_MEM_TYPES; i++) { if (i == 0) diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c index 7f1a9c787bd1..fed11ece0de6 100644 --- a/drivers/gpu/drm/radeon/radeon_vm.c +++ b/drivers/gpu/drm/radeon/radeon_vm.c @@ -831,7 +831,7 @@ static int radeon_vm_update_ptes(struct radeon_device *rdev, int r; radeon_sync_resv(rdev, &ib->sync, pt->tbo.resv, true); - r = reservation_object_reserve_shared(pt->tbo.resv); + r = reservation_object_reserve_shared(pt->tbo.resv, 1); if (r) return r; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 084f58df4a8c..7015974c247a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -404,32 +404,15 @@ static struct drm_driver rcar_du_driver = { static int rcar_du_pm_suspend(struct device *dev) { struct rcar_du_device *rcdu = dev_get_drvdata(dev); - struct drm_atomic_state *state; - drm_kms_helper_poll_disable(rcdu->ddev); - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, true); - - state = drm_atomic_helper_suspend(rcdu->ddev); - if (IS_ERR(state)) { - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, false); - drm_kms_helper_poll_enable(rcdu->ddev); - return PTR_ERR(state); - } - - rcdu->suspend_state = state; - - return 0; + return drm_mode_config_helper_suspend(rcdu->ddev); } static int rcar_du_pm_resume(struct device *dev) { struct rcar_du_device *rcdu = dev_get_drvdata(dev); - drm_atomic_helper_resume(rcdu->ddev, rcdu->suspend_state); - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, false); - drm_kms_helper_poll_enable(rcdu->ddev); - - return 0; + return drm_mode_config_helper_resume(rcdu->ddev); } #endif diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 143c037e2c0f..9f5563296c5a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -79,7 +79,6 @@ struct rcar_du_device { struct drm_device *ddev; struct drm_fbdev_cma *fbdev; - struct drm_atomic_state *suspend_state; struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS]; unsigned int num_crtcs; diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 26438d45732b..1e75196f9659 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -7,7 +7,7 @@ config DRM_ROCKCHIP select VIDEOMODE_HELPERS select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP select DRM_DW_HDMI if ROCKCHIP_DW_HDMI - select DRM_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI + select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI select DRM_RGB if ROCKCHIP_RGB select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC help diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 868263ff0302..f6fc9d5dd0ad 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -11,7 +11,7 @@ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o 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_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c new file mode 100644 index 000000000000..7ee359bcee62 --- /dev/null +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -0,0 +1,1076 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: + * Chris Zhong <zyw@rock-chips.com> + * Nickey Yang <nickey.yang@rock-chips.com> + */ + +#include <drm/drmP.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_of.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/math64.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <video/mipi_display.h> + +#include "rockchip_drm_drv.h" +#include "rockchip_drm_vop.h" + +#define DSI_PHY_RSTZ 0xa0 +#define PHY_DISFORCEPLL 0 +#define PHY_ENFORCEPLL BIT(3) +#define PHY_DISABLECLK 0 +#define PHY_ENABLECLK BIT(2) +#define PHY_RSTZ 0 +#define PHY_UNRSTZ BIT(1) +#define PHY_SHUTDOWNZ 0 +#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 DSI_PHY_STATUS 0xb0 +#define LOCK BIT(0) +#define STOP_STATE_CLK_LANE BIT(2) + +#define DSI_PHY_TST_CTRL0 0xb4 +#define PHY_TESTCLK BIT(1) +#define PHY_UNTESTCLK 0 +#define PHY_TESTCLR BIT(0) +#define PHY_UNTESTCLR 0 + +#define DSI_PHY_TST_CTRL1 0xb8 +#define PHY_TESTEN BIT(16) +#define PHY_UNTESTEN 0 +#define PHY_TESTDOUT(n) (((n) & 0xff) << 8) +#define PHY_TESTDIN(n) (((n) & 0xff) << 0) + +#define DSI_INT_ST0 0xbc +#define DSI_INT_ST1 0xc0 +#define DSI_INT_MSK0 0xc4 +#define DSI_INT_MSK1 0xc8 + +#define PHY_STATUS_TIMEOUT_US 10000 +#define CMD_PKT_STATUS_TIMEOUT_US 20000 + +#define BYPASS_VCO_RANGE BIT(7) +#define VCO_RANGE_CON_SEL(val) (((val) & 0x7) << 3) +#define VCO_IN_CAP_CON_DEFAULT (0x0 << 1) +#define VCO_IN_CAP_CON_LOW (0x1 << 1) +#define VCO_IN_CAP_CON_HIGH (0x2 << 1) +#define REF_BIAS_CUR_SEL BIT(0) + +#define CP_CURRENT_3UA 0x1 +#define CP_CURRENT_4_5UA 0x2 +#define CP_CURRENT_7_5UA 0x6 +#define CP_CURRENT_6UA 0x9 +#define CP_CURRENT_12UA 0xb +#define CP_CURRENT_SEL(val) ((val) & 0xf) +#define CP_PROGRAM_EN BIT(7) + +#define LPF_RESISTORS_15_5KOHM 0x1 +#define LPF_RESISTORS_13KOHM 0x2 +#define LPF_RESISTORS_11_5KOHM 0x4 +#define LPF_RESISTORS_10_5KOHM 0x8 +#define LPF_RESISTORS_8KOHM 0x10 +#define LPF_PROGRAM_EN BIT(6) +#define LPF_RESISTORS_SEL(val) ((val) & 0x3f) + +#define HSFREQRANGE_SEL(val) (((val) & 0x3f) << 1) + +#define INPUT_DIVIDER(val) (((val) - 1) & 0x7f) +#define LOW_PROGRAM_EN 0 +#define HIGH_PROGRAM_EN BIT(7) +#define LOOP_DIV_LOW_SEL(val) (((val) - 1) & 0x1f) +#define LOOP_DIV_HIGH_SEL(val) ((((val) - 1) >> 5) & 0xf) +#define PLL_LOOP_DIV_EN BIT(5) +#define PLL_INPUT_DIV_EN BIT(4) + +#define POWER_CONTROL BIT(6) +#define INTERNAL_REG_CURRENT BIT(3) +#define BIAS_BLOCK_ON BIT(2) +#define BANDGAP_ON BIT(0) + +#define TER_RESISTOR_HIGH BIT(7) +#define TER_RESISTOR_LOW 0 +#define LEVEL_SHIFTERS_ON BIT(6) +#define TER_CAL_DONE BIT(5) +#define SETRD_MAX (0x7 << 2) +#define POWER_MANAGE BIT(1) +#define TER_RESISTORS_ON BIT(0) + +#define BIASEXTR_SEL(val) ((val) & 0x7) +#define BANDGAP_SEL(val) ((val) & 0x7) +#define TLP_PROGRAM_EN BIT(7) +#define THS_PRE_PROGRAM_EN BIT(7) +#define THS_ZERO_PROGRAM_EN BIT(6) + +#define PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL 0x10 +#define PLL_CP_CONTROL_PLL_LOCK_BYPASS 0x11 +#define PLL_LPF_AND_CP_CONTROL 0x12 +#define PLL_INPUT_DIVIDER_RATIO 0x17 +#define PLL_LOOP_DIVIDER_RATIO 0x18 +#define PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL 0x19 +#define BANDGAP_AND_BIAS_CONTROL 0x20 +#define TERMINATION_RESISTER_CONTROL 0x21 +#define AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY 0x22 +#define HS_RX_CONTROL_OF_LANE_0 0x44 +#define HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL 0x60 +#define HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL 0x61 +#define HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL 0x62 +#define HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL 0x63 +#define HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL 0x64 +#define HS_TX_CLOCK_LANE_POST_TIME_CONTROL 0x65 +#define HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL 0x70 +#define HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL 0x71 +#define HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL 0x72 +#define HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL 0x73 +#define HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL 0x74 + +#define DW_MIPI_NEEDS_PHY_CFG_CLK BIT(0) +#define DW_MIPI_NEEDS_GRF_CLK BIT(1) + +#define RK3288_GRF_SOC_CON6 0x025c +#define RK3288_DSI0_LCDC_SEL BIT(6) +#define RK3288_DSI1_LCDC_SEL BIT(9) + +#define RK3399_GRF_SOC_CON20 0x6250 +#define RK3399_DSI0_LCDC_SEL BIT(0) +#define RK3399_DSI1_LCDC_SEL BIT(4) + +#define RK3399_GRF_SOC_CON22 0x6258 +#define RK3399_DSI0_TURNREQUEST (0xf << 12) +#define RK3399_DSI0_TURNDISABLE (0xf << 8) +#define RK3399_DSI0_FORCETXSTOPMODE (0xf << 4) +#define RK3399_DSI0_FORCERXMODE (0xf << 0) + +#define RK3399_GRF_SOC_CON23 0x625c +#define RK3399_DSI1_TURNDISABLE (0xf << 12) +#define RK3399_DSI1_FORCETXSTOPMODE (0xf << 8) +#define RK3399_DSI1_FORCERXMODE (0xf << 4) +#define RK3399_DSI1_ENABLE (0xf << 0) + +#define RK3399_GRF_SOC_CON24 0x6260 +#define RK3399_TXRX_MASTERSLAVEZ BIT(7) +#define RK3399_TXRX_ENABLECLK BIT(6) +#define RK3399_TXRX_BASEDIR BIT(5) + +#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) + +#define to_dsi(nm) container_of(nm, struct dw_mipi_dsi_rockchip, nm) + +enum { + BANDGAP_97_07, + BANDGAP_98_05, + BANDGAP_99_02, + BANDGAP_100_00, + BANDGAP_93_17, + BANDGAP_94_15, + BANDGAP_95_12, + BANDGAP_96_10, +}; + +enum { + BIASEXTR_87_1, + BIASEXTR_91_5, + BIASEXTR_95_9, + BIASEXTR_100, + BIASEXTR_105_94, + BIASEXTR_111_88, + BIASEXTR_118_8, + BIASEXTR_127_7, +}; + +struct rockchip_dw_dsi_chip_data { + u32 reg; + + u32 lcdsel_grf_reg; + u32 lcdsel_big; + u32 lcdsel_lit; + + u32 enable_grf_reg; + u32 enable; + + u32 lanecfg1_grf_reg; + u32 lanecfg1; + u32 lanecfg2_grf_reg; + u32 lanecfg2; + + unsigned int flags; + unsigned int max_data_lanes; +}; + +struct dw_mipi_dsi_rockchip { + struct device *dev; + struct drm_encoder encoder; + void __iomem *base; + + struct regmap *grf_regmap; + struct clk *pllref_clk; + struct clk *grf_clk; + struct clk *phy_cfg_clk; + + /* dual-channel */ + bool is_slave; + struct dw_mipi_dsi_rockchip *slave; + + unsigned int lane_mbps; /* per lane */ + u16 input_div; + u16 feedback_div; + u32 format; + + struct dw_mipi_dsi *dmd; + const struct rockchip_dw_dsi_chip_data *cdata; + struct dw_mipi_dsi_plat_data pdata; + int devcnt; +}; + +struct dphy_pll_parameter_map { + unsigned int max_mbps; + u8 hsfreqrange; + u8 icpctrl; + u8 lpfctrl; +}; + +/* The table is based on 27MHz DPHY pll reference clock. */ +static const struct dphy_pll_parameter_map dppa_map[] = { + { 89, 0x00, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM }, + { 99, 0x10, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM }, + { 109, 0x20, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM }, + { 129, 0x01, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM }, + { 139, 0x11, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM }, + { 149, 0x21, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM }, + { 169, 0x02, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM }, + { 179, 0x12, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM }, + { 199, 0x22, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM }, + { 219, 0x03, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM }, + { 239, 0x13, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM }, + { 249, 0x23, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM }, + { 269, 0x04, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM }, + { 299, 0x14, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM }, + { 329, 0x05, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM }, + { 359, 0x15, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM }, + { 399, 0x25, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM }, + { 449, 0x06, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM }, + { 499, 0x16, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM }, + { 549, 0x07, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM }, + { 599, 0x17, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM }, + { 649, 0x08, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM }, + { 699, 0x18, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM }, + { 749, 0x09, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM }, + { 799, 0x19, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM }, + { 849, 0x29, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM }, + { 899, 0x39, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM }, + { 949, 0x0a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM }, + { 999, 0x1a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM }, + {1049, 0x2a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM }, + {1099, 0x3a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM }, + {1149, 0x0b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }, + {1199, 0x1b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }, + {1249, 0x2b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }, + {1299, 0x3b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }, + {1349, 0x0c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }, + {1399, 0x1c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }, + {1449, 0x2c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }, + {1500, 0x3c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM } +}; + +static int max_mbps_to_parameter(unsigned int max_mbps) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dppa_map); i++) + if (dppa_map[i].max_mbps >= max_mbps) + return i; + + return -EINVAL; +} + +static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val) +{ + writel(val, dsi->base + reg); +} + +static inline u32 dsi_read(struct dw_mipi_dsi_rockchip *dsi, u32 reg) +{ + return readl(dsi->base + reg); +} + +static inline void dsi_set(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 mask) +{ + dsi_write(dsi, reg, dsi_read(dsi, reg) | mask); +} + +static inline void dsi_update_bits(struct dw_mipi_dsi_rockchip *dsi, u32 reg, + u32 mask, u32 val) +{ + dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val); +} + +static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi, + u8 test_code, + u8 test_data) +{ + /* + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content + * is latched internally as the current test code. Test data is + * programmed internally by rising edge on TESTCLK. + */ + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); + + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(test_code)); + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); + + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(test_data)); + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); +} + +/** + * ns2bc - Nanoseconds to byte clock cycles + */ +static inline unsigned int ns2bc(struct dw_mipi_dsi_rockchip *dsi, int ns) +{ + return DIV_ROUND_UP(ns * dsi->lane_mbps / 8, 1000); +} + +/** + * ns2ui - Nanoseconds to UI time periods + */ +static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns) +{ + return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000); +} + +static int dw_mipi_dsi_phy_init(void *priv_data) +{ + struct dw_mipi_dsi_rockchip *dsi = priv_data; + int ret, i, vco; + + /* + * Get vco from frequency(lane_mbps) + * vco frequency table + * 000 - between 80 and 200 MHz + * 001 - between 200 and 300 MHz + * 010 - between 300 and 500 MHz + * 011 - between 500 and 700 MHz + * 100 - between 700 and 900 MHz + * 101 - between 900 and 1100 MHz + * 110 - between 1100 and 1300 MHz + * 111 - between 1300 and 1500 MHz + */ + vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200; + + i = max_mbps_to_parameter(dsi->lane_mbps); + if (i < 0) { + DRM_DEV_ERROR(dsi->dev, + "failed to get parameter for %dmbps clock\n", + dsi->lane_mbps); + return i; + } + + ret = clk_prepare_enable(dsi->phy_cfg_clk); + if (ret) { + DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n"); + return ret; + } + + dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, + BYPASS_VCO_RANGE | + VCO_RANGE_CON_SEL(vco) | + VCO_IN_CAP_CON_LOW | + REF_BIAS_CUR_SEL); + + dw_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS, + CP_CURRENT_SEL(dppa_map[i].icpctrl)); + dw_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL, + CP_PROGRAM_EN | LPF_PROGRAM_EN | + LPF_RESISTORS_SEL(dppa_map[i].lpfctrl)); + + dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0, + HSFREQRANGE_SEL(dppa_map[i].hsfreqrange)); + + dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO, + INPUT_DIVIDER(dsi->input_div)); + dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + LOOP_DIV_LOW_SEL(dsi->feedback_div) | + LOW_PROGRAM_EN); + /* + * We need set PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL immediately + * to make the configured LSB effective according to IP simulation + * and lab test results. + * Only in this way can we get correct mipi phy pll frequency. + */ + dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); + dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + LOOP_DIV_HIGH_SEL(dsi->feedback_div) | + HIGH_PROGRAM_EN); + dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); + + dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, + LOW_PROGRAM_EN | BIASEXTR_SEL(BIASEXTR_127_7)); + dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY, + HIGH_PROGRAM_EN | BANDGAP_SEL(BANDGAP_96_10)); + + dw_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL, + POWER_CONTROL | INTERNAL_REG_CURRENT | + BIAS_BLOCK_ON | BANDGAP_ON); + + dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, + TER_RESISTOR_LOW | TER_CAL_DONE | + SETRD_MAX | TER_RESISTORS_ON); + dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL, + TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON | + SETRD_MAX | POWER_MANAGE | + TER_RESISTORS_ON); + + dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL, + TLP_PROGRAM_EN | ns2bc(dsi, 500)); + dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL, + THS_PRE_PROGRAM_EN | ns2ui(dsi, 40)); + dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL, + THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300)); + dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL, + THS_PRE_PROGRAM_EN | ns2ui(dsi, 100)); + dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL, + BIT(5) | ns2bc(dsi, 100)); + dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_POST_TIME_CONTROL, + BIT(5) | (ns2bc(dsi, 60) + 7)); + + dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL, + TLP_PROGRAM_EN | ns2bc(dsi, 500)); + dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL, + THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 20)); + dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL, + THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2)); + dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL, + THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8)); + dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL, + BIT(5) | ns2bc(dsi, 100)); + + clk_disable_unprepare(dsi->phy_cfg_clk); + + return ret; +} + +static int +dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format, + unsigned int *lane_mbps) +{ + struct dw_mipi_dsi_rockchip *dsi = priv_data; + int bpp; + unsigned long mpclk, tmp; + unsigned int target_mbps = 1000; + unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps; + unsigned long best_freq = 0; + unsigned long fvco_min, fvco_max, fin, fout; + unsigned int min_prediv, max_prediv; + unsigned int _prediv, uninitialized_var(best_prediv); + unsigned long _fbdiv, uninitialized_var(best_fbdiv); + unsigned long min_delta = ULONG_MAX; + + dsi->format = format; + bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + if (bpp < 0) { + DRM_DEV_ERROR(dsi->dev, + "failed to get bpp for pixel format %d\n", + dsi->format); + return bpp; + } + + mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC); + if (mpclk) { + /* take 1 / 0.8, since mbps must big than bandwidth of RGB */ + tmp = mpclk * (bpp / lanes) * 10 / 8; + if (tmp < max_mbps) + target_mbps = tmp; + else + DRM_DEV_ERROR(dsi->dev, + "DPHY clock frequency is out of range\n"); + } + + fin = clk_get_rate(dsi->pllref_clk); + fout = target_mbps * USEC_PER_SEC; + + /* constraint: 5Mhz <= Fref / N <= 40MHz */ + min_prediv = DIV_ROUND_UP(fin, 40 * USEC_PER_SEC); + max_prediv = fin / (5 * USEC_PER_SEC); + + /* constraint: 80MHz <= Fvco <= 1500Mhz */ + fvco_min = 80 * USEC_PER_SEC; + fvco_max = 1500 * USEC_PER_SEC; + + for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) { + u64 tmp; + u32 delta; + /* Fvco = Fref * M / N */ + tmp = (u64)fout * _prediv; + do_div(tmp, fin); + _fbdiv = tmp; + /* + * Due to the use of a "by 2 pre-scaler," the range of the + * feedback multiplication value M is limited to even division + * numbers, and m must be greater than 6, not bigger than 512. + */ + if (_fbdiv < 6 || _fbdiv > 512) + continue; + + _fbdiv += _fbdiv % 2; + + tmp = (u64)_fbdiv * fin; + do_div(tmp, _prediv); + if (tmp < fvco_min || tmp > fvco_max) + continue; + + delta = abs(fout - tmp); + if (delta < min_delta) { + best_prediv = _prediv; + best_fbdiv = _fbdiv; + min_delta = delta; + best_freq = tmp; + } + } + + if (best_freq) { + dsi->lane_mbps = DIV_ROUND_UP(best_freq, USEC_PER_SEC); + *lane_mbps = dsi->lane_mbps; + dsi->input_div = best_prediv; + dsi->feedback_div = best_fbdiv; + } else { + DRM_DEV_ERROR(dsi->dev, "Can not find best_freq for DPHY\n"); + return -EINVAL; + } + + return 0; +} + +static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_rockchip_phy_ops = { + .init = dw_mipi_dsi_phy_init, + .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, +}; + +static void dw_mipi_dsi_rockchip_config(struct dw_mipi_dsi_rockchip *dsi, + int mux) +{ + if (dsi->cdata->lcdsel_grf_reg) + regmap_write(dsi->grf_regmap, dsi->cdata->lcdsel_grf_reg, + mux ? dsi->cdata->lcdsel_lit : dsi->cdata->lcdsel_big); + + if (dsi->cdata->lanecfg1_grf_reg) + regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg1_grf_reg, + dsi->cdata->lanecfg1); + + if (dsi->cdata->lanecfg2_grf_reg) + regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg2_grf_reg, + dsi->cdata->lanecfg2); + + if (dsi->cdata->enable_grf_reg) + regmap_write(dsi->grf_regmap, dsi->cdata->enable_grf_reg, + dsi->cdata->enable); +} + +static int +dw_mipi_dsi_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); + struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder); + + switch (dsi->format) { + case MIPI_DSI_FMT_RGB888: + s->output_mode = ROCKCHIP_OUT_MODE_P888; + break; + case MIPI_DSI_FMT_RGB666: + s->output_mode = ROCKCHIP_OUT_MODE_P666; + break; + case MIPI_DSI_FMT_RGB565: + s->output_mode = ROCKCHIP_OUT_MODE_P565; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + s->output_type = DRM_MODE_CONNECTOR_DSI; + if (dsi->slave) + s->output_flags = ROCKCHIP_OUTPUT_DSI_DUAL; + + return 0; +} + +static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) +{ + struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder); + int ret, mux; + + mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node, + &dsi->encoder); + if (mux < 0) + return; + + pm_runtime_get_sync(dsi->dev); + if (dsi->slave) + pm_runtime_get_sync(dsi->slave->dev); + + /* + * For the RK3399, the clk of grf must be enabled before writing grf + * register. And for RK3288 or other soc, this grf_clk must be NULL, + * the clk_prepare_enable return true directly. + */ + ret = clk_prepare_enable(dsi->grf_clk); + if (ret) { + DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); + return; + } + + dw_mipi_dsi_rockchip_config(dsi, mux); + if (dsi->slave) + dw_mipi_dsi_rockchip_config(dsi->slave, mux); + + clk_disable_unprepare(dsi->grf_clk); +} + +static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder) +{ + struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder); + + if (dsi->slave) + pm_runtime_put(dsi->slave->dev); + pm_runtime_put(dsi->dev); +} + +static const struct drm_encoder_helper_funcs +dw_mipi_dsi_encoder_helper_funcs = { + .atomic_check = dw_mipi_dsi_encoder_atomic_check, + .enable = dw_mipi_dsi_encoder_enable, + .disable = dw_mipi_dsi_encoder_disable, +}; + +static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi, + struct drm_device *drm_dev) +{ + struct drm_encoder *encoder = &dsi->encoder; + int ret; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, + dsi->dev->of_node); + + ret = drm_encoder_init(drm_dev, encoder, &dw_mipi_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder with drm\n"); + return ret; + } + + drm_encoder_helper_add(encoder, &dw_mipi_dsi_encoder_helper_funcs); + + return 0; +} + +static struct device +*dw_mipi_dsi_rockchip_find_second(struct dw_mipi_dsi_rockchip *dsi) +{ + const struct of_device_id *match; + struct device_node *node = NULL, *local; + + match = of_match_device(dsi->dev->driver->of_match_table, dsi->dev); + + local = of_graph_get_remote_node(dsi->dev->of_node, 1, 0); + if (!local) + return NULL; + + while ((node = of_find_compatible_node(node, NULL, + match->compatible))) { + struct device_node *remote; + + /* found ourself */ + if (node == dsi->dev->of_node) + continue; + + remote = of_graph_get_remote_node(node, 1, 0); + if (!remote) + continue; + + /* same display device in port1-ep0 for both */ + if (remote == local) { + struct dw_mipi_dsi_rockchip *dsi2; + struct platform_device *pdev; + + pdev = of_find_device_by_node(node); + + /* + * we have found the second, so will either return it + * or return with an error. In any case won't need the + * nodes anymore nor continue the loop. + */ + of_node_put(remote); + of_node_put(node); + of_node_put(local); + + if (!pdev) + return ERR_PTR(-EPROBE_DEFER); + + dsi2 = platform_get_drvdata(pdev); + if (!dsi2) { + platform_device_put(pdev); + return ERR_PTR(-EPROBE_DEFER); + } + + return &pdev->dev; + } + + of_node_put(remote); + } + + of_node_put(local); + + return NULL; +} + +static int dw_mipi_dsi_rockchip_bind(struct device *dev, + struct device *master, + void *data) +{ + struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + struct device *second; + bool master1, master2; + int ret; + + second = dw_mipi_dsi_rockchip_find_second(dsi); + if (IS_ERR(second)) + return PTR_ERR(second); + + if (second) { + master1 = of_property_read_bool(dsi->dev->of_node, + "clock-master"); + master2 = of_property_read_bool(second->of_node, + "clock-master"); + + if (master1 && master2) { + DRM_DEV_ERROR(dsi->dev, "only one clock-master allowed\n"); + return -EINVAL; + } + + if (!master1 && !master2) { + DRM_DEV_ERROR(dsi->dev, "no clock-master defined\n"); + return -EINVAL; + } + + /* we are the slave in dual-DSI */ + if (!master1) { + dsi->is_slave = true; + return 0; + } + + dsi->slave = dev_get_drvdata(second); + if (!dsi->slave) { + DRM_DEV_ERROR(dev, "could not get slaves data\n"); + return -ENODEV; + } + + dsi->slave->is_slave = true; + dw_mipi_dsi_set_slave(dsi->dmd, dsi->slave->dmd); + put_device(second); + } + + ret = clk_prepare_enable(dsi->pllref_clk); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret); + return ret; + } + + ret = rockchip_dsi_drm_create_encoder(dsi, drm_dev); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to create drm encoder\n"); + return ret; + } + + ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret); + return ret; + } + + return 0; +} + +static void dw_mipi_dsi_rockchip_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev); + + if (dsi->is_slave) + return; + + dw_mipi_dsi_unbind(dsi->dmd); + + clk_disable_unprepare(dsi->pllref_clk); +} + +static const struct component_ops dw_mipi_dsi_rockchip_ops = { + .bind = dw_mipi_dsi_rockchip_bind, + .unbind = dw_mipi_dsi_rockchip_unbind, +}; + +static int dw_mipi_dsi_rockchip_host_attach(void *priv_data, + struct mipi_dsi_device *device) +{ + struct dw_mipi_dsi_rockchip *dsi = priv_data; + struct device *second; + int ret; + + ret = component_add(dsi->dev, &dw_mipi_dsi_rockchip_ops); + if (ret) { + DRM_DEV_ERROR(dsi->dev, "Failed to register component: %d\n", + ret); + return ret; + } + + second = dw_mipi_dsi_rockchip_find_second(dsi); + if (IS_ERR(second)) + return PTR_ERR(second); + if (second) { + ret = component_add(second, &dw_mipi_dsi_rockchip_ops); + if (ret) { + DRM_DEV_ERROR(second, + "Failed to register component: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static int dw_mipi_dsi_rockchip_host_detach(void *priv_data, + struct mipi_dsi_device *device) +{ + struct dw_mipi_dsi_rockchip *dsi = priv_data; + struct device *second; + + second = dw_mipi_dsi_rockchip_find_second(dsi); + if (second && !IS_ERR(second)) + component_del(second, &dw_mipi_dsi_rockchip_ops); + + component_del(dsi->dev, &dw_mipi_dsi_rockchip_ops); + + return 0; +} + +static const struct dw_mipi_dsi_host_ops dw_mipi_dsi_rockchip_host_ops = { + .attach = dw_mipi_dsi_rockchip_host_attach, + .detach = dw_mipi_dsi_rockchip_host_detach, +}; + +static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct dw_mipi_dsi_rockchip *dsi; + struct resource *res; + const struct rockchip_dw_dsi_chip_data *cdata = + of_device_get_match_data(dev); + int ret, i; + + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dsi->base = devm_ioremap_resource(dev, res); + if (IS_ERR(dsi->base)) { + DRM_DEV_ERROR(dev, "Unable to get dsi registers\n"); + return PTR_ERR(dsi->base); + } + + i = 0; + while (cdata[i].reg) { + if (cdata[i].reg == res->start) { + dsi->cdata = &cdata[i]; + break; + } + + i++; + } + + if (!dsi->cdata) { + dev_err(dev, "no dsi-config for %s node\n", np->name); + return -EINVAL; + } + + dsi->pllref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(dsi->pllref_clk)) { + ret = PTR_ERR(dsi->pllref_clk); + DRM_DEV_ERROR(dev, + "Unable to get pll reference clock: %d\n", ret); + return ret; + } + + if (dsi->cdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) { + dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg"); + if (IS_ERR(dsi->phy_cfg_clk)) { + ret = PTR_ERR(dsi->phy_cfg_clk); + DRM_DEV_ERROR(dev, + "Unable to get phy_cfg_clk: %d\n", ret); + return ret; + } + } + + if (dsi->cdata->flags & DW_MIPI_NEEDS_GRF_CLK) { + dsi->grf_clk = devm_clk_get(dev, "grf"); + if (IS_ERR(dsi->grf_clk)) { + ret = PTR_ERR(dsi->grf_clk); + DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret); + return ret; + } + } + + dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(dsi->grf_regmap)) { + DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n"); + return PTR_ERR(dsi->grf_regmap); + } + + dsi->dev = dev; + dsi->pdata.base = dsi->base; + dsi->pdata.max_data_lanes = dsi->cdata->max_data_lanes; + dsi->pdata.phy_ops = &dw_mipi_dsi_rockchip_phy_ops; + dsi->pdata.host_ops = &dw_mipi_dsi_rockchip_host_ops; + dsi->pdata.priv_data = dsi; + platform_set_drvdata(pdev, dsi); + + dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata); + if (IS_ERR(dsi->dmd)) { + ret = PTR_ERR(dsi->dmd); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, + "Failed to probe dw_mipi_dsi: %d\n", ret); + goto err_clkdisable; + } + + return 0; + +err_clkdisable: + clk_disable_unprepare(dsi->pllref_clk); + return ret; +} + +static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev) +{ + struct dw_mipi_dsi_rockchip *dsi = platform_get_drvdata(pdev); + + if (dsi->devcnt == 0) + component_del(dsi->dev, &dw_mipi_dsi_rockchip_ops); + + dw_mipi_dsi_remove(dsi->dmd); + + return 0; +} + +static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = { + { + .reg = 0xff960000, + .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, + .lcdsel_big = HIWORD_UPDATE(0, RK3288_DSI0_LCDC_SEL), + .lcdsel_lit = HIWORD_UPDATE(RK3288_DSI0_LCDC_SEL, RK3288_DSI0_LCDC_SEL), + + .max_data_lanes = 4, + }, + { + .reg = 0xff964000, + .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, + .lcdsel_big = HIWORD_UPDATE(0, RK3288_DSI1_LCDC_SEL), + .lcdsel_lit = HIWORD_UPDATE(RK3288_DSI1_LCDC_SEL, RK3288_DSI1_LCDC_SEL), + + .max_data_lanes = 4, + }, + { /* sentinel */ } +}; + +static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = { + { + .reg = 0xff960000, + .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, + .lcdsel_big = HIWORD_UPDATE(0, RK3399_DSI0_LCDC_SEL), + .lcdsel_lit = HIWORD_UPDATE(RK3399_DSI0_LCDC_SEL, + RK3399_DSI0_LCDC_SEL), + + .lanecfg1_grf_reg = RK3399_GRF_SOC_CON22, + .lanecfg1 = HIWORD_UPDATE(0, RK3399_DSI0_TURNREQUEST | + RK3399_DSI0_TURNDISABLE | + RK3399_DSI0_FORCETXSTOPMODE | + RK3399_DSI0_FORCERXMODE), + + .flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK, + .max_data_lanes = 4, + }, + { + .reg = 0xff968000, + .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, + .lcdsel_big = HIWORD_UPDATE(0, RK3399_DSI1_LCDC_SEL), + .lcdsel_lit = HIWORD_UPDATE(RK3399_DSI1_LCDC_SEL, + RK3399_DSI1_LCDC_SEL), + + .lanecfg1_grf_reg = RK3399_GRF_SOC_CON23, + .lanecfg1 = HIWORD_UPDATE(0, RK3399_DSI1_TURNDISABLE | + RK3399_DSI1_FORCETXSTOPMODE | + RK3399_DSI1_FORCERXMODE | + RK3399_DSI1_ENABLE), + + .lanecfg2_grf_reg = RK3399_GRF_SOC_CON24, + .lanecfg2 = HIWORD_UPDATE(RK3399_TXRX_MASTERSLAVEZ | + RK3399_TXRX_ENABLECLK, + RK3399_TXRX_MASTERSLAVEZ | + RK3399_TXRX_ENABLECLK | + RK3399_TXRX_BASEDIR), + + .enable_grf_reg = RK3399_GRF_SOC_CON23, + .enable = HIWORD_UPDATE(RK3399_DSI1_ENABLE, RK3399_DSI1_ENABLE), + + .flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK, + .max_data_lanes = 4, + }, + { /* sentinel */ } +}; + +static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = { + { + .compatible = "rockchip,rk3288-mipi-dsi", + .data = &rk3288_chip_data, + }, { + .compatible = "rockchip,rk3399-mipi-dsi", + .data = &rk3399_chip_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw_mipi_dsi_rockchip_dt_ids); + +struct platform_driver dw_mipi_dsi_rockchip_driver = { + .probe = dw_mipi_dsi_rockchip_probe, + .remove = dw_mipi_dsi_rockchip_remove, + .driver = { + .of_match_table = dw_mipi_dsi_rockchip_dt_ids, + .name = "dw-mipi-dsi-rockchip", + }, +}; diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c deleted file mode 100644 index 662b6cb5d3f0..000000000000 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c +++ /dev/null @@ -1,1349 +0,0 @@ -/* - * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ -#include <linux/clk.h> -#include <linux/component.h> -#include <linux/iopoll.h> -#include <linux/math64.h> -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/pm_runtime.h> -#include <linux/regmap.h> -#include <linux/reset.h> -#include <linux/mfd/syscon.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drmP.h> -#include <video/mipi_display.h> - -#include "rockchip_drm_drv.h" -#include "rockchip_drm_vop.h" - -#define DRIVER_NAME "dw-mipi-dsi" - -#define RK3288_GRF_SOC_CON6 0x025c -#define RK3288_DSI0_SEL_VOP_LIT BIT(6) -#define RK3288_DSI1_SEL_VOP_LIT BIT(9) - -#define RK3399_GRF_SOC_CON20 0x6250 -#define RK3399_DSI0_SEL_VOP_LIT BIT(0) -#define RK3399_DSI1_SEL_VOP_LIT BIT(4) - -/* disable turnrequest, turndisable, forcetxstopmode, forcerxmode */ -#define RK3399_GRF_SOC_CON22 0x6258 -#define RK3399_GRF_DSI_MODE 0xffff0000 - -#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 DSI_DPI_VCID 0x0c -#define DPI_VID(vid) (((vid) & 0x3) << 0) - -#define DSI_DPI_COLOR_CODING 0x10 -#define EN18_LOOSELY BIT(8) -#define DPI_COLOR_CODING_16BIT_1 0x0 -#define DPI_COLOR_CODING_16BIT_2 0x1 -#define DPI_COLOR_CODING_16BIT_3 0x2 -#define DPI_COLOR_CODING_18BIT_1 0x3 -#define DPI_COLOR_CODING_18BIT_2 0x4 -#define DPI_COLOR_CODING_24BIT 0x5 - -#define DSI_DPI_CFG_POL 0x14 -#define COLORM_ACTIVE_LOW BIT(4) -#define SHUTD_ACTIVE_LOW BIT(3) -#define HSYNC_ACTIVE_LOW BIT(2) -#define VSYNC_ACTIVE_LOW BIT(1) -#define DATAEN_ACTIVE_LOW BIT(0) - -#define DSI_DPI_LP_CMD_TIM 0x18 -#define OUTVACT_LPCMD_TIME(p) (((p) & 0xff) << 16) -#define INVACT_LPCMD_TIME(p) ((p) & 0xff) - -#define DSI_DBI_CFG 0x20 -#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 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_NON_BURST_SYNC_EVENTS 0x1 -#define VID_MODE_TYPE_BURST 0x2 -#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 DSI_VID_HSA_TIME 0x48 -#define DSI_VID_HBP_TIME 0x4c -#define DSI_VID_HLINE_TIME 0x50 -#define DSI_VID_VSA_LINES 0x54 -#define DSI_VID_VBP_LINES 0x58 -#define DSI_VID_VFP_LINES 0x5c -#define DSI_VID_VACTIVE_LINES 0x60 -#define DSI_CMD_MODE_CFG 0x68 -#define MAX_RD_PKT_SIZE_LP BIT(24) -#define DCS_LW_TX_LP BIT(19) -#define DCS_SR_0P_TX_LP BIT(18) -#define DCS_SW_1P_TX_LP BIT(17) -#define DCS_SW_0P_TX_LP BIT(16) -#define GEN_LW_TX_LP BIT(14) -#define GEN_SR_2P_TX_LP BIT(13) -#define GEN_SR_1P_TX_LP BIT(12) -#define GEN_SR_0P_TX_LP BIT(11) -#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 CMD_MODE_ALL_LP (MAX_RD_PKT_SIZE_LP | \ - DCS_LW_TX_LP | \ - DCS_SR_0P_TX_LP | \ - DCS_SW_1P_TX_LP | \ - DCS_SW_0P_TX_LP | \ - GEN_LW_TX_LP | \ - GEN_SR_2P_TX_LP | \ - GEN_SR_1P_TX_LP | \ - GEN_SR_0P_TX_LP | \ - GEN_SW_2P_TX_LP | \ - GEN_SW_1P_TX_LP | \ - GEN_SW_0P_TX_LP) - -#define DSI_GEN_HDR 0x6c -#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 DSI_TO_CNT_CFG 0x78 -#define HSTX_TO_CNT(p) (((p) & 0xffff) << 16) -#define LPRX_TO_CNT(p) ((p) & 0xffff) - -#define DSI_BTA_TO_CNT 0x8c -#define DSI_LPCLK_CTRL 0x94 -#define AUTO_CLKLANE_CTRL BIT(1) -#define PHY_TXREQUESTCLKHS BIT(0) - -#define DSI_PHY_TMR_LPCLK_CFG 0x98 -#define PHY_CLKHS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16) -#define PHY_CLKLP2HS_TIME(lbcc) ((lbcc) & 0x3ff) - -#define DSI_PHY_TMR_CFG 0x9c -#define PHY_HS2LP_TIME(lbcc) (((lbcc) & 0xff) << 24) -#define PHY_LP2HS_TIME(lbcc) (((lbcc) & 0xff) << 16) -#define MAX_RD_TIME(lbcc) ((lbcc) & 0x7fff) - -#define DSI_PHY_RSTZ 0xa0 -#define PHY_DISFORCEPLL 0 -#define PHY_ENFORCEPLL BIT(3) -#define PHY_DISABLECLK 0 -#define PHY_ENABLECLK BIT(2) -#define PHY_RSTZ 0 -#define PHY_UNRSTZ BIT(1) -#define PHY_SHUTDOWNZ 0 -#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 DSI_PHY_STATUS 0xb0 -#define LOCK BIT(0) -#define STOP_STATE_CLK_LANE BIT(2) - -#define DSI_PHY_TST_CTRL0 0xb4 -#define PHY_TESTCLK BIT(1) -#define PHY_UNTESTCLK 0 -#define PHY_TESTCLR BIT(0) -#define PHY_UNTESTCLR 0 - -#define DSI_PHY_TST_CTRL1 0xb8 -#define PHY_TESTEN BIT(16) -#define PHY_UNTESTEN 0 -#define PHY_TESTDOUT(n) (((n) & 0xff) << 8) -#define PHY_TESTDIN(n) (((n) & 0xff) << 0) - -#define DSI_INT_ST0 0xbc -#define DSI_INT_ST1 0xc0 -#define DSI_INT_MSK0 0xc4 -#define DSI_INT_MSK1 0xc8 - -#define PHY_STATUS_TIMEOUT_US 10000 -#define CMD_PKT_STATUS_TIMEOUT_US 20000 - -#define BYPASS_VCO_RANGE BIT(7) -#define VCO_RANGE_CON_SEL(val) (((val) & 0x7) << 3) -#define VCO_IN_CAP_CON_DEFAULT (0x0 << 1) -#define VCO_IN_CAP_CON_LOW (0x1 << 1) -#define VCO_IN_CAP_CON_HIGH (0x2 << 1) -#define REF_BIAS_CUR_SEL BIT(0) - -#define CP_CURRENT_3MA BIT(3) -#define CP_PROGRAM_EN BIT(7) -#define LPF_PROGRAM_EN BIT(6) -#define LPF_RESISTORS_20_KOHM 0 - -#define HSFREQRANGE_SEL(val) (((val) & 0x3f) << 1) - -#define INPUT_DIVIDER(val) (((val) - 1) & 0x7f) -#define LOW_PROGRAM_EN 0 -#define HIGH_PROGRAM_EN BIT(7) -#define LOOP_DIV_LOW_SEL(val) (((val) - 1) & 0x1f) -#define LOOP_DIV_HIGH_SEL(val) ((((val) - 1) >> 5) & 0x1f) -#define PLL_LOOP_DIV_EN BIT(5) -#define PLL_INPUT_DIV_EN BIT(4) - -#define POWER_CONTROL BIT(6) -#define INTERNAL_REG_CURRENT BIT(3) -#define BIAS_BLOCK_ON BIT(2) -#define BANDGAP_ON BIT(0) - -#define TER_RESISTOR_HIGH BIT(7) -#define TER_RESISTOR_LOW 0 -#define LEVEL_SHIFTERS_ON BIT(6) -#define TER_CAL_DONE BIT(5) -#define SETRD_MAX (0x7 << 2) -#define POWER_MANAGE BIT(1) -#define TER_RESISTORS_ON BIT(0) - -#define BIASEXTR_SEL(val) ((val) & 0x7) -#define BANDGAP_SEL(val) ((val) & 0x7) -#define TLP_PROGRAM_EN BIT(7) -#define THS_PRE_PROGRAM_EN BIT(7) -#define THS_ZERO_PROGRAM_EN BIT(6) - -#define DW_MIPI_NEEDS_PHY_CFG_CLK BIT(0) -#define DW_MIPI_NEEDS_GRF_CLK BIT(1) - -enum { - BANDGAP_97_07, - BANDGAP_98_05, - BANDGAP_99_02, - BANDGAP_100_00, - BANDGAP_93_17, - BANDGAP_94_15, - BANDGAP_95_12, - BANDGAP_96_10, -}; - -enum { - BIASEXTR_87_1, - BIASEXTR_91_5, - BIASEXTR_95_9, - BIASEXTR_100, - BIASEXTR_105_94, - BIASEXTR_111_88, - BIASEXTR_118_8, - BIASEXTR_127_7, -}; - -struct dw_mipi_dsi_plat_data { - u32 dsi0_en_bit; - u32 dsi1_en_bit; - u32 grf_switch_reg; - u32 grf_dsi0_mode; - u32 grf_dsi0_mode_reg; - unsigned int flags; - unsigned int max_data_lanes; -}; - -struct dw_mipi_dsi { - struct drm_encoder encoder; - struct drm_connector connector; - struct mipi_dsi_host dsi_host; - struct drm_panel *panel; - struct device *dev; - struct regmap *grf_regmap; - void __iomem *base; - - struct clk *grf_clk; - struct clk *pllref_clk; - struct clk *pclk; - struct clk *phy_cfg_clk; - - int dpms_mode; - unsigned int lane_mbps; /* per lane */ - u32 channel; - u32 lanes; - u32 format; - u16 input_div; - u16 feedback_div; - unsigned long mode_flags; - - const struct dw_mipi_dsi_plat_data *pdata; -}; - -enum dw_mipi_dsi_mode { - DW_MIPI_DSI_CMD_MODE, - DW_MIPI_DSI_VID_MODE, -}; - -struct dphy_pll_testdin_map { - unsigned int max_mbps; - u8 testdin; -}; - -/* The table is based on 27MHz DPHY pll reference clock. */ -static const struct dphy_pll_testdin_map dptdin_map[] = { - { 90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01}, - { 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12}, - { 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23}, - { 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15}, - { 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07}, - { 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09}, - { 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a}, - {1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b}, - {1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c}, - {1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c} -}; - -static int max_mbps_to_testdin(unsigned int max_mbps) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dptdin_map); i++) - if (dptdin_map[i].max_mbps > max_mbps) - return dptdin_map[i].testdin; - - return -EINVAL; -} - -/* - * The controller should generate 2 frames before - * preparing the peripheral. - */ -static void dw_mipi_dsi_wait_for_two_frames(struct drm_display_mode *mode) -{ - int refresh, two_frames; - - refresh = drm_mode_vrefresh(mode); - two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2; - msleep(two_frames); -} - -static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host) -{ - return container_of(host, struct dw_mipi_dsi, dsi_host); -} - -static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con) -{ - return container_of(con, struct dw_mipi_dsi, connector); -} - -static inline struct dw_mipi_dsi *encoder_to_dsi(struct drm_encoder *encoder) -{ - return container_of(encoder, struct dw_mipi_dsi, encoder); -} - -static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val) -{ - writel(val, dsi->base + reg); -} - -static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg) -{ - return readl(dsi->base + reg); -} - -static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 test_code, - u8 test_data) -{ - /* - * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content - * is latched internally as the current test code. Test data is - * programmed internally by rising edge on TESTCLK. - */ - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); - - dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) | - PHY_TESTDIN(test_code)); - - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); - - dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | - PHY_TESTDIN(test_data)); - - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); -} - -/** - * ns2bc - Nanoseconds to byte clock cycles - */ -static inline unsigned int ns2bc(struct dw_mipi_dsi *dsi, int ns) -{ - return DIV_ROUND_UP(ns * dsi->lane_mbps / 8, 1000); -} - -/** - * ns2ui - Nanoseconds to UI time periods - */ -static inline unsigned int ns2ui(struct dw_mipi_dsi *dsi, int ns) -{ - return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000); -} - -static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi) -{ - int ret, testdin, vco, val; - - vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200; - - testdin = max_mbps_to_testdin(dsi->lane_mbps); - if (testdin < 0) { - DRM_DEV_ERROR(dsi->dev, - "failed to get testdin for %dmbps lane clock\n", - dsi->lane_mbps); - return testdin; - } - - /* Start by clearing PHY state */ - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR); - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLR); - dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR); - - ret = clk_prepare_enable(dsi->phy_cfg_clk); - if (ret) { - DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n"); - return ret; - } - - dw_mipi_dsi_phy_write(dsi, 0x10, BYPASS_VCO_RANGE | - VCO_RANGE_CON_SEL(vco) | - VCO_IN_CAP_CON_LOW | - REF_BIAS_CUR_SEL); - - dw_mipi_dsi_phy_write(dsi, 0x11, CP_CURRENT_3MA); - dw_mipi_dsi_phy_write(dsi, 0x12, CP_PROGRAM_EN | LPF_PROGRAM_EN | - LPF_RESISTORS_20_KOHM); - - dw_mipi_dsi_phy_write(dsi, 0x44, HSFREQRANGE_SEL(testdin)); - - dw_mipi_dsi_phy_write(dsi, 0x17, INPUT_DIVIDER(dsi->input_div)); - dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_LOW_SEL(dsi->feedback_div) | - LOW_PROGRAM_EN); - dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_HIGH_SEL(dsi->feedback_div) | - HIGH_PROGRAM_EN); - dw_mipi_dsi_phy_write(dsi, 0x19, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); - - dw_mipi_dsi_phy_write(dsi, 0x22, LOW_PROGRAM_EN | - BIASEXTR_SEL(BIASEXTR_127_7)); - dw_mipi_dsi_phy_write(dsi, 0x22, HIGH_PROGRAM_EN | - BANDGAP_SEL(BANDGAP_96_10)); - - dw_mipi_dsi_phy_write(dsi, 0x20, POWER_CONTROL | INTERNAL_REG_CURRENT | - BIAS_BLOCK_ON | BANDGAP_ON); - - dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_LOW | TER_CAL_DONE | - SETRD_MAX | TER_RESISTORS_ON); - dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON | - SETRD_MAX | POWER_MANAGE | - TER_RESISTORS_ON); - - dw_mipi_dsi_phy_write(dsi, 0x60, TLP_PROGRAM_EN | ns2bc(dsi, 500)); - dw_mipi_dsi_phy_write(dsi, 0x61, THS_PRE_PROGRAM_EN | ns2ui(dsi, 40)); - dw_mipi_dsi_phy_write(dsi, 0x62, THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300)); - dw_mipi_dsi_phy_write(dsi, 0x63, THS_PRE_PROGRAM_EN | ns2ui(dsi, 100)); - dw_mipi_dsi_phy_write(dsi, 0x64, BIT(5) | ns2bc(dsi, 100)); - dw_mipi_dsi_phy_write(dsi, 0x65, BIT(5) | (ns2bc(dsi, 60) + 7)); - - dw_mipi_dsi_phy_write(dsi, 0x70, TLP_PROGRAM_EN | ns2bc(dsi, 500)); - dw_mipi_dsi_phy_write(dsi, 0x71, - THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 5)); - dw_mipi_dsi_phy_write(dsi, 0x72, - THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2)); - dw_mipi_dsi_phy_write(dsi, 0x73, - THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8)); - dw_mipi_dsi_phy_write(dsi, 0x74, BIT(5) | ns2bc(dsi, 100)); - - 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); - if (ret < 0) { - DRM_DEV_ERROR(dsi->dev, "failed to wait for phy lock state\n"); - goto phy_init_end; - } - - ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, - val, val & STOP_STATE_CLK_LANE, 1000, - PHY_STATUS_TIMEOUT_US); - if (ret < 0) - 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); - - return ret; -} - -static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi, - struct drm_display_mode *mode) -{ - unsigned int i, pre; - unsigned long mpclk, pllref, tmp; - unsigned int m = 1, n = 1, target_mbps = 1000; - unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps; - int bpp; - - bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); - if (bpp < 0) { - DRM_DEV_ERROR(dsi->dev, - "failed to get bpp for pixel format %d\n", - dsi->format); - return bpp; - } - - mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC); - if (mpclk) { - /* take 1 / 0.8, since mbps must big than bandwidth of RGB */ - tmp = mpclk * (bpp / dsi->lanes) * 10 / 8; - if (tmp < max_mbps) - target_mbps = tmp; - else - 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); - tmp = pllref; - - /* - * The limits on the PLL divisor are: - * - * 5MHz <= (pllref / n) <= 40MHz - * - * we walk over these values in descreasing order so that if we hit - * an exact match for target_mbps it is more likely that "m" will be - * even. - * - * TODO: ensure that "m" is even after this loop. - */ - for (i = pllref / 5; i > (pllref / 40); i--) { - pre = pllref / i; - if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) { - tmp = target_mbps % pre; - n = i; - m = target_mbps / pre; - } - if (tmp == 0) - break; - } - - dsi->lane_mbps = pllref / n * m; - dsi->input_div = n; - dsi->feedback_div = m; - - return 0; -} - -static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, - struct mipi_dsi_device *device) -{ - struct dw_mipi_dsi *dsi = host_to_dsi(host); - - if (device->lanes > dsi->pdata->max_data_lanes) { - DRM_DEV_ERROR(dsi->dev, - "the number of data lanes(%u) is too many\n", - device->lanes); - return -EINVAL; - } - - dsi->lanes = device->lanes; - dsi->channel = device->channel; - dsi->format = device->format; - dsi->mode_flags = device->mode_flags; - dsi->panel = of_drm_find_panel(device->dev.of_node); - if (!IS_ERR(dsi->panel)) - return drm_panel_attach(dsi->panel, &dsi->connector); - - return -EINVAL; -} - -static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host, - struct mipi_dsi_device *device) -{ - struct dw_mipi_dsi *dsi = host_to_dsi(host); - - drm_panel_detach(dsi->panel); - - return 0; -} - -static void dw_mipi_message_config(struct dw_mipi_dsi *dsi, - const struct mipi_dsi_msg *msg) -{ - bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM; - u32 val = 0; - - if (msg->flags & MIPI_DSI_MSG_REQ_ACK) - val |= EN_ACK_RQST; - if (lpm) - val |= CMD_MODE_ALL_LP; - - dsi_write(dsi, DSI_LPCLK_CTRL, lpm ? 0 : PHY_TXREQUESTCLKHS); - dsi_write(dsi, DSI_CMD_MODE_CFG, val); -} - -static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val) -{ - int ret; - u32 val, mask; - - ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS, - val, !(val & GEN_CMD_FULL), 1000, - CMD_PKT_STATUS_TIMEOUT_US); - if (ret < 0) { - DRM_DEV_ERROR(dsi->dev, - "failed to get available command FIFO\n"); - return ret; - } - - dsi_write(dsi, DSI_GEN_HDR, hdr_val); - - mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY; - ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS, - val, (val & mask) == mask, - 1000, CMD_PKT_STATUS_TIMEOUT_US); - if (ret < 0) { - DRM_DEV_ERROR(dsi->dev, "failed to write command FIFO\n"); - return ret; - } - - return 0; -} - -static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi, - const struct mipi_dsi_msg *msg) -{ - const u8 *tx_buf = msg->tx_buf; - u16 data = 0; - u32 val; - - if (msg->tx_len > 0) - data |= tx_buf[0]; - if (msg->tx_len > 1) - data |= tx_buf[1] << 8; - - if (msg->tx_len > 2) { - DRM_DEV_ERROR(dsi->dev, - "too long tx buf length %zu for short write\n", - msg->tx_len); - return -EINVAL; - } - - val = GEN_HDATA(data) | GEN_HTYPE(msg->type); - return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val); -} - -static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi, - const struct mipi_dsi_msg *msg) -{ - const u8 *tx_buf = msg->tx_buf; - int len = msg->tx_len, pld_data_bytes = sizeof(u32), ret; - u32 hdr_val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type); - u32 remainder; - u32 val; - - if (msg->tx_len < 3) { - DRM_DEV_ERROR(dsi->dev, - "wrong tx buf length %zu for long write\n", - msg->tx_len); - return -EINVAL; - } - - while (DIV_ROUND_UP(len, pld_data_bytes)) { - if (len < pld_data_bytes) { - remainder = 0; - memcpy(&remainder, tx_buf, len); - dsi_write(dsi, DSI_GEN_PLD_DATA, remainder); - len = 0; - } else { - memcpy(&remainder, tx_buf, pld_data_bytes); - dsi_write(dsi, DSI_GEN_PLD_DATA, remainder); - tx_buf += pld_data_bytes; - len -= pld_data_bytes; - } - - ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS, - val, !(val & GEN_PLD_W_FULL), 1000, - CMD_PKT_STATUS_TIMEOUT_US); - if (ret < 0) { - DRM_DEV_ERROR(dsi->dev, - "failed to get available write payload FIFO\n"); - return ret; - } - } - - return dw_mipi_dsi_gen_pkt_hdr_write(dsi, hdr_val); -} - -static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host, - const struct mipi_dsi_msg *msg) -{ - struct dw_mipi_dsi *dsi = host_to_dsi(host); - int ret; - - dw_mipi_message_config(dsi, msg); - - switch (msg->type) { - case MIPI_DSI_DCS_SHORT_WRITE: - case MIPI_DSI_DCS_SHORT_WRITE_PARAM: - case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: - ret = dw_mipi_dsi_dcs_short_write(dsi, msg); - break; - case MIPI_DSI_DCS_LONG_WRITE: - ret = dw_mipi_dsi_dcs_long_write(dsi, msg); - break; - default: - DRM_DEV_ERROR(dsi->dev, "unsupported message type 0x%02x\n", - msg->type); - ret = -EINVAL; - } - - return ret; -} - -static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = { - .attach = dw_mipi_dsi_host_attach, - .detach = dw_mipi_dsi_host_detach, - .transfer = dw_mipi_dsi_host_transfer, -}; - -static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi) -{ - u32 val; - - val = ENABLE_LOW_POWER; - - if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) - val |= VID_MODE_TYPE_BURST; - else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) - val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES; - else - val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS; - - dsi_write(dsi, DSI_VID_MODE_CFG, val); -} - -static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi, - enum dw_mipi_dsi_mode mode) -{ - if (mode == DW_MIPI_DSI_CMD_MODE) { - dsi_write(dsi, DSI_PWR_UP, RESET); - dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE); - dsi_write(dsi, DSI_PWR_UP, POWERUP); - } else { - dsi_write(dsi, DSI_PWR_UP, RESET); - dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE); - dw_mipi_dsi_video_mode_config(dsi); - dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS); - dsi_write(dsi, DSI_PWR_UP, POWERUP); - } -} - -static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi) -{ - dsi_write(dsi, DSI_PWR_UP, RESET); - dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ); -} - -static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi) -{ - /* - * The maximum permitted escape clock is 20MHz and it is derived from - * lanebyteclk, which is running at "lane_mbps / 8". Thus we want: - * - * (lane_mbps >> 3) / esc_clk_division < 20 - * which is: - * (lane_mbps >> 3) / 20 > esc_clk_division - */ - u32 esc_clk_division = (dsi->lane_mbps >> 3) / 20 + 1; - - dsi_write(dsi, DSI_PWR_UP, RESET); - dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK - | PHY_RSTZ | PHY_SHUTDOWNZ); - dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) | - TX_ESC_CLK_DIVIDSION(esc_clk_division)); -} - -static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi, - struct drm_display_mode *mode) -{ - u32 val = 0, color = 0; - - switch (dsi->format) { - case MIPI_DSI_FMT_RGB888: - color = DPI_COLOR_CODING_24BIT; - break; - case MIPI_DSI_FMT_RGB666: - color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY; - break; - case MIPI_DSI_FMT_RGB666_PACKED: - color = DPI_COLOR_CODING_18BIT_1; - break; - case MIPI_DSI_FMT_RGB565: - color = DPI_COLOR_CODING_16BIT_1; - break; - } - - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - val |= VSYNC_ACTIVE_LOW; - 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_COLOR_CODING, color); - dsi_write(dsi, DSI_DPI_CFG_POL, val); - dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4) - | INVACT_LPCMD_TIME(4)); -} - -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); -} - -static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi, - struct drm_display_mode *mode) -{ - dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay)); -} - -static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi) -{ - dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000)); - dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00); - dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE); -} - -/* Get lane byte clock cycles. */ -static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi, - struct drm_display_mode *mode, - u32 hcomponent) -{ - u32 frac, lbcc; - - lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8; - - frac = lbcc % mode->clock; - lbcc = lbcc / mode->clock; - if (frac) - lbcc++; - - return lbcc; -} - -static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi, - struct drm_display_mode *mode) -{ - u32 htotal, hsa, hbp, lbcc; - - htotal = mode->htotal; - hsa = mode->hsync_end - mode->hsync_start; - hbp = mode->htotal - mode->hsync_end; - - lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, htotal); - dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc); - - lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hsa); - dsi_write(dsi, DSI_VID_HSA_TIME, lbcc); - - lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hbp); - dsi_write(dsi, DSI_VID_HBP_TIME, lbcc); -} - -static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi, - struct drm_display_mode *mode) -{ - u32 vactive, vsa, vfp, vbp; - - vactive = mode->vdisplay; - vsa = mode->vsync_end - mode->vsync_start; - vfp = mode->vsync_start - mode->vdisplay; - vbp = mode->vtotal - mode->vsync_end; - - dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive); - dsi_write(dsi, DSI_VID_VSA_LINES, vsa); - dsi_write(dsi, DSI_VID_VFP_LINES, vfp); - dsi_write(dsi, DSI_VID_VBP_LINES, vbp); -} - -static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi) -{ - dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40) - | PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000)); - - dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40) - | PHY_CLKLP2HS_TIME(0x40)); -} - -static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi) -{ - dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) | - N_LANES(dsi->lanes)); -} - -static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi) -{ - dsi_read(dsi, DSI_INT_ST0); - dsi_read(dsi, DSI_INT_ST1); - dsi_write(dsi, DSI_INT_MSK0, 0); - dsi_write(dsi, DSI_INT_MSK1, 0); -} - -static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder) -{ - struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder); - - if (dsi->dpms_mode != DRM_MODE_DPMS_ON) - return; - - if (clk_prepare_enable(dsi->pclk)) { - DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n"); - return; - } - - drm_panel_disable(dsi->panel); - - dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE); - drm_panel_unprepare(dsi->panel); - - dw_mipi_dsi_disable(dsi); - pm_runtime_put(dsi->dev); - clk_disable_unprepare(dsi->pclk); - dsi->dpms_mode = DRM_MODE_DPMS_OFF; -} - -static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) -{ - struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder); - struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; - const struct dw_mipi_dsi_plat_data *pdata = dsi->pdata; - int mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node, encoder); - u32 val; - int ret; - - ret = dw_mipi_dsi_get_lane_bps(dsi, mode); - if (ret < 0) - return; - - if (dsi->dpms_mode == DRM_MODE_DPMS_ON) - return; - - if (clk_prepare_enable(dsi->pclk)) { - DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n"); - return; - } - - pm_runtime_get_sync(dsi->dev); - dw_mipi_dsi_init(dsi); - dw_mipi_dsi_dpi_config(dsi, mode); - dw_mipi_dsi_packet_handler_config(dsi); - dw_mipi_dsi_video_mode_config(dsi); - dw_mipi_dsi_video_packet_config(dsi, mode); - dw_mipi_dsi_command_mode_config(dsi); - dw_mipi_dsi_line_timer_config(dsi, mode); - dw_mipi_dsi_vertical_timing_config(dsi, mode); - dw_mipi_dsi_dphy_timing_config(dsi); - dw_mipi_dsi_dphy_interface_config(dsi); - dw_mipi_dsi_clear_err(dsi); - - /* - * For the RK3399, the clk of grf must be enabled before writing grf - * register. And for RK3288 or other soc, this grf_clk must be NULL, - * the clk_prepare_enable return true directly. - */ - ret = clk_prepare_enable(dsi->grf_clk); - if (ret) { - DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); - return; - } - - if (pdata->grf_dsi0_mode_reg) - regmap_write(dsi->grf_regmap, pdata->grf_dsi0_mode_reg, - pdata->grf_dsi0_mode); - - dw_mipi_dsi_phy_init(dsi); - dw_mipi_dsi_wait_for_two_frames(mode); - - dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE); - if (drm_panel_prepare(dsi->panel)) - 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); - - clk_disable_unprepare(dsi->pclk); - - if (mux) - val = pdata->dsi0_en_bit | (pdata->dsi0_en_bit << 16); - else - val = pdata->dsi0_en_bit << 16; - - regmap_write(dsi->grf_regmap, pdata->grf_switch_reg, val); - 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); -} - -static int -dw_mipi_dsi_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); - struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder); - - switch (dsi->format) { - case MIPI_DSI_FMT_RGB888: - s->output_mode = ROCKCHIP_OUT_MODE_P888; - break; - case MIPI_DSI_FMT_RGB666: - s->output_mode = ROCKCHIP_OUT_MODE_P666; - break; - case MIPI_DSI_FMT_RGB565: - s->output_mode = ROCKCHIP_OUT_MODE_P565; - break; - default: - WARN_ON(1); - return -EINVAL; - } - - s->output_type = DRM_MODE_CONNECTOR_DSI; - - return 0; -} - -static const struct drm_encoder_helper_funcs -dw_mipi_dsi_encoder_helper_funcs = { - .enable = dw_mipi_dsi_encoder_enable, - .disable = dw_mipi_dsi_encoder_disable, - .atomic_check = dw_mipi_dsi_encoder_atomic_check, -}; - -static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector) -{ - struct dw_mipi_dsi *dsi = con_to_dsi(connector); - - return drm_panel_get_modes(dsi->panel); -} - -static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = { - .get_modes = dw_mipi_dsi_connector_get_modes, -}; - -static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - -static const struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = dw_mipi_dsi_drm_connector_destroy, - .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 dw_mipi_dsi_register(struct drm_device *drm, - struct dw_mipi_dsi *dsi) -{ - struct drm_encoder *encoder = &dsi->encoder; - struct drm_connector *connector = &dsi->connector; - struct device *dev = dsi->dev; - int ret; - - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, - dev->of_node); - /* - * If we failed to find the CRTC(s) which this encoder is - * supposed to be connected to, it's because the CRTC has - * not been registered yet. Defer probing, and hope that - * the required CRTC is added later. - */ - if (encoder->possible_crtcs == 0) - return -EPROBE_DEFER; - - drm_encoder_helper_add(&dsi->encoder, - &dw_mipi_dsi_encoder_helper_funcs); - ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs, - DRM_MODE_ENCODER_DSI, NULL); - if (ret) { - DRM_DEV_ERROR(dev, "Failed to initialize encoder with drm\n"); - return ret; - } - - drm_connector_helper_add(connector, - &dw_mipi_dsi_connector_helper_funcs); - - drm_connector_init(drm, &dsi->connector, - &dw_mipi_dsi_atomic_connector_funcs, - DRM_MODE_CONNECTOR_DSI); - - drm_connector_attach_encoder(connector, encoder); - - return 0; -} - -static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi) -{ - struct device_node *np = dsi->dev->of_node; - - dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); - if (IS_ERR(dsi->grf_regmap)) { - DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n"); - return PTR_ERR(dsi->grf_regmap); - } - - return 0; -} - -static struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_drv_data = { - .dsi0_en_bit = RK3288_DSI0_SEL_VOP_LIT, - .dsi1_en_bit = RK3288_DSI1_SEL_VOP_LIT, - .grf_switch_reg = RK3288_GRF_SOC_CON6, - .max_data_lanes = 4, -}; - -static struct dw_mipi_dsi_plat_data rk3399_mipi_dsi_drv_data = { - .dsi0_en_bit = RK3399_DSI0_SEL_VOP_LIT, - .dsi1_en_bit = RK3399_DSI1_SEL_VOP_LIT, - .grf_switch_reg = RK3399_GRF_SOC_CON20, - .grf_dsi0_mode = RK3399_GRF_DSI_MODE, - .grf_dsi0_mode_reg = RK3399_GRF_SOC_CON22, - .flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK, - .max_data_lanes = 4, -}; - -static const struct of_device_id dw_mipi_dsi_dt_ids[] = { - { - .compatible = "rockchip,rk3288-mipi-dsi", - .data = &rk3288_mipi_dsi_drv_data, - }, { - .compatible = "rockchip,rk3399-mipi-dsi", - .data = &rk3399_mipi_dsi_drv_data, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids); - -static int dw_mipi_dsi_bind(struct device *dev, struct device *master, - void *data) -{ - const struct of_device_id *of_id = - of_match_device(dw_mipi_dsi_dt_ids, dev); - const struct dw_mipi_dsi_plat_data *pdata = of_id->data; - struct platform_device *pdev = to_platform_device(dev); - struct reset_control *apb_rst; - struct drm_device *drm = data; - struct dw_mipi_dsi *dsi; - struct resource *res; - int ret; - - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; - - dsi->dev = dev; - dsi->pdata = pdata; - dsi->dpms_mode = DRM_MODE_DPMS_OFF; - - ret = rockchip_mipi_parse_dt(dsi); - if (ret) - return ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dsi->base = devm_ioremap_resource(dev, res); - if (IS_ERR(dsi->base)) - return PTR_ERR(dsi->base); - - dsi->pllref_clk = devm_clk_get(dev, "ref"); - if (IS_ERR(dsi->pllref_clk)) { - ret = PTR_ERR(dsi->pllref_clk); - 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); - DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret); - return ret; - } - - /* - * 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"); - if (IS_ERR(apb_rst)) { - ret = PTR_ERR(apb_rst); - if (ret == -ENOENT) { - apb_rst = NULL; - } else { - DRM_DEV_ERROR(dev, - "Unable to get reset control: %d\n", ret); - return ret; - } - } - - if (apb_rst) { - ret = clk_prepare_enable(dsi->pclk); - if (ret) { - DRM_DEV_ERROR(dev, "Failed to enable pclk\n"); - return ret; - } - - reset_control_assert(apb_rst); - usleep_range(10, 20); - reset_control_deassert(apb_rst); - - clk_disable_unprepare(dsi->pclk); - } - - if (pdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) { - dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg"); - if (IS_ERR(dsi->phy_cfg_clk)) { - ret = PTR_ERR(dsi->phy_cfg_clk); - DRM_DEV_ERROR(dev, - "Unable to get phy_cfg_clk: %d\n", ret); - return ret; - } - } - - if (pdata->flags & DW_MIPI_NEEDS_GRF_CLK) { - dsi->grf_clk = devm_clk_get(dev, "grf"); - if (IS_ERR(dsi->grf_clk)) { - ret = PTR_ERR(dsi->grf_clk); - DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret); - return ret; - } - } - - ret = clk_prepare_enable(dsi->pllref_clk); - if (ret) { - DRM_DEV_ERROR(dev, "Failed to enable pllref_clk\n"); - return ret; - } - - ret = dw_mipi_dsi_register(drm, dsi); - if (ret) { - DRM_DEV_ERROR(dev, "Failed to register mipi_dsi: %d\n", ret); - goto err_pllref; - } - - dsi->dsi_host.ops = &dw_mipi_dsi_host_ops; - dsi->dsi_host.dev = dev; - ret = mipi_dsi_host_register(&dsi->dsi_host); - if (ret) { - DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret); - goto err_cleanup; - } - - if (!dsi->panel) { - ret = -EPROBE_DEFER; - goto err_mipi_dsi_host; - } - - dev_set_drvdata(dev, dsi); - pm_runtime_enable(dev); - return 0; - -err_mipi_dsi_host: - mipi_dsi_host_unregister(&dsi->dsi_host); -err_cleanup: - dsi->connector.funcs->destroy(&dsi->connector); - dsi->encoder.funcs->destroy(&dsi->encoder); -err_pllref: - clk_disable_unprepare(dsi->pllref_clk); - return ret; -} - -static void dw_mipi_dsi_unbind(struct device *dev, struct device *master, - void *data) -{ - struct dw_mipi_dsi *dsi = dev_get_drvdata(dev); - - mipi_dsi_host_unregister(&dsi->dsi_host); - pm_runtime_disable(dev); - - dsi->connector.funcs->destroy(&dsi->connector); - dsi->encoder.funcs->destroy(&dsi->encoder); - - clk_disable_unprepare(dsi->pllref_clk); -} - -static const struct component_ops dw_mipi_dsi_ops = { - .bind = dw_mipi_dsi_bind, - .unbind = dw_mipi_dsi_unbind, -}; - -static int dw_mipi_dsi_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &dw_mipi_dsi_ops); -} - -static int dw_mipi_dsi_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &dw_mipi_dsi_ops); - return 0; -} - -struct platform_driver dw_mipi_dsi_driver = { - .probe = dw_mipi_dsi_probe, - .remove = dw_mipi_dsi_remove, - .driver = { - .of_match_table = dw_mipi_dsi_dt_ids, - .name = DRIVER_NAME, - }, -}; diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 11309a2a4e43..89c63cfde5c8 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -11,6 +11,7 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/phy/phy.h> #include <linux/regmap.h> #include <drm/drm_of.h> @@ -24,6 +25,24 @@ #define RK3288_GRF_SOC_CON6 0x025C #define RK3288_HDMI_LCDC_SEL BIT(4) +#define RK3328_GRF_SOC_CON2 0x0408 + +#define RK3328_HDMI_SDAIN_MSK BIT(11) +#define RK3328_HDMI_SCLIN_MSK BIT(10) +#define RK3328_HDMI_HPD_IOE BIT(2) +#define RK3328_GRF_SOC_CON3 0x040c +/* need to be unset if hdmi or i2c should control voltage */ +#define RK3328_HDMI_SDA5V_GRF BIT(15) +#define RK3328_HDMI_SCL5V_GRF BIT(14) +#define RK3328_HDMI_HPD5V_GRF BIT(13) +#define RK3328_HDMI_CEC5V_GRF BIT(12) +#define RK3328_GRF_SOC_CON4 0x0410 +#define RK3328_HDMI_HPD_SARADC BIT(13) +#define RK3328_HDMI_CEC_5V BIT(11) +#define RK3328_HDMI_SDA_5V BIT(10) +#define RK3328_HDMI_SCL_5V BIT(9) +#define RK3328_HDMI_HPD_5V BIT(8) + #define RK3399_GRF_SOC_CON20 0x6250 #define RK3399_HDMI_LCDC_SEL BIT(6) @@ -36,7 +55,7 @@ * @lcdsel_lit: reg value of selecting vop little for HDMI */ struct rockchip_hdmi_chip_data { - u32 lcdsel_grf_reg; + int lcdsel_grf_reg; u32 lcdsel_big; u32 lcdsel_lit; }; @@ -49,6 +68,7 @@ struct rockchip_hdmi { struct clk *vpll_clk; struct clk *grf_clk; struct dw_hdmi *hdmi; + struct phy *phy; }; #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) @@ -245,6 +265,9 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) u32 val; int ret; + if (hdmi->chip_data->lcdsel_grf_reg < 0) + return; + ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); if (ret) val = hdmi->chip_data->lcdsel_lit; @@ -287,6 +310,66 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, }; +static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, + struct drm_display_mode *mode) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + return phy_power_on(hdmi->phy); +} + +static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + phy_power_off(hdmi->phy); +} + +static enum drm_connector_status +dw_hdmi_rk3328_read_hpd(struct dw_hdmi *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + enum drm_connector_status status; + + status = dw_hdmi_phy_read_hpd(dw_hdmi, data); + + if (status == connector_status_connected) + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + HIWORD_UPDATE(RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V, + RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V)); + else + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + HIWORD_UPDATE(0, RK3328_HDMI_SDA_5V | + RK3328_HDMI_SCL_5V)); + return status; +} + +static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + dw_hdmi_phy_setup_hpd(dw_hdmi, data); + + /* Enable and map pins to 3V grf-controlled io-voltage */ + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + HIWORD_UPDATE(0, RK3328_HDMI_HPD_SARADC | RK3328_HDMI_CEC_5V | + RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V | + RK3328_HDMI_HPD_5V)); + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON3, + HIWORD_UPDATE(0, RK3328_HDMI_SDA5V_GRF | RK3328_HDMI_SCL5V_GRF | + RK3328_HDMI_HPD5V_GRF | + RK3328_HDMI_CEC5V_GRF)); + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON2, + HIWORD_UPDATE(RK3328_HDMI_SDAIN_MSK | RK3328_HDMI_SCLIN_MSK, + RK3328_HDMI_SDAIN_MSK | RK3328_HDMI_SCLIN_MSK | + RK3328_HDMI_HPD_IOE)); +} + static struct rockchip_hdmi_chip_data rk3288_chip_data = { .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, .lcdsel_big = HIWORD_UPDATE(0, RK3288_HDMI_LCDC_SEL), @@ -301,6 +384,29 @@ static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = { .phy_data = &rk3288_chip_data, }; +static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = { + .init = dw_hdmi_rockchip_genphy_init, + .disable = dw_hdmi_rockchip_genphy_disable, + .read_hpd = dw_hdmi_rk3328_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_rk3328_setup_hpd, +}; + +static struct rockchip_hdmi_chip_data rk3328_chip_data = { + .lcdsel_grf_reg = -1, +}; + +static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = { + .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, + .phy_data = &rk3328_chip_data, + .phy_ops = &rk3328_hdmi_phy_ops, + .phy_name = "inno_dw_hdmi_phy2", + .phy_force_vendor = true, +}; + static struct rockchip_hdmi_chip_data rk3399_chip_data = { .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, .lcdsel_big = HIWORD_UPDATE(0, RK3399_HDMI_LCDC_SEL), @@ -319,6 +425,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { { .compatible = "rockchip,rk3288-dw-hdmi", .data = &rk3288_hdmi_drv_data }, + { .compatible = "rockchip,rk3328-dw-hdmi", + .data = &rk3328_hdmi_drv_data + }, { .compatible = "rockchip,rk3399-dw-hdmi", .data = &rk3399_hdmi_drv_data }, @@ -330,7 +439,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); - const struct dw_hdmi_plat_data *plat_data; + struct dw_hdmi_plat_data *plat_data; const struct of_device_id *match; struct drm_device *drm = data; struct drm_encoder *encoder; @@ -345,9 +454,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, return -ENOMEM; match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); - plat_data = match->data; + plat_data = devm_kmemdup(&pdev->dev, match->data, + sizeof(*plat_data), GFP_KERNEL); + if (!plat_data) + return -ENOMEM; + hdmi->dev = &pdev->dev; hdmi->chip_data = plat_data->phy_data; + plat_data->phy_data = hdmi; encoder = &hdmi->encoder; encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); @@ -373,6 +487,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, return ret; } + hdmi->phy = devm_phy_optional_get(dev, "hdmi"); + if (IS_ERR(hdmi->phy)) { + ret = PTR_ERR(hdmi->phy); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); + return ret; + } + drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 941f35233b1f..37f9a3b651ab 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -489,7 +489,7 @@ static int __init rockchip_drm_init(void) ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP); ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver, CONFIG_ROCKCHIP_DW_HDMI); - ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_driver, + ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, CONFIG_ROCKCHIP_DW_MIPI_DSI); ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 21a023a97bb8..ce48568ec8a0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -37,6 +37,7 @@ struct rockchip_crtc_state { int output_type; int output_mode; int output_bpc; + int output_flags; }; #define to_rockchip_crtc_state(s) \ container_of(s, struct rockchip_crtc_state, base) @@ -67,7 +68,7 @@ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout); int rockchip_drm_endpoint_is_subdriver(struct device_node *ep); extern struct platform_driver cdn_dp_driver; extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; -extern struct platform_driver dw_mipi_dsi_driver; +extern struct platform_driver dw_mipi_dsi_rockchip_driver; extern struct platform_driver inno_hdmi_driver; extern struct platform_driver rockchip_dp_driver; extern struct platform_driver rockchip_lvds_driver; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c index 79d00d861a31..01ff3c858875 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c @@ -189,12 +189,14 @@ EXPORT_SYMBOL(rockchip_drm_psr_flush_all); int rockchip_drm_psr_register(struct drm_encoder *encoder, int (*psr_set)(struct drm_encoder *, bool enable)) { - struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; + struct rockchip_drm_private *drm_drv; struct psr_drv *psr; if (!encoder || !psr_set) return -EINVAL; + drm_drv = encoder->dev->dev_private; + psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL); if (!psr) return -ENOMEM; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 0c35a88e33dd..fb70fb486fbf 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -916,6 +916,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ? BIT(VSYNC_POSITIVE) : 0; VOP_REG_SET(vop, output, pin_pol, pin_pol); + VOP_REG_SET(vop, output, mipi_dual_channel_en, 0); switch (s->output_type) { case DRM_MODE_CONNECTOR_LVDS: @@ -933,6 +934,8 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, case DRM_MODE_CONNECTOR_DSI: VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol); VOP_REG_SET(vop, output, mipi_en, 1); + VOP_REG_SET(vop, output, mipi_dual_channel_en, + !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL)); break; case DRM_MODE_CONNECTOR_DisplayPort: pin_pol &= ~BIT(DCLK_INVERT); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index fd5765dfd637..0fe40e1983d9 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -60,6 +60,7 @@ struct vop_output { struct vop_reg edp_en; struct vop_reg hdmi_en; struct vop_reg mipi_en; + struct vop_reg mipi_dual_channel_en; struct vop_reg rgb_en; }; @@ -214,6 +215,9 @@ struct vop_data { /* for use special outface */ #define ROCKCHIP_OUT_MODE_AAAA 15 +/* output flags */ +#define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0) + enum alpha_mode { ALPHA_STRAIGHT, ALPHA_INVERSE, diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index a6db3cd5544b..08fc40af52c8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -361,7 +361,11 @@ static const struct vop_win_data rk3188_vop_win_data[] = { }; static const int rk3188_vop_intrs[] = { - 0, + /* + * hs_start interrupt fires at frame-start, so serves + * the same purpose as dsp_hold in the driver. + */ + DSP_HOLD_VALID_INTR, FS_INTR, LINE_FLAG_INTR, BUS_ERROR_INTR, @@ -630,6 +634,7 @@ static const struct vop_output rk3399_output = { .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), + .mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3), }; static const struct vop_data rk3399_vop_big = { diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 9fc349fa18e9..383d8d6c5847 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1 +1,4 @@ -obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm-helper.o +test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \ + test-drm_format.o test-drm_framebuffer.o + +obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o diff --git a/drivers/gpu/drm/selftests/drm_helper_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h index 9771290ed228..92601defd8f6 100644 --- a/drivers/gpu/drm/selftests/drm_helper_selftests.h +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h @@ -7,3 +7,7 @@ * Tests are executed in order by igt/drm_selftests_helper */ selftest(check_plane_state, igt_check_plane_state) +selftest(check_drm_format_block_width, igt_check_drm_format_block_width) +selftest(check_drm_format_block_height, igt_check_drm_format_block_height) +selftest(check_drm_format_min_pitch, igt_check_drm_format_min_pitch) +selftest(check_drm_framebuffer_create, igt_check_drm_framebuffer_create) diff --git a/drivers/gpu/drm/selftests/test-drm_format.c b/drivers/gpu/drm/selftests/test-drm_format.c new file mode 100644 index 000000000000..c5e212afa27a --- /dev/null +++ b/drivers/gpu/drm/selftests/test-drm_format.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for the drm_format functions + */ + +#define pr_fmt(fmt) "drm_format: " fmt + +#include <linux/errno.h> +#include <linux/kernel.h> + +#include <drm/drm_fourcc.h> + +#include "test-drm_modeset_common.h" + +int igt_check_drm_format_block_width(void *ignored) +{ + const struct drm_format_info *info = NULL; + + /* Test invalid arguments */ + FAIL_ON(drm_format_info_block_width(info, 0) != 0); + FAIL_ON(drm_format_info_block_width(info, -1) != 0); + FAIL_ON(drm_format_info_block_width(info, 1) != 0); + + /* Test 1 plane format */ + info = drm_format_info(DRM_FORMAT_XRGB4444); + FAIL_ON(!info); + FAIL_ON(drm_format_info_block_width(info, 0) != 1); + FAIL_ON(drm_format_info_block_width(info, 1) != 0); + FAIL_ON(drm_format_info_block_width(info, -1) != 0); + + /* Test 2 planes format */ + info = drm_format_info(DRM_FORMAT_NV12); + FAIL_ON(!info); + FAIL_ON(drm_format_info_block_width(info, 0) != 1); + FAIL_ON(drm_format_info_block_width(info, 1) != 1); + FAIL_ON(drm_format_info_block_width(info, 2) != 0); + FAIL_ON(drm_format_info_block_width(info, -1) != 0); + + /* Test 3 planes format */ + info = drm_format_info(DRM_FORMAT_YUV422); + FAIL_ON(!info); + FAIL_ON(drm_format_info_block_width(info, 0) != 1); + FAIL_ON(drm_format_info_block_width(info, 1) != 1); + FAIL_ON(drm_format_info_block_width(info, 2) != 1); + FAIL_ON(drm_format_info_block_width(info, 3) != 0); + FAIL_ON(drm_format_info_block_width(info, -1) != 0); + + /* Test a tiled format */ + info = drm_format_info(DRM_FORMAT_X0L0); + FAIL_ON(!info); + FAIL_ON(drm_format_info_block_width(info, 0) != 2); + FAIL_ON(drm_format_info_block_width(info, 1) != 0); + FAIL_ON(drm_format_info_block_width(info, -1) != 0); + + return 0; +} + +int igt_check_drm_format_block_height(void *ignored) +{ + const struct drm_format_info *info = NULL; + + /* Test invalid arguments */ + FAIL_ON(drm_format_info_block_height(info, 0) != 0); + FAIL_ON(drm_format_info_block_height(info, -1) != 0); + FAIL_ON(drm_format_info_block_height(info, 1) != 0); + + /* Test 1 plane format */ + info = drm_format_info(DRM_FORMAT_XRGB4444); + FAIL_ON(!info); + FAIL_ON(drm_format_info_block_height(info, 0) != 1); + FAIL_ON(drm_format_info_block_height(info, 1) != 0); + FAIL_ON(drm_format_info_block_height(info, -1) != 0); + + /* Test 2 planes format */ + info = drm_format_info(DRM_FORMAT_NV12); + FAIL_ON(!info); + FAIL_ON(drm_format_info_block_height(info, 0) != 1); + FAIL_ON(drm_format_info_block_height(info, 1) != 1); + FAIL_ON(drm_format_info_block_height(info, 2) != 0); + FAIL_ON(drm_format_info_block_height(info, -1) != 0); + + /* Test 3 planes format */ + info = drm_format_info(DRM_FORMAT_YUV422); + FAIL_ON(!info); + FAIL_ON(drm_format_info_block_height(info, 0) != 1); + FAIL_ON(drm_format_info_block_height(info, 1) != 1); + FAIL_ON(drm_format_info_block_height(info, 2) != 1); + FAIL_ON(drm_format_info_block_height(info, 3) != 0); + FAIL_ON(drm_format_info_block_height(info, -1) != 0); + + /* Test a tiled format */ + info = drm_format_info(DRM_FORMAT_X0L0); + FAIL_ON(!info); + FAIL_ON(drm_format_info_block_height(info, 0) != 2); + FAIL_ON(drm_format_info_block_height(info, 1) != 0); + FAIL_ON(drm_format_info_block_height(info, -1) != 0); + + return 0; +} + +int igt_check_drm_format_min_pitch(void *ignored) +{ + const struct drm_format_info *info = NULL; + + /* Test invalid arguments */ + FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); + + /* Test 1 plane 8 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_RGB332); + FAIL_ON(!info); + FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); + + FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 1); + FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 640); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 1024); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 1920); + FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 4096); + FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 671); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != + (uint64_t)UINT_MAX); + FAIL_ON(drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)) != + (uint64_t)(UINT_MAX - 1)); + + /* Test 1 plane 16 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_XRGB4444); + FAIL_ON(!info); + FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); + + FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 4); + FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 1280); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 2048); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 3840); + FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 8192); + FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 1342); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != + (uint64_t)UINT_MAX * 2); + FAIL_ON(drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)) != + (uint64_t)(UINT_MAX - 1) * 2); + + /* Test 1 plane 24 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_RGB888); + FAIL_ON(!info); + FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); + + FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 3); + FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 6); + FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 1920); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 3072); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 5760); + FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 12288); + FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 2013); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != + (uint64_t)UINT_MAX * 3); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX - 1) != + (uint64_t)(UINT_MAX - 1) * 3); + + /* Test 1 plane 32 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_ABGR8888); + FAIL_ON(!info); + FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); + + FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 4); + FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 8); + FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 2560); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 4096); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 7680); + FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 16384); + FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 2684); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != + (uint64_t)UINT_MAX * 4); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX - 1) != + (uint64_t)(UINT_MAX - 1) * 4); + + /* Test 2 planes format */ + info = drm_format_info(DRM_FORMAT_NV12); + FAIL_ON(!info); + FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 2, 0) != 0); + + FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 1); + FAIL_ON(drm_format_info_min_pitch(info, 1, 1) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 1, 1) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 640); + FAIL_ON(drm_format_info_min_pitch(info, 1, 320) != 640); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 1024); + FAIL_ON(drm_format_info_min_pitch(info, 1, 512) != 1024); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 1920); + FAIL_ON(drm_format_info_min_pitch(info, 1, 960) != 1920); + FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 4096); + FAIL_ON(drm_format_info_min_pitch(info, 1, 2048) != 4096); + FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 671); + FAIL_ON(drm_format_info_min_pitch(info, 1, 336) != 672); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != + (uint64_t)UINT_MAX); + FAIL_ON(drm_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) != + (uint64_t)UINT_MAX + 1); + FAIL_ON(drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)) != + (uint64_t)(UINT_MAX - 1)); + FAIL_ON(drm_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2) != + (uint64_t)(UINT_MAX - 1)); + + /* Test 3 planes 8 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_YUV422); + FAIL_ON(!info); + FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 2, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 3, 0) != 0); + + FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 1); + FAIL_ON(drm_format_info_min_pitch(info, 1, 1) != 1); + FAIL_ON(drm_format_info_min_pitch(info, 2, 1) != 1); + FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 1, 2) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 2, 2) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 640); + FAIL_ON(drm_format_info_min_pitch(info, 1, 320) != 320); + FAIL_ON(drm_format_info_min_pitch(info, 2, 320) != 320); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 1024); + FAIL_ON(drm_format_info_min_pitch(info, 1, 512) != 512); + FAIL_ON(drm_format_info_min_pitch(info, 2, 512) != 512); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 1920); + FAIL_ON(drm_format_info_min_pitch(info, 1, 960) != 960); + FAIL_ON(drm_format_info_min_pitch(info, 2, 960) != 960); + FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 4096); + FAIL_ON(drm_format_info_min_pitch(info, 1, 2048) != 2048); + FAIL_ON(drm_format_info_min_pitch(info, 2, 2048) != 2048); + FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 671); + FAIL_ON(drm_format_info_min_pitch(info, 1, 336) != 336); + FAIL_ON(drm_format_info_min_pitch(info, 2, 336) != 336); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != + (uint64_t)UINT_MAX); + FAIL_ON(drm_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) != + (uint64_t)UINT_MAX / 2 + 1); + FAIL_ON(drm_format_info_min_pitch(info, 2, UINT_MAX / 2 + 1) != + (uint64_t)UINT_MAX / 2 + 1); + FAIL_ON(drm_format_info_min_pitch(info, 0, (UINT_MAX - 1) / 2) != + (uint64_t)(UINT_MAX - 1) / 2); + FAIL_ON(drm_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2) != + (uint64_t)(UINT_MAX - 1) / 2); + FAIL_ON(drm_format_info_min_pitch(info, 2, (UINT_MAX - 1) / 2) != + (uint64_t)(UINT_MAX - 1) / 2); + + /* Test tiled format */ + info = drm_format_info(DRM_FORMAT_X0L2); + FAIL_ON(!info); + FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); + FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); + + FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 2); + FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 4); + FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 1280); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 2048); + FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 3840); + FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 8192); + FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 1342); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != + (uint64_t)UINT_MAX * 2); + FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX - 1) != + (uint64_t)(UINT_MAX - 1) * 2); + + return 0; +} diff --git a/drivers/gpu/drm/selftests/test-drm_framebuffer.c b/drivers/gpu/drm/selftests/test-drm_framebuffer.c new file mode 100644 index 000000000000..a04d02dacce2 --- /dev/null +++ b/drivers/gpu/drm/selftests/test-drm_framebuffer.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for the drm_framebuffer functions + */ + +#include <drm/drmP.h> +#include "../drm_crtc_internal.h" + +#include "test-drm_modeset_common.h" + +#define MIN_WIDTH 4 +#define MAX_WIDTH 4096 +#define MIN_HEIGHT 4 +#define MAX_HEIGHT 4096 + +struct drm_framebuffer_test { + int buffer_created; + struct drm_mode_fb_cmd2 cmd; + const char *name; +}; + +static struct drm_framebuffer_test createbuffer_tests[] = { +{ .buffer_created = 1, .name = "ABGR8888 normal sizes", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 max sizes", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 pitch greater than min required", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 pitch less than min required", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Invalid width", + .cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "No pixel format", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Width 0", + .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Height 0", + .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 Large buffer offset", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + .flags = DRM_MODE_FB_MODIFIERS, .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 1, .name = "NV12 Normal sizes", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 }, + } +}, +{ .buffer_created = 1, .name = "NV12 Max sizes", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Invalid pitch", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Invalid modifier/misssing DRM_MODE_FB_MODIFIERS flag", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 different modifier per-plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, + DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Modifier for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, + DRM_FORMAT_MOD_SAMSUNG_64_32_TILE }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Handle for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Normal sizes", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .pitches = { 600, 300, 300 }, + } +}, +{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Max sizes", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), + DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Invalid pitch", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1, + DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Different pitches", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, + DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH + MAX_WIDTH * MAX_HEIGHT, + MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Valid modifier", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Different modifiers per plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR, + AFBC_FORMAT_MOD_SPARSE }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, + AFBC_FORMAT_MOD_SPARSE }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 1, .name = "X0L2 Normal sizes", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 } + } +}, +{ .buffer_created = 1, .name = "X0L2 Max sizes", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 } + } +}, +{ .buffer_created = 0, .name = "X0L2 Invalid pitch", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 } + } +}, +{ .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } + } +}, +{ .buffer_created = 0, .name = "X0L2 Handle for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } + } +}, +{ .buffer_created = 1, .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 }, + .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } + } +}, +{ .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, + .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "X0L2 Valid modifier", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, + .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + } +}, +{ .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, + .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 }, + .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, + .flags = DRM_MODE_FB_MODIFIERS, + } +}, +}; + +static struct drm_framebuffer *fb_create_mock(struct drm_device *dev, + struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + int *buffer_created = dev->dev_private; + *buffer_created = 1; + return ERR_PTR(-EINVAL); +} + +static struct drm_mode_config_funcs mock_config_funcs = { + .fb_create = fb_create_mock, +}; + +static struct drm_device mock_drm_device = { + .mode_config = { + .min_width = MIN_WIDTH, + .max_width = MAX_WIDTH, + .min_height = MIN_HEIGHT, + .max_height = MAX_HEIGHT, + .allow_fb_modifiers = true, + .funcs = &mock_config_funcs, + }, +}; + +static int execute_drm_mode_fb_cmd2(struct drm_mode_fb_cmd2 *r) +{ + int buffer_created = 0; + struct drm_framebuffer *fb; + + mock_drm_device.dev_private = &buffer_created; + fb = drm_internal_framebuffer_create(&mock_drm_device, r, NULL); + return buffer_created; +} + +int igt_check_drm_framebuffer_create(void *ignored) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(createbuffer_tests); i++) { + FAIL(createbuffer_tests[i].buffer_created != + execute_drm_mode_fb_cmd2(&createbuffer_tests[i].cmd), + "Test %d: \"%s\" failed\n", i, createbuffer_tests[i].name); + } + + return 0; +} diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.c b/drivers/gpu/drm/selftests/test-drm_modeset_common.c new file mode 100644 index 000000000000..2a7f93774006 --- /dev/null +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common file for modeset selftests. + */ + +#include <linux/module.h> + +#include "test-drm_modeset_common.h" + +#define TESTS "drm_modeset_selftests.h" +#include "drm_selftest.h" + +#include "drm_selftest.c" + +static int __init test_drm_modeset_init(void) +{ + int err; + + err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); + + return err > 0 ? 0 : err; +} + +static void __exit test_drm_modeset_exit(void) +{ +} + +module_init(test_drm_modeset_init); +module_exit(test_drm_modeset_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h new file mode 100644 index 000000000000..d5df5bd51b05 --- /dev/null +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __TEST_DRM_MODESET_COMMON_H__ +#define __TEST_DRM_MODESET_COMMON_H__ + +#define FAIL(test, msg, ...) \ + do { \ + if (test) { \ + pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + return -EINVAL; \ + } \ + } while (0) + +#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") + +int igt_check_plane_state(void *ignored); +int igt_check_drm_format_block_width(void *ignored); +int igt_check_drm_format_block_height(void *ignored); +int igt_check_drm_format_min_pitch(void *ignored); +int igt_check_drm_framebuffer_create(void *ignored); + +#endif diff --git a/drivers/gpu/drm/selftests/test-drm-helper.c b/drivers/gpu/drm/selftests/test-drm_plane_helper.c index a015712b43e8..0a9553f51796 100644 --- a/drivers/gpu/drm/selftests/test-drm-helper.c +++ b/drivers/gpu/drm/selftests/test-drm_plane_helper.c @@ -1,27 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Test cases for the drm_kms_helper functions + * Test cases for the drm_plane_helper functions */ -#define pr_fmt(fmt) "drm_kms_helper: " fmt - -#include <linux/module.h> +#define pr_fmt(fmt) "drm_plane_helper: " fmt #include <drm/drm_atomic_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drm_modes.h> -#define TESTS "drm_helper_selftests.h" -#include "drm_selftest.h" - -#define FAIL(test, msg, ...) \ - do { \ - if (test) { \ - pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ - return -EINVAL; \ - } \ - } while (0) - -#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") +#include "test-drm_modeset_common.h" static void set_src(struct drm_plane_state *plane_state, unsigned src_x, unsigned src_y, @@ -85,7 +73,7 @@ static bool check_crtc_eq(struct drm_plane_state *plane_state, return true; } -static int igt_check_plane_state(void *ignored) +int igt_check_plane_state(void *ignored) { int ret; @@ -229,19 +217,3 @@ static int igt_check_plane_state(void *ignored) return 0; } - -#include "drm_selftest.c" - -static int __init test_drm_helper_init(void) -{ - int err; - - err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); - - return err > 0 ? 0 : err; -} - -module_init(test_drm_helper_init); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index 5824e6aca8f4..61c2379fba87 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c @@ -40,6 +40,8 @@ static void sti_crtc_atomic_disable(struct drm_crtc *crtc, DRM_DEBUG_DRIVER("\n"); mixer->status = STI_MIXER_DISABLING; + + drm_crtc_wait_one_vblank(crtc); } static int diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c index 57b870e1e696..bc908453ffb3 100644 --- a/drivers/gpu/drm/sti/sti_cursor.c +++ b/drivers/gpu/drm/sti/sti_cursor.c @@ -332,7 +332,6 @@ static void sti_cursor_destroy(struct drm_plane *drm_plane) { DRM_DEBUG_DRIVER("\n"); - drm_plane_helper_disable(drm_plane, NULL); drm_plane_cleanup(drm_plane); } diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 6dced8abcf16..ac54e0f9caea 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -206,6 +206,8 @@ static void sti_cleanup(struct drm_device *ddev) struct sti_private *private = ddev->dev_private; drm_kms_helper_poll_fini(ddev); + drm_atomic_helper_shutdown(ddev); + drm_mode_config_cleanup(ddev); component_unbind_all(ddev->dev, ddev); kfree(private); ddev->dev_private = NULL; @@ -230,7 +232,7 @@ static int sti_bind(struct device *dev) ret = drm_dev_register(ddev, 0); if (ret) - goto err_register; + goto err_cleanup; drm_mode_config_reset(ddev); @@ -238,8 +240,6 @@ static int sti_bind(struct device *dev) return 0; -err_register: - drm_mode_config_cleanup(ddev); err_cleanup: sti_cleanup(ddev); err_drm_dev_put: diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index c32de6cbf061..cff7b2b5ee9e 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -517,7 +517,7 @@ static void sti_gdp_init(struct sti_gdp *gdp) /* Allocate all the nodes within a single memory page */ size = sizeof(struct sti_gdp_node) * GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK; - base = dma_alloc_wc(gdp->dev, size, &dma_addr, GFP_KERNEL | GFP_DMA); + base = dma_alloc_wc(gdp->dev, size, &dma_addr, GFP_KERNEL); if (!base) { DRM_ERROR("Failed to allocate memory for GDP node\n"); @@ -883,7 +883,6 @@ static void sti_gdp_destroy(struct drm_plane *drm_plane) { DRM_DEBUG_DRIVER("\n"); - drm_plane_helper_disable(drm_plane, NULL); drm_plane_cleanup(drm_plane); } diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index 03ac3b4a4469..23565f52dd71 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -1260,7 +1260,6 @@ static void sti_hqvdp_destroy(struct drm_plane *drm_plane) { DRM_DEBUG_DRIVER("\n"); - drm_plane_helper_disable(drm_plane, NULL); drm_plane_cleanup(drm_plane); } diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index f2021b23554d..8dec001b9d37 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -26,7 +26,6 @@ static const struct drm_mode_config_funcs drv_mode_config_funcs = { .fb_create = drm_gem_fb_create, - .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -52,7 +51,6 @@ DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops); static struct drm_driver drv_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = drm_fb_helper_lastclose, .name = "stm", .desc = "STMicroelectronics SoC DRM", .date = "20170330", @@ -72,6 +70,8 @@ static struct drm_driver drv_driver = { .gem_prime_vmap = drm_gem_cma_prime_vmap, .gem_prime_vunmap = drm_gem_cma_prime_vunmap, .gem_prime_mmap = drm_gem_cma_prime_mmap, + .get_scanout_position = ltdc_crtc_scanoutpos, + .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos, }; static int drv_load(struct drm_device *ddev) @@ -108,12 +108,6 @@ static int drv_load(struct drm_device *ddev) drm_mode_config_reset(ddev); drm_kms_helper_poll_init(ddev); - if (ddev->mode_config.num_connector) { - ret = drm_fb_cma_fbdev_init(ddev, 16, 0); - if (ret) - DRM_DEBUG("Warning: fails to create fbdev\n"); - } - platform_set_drvdata(pdev, ddev); return 0; @@ -126,7 +120,6 @@ static void drv_unload(struct drm_device *ddev) { DRM_DEBUG("%s\n", __func__); - drm_fb_cma_fbdev_fini(ddev); drm_kms_helper_poll_fini(ddev); ltdc_unload(ddev); drm_mode_config_cleanup(ddev); @@ -154,6 +147,8 @@ static int stm_drm_platform_probe(struct platform_device *pdev) if (ret) goto err_put; + drm_fbdev_generic_setup(ddev, 16); + return 0; err_put: diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 808d9fb627e9..61dd661aa0ac 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -148,6 +148,8 @@ #define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */ #define IER_RRIE BIT(3) /* Register Reload Interrupt enable */ +#define CPSR_CYPOS GENMASK(15, 0) /* Current Y position */ + #define ISR_LIF BIT(0) /* Line Interrupt Flag */ #define ISR_FUIF BIT(1) /* Fifo Underrun Interrupt Flag */ #define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */ @@ -626,6 +628,49 @@ static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc) reg_clear(ldev->regs, LTDC_IER, IER_LIE); } +bool ltdc_crtc_scanoutpos(struct drm_device *ddev, unsigned int pipe, + bool in_vblank_irq, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) +{ + struct ltdc_device *ldev = ddev->dev_private; + int line, vactive_start, vactive_end, vtotal; + + if (stime) + *stime = ktime_get(); + + /* The active area starts after vsync + front porch and ends + * at vsync + front porc + display size. + * The total height also include back porch. + * We have 3 possible cases to handle: + * - line < vactive_start: vpos = line - vactive_start and will be + * negative + * - vactive_start < line < vactive_end: vpos = line - vactive_start + * and will be positive + * - line > vactive_end: vpos = line - vtotal - vactive_start + * and will negative + * + * Computation for the two first cases are identical so we can + * simplify the code and only test if line > vactive_end + */ + line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS; + vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP; + vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH; + vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH; + + if (line > vactive_end) + *vpos = line - vtotal - vactive_start; + else + *vpos = line - vactive_start; + + *hpos = 0; + + if (etime) + *etime = ktime_get(); + + return true; +} + static const struct drm_crtc_funcs ltdc_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index d5afb8960867..e46f477a8494 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -38,6 +38,11 @@ struct ltdc_device { struct fps_info plane_fpsi[LTDC_MAX_LAYER]; }; +bool ltdc_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, + bool in_vblank_irq, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode); + int ltdc_load(struct drm_device *ddev); void ltdc_unload(struct drm_device *ddev); diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 1e41c3f5fd6d..ef773d36baf0 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -34,7 +34,6 @@ static struct drm_driver sun4i_drv_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, /* Generic Operations */ - .lastclose = drm_fb_helper_lastclose, .fops = &sun4i_drv_fops, .name = "sun4i-drm", .desc = "Allwinner sun4i Display Engine", @@ -105,12 +104,7 @@ static int sun4i_drv_bind(struct device *dev) /* Remove early framebuffers (ie. simplefb) */ drm_fb_helper_remove_conflicting_framebuffers(NULL, "sun4i-drm-fb", false); - /* Create our framebuffer */ - ret = sun4i_framebuffer_init(drm); - if (ret) { - dev_err(drm->dev, "Couldn't create our framebuffer\n"); - goto cleanup_mode_config; - } + sun4i_framebuffer_init(drm); /* Enable connectors polling */ drm_kms_helper_poll_init(drm); @@ -119,11 +113,12 @@ static int sun4i_drv_bind(struct device *dev) if (ret) goto finish_poll; + drm_fbdev_generic_setup(drm, 32); + return 0; finish_poll: drm_kms_helper_poll_fini(drm); - sun4i_framebuffer_free(drm); cleanup_mode_config: drm_mode_config_cleanup(drm); of_reserved_mem_device_release(dev); @@ -138,7 +133,6 @@ static void sun4i_drv_unbind(struct device *dev) drm_dev_unregister(drm); drm_kms_helper_poll_fini(drm); - sun4i_framebuffer_free(drm); drm_mode_config_cleanup(drm); of_reserved_mem_device_release(dev); drm_dev_put(drm); diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c index 5f29850ef8ac..cb828028ae06 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c @@ -12,8 +12,6 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_fb_helper.h> -#include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drmP.h> @@ -37,7 +35,6 @@ static int sun4i_de_atomic_check(struct drm_device *dev, } static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = { - .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = sun4i_de_atomic_check, .atomic_commit = drm_atomic_helper_commit, .fb_create = drm_gem_fb_create, @@ -47,7 +44,7 @@ static struct drm_mode_config_helper_funcs sun4i_de_mode_config_helpers = { .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, }; -int sun4i_framebuffer_init(struct drm_device *drm) +void sun4i_framebuffer_init(struct drm_device *drm) { drm_mode_config_reset(drm); @@ -56,11 +53,4 @@ int sun4i_framebuffer_init(struct drm_device *drm) drm->mode_config.funcs = &sun4i_de_mode_config_funcs; drm->mode_config.helper_private = &sun4i_de_mode_config_helpers; - - return drm_fb_cma_fbdev_init(drm, 32, 0); -} - -void sun4i_framebuffer_free(struct drm_device *drm) -{ - drm_fb_cma_fbdev_fini(drm); } diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.h b/drivers/gpu/drm/sun4i/sun4i_framebuffer.h index 7ef0aed8384c..6fe5bd8c4026 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.h +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.h @@ -13,7 +13,6 @@ #ifndef _SUN4I_FRAMEBUFFER_H_ #define _SUN4I_FRAMEBUFFER_H_ -int sun4i_framebuffer_init(struct drm_device *drm); -void sun4i_framebuffer_free(struct drm_device *drm); +void sun4i_framebuffer_init(struct drm_device *drm); #endif /* _SUN4I_FRAMEBUFFER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c index 3ecffa52c814..fb985ba1a176 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c @@ -35,7 +35,7 @@ static unsigned long sun4i_tmds_calc_divider(unsigned long rate, { unsigned long best_rate = 0; u8 best_m = 0, m; - bool is_double; + bool is_double = false; for (m = div_offset ?: 1; m < (16 + div_offset); m++) { u8 d; @@ -52,7 +52,7 @@ static unsigned long sun4i_tmds_calc_divider(unsigned long rate, (rate - tmp_rate) < (rate - best_rate)) { best_rate = tmp_rate; best_m = m; - is_double = d; + is_double = (d == 2) ? true : false; } } } diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c index b14925b40ccf..e7608a72f26f 100644 --- a/drivers/gpu/drm/sun4i/sun8i_csc.c +++ b/drivers/gpu/drm/sun4i/sun8i_csc.c @@ -34,6 +34,41 @@ static const u32 yvu2rgb[] = { 0x000004A8, 0x00000000, 0x00000813, 0xFFFBAC4A, }; +/* + * DE3 has a bit different CSC units. Factors are in two's complement format. + * First three factors in a row are multiplication factors which have 17 bits + * for fractional part. Fourth value in a row is comprised of two factors. + * Upper 16 bits represents difference, which is subtracted from the input + * value before multiplication and lower 16 bits represents constant, which + * is addes at the end. + * + * x' = c00 * (x + d0) + c01 * (y + d1) + c02 * (z + d2) + const0 + * y' = c10 * (x + d0) + c11 * (y + d1) + c12 * (z + d2) + const1 + * z' = c20 * (x + d0) + c21 * (y + d1) + c22 * (z + d2) + const2 + * + * Please note that above formula is true only for Blender CSC. Other DE3 CSC + * units takes only positive value for difference. From what can be deducted + * from BSP driver code, those units probably automatically assume that + * difference has to be subtracted. + * + * Layout of factors in table: + * c00 c01 c02 [d0 const0] + * c10 c11 c12 [d1 const1] + * c20 c21 c22 [d2 const2] + */ + +static const u32 yuv2rgb_de3[] = { + 0x0002542a, 0x00000000, 0x0003312a, 0xffc00000, + 0x0002542a, 0xffff376b, 0xfffe5fc3, 0xfe000000, + 0x0002542a, 0x000408d3, 0x00000000, 0xfe000000, +}; + +static const u32 yvu2rgb_de3[] = { + 0x0002542a, 0x0003312a, 0x00000000, 0xffc00000, + 0x0002542a, 0xfffe5fc3, 0xffff376b, 0xfe000000, + 0x0002542a, 0x00000000, 0x000408d3, 0xfe000000, +}; + static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, enum sun8i_csc_mode mode) { @@ -61,6 +96,28 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, } } +static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer, + enum sun8i_csc_mode mode) +{ + const u32 *table; + u32 base_reg; + + switch (mode) { + case SUN8I_CSC_MODE_YUV2RGB: + table = yuv2rgb_de3; + break; + case SUN8I_CSC_MODE_YVU2RGB: + table = yvu2rgb_de3; + break; + default: + DRM_WARN("Wrong CSC mode specified.\n"); + return; + } + + base_reg = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0, 0); + regmap_bulk_write(map, base_reg, table, 12); +} + static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable) { u32 val; @@ -73,11 +130,32 @@ static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable) regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val); } +static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable) +{ + u32 val, mask; + + mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer); + + if (enable) + val = mask; + else + val = 0; + + regmap_update_bits(map, SUN50I_MIXER_BLEND_CSC_CTL(DE3_BLD_BASE), + mask, val); +} + void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, enum sun8i_csc_mode mode) { u32 base; + if (mixer->cfg->is_de3) { + sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, + layer, mode); + return; + } + base = ccsc_base[mixer->cfg->ccsc][layer]; sun8i_csc_set_coefficients(mixer->engine.regs, base, mode); @@ -87,6 +165,11 @@ void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable) { u32 base; + if (mixer->cfg->is_de3) { + sun8i_de3_ccsc_enable(mixer->engine.regs, layer, enable); + return; + } + base = ccsc_base[mixer->cfg->ccsc][layer]; sun8i_csc_enable(mixer->engine.regs, base, enable); diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c index ed2983770e9c..dc47720c99ba 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c @@ -5,6 +5,7 @@ #include <linux/component.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <drm/drm_of.h> @@ -20,7 +21,8 @@ static void sun8i_dw_hdmi_encoder_mode_set(struct drm_encoder *encoder, { struct sun8i_dw_hdmi *hdmi = encoder_to_sun8i_dw_hdmi(encoder); - clk_set_rate(hdmi->clk_tmds, mode->crtc_clock * 1000); + if (hdmi->quirks->set_rate) + clk_set_rate(hdmi->clk_tmds, mode->crtc_clock * 1000); } static const struct drm_encoder_helper_funcs @@ -33,8 +35,8 @@ static const struct drm_encoder_funcs sun8i_dw_hdmi_encoder_funcs = { }; static enum drm_mode_status -sun8i_dw_hdmi_mode_valid(struct drm_connector *connector, - const struct drm_display_mode *mode) +sun8i_dw_hdmi_mode_valid_a83t(struct drm_connector *connector, + const struct drm_display_mode *mode) { if (mode->clock > 297000) return MODE_CLOCK_HIGH; @@ -42,6 +44,17 @@ sun8i_dw_hdmi_mode_valid(struct drm_connector *connector, return MODE_OK; } +static enum drm_mode_status +sun8i_dw_hdmi_mode_valid_h6(struct drm_connector *connector, + const struct drm_display_mode *mode) +{ + /* This is max for HDMI 2.0b (4K@60Hz) */ + if (mode->clock > 594000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + static bool sun8i_dw_hdmi_node_is_tcon_top(struct device_node *node) { return IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) && @@ -102,6 +115,8 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, hdmi->dev = &pdev->dev; encoder = &hdmi->encoder; + hdmi->quirks = of_device_get_match_data(dev); + encoder->possible_crtcs = sun8i_dw_hdmi_find_possible_crtcs(drm, dev->of_node); /* @@ -168,10 +183,8 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, sun8i_hdmi_phy_init(hdmi->phy); - plat_data->mode_valid = &sun8i_dw_hdmi_mode_valid; - plat_data->phy_ops = sun8i_hdmi_phy_get_ops(); - plat_data->phy_name = "sun8i_dw_hdmi_phy"; - plat_data->phy_data = hdmi->phy; + plat_data->mode_valid = hdmi->quirks->mode_valid; + sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data); platform_set_drvdata(pdev, hdmi); @@ -230,8 +243,24 @@ static int sun8i_dw_hdmi_remove(struct platform_device *pdev) return 0; } +static const struct sun8i_dw_hdmi_quirks sun8i_a83t_quirks = { + .mode_valid = sun8i_dw_hdmi_mode_valid_a83t, + .set_rate = true, +}; + +static const struct sun8i_dw_hdmi_quirks sun50i_h6_quirks = { + .mode_valid = sun8i_dw_hdmi_mode_valid_h6, +}; + static const struct of_device_id sun8i_dw_hdmi_dt_ids[] = { - { .compatible = "allwinner,sun8i-a83t-dw-hdmi" }, + { + .compatible = "allwinner,sun8i-a83t-dw-hdmi", + .data = &sun8i_a83t_quirks, + }, + { + .compatible = "allwinner,sun50i-h6-dw-hdmi", + .data = &sun50i_h6_quirks, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, sun8i_dw_hdmi_dt_ids); diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index 7fdc1ecd2892..720c5aa8adc1 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -150,6 +150,10 @@ struct sun8i_hdmi_phy; struct sun8i_hdmi_phy_variant { bool has_phy_clk; bool has_second_pll; + unsigned int is_custom_phy : 1; + const struct dw_hdmi_curr_ctrl *cur_ctr; + const struct dw_hdmi_mpll_config *mpll_cfg; + const struct dw_hdmi_phy_config *phy_cfg; void (*phy_init)(struct sun8i_hdmi_phy *phy); void (*phy_disable)(struct dw_hdmi *hdmi, struct sun8i_hdmi_phy *phy); @@ -170,6 +174,12 @@ struct sun8i_hdmi_phy { struct sun8i_hdmi_phy_variant *variant; }; +struct sun8i_dw_hdmi_quirks { + enum drm_mode_status (*mode_valid)(struct drm_connector *connector, + const struct drm_display_mode *mode); + unsigned int set_rate : 1; +}; + struct sun8i_dw_hdmi { struct clk *clk_tmds; struct device *dev; @@ -178,6 +188,7 @@ struct sun8i_dw_hdmi { struct sun8i_hdmi_phy *phy; struct dw_hdmi_plat_data plat_data; struct regulator *regulator; + const struct sun8i_dw_hdmi_quirks *quirks; struct reset_control *rst_ctrl; }; @@ -191,7 +202,8 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node); void sun8i_hdmi_phy_remove(struct sun8i_dw_hdmi *hdmi); void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy); -const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void); +void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy, + struct dw_hdmi_plat_data *plat_data); int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev, bool second_parent); diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 471993097ced..66ea3a902e36 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -14,6 +14,122 @@ */ #define I2C_ADDR 0x69 +static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = { + { + 30666000, { + { 0x00b3, 0x0000 }, + { 0x2153, 0x0000 }, + { 0x40f3, 0x0000 }, + }, + }, { + 36800000, { + { 0x00b3, 0x0000 }, + { 0x2153, 0x0000 }, + { 0x40a2, 0x0001 }, + }, + }, { + 46000000, { + { 0x00b3, 0x0000 }, + { 0x2142, 0x0001 }, + { 0x40a2, 0x0001 }, + }, + }, { + 61333000, { + { 0x0072, 0x0001 }, + { 0x2142, 0x0001 }, + { 0x40a2, 0x0001 }, + }, + }, { + 73600000, { + { 0x0072, 0x0001 }, + { 0x2142, 0x0001 }, + { 0x4061, 0x0002 }, + }, + }, { + 92000000, { + { 0x0072, 0x0001 }, + { 0x2145, 0x0002 }, + { 0x4061, 0x0002 }, + }, + }, { + 122666000, { + { 0x0051, 0x0002 }, + { 0x2145, 0x0002 }, + { 0x4061, 0x0002 }, + }, + }, { + 147200000, { + { 0x0051, 0x0002 }, + { 0x2145, 0x0002 }, + { 0x4064, 0x0003 }, + }, + }, { + 184000000, { + { 0x0051, 0x0002 }, + { 0x214c, 0x0003 }, + { 0x4064, 0x0003 }, + }, + }, { + 226666000, { + { 0x0040, 0x0003 }, + { 0x214c, 0x0003 }, + { 0x4064, 0x0003 }, + }, + }, { + 272000000, { + { 0x0040, 0x0003 }, + { 0x214c, 0x0003 }, + { 0x5a64, 0x0003 }, + }, + }, { + 340000000, { + { 0x0040, 0x0003 }, + { 0x3b4c, 0x0003 }, + { 0x5a64, 0x0003 }, + }, + }, { + 594000000, { + { 0x1a40, 0x0003 }, + { 0x3b4c, 0x0003 }, + { 0x5a64, 0x0003 }, + }, + }, { + ~0UL, { + { 0x0000, 0x0000 }, + { 0x0000, 0x0000 }, + { 0x0000, 0x0000 }, + }, + } +}; + +static const struct dw_hdmi_curr_ctrl sun50i_h6_cur_ctr[] = { + /* pixelclk bpp8 bpp10 bpp12 */ + { 25175000, { 0x0000, 0x0000, 0x0000 }, }, + { 27000000, { 0x0012, 0x0000, 0x0000 }, }, + { 59400000, { 0x0008, 0x0008, 0x0008 }, }, + { 72000000, { 0x0008, 0x0008, 0x001b }, }, + { 74250000, { 0x0013, 0x0013, 0x0013 }, }, + { 90000000, { 0x0008, 0x001a, 0x001b }, }, + { 118800000, { 0x001b, 0x001a, 0x001b }, }, + { 144000000, { 0x001b, 0x001a, 0x0034 }, }, + { 180000000, { 0x001b, 0x0033, 0x0034 }, }, + { 216000000, { 0x0036, 0x0033, 0x0034 }, }, + { 237600000, { 0x0036, 0x0033, 0x001b }, }, + { 288000000, { 0x0036, 0x001b, 0x001b }, }, + { 297000000, { 0x0019, 0x001b, 0x0019 }, }, + { 330000000, { 0x0036, 0x001b, 0x001b }, }, + { 594000000, { 0x003f, 0x001b, 0x001b }, }, + { ~0UL, { 0x0000, 0x0000, 0x0000 }, } +}; + +static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = { + /*pixelclk symbol term vlev*/ + { 74250000, 0x8009, 0x0004, 0x0232}, + { 148500000, 0x8029, 0x0004, 0x0273}, + { 594000000, 0x8039, 0x0004, 0x014a}, + { ~0UL, 0x0000, 0x0000, 0x0000} +}; + static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi, struct sun8i_hdmi_phy *phy, unsigned int clk_rate) @@ -279,8 +395,31 @@ static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = { .setup_hpd = &dw_hdmi_phy_setup_hpd, }; +static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy) +{ + /* enable read access to HDMI controller */ + regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG, + SUN8I_HDMI_PHY_READ_EN_MAGIC); + + /* unscramble register offsets */ + regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG, + SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC); +} + +static void sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy *phy) +{ + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, + SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, + SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN); + + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, + 0xffff0000, 0x80c00000); +} + static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy) { + sun8i_hdmi_phy_unlock(phy); + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK, SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK); @@ -298,6 +437,8 @@ static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy) { unsigned int val; + sun8i_hdmi_phy_unlock(phy); + regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0); regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, SUN8I_HDMI_PHY_ANA_CFG1_ENBI, @@ -372,20 +513,23 @@ static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy) void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy) { - /* enable read access to HDMI controller */ - regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG, - SUN8I_HDMI_PHY_READ_EN_MAGIC); - - /* unscramble register offsets */ - regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG, - SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC); - phy->variant->phy_init(phy); } -const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void) +void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy, + struct dw_hdmi_plat_data *plat_data) { - return &sun8i_hdmi_phy_ops; + struct sun8i_hdmi_phy_variant *variant = phy->variant; + + if (variant->is_custom_phy) { + plat_data->phy_ops = &sun8i_hdmi_phy_ops; + plat_data->phy_name = "sun8i_dw_hdmi_phy"; + plat_data->phy_data = phy; + } else { + plat_data->mpll_cfg = variant->mpll_cfg; + plat_data->cur_ctr = variant->cur_ctr; + plat_data->phy_config = variant->phy_cfg; + } } static struct regmap_config sun8i_hdmi_phy_regmap_config = { @@ -396,14 +540,8 @@ static struct regmap_config sun8i_hdmi_phy_regmap_config = { .name = "phy" }; -static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = { - .has_phy_clk = true, - .phy_init = &sun8i_hdmi_phy_init_h3, - .phy_disable = &sun8i_hdmi_phy_disable_h3, - .phy_config = &sun8i_hdmi_phy_config_h3, -}; - static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = { + .is_custom_phy = true, .phy_init = &sun8i_hdmi_phy_init_a83t, .phy_disable = &sun8i_hdmi_phy_disable_a83t, .phy_config = &sun8i_hdmi_phy_config_a83t, @@ -411,6 +549,7 @@ static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = { static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = { .has_phy_clk = true, + .is_custom_phy = true, .phy_init = &sun8i_hdmi_phy_init_h3, .phy_disable = &sun8i_hdmi_phy_disable_h3, .phy_config = &sun8i_hdmi_phy_config_h3, @@ -419,17 +558,29 @@ static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = { static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = { .has_phy_clk = true, .has_second_pll = true, + .is_custom_phy = true, + .phy_init = &sun8i_hdmi_phy_init_h3, + .phy_disable = &sun8i_hdmi_phy_disable_h3, + .phy_config = &sun8i_hdmi_phy_config_h3, +}; + +static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = { + .has_phy_clk = true, + .is_custom_phy = true, .phy_init = &sun8i_hdmi_phy_init_h3, .phy_disable = &sun8i_hdmi_phy_disable_h3, .phy_config = &sun8i_hdmi_phy_config_h3, }; +static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = { + .cur_ctr = sun50i_h6_cur_ctr, + .mpll_cfg = sun50i_h6_mpll_cfg, + .phy_cfg = sun50i_h6_phy_config, + .phy_init = &sun50i_hdmi_phy_init_h6, +}; + static const struct of_device_id sun8i_hdmi_phy_of_table[] = { { - .compatible = "allwinner,sun50i-a64-hdmi-phy", - .data = &sun50i_a64_hdmi_phy, - }, - { .compatible = "allwinner,sun8i-a83t-hdmi-phy", .data = &sun8i_a83t_hdmi_phy, }, @@ -441,6 +592,14 @@ static const struct of_device_id sun8i_hdmi_phy_of_table[] = { .compatible = "allwinner,sun8i-r40-hdmi-phy", .data = &sun8i_r40_hdmi_phy, }, + { + .compatible = "allwinner,sun50i-a64-hdmi-phy", + .data = &sun50i_a64_hdmi_phy, + }, + { + .compatible = "allwinner,sun50i-h6-hdmi-phy", + .data = &sun50i_h6_hdmi_phy, + }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index 8b3d02b146b7..44a9ba7d8433 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -368,6 +368,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, struct sun8i_mixer *mixer; struct resource *res; void __iomem *regs; + unsigned int base; int plane_cnt; int i, ret; @@ -456,33 +457,60 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, list_add_tail(&mixer->engine.list, &drv->engine_list); - /* Reset the registers */ - for (i = 0x0; i < 0x20000; i += 4) - regmap_write(mixer->engine.regs, i, 0); + base = sun8i_blender_base(mixer); + + /* Reset registers and disable unused sub-engines */ + if (mixer->cfg->is_de3) { + for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4) + regmap_write(mixer->engine.regs, i, 0); + + regmap_write(mixer->engine.regs, SUN50I_MIXER_FCE_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_PEAK_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_LCTI_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_BLS_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_FCC_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_DNS_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_DRC_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0); + } else { + for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4) + regmap_write(mixer->engine.regs, i, 0); + + regmap_write(mixer->engine.regs, SUN8I_MIXER_FCE_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_BWS_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_LTI_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_PEAK_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_ASE_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_FCC_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0); + } /* Enable the mixer */ regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, SUN8I_MIXER_GLOBAL_CTL_RT_EN); /* Set background color to black */ - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR, + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), SUN8I_MIXER_BLEND_COLOR_BLACK); /* * Set fill color of bottom plane to black. Generally not needed * except when VI plane is at bottom (zpos = 0) and enabled. */ - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL, + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(0), + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), SUN8I_MIXER_BLEND_COLOR_BLACK); plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; for (i = 0; i < plane_cnt; i++) - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(i), + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_MODE(base, i), SUN8I_MIXER_BLEND_MODE_DEF); - regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL, + regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); return 0; @@ -585,6 +613,15 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { .vi_num = 1, }; +static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { + .ccsc = 0, + .is_de3 = true, + .mod_rate = 600000000, + .scaler_mask = 0xf, + .ui_num = 3, + .vi_num = 1, +}; + static const struct of_device_id sun8i_mixer_of_table[] = { { .compatible = "allwinner,sun8i-a83t-de2-mixer-0", @@ -618,6 +655,10 @@ static const struct of_device_id sun8i_mixer_of_table[] = { .compatible = "allwinner,sun50i-a64-de2-mixer-1", .data = &sun50i_a64_mixer1_cfg, }, + { + .compatible = "allwinner,sun50i-h6-de3-mixer-0", + .data = &sun50i_h6_mixer0_cfg, + }, { } }; MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h index 406c42e752d7..913d14ce68b0 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.h +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h @@ -29,24 +29,41 @@ #define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE BIT(0) -#define SUN8I_MIXER_BLEND_PIPE_CTL 0x1000 -#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x) (0x1004 + 0x10 * (x) + 0x0) -#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x) (0x1004 + 0x10 * (x) + 0x4) -#define SUN8I_MIXER_BLEND_ATTR_COORD(x) (0x1004 + 0x10 * (x) + 0x8) -#define SUN8I_MIXER_BLEND_ROUTE 0x1080 -#define SUN8I_MIXER_BLEND_PREMULTIPLY 0x1084 -#define SUN8I_MIXER_BLEND_BKCOLOR 0x1088 -#define SUN8I_MIXER_BLEND_OUTSIZE 0x108c -#define SUN8I_MIXER_BLEND_MODE(x) (0x1090 + 0x04 * (x)) -#define SUN8I_MIXER_BLEND_CK_CTL 0x10b0 -#define SUN8I_MIXER_BLEND_CK_CFG 0x10b4 -#define SUN8I_MIXER_BLEND_CK_MAX(x) (0x10c0 + 0x04 * (x)) -#define SUN8I_MIXER_BLEND_CK_MIN(x) (0x10e0 + 0x04 * (x)) -#define SUN8I_MIXER_BLEND_OUTCTL 0x10fc +#define DE2_MIXER_UNIT_SIZE 0x6000 +#define DE3_MIXER_UNIT_SIZE 0x3000 + +#define DE2_BLD_BASE 0x1000 +#define DE2_CH_BASE 0x2000 +#define DE2_CH_SIZE 0x1000 + +#define DE3_BLD_BASE 0x0800 +#define DE3_CH_BASE 0x1000 +#define DE3_CH_SIZE 0x0800 + +#define SUN8I_MIXER_BLEND_PIPE_CTL(base) ((base) + 0) +#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, x) ((base) + 0x4 + 0x10 * (x)) +#define SUN8I_MIXER_BLEND_ATTR_INSIZE(base, x) ((base) + 0x8 + 0x10 * (x)) +#define SUN8I_MIXER_BLEND_ATTR_COORD(base, x) ((base) + 0xc + 0x10 * (x)) +#define SUN8I_MIXER_BLEND_ROUTE(base) ((base) + 0x80) +#define SUN8I_MIXER_BLEND_PREMULTIPLY(base) ((base) + 0x84) +#define SUN8I_MIXER_BLEND_BKCOLOR(base) ((base) + 0x88) +#define SUN8I_MIXER_BLEND_OUTSIZE(base) ((base) + 0x8c) +#define SUN8I_MIXER_BLEND_MODE(base, x) ((base) + 0x90 + 0x04 * (x)) +#define SUN8I_MIXER_BLEND_CK_CTL(base) ((base) + 0xb0) +#define SUN8I_MIXER_BLEND_CK_CFG(base) ((base) + 0xb4) +#define SUN8I_MIXER_BLEND_CK_MAX(base, x) ((base) + 0xc0 + 0x04 * (x)) +#define SUN8I_MIXER_BLEND_CK_MIN(base, x) ((base) + 0xe0 + 0x04 * (x)) +#define SUN8I_MIXER_BLEND_OUTCTL(base) ((base) + 0xfc) +#define SUN50I_MIXER_BLEND_CSC_CTL(base) ((base) + 0x100) +#define SUN50I_MIXER_BLEND_CSC_COEFF(base, layer, x, y) \ + ((base) + 0x110 + (layer) * 0x30 + (x) * 0x10 + 4 * (y)) +#define SUN50I_MIXER_BLEND_CSC_CONST(base, layer, i) \ + ((base) + 0x110 + (layer) * 0x30 + (i) * 0x10 + 0x0c) #define SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK GENMASK(12, 8) #define SUN8I_MIXER_BLEND_PIPE_CTL_EN(pipe) BIT(8 + pipe) #define SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(pipe) BIT(pipe) + /* colors are always in AARRGGBB format */ #define SUN8I_MIXER_BLEND_COLOR_BLACK 0xff000000 /* The following numbers are some still unknown magic numbers */ @@ -57,6 +74,9 @@ #define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED BIT(1) +#define SUN50I_MIXER_BLEND_CSC_CTL_EN(ch) BIT(ch) +#define SUN50I_MIXER_BLEND_CSC_CONST_VAL(d, c) (((d) << 16) | ((c) & 0xffff)) + #define SUN8I_MIXER_FBFMT_ARGB8888 0 #define SUN8I_MIXER_FBFMT_ABGR8888 1 #define SUN8I_MIXER_FBFMT_RGBA8888 2 @@ -95,8 +115,8 @@ #define SUN8I_MIXER_FBFMT_YUV411 14 /* - * These sub-engines are still unknown now, the EN registers are here only to - * be used to disable these sub-engines. + * Sub-engines listed bellow are unused for now. The EN registers are here only + * to be used to disable these sub-engines. */ #define SUN8I_MIXER_FCE_EN 0xa0000 #define SUN8I_MIXER_BWS_EN 0xa2000 @@ -106,6 +126,17 @@ #define SUN8I_MIXER_FCC_EN 0xaa000 #define SUN8I_MIXER_DCSC_EN 0xb0000 +#define SUN50I_MIXER_FCE_EN 0x70000 +#define SUN50I_MIXER_PEAK_EN 0x70800 +#define SUN50I_MIXER_LCTI_EN 0x71000 +#define SUN50I_MIXER_BLS_EN 0x71800 +#define SUN50I_MIXER_FCC_EN 0x72000 +#define SUN50I_MIXER_DNS_EN 0x80000 +#define SUN50I_MIXER_DRC_EN 0xa0000 +#define SUN50I_MIXER_FMT_EN 0xa8000 +#define SUN50I_MIXER_CDC0_EN 0xd0000 +#define SUN50I_MIXER_CDC1_EN 0xd8000 + struct de2_fmt_info { u32 drm_fmt; u32 de2_fmt; @@ -127,6 +158,7 @@ struct de2_fmt_info { * are invalid. * @mod_rate: module clock rate that needs to be set in order to have * a functional block. + * @is_de3: true, if this is next gen display engine 3.0, false otherwise. */ struct sun8i_mixer_cfg { int vi_num; @@ -134,6 +166,7 @@ struct sun8i_mixer_cfg { int scaler_mask; int ccsc; unsigned long mod_rate; + unsigned int is_de3 : 1; }; struct sun8i_mixer { @@ -153,5 +186,20 @@ engine_to_sun8i_mixer(struct sunxi_engine *engine) return container_of(engine, struct sun8i_mixer, engine); } +static inline u32 +sun8i_blender_base(struct sun8i_mixer *mixer) +{ + return mixer->cfg->is_de3 ? DE3_BLD_BASE : DE2_BLD_BASE; +} + +static inline u32 +sun8i_channel_base(struct sun8i_mixer *mixer, int channel) +{ + if (mixer->cfg->is_de3) + return DE3_CH_BASE + channel * DE3_CH_SIZE; + else + return DE2_CH_BASE + channel * DE2_CH_SIZE; +} + const struct de2_fmt_info *sun8i_mixer_format_info(u32 format); #endif /* _SUN8I_MIXER_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c index 3040a79f298f..fc36e0c10a37 100644 --- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c +++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c @@ -9,11 +9,17 @@ #include <linux/component.h> #include <linux/device.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/platform_device.h> #include "sun8i_tcon_top.h" +struct sun8i_tcon_top_quirks { + bool has_tcon_tv1; + bool has_dsi; +}; + static bool sun8i_tcon_top_node_is_tcon_top(struct device_node *node) { return !!of_match_node(sun8i_tcon_top_of_table, node); @@ -121,10 +127,13 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, struct platform_device *pdev = to_platform_device(dev); struct clk_hw_onecell_data *clk_data; struct sun8i_tcon_top *tcon_top; + const struct sun8i_tcon_top_quirks *quirks; struct resource *res; void __iomem *regs; int ret, i; + quirks = of_device_get_match_data(&pdev->dev); + tcon_top = devm_kzalloc(dev, sizeof(*tcon_top), GFP_KERNEL); if (!tcon_top) return -ENOMEM; @@ -168,6 +177,13 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, } /* + * At least on H6, some registers have some bits set by default + * which may cause issues. Clear them here. + */ + writel(0, regs + TCON_TOP_PORT_SEL_REG); + writel(0, regs + TCON_TOP_GATE_SRC_REG); + + /* * TCON TOP has two muxes, which select parent clock for each TCON TV * channel clock. Parent could be either TCON TV or TVE clock. For now * we leave this fixed to TCON TV, since TVE driver for R40 is not yet @@ -180,15 +196,17 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, &tcon_top->reg_lock, TCON_TOP_TCON_TV0_GATE, 0); - clk_data->hws[CLK_TCON_TOP_TV1] = - sun8i_tcon_top_register_gate(dev, "tcon-tv1", regs, - &tcon_top->reg_lock, - TCON_TOP_TCON_TV1_GATE, 1); + if (quirks->has_tcon_tv1) + clk_data->hws[CLK_TCON_TOP_TV1] = + sun8i_tcon_top_register_gate(dev, "tcon-tv1", regs, + &tcon_top->reg_lock, + TCON_TOP_TCON_TV1_GATE, 1); - clk_data->hws[CLK_TCON_TOP_DSI] = - sun8i_tcon_top_register_gate(dev, "dsi", regs, - &tcon_top->reg_lock, - TCON_TOP_TCON_DSI_GATE, 2); + if (quirks->has_dsi) + clk_data->hws[CLK_TCON_TOP_DSI] = + sun8i_tcon_top_register_gate(dev, "dsi", regs, + &tcon_top->reg_lock, + TCON_TOP_TCON_DSI_GATE, 2); for (i = 0; i < CLK_NUM; i++) if (IS_ERR(clk_data->hws[i])) { @@ -250,9 +268,25 @@ static int sun8i_tcon_top_remove(struct platform_device *pdev) return 0; } +const struct sun8i_tcon_top_quirks sun8i_r40_tcon_top_quirks = { + .has_tcon_tv1 = true, + .has_dsi = true, +}; + +const struct sun8i_tcon_top_quirks sun50i_h6_tcon_top_quirks = { + /* Nothing special */ +}; + /* sun4i_drv uses this list to check if a device node is a TCON TOP */ const struct of_device_id sun8i_tcon_top_of_table[] = { - { .compatible = "allwinner,sun8i-r40-tcon-top" }, + { + .compatible = "allwinner,sun8i-r40-tcon-top", + .data = &sun8i_r40_tcon_top_quirks + }, + { + .compatible = "allwinner,sun50i-h6-tcon-top", + .data = &sun50i_h6_tcon_top_quirks + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun8i_tcon_top_of_table); diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c index 28c15c6ef1ef..e3fc8fa920fb 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -30,7 +30,10 @@ static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, int overlay, bool enable, unsigned int zpos, unsigned int old_zpos) { - u32 val; + u32 val, bld_base, ch_base; + + bld_base = sun8i_blender_base(mixer); + ch_base = sun8i_channel_base(mixer, channel); DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n", enable ? "En" : "Dis", channel, overlay); @@ -41,17 +44,17 @@ static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, val = 0; regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); if (!enable || zpos != old_zpos) { regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_PIPE_CTL, + SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), 0); regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_ROUTE, + SUN8I_MIXER_BLEND_ROUTE(bld_base), SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), 0); } @@ -60,12 +63,13 @@ static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_PIPE_CTL, val, val); + SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), + val, val); val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_ROUTE, + SUN8I_MIXER_BLEND_ROUTE(bld_base), SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), val); } @@ -77,12 +81,16 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, { struct drm_plane_state *state = plane->state; u32 src_w, src_h, dst_w, dst_h; + u32 bld_base, ch_base; u32 outsize, insize; u32 hphase, vphase; DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n", channel, overlay); + bld_base = sun8i_blender_base(mixer); + ch_base = sun8i_channel_base(mixer, channel); + src_w = drm_rect_width(&state->src) >> 16; src_h = drm_rect_height(&state->src) >> 16; dst_w = drm_rect_width(&state->dst); @@ -103,8 +111,8 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, outsize); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE, - outsize); + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_OUTSIZE(bld_base), outsize); if (state->crtc) interlaced = state->crtc->state->adjusted_mode.flags @@ -116,7 +124,7 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, val = 0; regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_OUTCTL, + SUN8I_MIXER_BLEND_OUTCTL(bld_base), SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val); @@ -129,10 +137,10 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, state->src.x1 >> 16, state->src.y1 >> 16); DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_SIZE(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay), insize); regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_OVL_SIZE(channel), + SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base), insize); if (insize != outsize || hphase || vphase) { @@ -156,10 +164,10 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, state->dst.x1, state->dst.y1); DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_ATTR_COORD(zpos), + SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_ATTR_INSIZE(zpos), + SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), outsize); return 0; @@ -170,7 +178,9 @@ static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel, { struct drm_plane_state *state = plane->state; const struct de2_fmt_info *fmt_info; - u32 val; + u32 val, ch_base; + + ch_base = sun8i_channel_base(mixer, channel); fmt_info = sun8i_mixer_format_info(state->fb->format->format); if (!fmt_info || !fmt_info->rgb) { @@ -180,7 +190,7 @@ static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel, val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET; regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); return 0; @@ -193,8 +203,11 @@ static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel, struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *gem; dma_addr_t paddr; + u32 ch_base; int bpp; + ch_base = sun8i_channel_base(mixer, channel); + /* Get the physical address of the buffer in memory */ gem = drm_fb_cma_get_gem_obj(fb, 0); @@ -211,13 +224,13 @@ static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel, /* Set the line width */ DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_PITCH(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay), fb->pitches[0]); DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(channel, overlay), + SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay), lower_32_bits(paddr)); return 0; diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h index 123b15ea9918..f4389cf0ba20 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h @@ -18,23 +18,26 @@ #include <drm/drm_plane.h> -#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0) -#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4) -#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8) -#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc) -#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10) -#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14) -#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18) -#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80) -#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84) -#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88) +#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(base, layer) \ + ((base) + 0x20 * (layer) + 0x0) +#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(base, layer) \ + ((base) + 0x20 * (layer) + 0x4) +#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(base, layer) \ + ((base) + 0x20 * (layer) + 0x8) +#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(base, layer) \ + ((base) + 0x20 * (layer) + 0xc) +#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(base, layer) \ + ((base) + 0x20 * (layer) + 0x10) +#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(base, layer) \ + ((base) + 0x20 * (layer) + 0x14) +#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(base, layer) \ + ((base) + 0x20 * (layer) + 0x18) +#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(base) \ + ((base) + 0x80) +#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(base) \ + ((base) + 0x84) +#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(base) \ + ((base) + 0x88) #define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0) #define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1) diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c index 6bb2aa164c8e..ae0806bccac7 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c @@ -10,6 +10,7 @@ */ #include "sun8i_ui_scaler.h" +#include "sun8i_vi_scaler.h" static const u32 lan2coefftab16[240] = { 0x00004000, 0x00033ffe, 0x00063efc, 0x000a3bfb, @@ -88,6 +89,20 @@ static const u32 lan2coefftab16[240] = { 0x0b1c1603, 0x0d1c1502, 0x0e1d1401, 0x0f1d1301, }; +static u32 sun8i_ui_scaler_base(struct sun8i_mixer *mixer, int channel) +{ + int vi_num = mixer->cfg->vi_num; + + if (mixer->cfg->is_de3) + return DE3_VI_SCALER_UNIT_BASE + + DE3_VI_SCALER_UNIT_SIZE * vi_num + + DE3_UI_SCALER_UNIT_SIZE * (channel - vi_num); + else + return DE2_VI_SCALER_UNIT_BASE + + DE2_VI_SCALER_UNIT_SIZE * vi_num + + DE2_UI_SCALER_UNIT_SIZE * (channel - vi_num); +} + static int sun8i_ui_scaler_coef_index(unsigned int step) { unsigned int scale, int_part, float_part; @@ -114,33 +129,35 @@ static int sun8i_ui_scaler_coef_index(unsigned int step) void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) { - int vi_cnt = mixer->cfg->vi_num; - u32 val; + u32 val, base; - if (WARN_ON(layer < vi_cnt)) + if (WARN_ON(layer < mixer->cfg->vi_num)) return; + base = sun8i_ui_scaler_base(mixer, layer); + if (enable) val = SUN8I_SCALER_GSU_CTRL_EN | SUN8I_SCALER_GSU_CTRL_COEFF_RDY; else val = 0; - regmap_write(mixer->engine.regs, - SUN8I_SCALER_GSU_CTRL(vi_cnt, layer - vi_cnt), val); + regmap_write(mixer->engine.regs, SUN8I_SCALER_GSU_CTRL(base), val); } void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer, u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, u32 hscale, u32 vscale, u32 hphase, u32 vphase) { - int vi_cnt = mixer->cfg->vi_num; u32 insize, outsize; int i, offset; + u32 base; - if (WARN_ON(layer < vi_cnt)) + if (WARN_ON(layer < mixer->cfg->vi_num)) return; + base = sun8i_ui_scaler_base(mixer, layer); + hphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16; vphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16; hscale <<= SUN8I_UI_SCALER_SCALE_FRAC - 16; @@ -149,24 +166,22 @@ void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer, insize = SUN8I_UI_SCALER_SIZE(src_w, src_h); outsize = SUN8I_UI_SCALER_SIZE(dst_w, dst_h); - layer -= vi_cnt; - regmap_write(mixer->engine.regs, - SUN8I_SCALER_GSU_OUTSIZE(vi_cnt, layer), outsize); + SUN8I_SCALER_GSU_OUTSIZE(base), outsize); regmap_write(mixer->engine.regs, - SUN8I_SCALER_GSU_INSIZE(vi_cnt, layer), insize); + SUN8I_SCALER_GSU_INSIZE(base), insize); regmap_write(mixer->engine.regs, - SUN8I_SCALER_GSU_HSTEP(vi_cnt, layer), hscale); + SUN8I_SCALER_GSU_HSTEP(base), hscale); regmap_write(mixer->engine.regs, - SUN8I_SCALER_GSU_VSTEP(vi_cnt, layer), vscale); + SUN8I_SCALER_GSU_VSTEP(base), vscale); regmap_write(mixer->engine.regs, - SUN8I_SCALER_GSU_HPHASE(vi_cnt, layer), hphase); + SUN8I_SCALER_GSU_HPHASE(base), hphase); regmap_write(mixer->engine.regs, - SUN8I_SCALER_GSU_VPHASE(vi_cnt, layer), vphase); + SUN8I_SCALER_GSU_VPHASE(base), vphase); offset = sun8i_ui_scaler_coef_index(hscale) * SUN8I_UI_SCALER_COEFF_COUNT; for (i = 0; i < SUN8I_UI_SCALER_COEFF_COUNT; i++) regmap_write(mixer->engine.regs, - SUN8I_SCALER_GSU_HCOEFF(vi_cnt, layer, i), + SUN8I_SCALER_GSU_HCOEFF(base, i), lan2coefftab16[offset + i]); } diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h index 86295be8be78..1ef4bd6f2718 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h +++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h @@ -11,6 +11,9 @@ #include "sun8i_mixer.h" +#define DE2_UI_SCALER_UNIT_SIZE 0x10000 +#define DE3_UI_SCALER_UNIT_SIZE 0x08000 + /* this two macros assumes 16 fractional bits which is standard in DRM */ #define SUN8I_UI_SCALER_SCALE_MIN 1 #define SUN8I_UI_SCALER_SCALE_MAX ((1UL << 20) - 1) @@ -20,23 +23,14 @@ #define SUN8I_UI_SCALER_COEFF_COUNT 16 #define SUN8I_UI_SCALER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) -#define SUN8I_SCALER_GSU_CTRL(vi_cnt, ui_idx) \ - (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x0) -#define SUN8I_SCALER_GSU_OUTSIZE(vi_cnt, ui_idx) \ - (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x40) -#define SUN8I_SCALER_GSU_INSIZE(vi_cnt, ui_idx) \ - (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x80) -#define SUN8I_SCALER_GSU_HSTEP(vi_cnt, ui_idx) \ - (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x88) -#define SUN8I_SCALER_GSU_VSTEP(vi_cnt, ui_idx) \ - (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x8c) -#define SUN8I_SCALER_GSU_HPHASE(vi_cnt, ui_idx) \ - (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x90) -#define SUN8I_SCALER_GSU_VPHASE(vi_cnt, ui_idx) \ - (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x98) -#define SUN8I_SCALER_GSU_HCOEFF(vi_cnt, ui_idx, index) \ - (0x20000 + 0x20000 * (vi_cnt) + 0x10000 * (ui_idx) + 0x200 + \ - 0x4 * (index)) +#define SUN8I_SCALER_GSU_CTRL(base) ((base) + 0x0) +#define SUN8I_SCALER_GSU_OUTSIZE(base) ((base) + 0x40) +#define SUN8I_SCALER_GSU_INSIZE(base) ((base) + 0x80) +#define SUN8I_SCALER_GSU_HSTEP(base) ((base) + 0x88) +#define SUN8I_SCALER_GSU_VSTEP(base) ((base) + 0x8c) +#define SUN8I_SCALER_GSU_HPHASE(base) ((base) + 0x90) +#define SUN8I_SCALER_GSU_VPHASE(base) ((base) + 0x98) +#define SUN8I_SCALER_GSU_HCOEFF(base, index) ((base) + 0x200 + 0x4 * (index)) #define SUN8I_SCALER_GSU_CTRL_EN BIT(0) #define SUN8I_SCALER_GSU_CTRL_COEFF_RDY BIT(4) diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index f4fe97813f94..4249edfb47ed 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -24,7 +24,10 @@ static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, int overlay, bool enable, unsigned int zpos, unsigned int old_zpos) { - u32 val; + u32 val, bld_base, ch_base; + + bld_base = sun8i_blender_base(mixer); + ch_base = sun8i_channel_base(mixer, channel); DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n", enable ? "En" : "Dis", channel, overlay); @@ -35,17 +38,17 @@ static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, val = 0; regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); if (!enable || zpos != old_zpos) { regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_PIPE_CTL, + SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), 0); regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_ROUTE, + SUN8I_MIXER_BLEND_ROUTE(bld_base), SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), 0); } @@ -54,12 +57,13 @@ static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_PIPE_CTL, val, val); + SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), + val, val); val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_ROUTE, + SUN8I_MIXER_BLEND_ROUTE(bld_base), SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), val); } @@ -72,6 +76,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, struct drm_plane_state *state = plane->state; const struct drm_format_info *format = state->fb->format; u32 src_w, src_h, dst_w, dst_h; + u32 bld_base, ch_base; u32 outsize, insize; u32 hphase, vphase; bool subsampled; @@ -79,6 +84,9 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n", channel, overlay); + bld_base = sun8i_blender_base(mixer); + ch_base = sun8i_channel_base(mixer, channel); + src_w = drm_rect_width(&state->src) >> 16; src_h = drm_rect_height(&state->src) >> 16; dst_w = drm_rect_width(&state->dst); @@ -115,10 +123,10 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, (state->src.y1 >> 16) & ~(format->vsub - 1)); DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay), insize); regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel), + SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base), insize); /* @@ -149,10 +157,10 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, state->dst.x1, state->dst.y1); DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_ATTR_COORD(zpos), + SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_ATTR_INSIZE(zpos), + SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), outsize); return 0; @@ -163,7 +171,9 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, { struct drm_plane_state *state = plane->state; const struct de2_fmt_info *fmt_info; - u32 val; + u32 val, ch_base; + + ch_base = sun8i_channel_base(mixer, channel); fmt_info = sun8i_mixer_format_info(state->fb->format->format); if (!fmt_info) { @@ -173,7 +183,7 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET; regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); if (fmt_info->csc != SUN8I_CSC_MODE_OFF) { @@ -189,9 +199,17 @@ static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, val = 0; regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val); + /* It seems that YUV formats use global alpha setting. */ + if (mixer->cfg->is_de3) + regmap_update_bits(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, + overlay), + SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK, + SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(0xff)); + return 0; } @@ -204,8 +222,11 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, struct drm_gem_cma_object *gem; u32 dx, dy, src_x, src_y; dma_addr_t paddr; + u32 ch_base; int i; + ch_base = sun8i_channel_base(mixer, channel); + /* Adjust x and y to be dividable by subsampling factor */ src_x = (state->src.x1 >> 16) & ~(format->hsub - 1); src_y = (state->src.y1 >> 16) & ~(format->vsub - 1); @@ -235,17 +256,17 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n", i + 1, fb->pitches[i]); regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel, + SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base, overlay, i), - fb->pitches[i]); + fb->pitches[i]); DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n", i + 1, &paddr); regmap_write(mixer->engine.regs, - SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel, + SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base, overlay, i), - lower_32_bits(paddr)); + lower_32_bits(paddr)); } return 0; diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.h b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h index 6996627a0a76..8a5e6d01c85d 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.h +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h @@ -12,23 +12,26 @@ #include <drm/drm_plane.h> -#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x0) -#define SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x4) -#define SUN8I_MIXER_CHAN_VI_LAYER_COORD(ch, layer) \ - (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x8) -#define SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch, layer, plane) \ - (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0xc + 4 * (plane)) -#define SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch, layer, plane) \ - (0x2000 + 0x1000 * (ch) + 0x30 * (layer) + 0x18 + 4 * (plane)) -#define SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0xe8) +#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR(base, layer) \ + ((base) + 0x30 * (layer) + 0x0) +#define SUN8I_MIXER_CHAN_VI_LAYER_SIZE(base, layer) \ + ((base) + 0x30 * (layer) + 0x4) +#define SUN8I_MIXER_CHAN_VI_LAYER_COORD(base, layer) \ + ((base) + 0x30 * (layer) + 0x8) +#define SUN8I_MIXER_CHAN_VI_LAYER_PITCH(base, layer, plane) \ + ((base) + 0x30 * (layer) + 0xc + 4 * (plane)) +#define SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(base, layer, plane) \ + ((base) + 0x30 * (layer) + 0x18 + 4 * (plane)) +#define SUN8I_MIXER_CHAN_VI_OVL_SIZE(base) \ + ((base) + 0xe8) #define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN BIT(0) /* RGB mode should be set for RGB formats and cleared for YCbCr */ #define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE BIT(15) #define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET 8 #define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK GENMASK(12, 8) +#define SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24) +#define SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(x) ((x) << 24) struct sun8i_mixer; diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c index d3f1acb234b7..7ba75011adf9 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c @@ -833,6 +833,16 @@ static const u32 bicubic4coefftab32[480] = { 0x1012110d, 0x1012110d, 0x1013110c, 0x1013110c, }; +static u32 sun8i_vi_scaler_base(struct sun8i_mixer *mixer, int channel) +{ + if (mixer->cfg->is_de3) + return DE3_VI_SCALER_UNIT_BASE + + DE3_VI_SCALER_UNIT_SIZE * channel; + else + return DE2_VI_SCALER_UNIT_BASE + + DE2_VI_SCALER_UNIT_SIZE * channel; +} + static int sun8i_vi_scaler_coef_index(unsigned int step) { unsigned int scale, int_part, float_part; @@ -857,7 +867,7 @@ static int sun8i_vi_scaler_coef_index(unsigned int step) } } -static void sun8i_vi_scaler_set_coeff(struct regmap *map, int layer, +static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base, u32 hstep, u32 vstep, const struct drm_format_info *format) { @@ -877,29 +887,31 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, int layer, offset = sun8i_vi_scaler_coef_index(hstep) * SUN8I_VI_SCALER_COEFF_COUNT; for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { - regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF0(layer, i), + regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF0(base, i), lan3coefftab32_left[offset + i]); - regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(layer, i), + regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(base, i), lan3coefftab32_right[offset + i]); - regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(layer, i), + regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, i), ch_left[offset + i]); - regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(layer, i), + regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(base, i), ch_right[offset + i]); } offset = sun8i_vi_scaler_coef_index(hstep) * SUN8I_VI_SCALER_COEFF_COUNT; for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { - regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(layer, i), + regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i), lan2coefftab32[offset + i]); - regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(layer, i), + regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(base, i), cy[offset + i]); } } void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) { - u32 val; + u32 val, base; + + base = sun8i_vi_scaler_base(mixer, layer); if (enable) val = SUN8I_SCALER_VSU_CTRL_EN | @@ -907,7 +919,8 @@ void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable) else val = 0; - regmap_write(mixer->engine.regs, SUN8I_SCALER_VSU_CTRL(layer), val); + regmap_write(mixer->engine.regs, + SUN8I_SCALER_VSU_CTRL(base), val); } void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, @@ -917,6 +930,9 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, { u32 chphase, cvphase; u32 insize, outsize; + u32 base; + + base = sun8i_vi_scaler_base(mixer, layer); hphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16; vphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16; @@ -940,32 +956,44 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, cvphase = vphase; } + if (mixer->cfg->is_de3) { + u32 val; + + if (format->hsub == 1 && format->vsub == 1) + val = SUN50I_SCALER_VSU_SCALE_MODE_UI; + else + val = SUN50I_SCALER_VSU_SCALE_MODE_NORMAL; + + regmap_write(mixer->engine.regs, + SUN50I_SCALER_VSU_SCALE_MODE(base), val); + } + regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_OUTSIZE(layer), outsize); + SUN8I_SCALER_VSU_OUTSIZE(base), outsize); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_YINSIZE(layer), insize); + SUN8I_SCALER_VSU_YINSIZE(base), insize); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_YHSTEP(layer), hscale); + SUN8I_SCALER_VSU_YHSTEP(base), hscale); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_YVSTEP(layer), vscale); + SUN8I_SCALER_VSU_YVSTEP(base), vscale); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_YHPHASE(layer), hphase); + SUN8I_SCALER_VSU_YHPHASE(base), hphase); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_YVPHASE(layer), vphase); + SUN8I_SCALER_VSU_YVPHASE(base), vphase); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_CINSIZE(layer), + SUN8I_SCALER_VSU_CINSIZE(base), SUN8I_VI_SCALER_SIZE(src_w / format->hsub, src_h / format->vsub)); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_CHSTEP(layer), + SUN8I_SCALER_VSU_CHSTEP(base), hscale / format->hsub); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_CVSTEP(layer), + SUN8I_SCALER_VSU_CVSTEP(base), vscale / format->vsub); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_CHPHASE(layer), chphase); + SUN8I_SCALER_VSU_CHPHASE(base), chphase); regmap_write(mixer->engine.regs, - SUN8I_SCALER_VSU_CVPHASE(layer), cvphase); - sun8i_vi_scaler_set_coeff(mixer->engine.regs, layer, + SUN8I_SCALER_VSU_CVPHASE(base), cvphase); + sun8i_vi_scaler_set_coeff(mixer->engine.regs, base, hscale, vscale, format); } diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h index a595ab643a5a..68f6593b369a 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h @@ -12,6 +12,12 @@ #include <drm/drm_fourcc.h> #include "sun8i_mixer.h" +#define DE2_VI_SCALER_UNIT_BASE 0x20000 +#define DE2_VI_SCALER_UNIT_SIZE 0x20000 + +#define DE3_VI_SCALER_UNIT_BASE 0x20000 +#define DE3_VI_SCALER_UNIT_SIZE 0x08000 + /* this two macros assumes 16 fractional bits which is standard in DRM */ #define SUN8I_VI_SCALER_SCALE_MIN 1 #define SUN8I_VI_SCALER_SCALE_MAX ((1UL << 20) - 1) @@ -21,34 +27,48 @@ #define SUN8I_VI_SCALER_COEFF_COUNT 32 #define SUN8I_VI_SCALER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) -#define SUN8I_SCALER_VSU_CTRL(ch) (0x20000 + 0x20000 * (ch) + 0x0) -#define SUN8I_SCALER_VSU_OUTSIZE(ch) (0x20000 + 0x20000 * (ch) + 0x40) -#define SUN8I_SCALER_VSU_YINSIZE(ch) (0x20000 + 0x20000 * (ch) + 0x80) -#define SUN8I_SCALER_VSU_YHSTEP(ch) (0x20000 + 0x20000 * (ch) + 0x88) -#define SUN8I_SCALER_VSU_YVSTEP(ch) (0x20000 + 0x20000 * (ch) + 0x8c) -#define SUN8I_SCALER_VSU_YHPHASE(ch) (0x20000 + 0x20000 * (ch) + 0x90) -#define SUN8I_SCALER_VSU_YVPHASE(ch) (0x20000 + 0x20000 * (ch) + 0x98) -#define SUN8I_SCALER_VSU_CINSIZE(ch) (0x20000 + 0x20000 * (ch) + 0xc0) -#define SUN8I_SCALER_VSU_CHSTEP(ch) (0x20000 + 0x20000 * (ch) + 0xc8) -#define SUN8I_SCALER_VSU_CVSTEP(ch) (0x20000 + 0x20000 * (ch) + 0xcc) -#define SUN8I_SCALER_VSU_CHPHASE(ch) (0x20000 + 0x20000 * (ch) + 0xd0) -#define SUN8I_SCALER_VSU_CVPHASE(ch) (0x20000 + 0x20000 * (ch) + 0xd8) -#define SUN8I_SCALER_VSU_YHCOEFF0(ch, i) \ - (0x20000 + 0x20000 * (ch) + 0x200 + 0x4 * (i)) -#define SUN8I_SCALER_VSU_YHCOEFF1(ch, i) \ - (0x20000 + 0x20000 * (ch) + 0x300 + 0x4 * (i)) -#define SUN8I_SCALER_VSU_YVCOEFF(ch, i) \ - (0x20000 + 0x20000 * (ch) + 0x400 + 0x4 * (i)) -#define SUN8I_SCALER_VSU_CHCOEFF0(ch, i) \ - (0x20000 + 0x20000 * (ch) + 0x600 + 0x4 * (i)) -#define SUN8I_SCALER_VSU_CHCOEFF1(ch, i) \ - (0x20000 + 0x20000 * (ch) + 0x700 + 0x4 * (i)) -#define SUN8I_SCALER_VSU_CVCOEFF(ch, i) \ - (0x20000 + 0x20000 * (ch) + 0x800 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CTRL(base) ((base) + 0x0) +#define SUN50I_SCALER_VSU_SCALE_MODE(base) ((base) + 0x10) +#define SUN50I_SCALER_VSU_DIR_THR(base) ((base) + 0x20) +#define SUN50I_SCALER_VSU_EDGE_THR(base) ((base) + 0x24) +#define SUN50I_SCALER_VSU_EDSCL_CTRL(base) ((base) + 0x28) +#define SUN50I_SCALER_VSU_ANGLE_THR(base) ((base) + 0x2c) +#define SUN8I_SCALER_VSU_OUTSIZE(base) ((base) + 0x40) +#define SUN8I_SCALER_VSU_YINSIZE(base) ((base) + 0x80) +#define SUN8I_SCALER_VSU_YHSTEP(base) ((base) + 0x88) +#define SUN8I_SCALER_VSU_YVSTEP(base) ((base) + 0x8c) +#define SUN8I_SCALER_VSU_YHPHASE(base) ((base) + 0x90) +#define SUN8I_SCALER_VSU_YVPHASE(base) ((base) + 0x98) +#define SUN8I_SCALER_VSU_CINSIZE(base) ((base) + 0xc0) +#define SUN8I_SCALER_VSU_CHSTEP(base) ((base) + 0xc8) +#define SUN8I_SCALER_VSU_CVSTEP(base) ((base) + 0xcc) +#define SUN8I_SCALER_VSU_CHPHASE(base) ((base) + 0xd0) +#define SUN8I_SCALER_VSU_CVPHASE(base) ((base) + 0xd8) +#define SUN8I_SCALER_VSU_YHCOEFF0(base, i) ((base) + 0x200 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_YHCOEFF1(base, i) ((base) + 0x300 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_YVCOEFF(base, i) ((base) + 0x400 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CHCOEFF0(base, i) ((base) + 0x600 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CHCOEFF1(base, i) ((base) + 0x700 + 0x4 * (i)) +#define SUN8I_SCALER_VSU_CVCOEFF(base, i) ((base) + 0x800 + 0x4 * (i)) #define SUN8I_SCALER_VSU_CTRL_EN BIT(0) #define SUN8I_SCALER_VSU_CTRL_COEFF_RDY BIT(4) +#define SUN50I_SCALER_VSU_SUB_ZERO_DIR_THR(x) (((x) << 24) & 0xFF) +#define SUN50I_SCALER_VSU_ZERO_DIR_THR(x) (((x) << 16) & 0xFF) +#define SUN50I_SCALER_VSU_HORZ_DIR_THR(x) (((x) << 8) & 0xFF) +#define SUN50I_SCALER_VSU_VERT_DIR_THR(x) ((x) & 0xFF) + +#define SUN50I_SCALER_VSU_SCALE_MODE_UI 0 +#define SUN50I_SCALER_VSU_SCALE_MODE_NORMAL 1 +#define SUN50I_SCALER_VSU_SCALE_MODE_ED_SCALE 2 + +#define SUN50I_SCALER_VSU_EDGE_SHIFT(x) (((x) << 16) & 0xF) +#define SUN50I_SCALER_VSU_EDGE_OFFSET(x) ((x) & 0xFF) + +#define SUN50I_SCALER_VSU_ANGLE_SHIFT(x) (((x) << 16) & 0xF) +#define SUN50I_SCALER_VSU_ANGLE_OFFSET(x) ((x) & 0xFF) + void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable); void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, u32 src_w, u32 src_h, u32 dst_w, u32 dst_h, diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 33e533268488..3dac08b24140 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -140,7 +140,6 @@ static int tilcdc_commit(struct drm_device *dev, static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = tilcdc_fb_create, - .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = tilcdc_atomic_check, .atomic_commit = tilcdc_commit, }; @@ -191,9 +190,6 @@ static void tilcdc_fini(struct drm_device *dev) drm_dev_unregister(dev); drm_kms_helper_poll_fini(dev); - - drm_fb_cma_fbdev_fini(dev); - drm_irq_uninstall(dev); drm_mode_config_cleanup(dev); tilcdc_remove_external_device(dev); @@ -396,16 +392,14 @@ static int tilcdc_init(struct drm_driver *ddrv, struct device *dev) drm_mode_config_reset(ddev); - ret = drm_fb_cma_fbdev_init(ddev, bpp, 0); - if (ret) - goto init_failed; - drm_kms_helper_poll_init(ddev); ret = drm_dev_register(ddev, 0); if (ret) goto init_failed; + drm_fbdev_generic_setup(ddev, bpp); + priv->is_registered = true; return 0; @@ -519,7 +513,6 @@ DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver tilcdc_driver = { .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC), - .lastclose = drm_fb_helper_lastclose, .irq_handler = tilcdc_irq, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_print_info = drm_gem_cma_print_info, diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig index 16f4b5c91f1b..2c408ac1a900 100644 --- a/drivers/gpu/drm/tinydrm/Kconfig +++ b/drivers/gpu/drm/tinydrm/Kconfig @@ -10,6 +10,17 @@ menuconfig DRM_TINYDRM config TINYDRM_MIPI_DBI tristate +config TINYDRM_HX8357D + tristate "DRM support for HX8357D display panels" + depends on DRM_TINYDRM && SPI + depends on BACKLIGHT_CLASS_DEVICE + select TINYDRM_MIPI_DBI + help + DRM driver for the following HX8357D panels: + * YX350HV15-T 3.5" 340x350 TFT (Adafruit 3.5") + + If M is selected the module will be called hx8357d. + config TINYDRM_ILI9225 tristate "DRM support for ILI9225 display panels" depends on DRM_TINYDRM && SPI diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile index 14d99080665a..f823066f7743 100644 --- a/drivers/gpu/drm/tinydrm/Makefile +++ b/drivers/gpu/drm/tinydrm/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_TINYDRM) += core/ obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o # Displays +obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o obj-$(CONFIG_TINYDRM_ILI9341) += ili9341.o obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c index 255341ee4eb9..9af51d982a33 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c @@ -146,6 +146,7 @@ static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev, drm->dev_private = tdev; drm_mode_config_init(drm); drm->mode_config.funcs = &tinydrm_mode_config_funcs; + drm->mode_config.allow_fb_modifiers = true; return 0; } diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c index 7e8e24d0b7a7..eacfc0ec8ff1 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c @@ -184,6 +184,10 @@ tinydrm_display_pipe_init(struct tinydrm_device *tdev, struct drm_display_mode mode_copy; struct drm_connector *connector; int ret; + static const uint64_t modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID + }; drm_mode_copy(&mode_copy, mode); ret = tinydrm_rotate_mode(&mode_copy, rotation); @@ -202,6 +206,6 @@ tinydrm_display_pipe_init(struct tinydrm_device *tdev, return PTR_ERR(connector); return drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats, - format_count, NULL, connector); + format_count, modifiers, connector); } EXPORT_SYMBOL(tinydrm_display_pipe_init); diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c b/drivers/gpu/drm/tinydrm/hx8357d.c new file mode 100644 index 000000000000..c3e51c2baebc --- /dev/null +++ b/drivers/gpu/drm/tinydrm/hx8357d.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DRM driver for the HX8357D LCD controller + * + * Copyright 2018 Broadcom + * Copyright 2018 David Lechner <david@lechnology.com> + * Copyright 2016 Noralf Trønnes + * Copyright (C) 2015 Adafruit Industries + * Copyright (C) 2013 Christian Vogelgsang + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/spi/spi.h> + +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_modeset_helper.h> +#include <drm/tinydrm/mipi-dbi.h> +#include <drm/tinydrm/tinydrm-helpers.h> +#include <video/mipi_display.h> + +#define HX8357D_SETOSC 0xb0 +#define HX8357D_SETPOWER 0xb1 +#define HX8357D_SETRGB 0xb3 +#define HX8357D_SETCYC 0xb3 +#define HX8357D_SETCOM 0xb6 +#define HX8357D_SETEXTC 0xb9 +#define HX8357D_SETSTBA 0xc0 +#define HX8357D_SETPANEL 0xcc +#define HX8357D_SETGAMMA 0xe0 + +#define HX8357D_MADCTL_MY 0x80 +#define HX8357D_MADCTL_MX 0x40 +#define HX8357D_MADCTL_MV 0x20 +#define HX8357D_MADCTL_ML 0x10 +#define HX8357D_MADCTL_RGB 0x00 +#define HX8357D_MADCTL_BGR 0x08 +#define HX8357D_MADCTL_MH 0x04 + +static void yx240qv29_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); + u8 addr_mode; + int ret; + + DRM_DEBUG_KMS("\n"); + + ret = mipi_dbi_poweron_conditional_reset(mipi); + if (ret < 0) + return; + if (ret == 1) + goto out_enable; + + /* setextc */ + mipi_dbi_command(mipi, HX8357D_SETEXTC, 0xFF, 0x83, 0x57); + msleep(150); + + /* setRGB which also enables SDO */ + mipi_dbi_command(mipi, HX8357D_SETRGB, 0x00, 0x00, 0x06, 0x06); + + /* -1.52V */ + mipi_dbi_command(mipi, HX8357D_SETCOM, 0x25); + + /* Normal mode 70Hz, Idle mode 55 Hz */ + mipi_dbi_command(mipi, HX8357D_SETOSC, 0x68); + + /* Set Panel - BGR, Gate direction swapped */ + mipi_dbi_command(mipi, HX8357D_SETPANEL, 0x05); + + mipi_dbi_command(mipi, HX8357D_SETPOWER, + 0x00, /* Not deep standby */ + 0x15, /* BT */ + 0x1C, /* VSPR */ + 0x1C, /* VSNR */ + 0x83, /* AP */ + 0xAA); /* FS */ + + mipi_dbi_command(mipi, HX8357D_SETSTBA, + 0x50, /* OPON normal */ + 0x50, /* OPON idle */ + 0x01, /* STBA */ + 0x3C, /* STBA */ + 0x1E, /* STBA */ + 0x08); /* GEN */ + + mipi_dbi_command(mipi, HX8357D_SETCYC, + 0x02, /* NW 0x02 */ + 0x40, /* RTN */ + 0x00, /* DIV */ + 0x2A, /* DUM */ + 0x2A, /* DUM */ + 0x0D, /* GDON */ + 0x78); /* GDOFF */ + + mipi_dbi_command(mipi, HX8357D_SETGAMMA, + 0x02, + 0x0A, + 0x11, + 0x1d, + 0x23, + 0x35, + 0x41, + 0x4b, + 0x4b, + 0x42, + 0x3A, + 0x27, + 0x1B, + 0x08, + 0x09, + 0x03, + 0x02, + 0x0A, + 0x11, + 0x1d, + 0x23, + 0x35, + 0x41, + 0x4b, + 0x4b, + 0x42, + 0x3A, + 0x27, + 0x1B, + 0x08, + 0x09, + 0x03, + 0x00, + 0x01); + + /* 16 bit */ + mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT, + MIPI_DCS_PIXEL_FMT_16BIT); + + /* TE off */ + mipi_dbi_command(mipi, MIPI_DCS_SET_TEAR_ON, 0x00); + + /* tear line */ + mipi_dbi_command(mipi, MIPI_DCS_SET_TEAR_SCANLINE, 0x00, 0x02); + + /* Exit Sleep */ + mipi_dbi_command(mipi, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(150); + + /* display on */ + mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON); + usleep_range(5000, 7000); + +out_enable: + switch (mipi->rotation) { + default: + addr_mode = HX8357D_MADCTL_MX | HX8357D_MADCTL_MY; + break; + case 90: + addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MY; + break; + case 180: + addr_mode = 0; + break; + case 270: + addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MX; + break; + } + mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); + mipi_dbi_enable_flush(mipi, crtc_state, plane_state); +} + +static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = { + .enable = yx240qv29_enable, + .disable = mipi_dbi_pipe_disable, + .update = tinydrm_display_pipe_update, + .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, +}; + +static const struct drm_display_mode yx350hv15_mode = { + TINYDRM_MODE(320, 480, 60, 75), +}; + +DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops); + +static struct drm_driver hx8357d_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, + .fops = &hx8357d_fops, + TINYDRM_GEM_DRIVER_OPS, + .debugfs_init = mipi_dbi_debugfs_init, + .name = "hx8357d", + .desc = "HX8357D", + .date = "20181023", + .major = 1, + .minor = 0, +}; + +static const struct of_device_id hx8357d_of_match[] = { + { .compatible = "adafruit,yx350hv15" }, + { } +}; +MODULE_DEVICE_TABLE(of, hx8357d_of_match); + +static const struct spi_device_id hx8357d_id[] = { + { "yx350hv15", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, hx8357d_id); + +static int hx8357d_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct mipi_dbi *mipi; + struct gpio_desc *dc; + u32 rotation = 0; + int ret; + + mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL); + if (!mipi) + return -ENOMEM; + + dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); + if (IS_ERR(dc)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); + return PTR_ERR(dc); + } + + mipi->backlight = devm_of_find_backlight(dev); + if (IS_ERR(mipi->backlight)) + return PTR_ERR(mipi->backlight); + + device_property_read_u32(dev, "rotation", &rotation); + + ret = mipi_dbi_spi_init(spi, mipi, dc); + if (ret) + return ret; + + ret = mipi_dbi_init(&spi->dev, mipi, &hx8357d_pipe_funcs, + &hx8357d_driver, &yx350hv15_mode, rotation); + if (ret) + return ret; + + spi_set_drvdata(spi, mipi); + + return devm_tinydrm_register(&mipi->tinydrm); +} + +static void hx8357d_shutdown(struct spi_device *spi) +{ + struct mipi_dbi *mipi = spi_get_drvdata(spi); + + tinydrm_shutdown(&mipi->tinydrm); +} + +static struct spi_driver hx8357d_spi_driver = { + .driver = { + .name = "hx8357d", + .of_match_table = hx8357d_of_match, + }, + .id_table = hx8357d_id, + .probe = hx8357d_probe, + .shutdown = hx8357d_shutdown, +}; +module_spi_driver(hx8357d_spi_driver); + +MODULE_DESCRIPTION("HX8357D DRM driver"); +MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c index cb3441e51d5f..1bb870021f6e 100644 --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -240,10 +240,10 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS, (clip.x1 >> 8) & 0xFF, clip.x1 & 0xFF, - (clip.x2 >> 8) & 0xFF, (clip.x2 - 1) & 0xFF); + ((clip.x2 - 1) >> 8) & 0xFF, (clip.x2 - 1) & 0xFF); mipi_dbi_command(mipi, MIPI_DCS_SET_PAGE_ADDRESS, (clip.y1 >> 8) & 0xFF, clip.y1 & 0xFF, - (clip.y2 >> 8) & 0xFF, (clip.y2 - 1) & 0xFF); + ((clip.y2 - 1) >> 8) & 0xFF, (clip.y2 - 1) & 0xFF); ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START, tr, (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 26b889f86670..83b4657ffb10 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -872,7 +872,7 @@ static int ttm_bo_add_move_fence(struct ttm_buffer_object *bo, if (fence) { reservation_object_add_shared_fence(bo->resv, fence); - ret = reservation_object_reserve_shared(bo->resv); + ret = reservation_object_reserve_shared(bo->resv, 1); if (unlikely(ret)) return ret; @@ -977,7 +977,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, bool has_erestartsys = false; int i, ret; - ret = reservation_object_reserve_shared(bo->resv); + ret = reservation_object_reserve_shared(bo->resv, 1); if (unlikely(ret)) return ret; diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index e73ae0d22897..e493edb0d3e7 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -129,7 +129,7 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, if (!entry->shared) continue; - ret = reservation_object_reserve_shared(bo->resv); + ret = reservation_object_reserve_shared(bo->resv, 1); if (!ret) continue; } @@ -151,7 +151,7 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, } if (!ret && entry->shared) - ret = reservation_object_reserve_shared(bo->resv); + ret = reservation_object_reserve_shared(bo->resv, 1); if (unlikely(ret != 0)) { if (ret == -EINTR) diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index f455f095a146..1b014d92855b 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -350,15 +350,10 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags) if (ret) goto err; - ret = drm_vblank_init(dev, 1); - if (ret) - goto err_fb; - drm_kms_helper_poll_init(dev); return 0; -err_fb: - udl_fbdev_cleanup(dev); + err: if (udl->urbs.count) udl_free_urb_list(dev); diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c index 4db62c545748..eb2b2d2f8553 100644 --- a/drivers/gpu/drm/v3d/v3d_debugfs.c +++ b/drivers/gpu/drm/v3d/v3d_debugfs.c @@ -71,10 +71,13 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused) V3D_READ(v3d_hub_reg_defs[i].reg)); } - for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - v3d_gca_reg_defs[i].name, v3d_gca_reg_defs[i].reg, - V3D_GCA_READ(v3d_gca_reg_defs[i].reg)); + if (v3d->ver < 41) { + for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + v3d_gca_reg_defs[i].name, + v3d_gca_reg_defs[i].reg, + V3D_GCA_READ(v3d_gca_reg_defs[i].reg)); + } } for (core = 0; core < v3d->cores; core++) { @@ -176,9 +179,44 @@ static int v3d_debugfs_bo_stats(struct seq_file *m, void *unused) return 0; } +static int v3d_measure_clock(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *dev = node->minor->dev; + struct v3d_dev *v3d = to_v3d_dev(dev); + uint32_t cycles; + int core = 0; + int measure_ms = 1000; + + if (v3d->ver >= 40) { + V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3, + V3D_SET_FIELD(V3D_PCTR_CYCLE_COUNT, + V3D_PCTR_S0)); + V3D_CORE_WRITE(core, V3D_V4_PCTR_0_CLR, 1); + V3D_CORE_WRITE(core, V3D_V4_PCTR_0_EN, 1); + } else { + V3D_CORE_WRITE(core, V3D_V3_PCTR_0_PCTRS0, + V3D_PCTR_CYCLE_COUNT); + V3D_CORE_WRITE(core, V3D_V3_PCTR_0_CLR, 1); + V3D_CORE_WRITE(core, V3D_V3_PCTR_0_EN, + V3D_V3_PCTR_0_EN_ENABLE | + 1); + } + msleep(measure_ms); + cycles = V3D_CORE_READ(core, V3D_PCTR_0_PCTR0); + + seq_printf(m, "cycles: %d (%d.%d Mhz)\n", + cycles, + cycles / (measure_ms * 1000), + (cycles / (measure_ms * 100)) % 10); + + return 0; +} + static const struct drm_info_list v3d_debugfs_list[] = { {"v3d_ident", v3d_v3d_debugfs_ident, 0}, {"v3d_regs", v3d_v3d_debugfs_regs, 0}, + {"measure_clock", v3d_measure_clock, 0}, {"bo_stats", v3d_debugfs_bo_stats, 0}, }; diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index e6fed696ad86..cbe5be0c47eb 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -198,6 +198,11 @@ struct v3d_exec_info { */ struct dma_fence *bin_done_fence; + /* Fence for when the scheduler considers the render to be + * done, for when the BOs reservations should be complete. + */ + struct dma_fence *render_done_fence; + struct kref refcount; /* This is the array of BOs that were looked up at the start of exec. */ diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 70c54774400b..b88c96911453 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -209,7 +209,7 @@ v3d_flush_caches(struct v3d_dev *v3d) static void v3d_attach_object_fences(struct v3d_exec_info *exec) { - struct dma_fence *out_fence = &exec->render.base.s_fence->finished; + struct dma_fence *out_fence = exec->render_done_fence; struct v3d_bo *bo; int i; @@ -305,7 +305,7 @@ retry: for (i = 0; i < exec->bo_count; i++) { bo = to_v3d_bo(&exec->bo[i]->base); - ret = reservation_object_reserve_shared(bo->resv); + ret = reservation_object_reserve_shared(bo->resv, 1); if (ret) { v3d_unlock_bo_reservations(dev, exec, acquire_ctx); return ret; @@ -409,6 +409,7 @@ v3d_exec_cleanup(struct kref *ref) dma_fence_put(exec->render.done_fence); dma_fence_put(exec->bin_done_fence); + dma_fence_put(exec->render_done_fence); for (i = 0; i < exec->bo_count; i++) drm_gem_object_put_unlocked(&exec->bo[i]->base); @@ -521,12 +522,12 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, kref_init(&exec->refcount); ret = drm_syncobj_find_fence(file_priv, args->in_sync_bcl, - 0, &exec->bin.in_fence); + 0, 0, &exec->bin.in_fence); if (ret == -EINVAL) goto fail; ret = drm_syncobj_find_fence(file_priv, args->in_sync_rcl, - 0, &exec->render.in_fence); + 0, 0, &exec->render.in_fence); if (ret == -EINVAL) goto fail; @@ -572,6 +573,9 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, if (ret) goto fail_unreserve; + exec->render_done_fence = + dma_fence_get(&exec->render.base.s_fence->finished); + kref_get(&exec->refcount); /* put by scheduler job completion */ drm_sched_entity_push_job(&exec->render.base, &v3d_priv->sched_entity[V3D_RENDER]); @@ -585,7 +589,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, sync_out = drm_syncobj_find(file_priv, args->out_sync); if (sync_out) { drm_syncobj_replace_fence(sync_out, 0, - &exec->render.base.s_fence->finished); + exec->render_done_fence); drm_syncobj_put(sync_out); } diff --git a/drivers/gpu/drm/v3d/v3d_regs.h b/drivers/gpu/drm/v3d/v3d_regs.h index 854046565989..c3a5e4e44f73 100644 --- a/drivers/gpu/drm/v3d/v3d_regs.h +++ b/drivers/gpu/drm/v3d/v3d_regs.h @@ -267,6 +267,36 @@ # define V3D_PTB_BXCF_RWORDERDISA BIT(1) # define V3D_PTB_BXCF_CLIPDISA BIT(0) +#define V3D_V3_PCTR_0_EN 0x00674 +#define V3D_V3_PCTR_0_EN_ENABLE BIT(31) +#define V3D_V4_PCTR_0_EN 0x00650 +/* When a bit is set, resets the counter to 0. */ +#define V3D_V3_PCTR_0_CLR 0x00670 +#define V3D_V4_PCTR_0_CLR 0x00654 +#define V3D_PCTR_0_OVERFLOW 0x00658 + +#define V3D_V3_PCTR_0_PCTRS0 0x00684 +#define V3D_V3_PCTR_0_PCTRS15 0x00660 +#define V3D_V3_PCTR_0_PCTRSX(x) (V3D_V3_PCTR_0_PCTRS0 + \ + 4 * (x)) +/* Each src reg muxes four counters each. */ +#define V3D_V4_PCTR_0_SRC_0_3 0x00660 +#define V3D_V4_PCTR_0_SRC_28_31 0x0067c +# define V3D_PCTR_S0_MASK V3D_MASK(6, 0) +# define V3D_PCTR_S0_SHIFT 0 +# define V3D_PCTR_S1_MASK V3D_MASK(14, 8) +# define V3D_PCTR_S1_SHIFT 8 +# define V3D_PCTR_S2_MASK V3D_MASK(22, 16) +# define V3D_PCTR_S2_SHIFT 16 +# define V3D_PCTR_S3_MASK V3D_MASK(30, 24) +# define V3D_PCTR_S3_SHIFT 24 +# define V3D_PCTR_CYCLE_COUNT 32 + +/* Output values of the counters. */ +#define V3D_PCTR_0_PCTR0 0x00680 +#define V3D_PCTR_0_PCTR31 0x006fc +#define V3D_PCTR_0_PCTRX(x) (V3D_PCTR_0_PCTR0 + \ + 4 * (x)) #define V3D_GMP_STATUS 0x00800 # define V3D_GMP_STATUS_GMPRST BIT(31) # define V3D_GMP_STATUS_WR_COUNT_MASK V3D_MASK(30, 24) diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 1f1780ccdbdf..f6f5cd80c04d 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -33,6 +33,7 @@ #include <linux/pm_runtime.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_atomic_helper.h> #include "uapi/drm/vc4_drm.h" #include "vc4_drv.h" @@ -308,6 +309,8 @@ static void vc4_drm_unbind(struct device *dev) drm_dev_unregister(drm); + drm_atomic_helper_shutdown(drm); + drm_mode_config_cleanup(drm); drm_atomic_private_obj_fini(&vc4->ctm_manager); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 5b22e996af6c..41881ce4132d 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -635,7 +635,7 @@ retry: for (i = 0; i < exec->bo_count; i++) { bo = to_vc4_bo(&exec->bo[i]->base); - ret = reservation_object_reserve_shared(bo->resv); + ret = reservation_object_reserve_shared(bo->resv, 1); if (ret) { vc4_unlock_bo_reservations(dev, exec, acquire_ctx); return ret; @@ -1173,7 +1173,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, if (args->in_sync) { ret = drm_syncobj_find_fence(file_priv, args->in_sync, - 0, &in_fence); + 0, 0, &in_fence); if (ret) goto fail; diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 9dc3fcbd290b..98fae4daa08c 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -266,30 +266,59 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) u32 subpixel_src_mask = (1 << 16) - 1; u32 format = fb->format->format; int num_planes = fb->format->num_planes; - u32 h_subsample = 1; - u32 v_subsample = 1; - int i; + int min_scale = 1, max_scale = INT_MAX; + struct drm_crtc_state *crtc_state; + u32 h_subsample, v_subsample; + int i, ret; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, + state->crtc); + if (!crtc_state) { + DRM_DEBUG_KMS("Invalid crtc state\n"); + return -EINVAL; + } + + /* No configuring scaling on the cursor plane, since it gets + * non-vblank-synced updates, and scaling requires LBM changes which + * have to be vblank-synced. + */ + if (plane->type == DRM_PLANE_TYPE_CURSOR) { + min_scale = DRM_PLANE_HELPER_NO_SCALING; + max_scale = DRM_PLANE_HELPER_NO_SCALING; + } else { + min_scale = 1; + max_scale = INT_MAX; + } + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + min_scale, max_scale, + true, true); + if (ret) + return ret; + + h_subsample = drm_format_horz_chroma_subsampling(format); + v_subsample = drm_format_vert_chroma_subsampling(format); for (i = 0; i < num_planes; i++) vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; /* We don't support subpixel source positioning for scaling. */ - if ((state->src_x & subpixel_src_mask) || - (state->src_y & subpixel_src_mask) || - (state->src_w & subpixel_src_mask) || - (state->src_h & subpixel_src_mask)) { + if ((state->src.x1 & subpixel_src_mask) || + (state->src.x2 & subpixel_src_mask) || + (state->src.y1 & subpixel_src_mask) || + (state->src.y2 & subpixel_src_mask)) { return -EINVAL; } - vc4_state->src_x = state->src_x >> 16; - vc4_state->src_y = state->src_y >> 16; - vc4_state->src_w[0] = state->src_w >> 16; - vc4_state->src_h[0] = state->src_h >> 16; + vc4_state->src_x = state->src.x1 >> 16; + vc4_state->src_y = state->src.y1 >> 16; + vc4_state->src_w[0] = (state->src.x2 - state->src.x1) >> 16; + vc4_state->src_h[0] = (state->src.y2 - state->src.y1) >> 16; - vc4_state->crtc_x = state->crtc_x; - vc4_state->crtc_y = state->crtc_y; - vc4_state->crtc_w = state->crtc_w; - vc4_state->crtc_h = state->crtc_h; + vc4_state->crtc_x = state->dst.x1; + vc4_state->crtc_y = state->dst.y1; + vc4_state->crtc_w = state->dst.x2 - state->dst.x1; + vc4_state->crtc_h = state->dst.y2 - state->dst.y1; vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], vc4_state->crtc_w); @@ -302,8 +331,6 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) if (num_planes > 1) { vc4_state->is_yuv = true; - h_subsample = drm_format_horz_chroma_subsampling(format); - v_subsample = drm_format_vert_chroma_subsampling(format); vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample; vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample; @@ -321,45 +348,11 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) if (vc4_state->is_unity) vc4_state->x_scaling[0] = VC4_SCALING_PPF; } else { + vc4_state->is_yuv = false; vc4_state->x_scaling[1] = VC4_SCALING_NONE; vc4_state->y_scaling[1] = VC4_SCALING_NONE; } - /* No configuring scaling on the cursor plane, since it gets - non-vblank-synced updates, and scaling requires requires - LBM changes which have to be vblank-synced. - */ - if (plane->type == DRM_PLANE_TYPE_CURSOR && !vc4_state->is_unity) - return -EINVAL; - - /* Clamp the on-screen start x/y to 0. The hardware doesn't - * support negative y, and negative x wastes bandwidth. - */ - if (vc4_state->crtc_x < 0) { - for (i = 0; i < num_planes; i++) { - u32 cpp = fb->format->cpp[i]; - u32 subs = ((i == 0) ? 1 : h_subsample); - - vc4_state->offsets[i] += (cpp * - (-vc4_state->crtc_x) / subs); - } - vc4_state->src_w[0] += vc4_state->crtc_x; - vc4_state->src_w[1] += vc4_state->crtc_x / h_subsample; - vc4_state->crtc_x = 0; - } - - if (vc4_state->crtc_y < 0) { - for (i = 0; i < num_planes; i++) { - u32 subs = ((i == 0) ? 1 : v_subsample); - - vc4_state->offsets[i] += (fb->pitches[i] * - (-vc4_state->crtc_y) / subs); - } - vc4_state->src_h[0] += vc4_state->crtc_y; - vc4_state->src_h[1] += vc4_state->crtc_y / v_subsample; - vc4_state->crtc_y = 0; - } - return 0; } @@ -467,6 +460,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier); int num_planes = drm_format_num_planes(format->drm); + u32 h_subsample, v_subsample; bool mix_plane_alpha; bool covers_screen; u32 scl0, scl1, pitch0; @@ -512,26 +506,77 @@ static int vc4_plane_mode_set(struct drm_plane *plane, scl1 = vc4_get_scl_field(state, 0); } + h_subsample = drm_format_horz_chroma_subsampling(format->drm); + v_subsample = drm_format_vert_chroma_subsampling(format->drm); + switch (base_format_mod) { case DRM_FORMAT_MOD_LINEAR: tiling = SCALER_CTL0_TILING_LINEAR; pitch0 = VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH); + + /* Adjust the base pointer to the first pixel to be scanned + * out. + */ + for (i = 0; i < num_planes; i++) { + vc4_state->offsets[i] += vc4_state->src_y / + (i ? v_subsample : 1) * + fb->pitches[i]; + vc4_state->offsets[i] += vc4_state->src_x / + (i ? h_subsample : 1) * + fb->format->cpp[i]; + } + break; case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: { - /* For T-tiled, the FB pitch is "how many bytes from - * one row to the next, such that pitch * tile_h == - * tile_size * tiles_per_row." - */ u32 tile_size_shift = 12; /* T tiles are 4kb */ + /* Whole-tile offsets, mostly for setting the pitch. */ + u32 tile_w_shift = fb->format->cpp[0] == 2 ? 6 : 5; u32 tile_h_shift = 5; /* 16 and 32bpp are 32 pixels high */ + u32 tile_w_mask = (1 << tile_w_shift) - 1; + /* The height mask on 32-bit-per-pixel tiles is 63, i.e. twice + * the height (in pixels) of a 4k tile. + */ + u32 tile_h_mask = (2 << tile_h_shift) - 1; + /* For T-tiled, the FB pitch is "how many bytes from one row to + * the next, such that + * + * pitch * tile_h == tile_size * tiles_per_row + */ u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift); + u32 tiles_l = vc4_state->src_x >> tile_w_shift; + u32 tiles_r = tiles_w - tiles_l; + u32 tiles_t = vc4_state->src_y >> tile_h_shift; + /* Intra-tile offsets, which modify the base address (the + * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that + * base address). + */ + u32 tile_y = (vc4_state->src_y >> 4) & 1; + u32 subtile_y = (vc4_state->src_y >> 2) & 3; + u32 utile_y = vc4_state->src_y & 3; + u32 x_off = vc4_state->src_x & tile_w_mask; + u32 y_off = vc4_state->src_y & tile_h_mask; tiling = SCALER_CTL0_TILING_256B_OR_T; + pitch0 = (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) | + VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) | + VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) | + VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R)); + vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift); + vc4_state->offsets[0] += subtile_y << 8; + vc4_state->offsets[0] += utile_y << 4; + + /* Rows of tiles alternate left-to-right and right-to-left. */ + if (tiles_t & 1) { + pitch0 |= SCALER_PITCH0_TILE_INITIAL_LINE_DIR; + vc4_state->offsets[0] += (tiles_w - tiles_l) << + tile_size_shift; + vc4_state->offsets[0] -= (1 + !tile_y) << 10; + } else { + vc4_state->offsets[0] += tiles_l << tile_size_shift; + vc4_state->offsets[0] += tile_y << 10; + } - pitch0 = (VC4_SET_FIELD(0, SCALER_PITCH0_TILE_Y_OFFSET) | - VC4_SET_FIELD(0, SCALER_PITCH0_TILE_WIDTH_L) | - VC4_SET_FIELD(tiles_w, SCALER_PITCH0_TILE_WIDTH_R)); break; } @@ -903,7 +948,6 @@ static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { static void vc4_plane_destroy(struct drm_plane *plane) { - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); } diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index d6864fa4bd14..931088014272 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -1037,14 +1037,18 @@ enum hvs_pixel_format { #define SCALER_TILE_HEIGHT_MASK VC4_MASK(15, 0) #define SCALER_TILE_HEIGHT_SHIFT 0 +/* Common PITCH0 fields */ +#define SCALER_PITCH0_SINK_PIX_MASK VC4_MASK(31, 26) +#define SCALER_PITCH0_SINK_PIX_SHIFT 26 + /* PITCH0 fields for T-tiled. */ #define SCALER_PITCH0_TILE_WIDTH_L_MASK VC4_MASK(22, 16) #define SCALER_PITCH0_TILE_WIDTH_L_SHIFT 16 #define SCALER_PITCH0_TILE_LINE_DIR BIT(15) #define SCALER_PITCH0_TILE_INITIAL_LINE_DIR BIT(14) /* Y offset within a tile. */ -#define SCALER_PITCH0_TILE_Y_OFFSET_MASK VC4_MASK(13, 7) -#define SCALER_PITCH0_TILE_Y_OFFSET_SHIFT 7 +#define SCALER_PITCH0_TILE_Y_OFFSET_MASK VC4_MASK(13, 8) +#define SCALER_PITCH0_TILE_Y_OFFSET_SHIFT 8 #define SCALER_PITCH0_TILE_WIDTH_R_MASK VC4_MASK(6, 0) #define SCALER_PITCH0_TILE_WIDTH_R_SHIFT 0 diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index ec6af8b920da..5930facd6d2d 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -431,7 +431,8 @@ static void vgem_release(struct drm_device *dev) } static struct drm_driver vgem_driver = { - .driver_features = DRIVER_GEM | DRIVER_PRIME, + .driver_features = DRIVER_GEM | DRIVER_PRIME | + DRIVER_RENDER, .release = vgem_release, .open = vgem_open, .postclose = vgem_postclose, @@ -471,31 +472,31 @@ static int __init vgem_init(void) if (!vgem_device) return -ENOMEM; - ret = drm_dev_init(&vgem_device->drm, &vgem_driver, NULL); - if (ret) - goto out_free; - vgem_device->platform = platform_device_register_simple("vgem", -1, NULL, 0); if (IS_ERR(vgem_device->platform)) { ret = PTR_ERR(vgem_device->platform); - goto out_fini; + goto out_free; } dma_coerce_mask_and_coherent(&vgem_device->platform->dev, DMA_BIT_MASK(64)); + ret = drm_dev_init(&vgem_device->drm, &vgem_driver, + &vgem_device->platform->dev); + if (ret) + goto out_unregister; /* Final step: expose the device/driver to userspace */ ret = drm_dev_register(&vgem_device->drm, 0); if (ret) - goto out_unregister; + goto out_fini; return 0; -out_unregister: - platform_device_unregister(vgem_device->platform); out_fini: drm_dev_fini(&vgem_device->drm); +out_unregister: + platform_device_unregister(vgem_device->platform); out_free: kfree(vgem_device); return ret; diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c index e6ee71323a66..c1c420afe2dd 100644 --- a/drivers/gpu/drm/vgem/vgem_fence.c +++ b/drivers/gpu/drm/vgem/vgem_fence.c @@ -180,7 +180,7 @@ int vgem_fence_attach_ioctl(struct drm_device *dev, reservation_object_lock(resv, NULL); if (arg->flags & VGEM_FENCE_WRITE) reservation_object_add_excl_fence(resv, fence); - else if ((ret = reservation_object_reserve_shared(resv)) == 0) + else if ((ret = reservation_object_reserve_shared(resv, 1)) == 0) reservation_object_add_shared_fence(resv, fence); reservation_object_unlock(resv); diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c index 757ca28ab93e..0887e0b64b9c 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -53,6 +53,37 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) 0, "virtiodrmfb"); + /* + * Normally the drm_dev_set_unique() call is done by core DRM. + * The following comment covers, why virtio cannot rely on it. + * + * Unlike the other virtual GPU drivers, virtio abstracts the + * underlying bus type by using struct virtio_device. + * + * Hence the dev_is_pci() check, used in core DRM, will fail + * and the unique returned will be the virtio_device "virtio0", + * while a "pci:..." one is required. + * + * A few other ideas were considered: + * - Extend the dev_is_pci() check [in drm_set_busid] to + * consider virtio. + * Seems like a bigger hack than what we have already. + * + * - Point drm_device::dev to the parent of the virtio_device + * Semantic changes: + * * Using the wrong device for i2c, framebuffer_alloc and + * prime import. + * Visual changes: + * * Helpers such as DRM_DEV_ERROR, dev_info, drm_printer, + * will print the wrong information. + * + * We could address the latter issues, by introducing + * drm_device::bus_dev, ... which would be used solely for this. + * + * So for the moment keep things as-is, with a bulky comment + * for the next person who feels like removing this + * drm_dev_set_unique() quirk. + */ snprintf(unique, sizeof(unique), "pci:%s", pname); ret = drm_dev_set_unique(dev, unique); if (ret) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index d29f0c7c768c..6474e83cbf3d 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -65,6 +65,7 @@ struct virtio_gpu_object { struct ttm_placement placement; struct ttm_buffer_object tbo; struct ttm_bo_kmap_obj kmap; + bool created; }; #define gem_to_virtio_gpu_obj(gobj) \ container_of((gobj), struct virtio_gpu_object, gem_base) @@ -190,8 +191,7 @@ struct virtio_gpu_device { struct kmem_cache *vbufs; bool vqs_ready; - struct idr resource_idr; - spinlock_t resource_idr_lock; + struct ida resource_ida; wait_queue_head_t resp_wq; /* current display info */ @@ -200,8 +200,7 @@ struct virtio_gpu_device { struct virtio_gpu_fence_driver fence_drv; - struct idr ctx_id_idr; - spinlock_t ctx_id_idr_lock; + struct ida ctx_id_ida; bool has_virgl_3d; @@ -259,11 +258,8 @@ int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *qfb, /* virtio vg */ int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev); void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev); -void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, - uint32_t *resid); -void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id); void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, - uint32_t resource_id, + struct virtio_gpu_object *bo, uint32_t format, uint32_t width, uint32_t height); @@ -285,7 +281,6 @@ void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, uint32_t x, uint32_t y); int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *obj, - uint32_t resource_id, struct virtio_gpu_fence **fence); void virtio_gpu_object_detach(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *obj); @@ -324,6 +319,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev, struct virtio_gpu_fence **fence); void virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *bo, struct virtio_gpu_resource_create_3d *rc_3d, struct virtio_gpu_fence **fence); void virtio_gpu_ctrl_ack(struct virtqueue *vq); diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c index cea749f4ec39..fb1cc8b2f119 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fb.c +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c @@ -214,7 +214,7 @@ static int virtio_gpufb_create(struct drm_fb_helper *helper, struct drm_framebuffer *fb; struct drm_mode_fb_cmd2 mode_cmd = {}; struct virtio_gpu_object *obj; - uint32_t resid, format, size; + uint32_t format, size; int ret; mode_cmd.width = sizes->surface_width; @@ -231,8 +231,7 @@ static int virtio_gpufb_create(struct drm_fb_helper *helper, if (IS_ERR(obj)) return PTR_ERR(obj); - virtio_gpu_resource_id_get(vgdev, &resid); - virtio_gpu_cmd_create_resource(vgdev, resid, format, + virtio_gpu_cmd_create_resource(vgdev, obj, format, mode_cmd.width, mode_cmd.height); ret = virtio_gpu_object_kmap(obj); @@ -242,7 +241,7 @@ static int virtio_gpufb_create(struct drm_fb_helper *helper, } /* attach the object to the resource */ - ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); + ret = virtio_gpu_object_attach(vgdev, obj, NULL); if (ret) goto err_obj_attach; diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c index 82c817f37cf7..f06586393974 100644 --- a/drivers/gpu/drm/virtio/virtgpu_gem.c +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -87,7 +87,6 @@ int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, struct virtio_gpu_object *obj; int ret; uint32_t pitch; - uint32_t resid; uint32_t format; if (args->bpp != 32) @@ -103,13 +102,12 @@ int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, goto fail; format = virtio_gpu_translate_format(DRM_FORMAT_HOST_XRGB8888); - virtio_gpu_resource_id_get(vgdev, &resid); - virtio_gpu_cmd_create_resource(vgdev, resid, format, + obj = gem_to_virtio_gpu_obj(gobj); + virtio_gpu_cmd_create_resource(vgdev, obj, format, args->width, args->height); /* attach the object to the resource */ - obj = gem_to_virtio_gpu_obj(gobj); - ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); + ret = virtio_gpu_object_attach(vgdev, obj, NULL); if (ret) goto fail; diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index f16b875d6a46..bc5afa4f906e 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -217,7 +217,6 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, struct virtio_gpu_device *vgdev = dev->dev_private; struct drm_virtgpu_resource_create *rc = data; int ret; - uint32_t res_id; struct virtio_gpu_object *qobj; struct drm_gem_object *obj; uint32_t handle = 0; @@ -244,8 +243,6 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, INIT_LIST_HEAD(&validate_list); memset(&mainbuf, 0, sizeof(struct ttm_validate_buffer)); - virtio_gpu_resource_id_get(vgdev, &res_id); - size = rc->size; /* allocate a single page size object */ @@ -253,17 +250,15 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, size = PAGE_SIZE; qobj = virtio_gpu_alloc_object(dev, size, false, false); - if (IS_ERR(qobj)) { - ret = PTR_ERR(qobj); - goto fail_id; - } + if (IS_ERR(qobj)) + return PTR_ERR(qobj); obj = &qobj->gem_base; if (!vgdev->has_virgl_3d) { - virtio_gpu_cmd_create_resource(vgdev, res_id, rc->format, + virtio_gpu_cmd_create_resource(vgdev, qobj, rc->format, rc->width, rc->height); - ret = virtio_gpu_object_attach(vgdev, qobj, res_id, NULL); + ret = virtio_gpu_object_attach(vgdev, qobj, NULL); } else { /* use a gem reference since unref list undoes them */ drm_gem_object_get(&qobj->gem_base); @@ -276,7 +271,7 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, goto fail_unref; } - rc_3d.resource_id = cpu_to_le32(res_id); + rc_3d.resource_id = cpu_to_le32(qobj->hw_res_handle); rc_3d.target = cpu_to_le32(rc->target); rc_3d.format = cpu_to_le32(rc->format); rc_3d.bind = cpu_to_le32(rc->bind); @@ -288,8 +283,8 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, rc_3d.nr_samples = cpu_to_le32(rc->nr_samples); rc_3d.flags = cpu_to_le32(rc->flags); - virtio_gpu_cmd_resource_create_3d(vgdev, &rc_3d, NULL); - ret = virtio_gpu_object_attach(vgdev, qobj, res_id, &fence); + virtio_gpu_cmd_resource_create_3d(vgdev, qobj, &rc_3d, NULL); + ret = virtio_gpu_object_attach(vgdev, qobj, &fence); if (ret) { ttm_eu_backoff_reservation(&ticket, &validate_list); goto fail_unref; @@ -297,8 +292,6 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f); } - qobj->hw_res_handle = res_id; - ret = drm_gem_handle_create(file_priv, obj, &handle); if (ret) { @@ -311,7 +304,7 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, } drm_gem_object_put_unlocked(obj); - rc->res_handle = res_id; /* similiar to a VM address */ + rc->res_handle = qobj->hw_res_handle; /* similiar to a VM address */ rc->bo_handle = handle; if (vgdev->has_virgl_3d) { @@ -326,8 +319,6 @@ fail_unref: } //fail_obj: // drm_gem_object_handle_unreference_unlocked(obj); -fail_id: - virtio_gpu_resource_id_put(vgdev, res_id); return ret; } diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 65060c08522d..bf609dcae224 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -52,39 +52,22 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) events_clear, &events_clear); } -static void virtio_gpu_ctx_id_get(struct virtio_gpu_device *vgdev, - uint32_t *resid) +static int virtio_gpu_context_create(struct virtio_gpu_device *vgdev, + uint32_t nlen, const char *name) { - int handle; - - idr_preload(GFP_KERNEL); - spin_lock(&vgdev->ctx_id_idr_lock); - handle = idr_alloc(&vgdev->ctx_id_idr, NULL, 1, 0, 0); - spin_unlock(&vgdev->ctx_id_idr_lock); - idr_preload_end(); - *resid = handle; -} + int handle = ida_alloc_min(&vgdev->ctx_id_ida, 1, GFP_KERNEL); -static void virtio_gpu_ctx_id_put(struct virtio_gpu_device *vgdev, uint32_t id) -{ - spin_lock(&vgdev->ctx_id_idr_lock); - idr_remove(&vgdev->ctx_id_idr, id); - spin_unlock(&vgdev->ctx_id_idr_lock); -} - -static void virtio_gpu_context_create(struct virtio_gpu_device *vgdev, - uint32_t nlen, const char *name, - uint32_t *ctx_id) -{ - virtio_gpu_ctx_id_get(vgdev, ctx_id); - virtio_gpu_cmd_context_create(vgdev, *ctx_id, nlen, name); + if (handle < 0) + return handle; + virtio_gpu_cmd_context_create(vgdev, handle, nlen, name); + return handle; } static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev, uint32_t ctx_id) { virtio_gpu_cmd_context_destroy(vgdev, ctx_id); - virtio_gpu_ctx_id_put(vgdev, ctx_id); + ida_free(&vgdev->ctx_id_ida, ctx_id); } static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, @@ -151,10 +134,8 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) vgdev->dev = dev->dev; spin_lock_init(&vgdev->display_info_lock); - spin_lock_init(&vgdev->ctx_id_idr_lock); - idr_init(&vgdev->ctx_id_idr); - spin_lock_init(&vgdev->resource_idr_lock); - idr_init(&vgdev->resource_idr); + ida_init(&vgdev->ctx_id_ida); + ida_init(&vgdev->resource_ida); init_waitqueue_head(&vgdev->resp_wq); virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func); virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func); @@ -271,7 +252,7 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) { struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_fpriv *vfpriv; - uint32_t id; + int id; char dbgname[TASK_COMM_LEN]; /* can't create contexts without 3d renderer */ @@ -284,7 +265,9 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) return -ENOMEM; get_task_comm(dbgname, current); - virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname, &id); + id = virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname); + if (id < 0) + return id; vfpriv->ctx_id = id; file->driver_priv = vfpriv; diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index eca765537470..77eac4eb06b1 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -25,6 +25,18 @@ #include "virtgpu_drv.h" +static void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, + uint32_t *resid) +{ + int handle = ida_alloc_min(&vgdev->resource_ida, 1, GFP_KERNEL); + *resid = handle; +} + +static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id) +{ + ida_free(&vgdev->resource_ida, id); +} + static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) { struct virtio_gpu_object *bo; @@ -33,13 +45,14 @@ static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) bo = container_of(tbo, struct virtio_gpu_object, tbo); vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; - if (bo->hw_res_handle) + if (bo->created) virtio_gpu_cmd_unref_resource(vgdev, bo->hw_res_handle); if (bo->pages) virtio_gpu_object_free_sg_table(bo); if (bo->vmap) virtio_gpu_object_kunmap(bo); drm_gem_object_release(&bo->gem_base); + virtio_gpu_resource_id_put(vgdev, bo->hw_res_handle); kfree(bo); } @@ -81,9 +94,11 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, bo = kzalloc(sizeof(struct virtio_gpu_object), GFP_KERNEL); if (bo == NULL) return -ENOMEM; + virtio_gpu_resource_id_get(vgdev, &bo->hw_res_handle); size = roundup(size, PAGE_SIZE); ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size); if (ret != 0) { + virtio_gpu_resource_id_put(vgdev, bo->hw_res_handle); kfree(bo); return ret; } diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c index e3152d45c5f1..cd63dffa6d40 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ttm.c +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c @@ -347,8 +347,7 @@ static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo, } else if (new_mem->placement & TTM_PL_FLAG_TT) { if (bo->hw_res_handle) { - virtio_gpu_object_attach(vgdev, bo, bo->hw_res_handle, - NULL); + virtio_gpu_object_attach(vgdev, bo, NULL); } } } diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 4e2e037aed34..51bef1775e47 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -38,26 +38,6 @@ + MAX_INLINE_CMD_SIZE \ + MAX_INLINE_RESP_SIZE) -void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, - uint32_t *resid) -{ - int handle; - - idr_preload(GFP_KERNEL); - spin_lock(&vgdev->resource_idr_lock); - handle = idr_alloc(&vgdev->resource_idr, NULL, 1, 0, GFP_NOWAIT); - spin_unlock(&vgdev->resource_idr_lock); - idr_preload_end(); - *resid = handle; -} - -void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id) -{ - spin_lock(&vgdev->resource_idr_lock); - idr_remove(&vgdev->resource_idr, id); - spin_unlock(&vgdev->resource_idr_lock); -} - void virtio_gpu_ctrl_ack(struct virtqueue *vq) { struct drm_device *dev = vq->vdev->priv; @@ -98,10 +78,9 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, { struct virtio_gpu_vbuffer *vbuf; - vbuf = kmem_cache_alloc(vgdev->vbufs, GFP_KERNEL); + vbuf = kmem_cache_zalloc(vgdev->vbufs, GFP_KERNEL); if (!vbuf) return ERR_PTR(-ENOMEM); - memset(vbuf, 0, VBUFFER_SIZE); BUG_ON(size > MAX_INLINE_CMD_SIZE); vbuf->buf = (void *)vbuf + sizeof(*vbuf); @@ -388,7 +367,7 @@ retry: /* create a basic resource */ void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, - uint32_t resource_id, + struct virtio_gpu_object *bo, uint32_t format, uint32_t width, uint32_t height) @@ -400,12 +379,13 @@ void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, memset(cmd_p, 0, sizeof(*cmd_p)); cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D); - cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->resource_id = cpu_to_le32(bo->hw_res_handle); cmd_p->format = cpu_to_le32(format); cmd_p->width = cpu_to_le32(width); cmd_p->height = cpu_to_le32(height); virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); + bo->created = true; } void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, @@ -772,6 +752,7 @@ void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev, void virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *bo, struct virtio_gpu_resource_create_3d *rc_3d, struct virtio_gpu_fence **fence) { @@ -786,6 +767,7 @@ virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev, cmd_p->hdr.flags = 0; virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + bo->created = true; } void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev, @@ -861,7 +843,6 @@ void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev, int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *obj, - uint32_t resource_id, struct virtio_gpu_fence **fence) { bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev); @@ -869,6 +850,9 @@ int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, struct scatterlist *sg; int si, nents; + if (!obj->created) + return 0; + if (!obj->pages) { int ret; @@ -902,10 +886,9 @@ int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, ents[si].padding = 0; } - virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id, + virtio_gpu_cmd_resource_attach_backing(vgdev, obj->hw_res_handle, ents, nents, fence); - obj->hw_res_handle = resource_id; return 0; } diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 07cfde1b4132..a3d57e0f5ee5 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -108,17 +108,18 @@ static int __init vkms_init(void) if (!vkms_device) return -ENOMEM; - ret = drm_dev_init(&vkms_device->drm, &vkms_driver, NULL); - if (ret) - goto out_free; - vkms_device->platform = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); if (IS_ERR(vkms_device->platform)) { ret = PTR_ERR(vkms_device->platform); - goto out_fini; + goto out_free; } + ret = drm_dev_init(&vkms_device->drm, &vkms_driver, + &vkms_device->platform->dev); + if (ret) + goto out_unregister; + vkms_device->drm.irq_enabled = true; ret = drm_vblank_init(&vkms_device->drm, 1); @@ -129,20 +130,20 @@ static int __init vkms_init(void) ret = vkms_modeset_init(vkms_device); if (ret) - goto out_unregister; + goto out_fini; ret = drm_dev_register(&vkms_device->drm, 0); if (ret) - goto out_unregister; + goto out_fini; return 0; -out_unregister: - platform_device_unregister(vkms_device->platform); - out_fini: drm_dev_fini(&vkms_device->drm); +out_unregister: + platform_device_unregister(vkms_device->platform); + out_free: kfree(vkms_device); return ret; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index dca04d4246ea..e6b11f6ae2e4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -493,24 +493,24 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *new_state) { int ret = 0; + struct drm_crtc_state *crtc_state = NULL; struct vmw_surface *surface = NULL; struct drm_framebuffer *fb = new_state->fb; - struct drm_rect src = drm_plane_state_src(new_state); - struct drm_rect dest = drm_plane_state_dest(new_state); + if (new_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(new_state->state, + new_state->crtc); - /* Turning off */ - if (!fb) + ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); + if (ret) return ret; - ret = drm_plane_helper_check_update(plane, new_state->crtc, fb, - &src, &dest, - DRM_MODE_ROTATE_0, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true, &new_state->visible); - if (!ret) - return ret; + /* Turning off */ + if (!fb) + return 0; /* A lot of the code assumes this */ if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 723578117191..4b5378495eea 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -274,7 +274,6 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = { static const struct drm_connector_helper_funcs vmw_ldu_connector_helper_funcs = { - .best_encoder = drm_atomic_helper_best_encoder, }; /* diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 53316b1bda3d..333418dc259f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -389,7 +389,6 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = { static const struct drm_connector_helper_funcs vmw_sou_connector_helper_funcs = { - .best_encoder = drm_atomic_helper_best_encoder, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index e086565c1da6..c3e435f444c1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1054,7 +1054,6 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { static const struct drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { - .best_encoder = drm_atomic_helper_best_encoder, }; diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c index 11ef17c2d1c1..f5ea32ae8600 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.c +++ b/drivers/gpu/drm/zte/zx_drm_drv.c @@ -114,7 +114,7 @@ out_unbind: component_unbind_all(dev, drm); out_unregister: dev_set_drvdata(dev, NULL); - drm_dev_unref(drm); + drm_dev_put(drm); return ret; } @@ -124,10 +124,11 @@ static void zx_drm_unbind(struct device *dev) drm_dev_unregister(drm); drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); drm_mode_config_cleanup(drm); component_unbind_all(dev, drm); dev_set_drvdata(dev, NULL); - drm_dev_unref(drm); + drm_dev_put(drm); } static const struct component_master_ops zx_drm_master_ops = { diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index ae8c53b4b261..83d236fd893c 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -446,7 +446,6 @@ static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = { static void zx_plane_destroy(struct drm_plane *plane) { - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); } diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index c61b04555779..dc8e039bfab5 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -676,7 +676,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) vga_arbiter_check_bridge_sharing(vgadev); /* Add to the list */ - list_add(&vgadev->list, &vga_list); + list_add_tail(&vgadev->list, &vga_list); vga_count++; vgaarb_info(&pdev->dev, "VGA device added: decodes=%s,owns=%s,locks=%s\n", vga_iostate_to_str(vgadev->decodes), @@ -1408,6 +1408,18 @@ static void __init vga_arb_select_default_device(void) struct vga_device *vgadev; #if defined(CONFIG_X86) || defined(CONFIG_IA64) + u64 base = screen_info.lfb_base; + u64 size = screen_info.lfb_size; + u64 limit; + resource_size_t start, end; + unsigned long flags; + int i; + + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) + base |= (u64)screen_info.ext_lfb_base << 32; + + limit = base + size; + list_for_each_entry(vgadev, &vga_list, list) { struct device *dev = &vgadev->pdev->dev; /* @@ -1418,11 +1430,6 @@ static void __init vga_arb_select_default_device(void) * Select the device owning the boot framebuffer if there is * one. */ - resource_size_t start, end, limit; - unsigned long flags; - int i; - - limit = screen_info.lfb_base + screen_info.lfb_size; /* Does firmware framebuffer belong to us? */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { @@ -1437,7 +1444,7 @@ static void __init vga_arb_select_default_device(void) if (!start || !end) continue; - if (screen_info.lfb_base < start || limit >= end) + if (base < start || limit >= end) continue; if (!vga_default_device()) diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index f3899cc84e27..4551bca3c127 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -550,7 +550,7 @@ static void log_infoframe(struct v4l2_subdev *sd, const struct adv7511_cfg_read_ buffer[3] = 0; buffer[3] = hdmi_infoframe_checksum(buffer, len + 4); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); return; } diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 9eb7c70a7712..9f99ef38bcca 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2420,7 +2420,7 @@ static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, buffer[i + 3] = infoframe_read(sd, adv76xx_cri[index].payload_addr + i); - if (hdmi_infoframe_unpack(frame, buffer) < 0) { + if (hdmi_infoframe_unpack(frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, adv76xx_cri[index].desc); return -ENOENT; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 4721d49dcf0f..0e6384f2016a 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2574,7 +2574,7 @@ static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infofr for (i = 0; i < len; i++) buffer[i + 3] = infoframe_read(sd, cri->payload_addr + i); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); return; } diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index ca5d92942820..a38e54b8f687 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -444,7 +444,7 @@ static void print_avi_infoframe(struct v4l2_subdev *sd) i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); return; } diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index c4c2a6134e1e..e8613e364403 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1253,7 +1253,7 @@ tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr) /* read data */ len = io_readn(sd, addr, sizeof(buffer), buffer); - err = hdmi_infoframe_unpack(&frame, buffer); + err = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)); if (err) { v4l_err(state->client, "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", @@ -1928,7 +1928,7 @@ static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr) /* read data */ len = io_readn(sd, addr, sizeof(buffer), buffer); v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len); - err = hdmi_infoframe_unpack(&frame, buffer); + err = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)); if (err) { v4l_err(state->client, "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 8a3e8f61b991..799ae49774f5 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -31,7 +31,7 @@ #define hdmi_log(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__) -static u8 hdmi_infoframe_checksum(u8 *ptr, size_t size) +static u8 hdmi_infoframe_checksum(const u8 *ptr, size_t size) { u8 csum = 0; size_t i; @@ -68,8 +68,36 @@ int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) } EXPORT_SYMBOL(hdmi_avi_infoframe_init); +static int hdmi_avi_infoframe_check_only(const struct hdmi_avi_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_AVI || + frame->version != 2 || + frame->length != HDMI_AVI_INFOFRAME_SIZE) + return -EINVAL; + + if (frame->picture_aspect > HDMI_PICTURE_ASPECT_16_9) + return -EINVAL; + + return 0; +} + /** - * hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer + * hdmi_avi_infoframe_check() - check a HDMI AVI infoframe + * @frame: HDMI AVI infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_avi_infoframe_check(struct hdmi_avi_infoframe *frame) +{ + return hdmi_avi_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_avi_infoframe_check); + +/** + * hdmi_avi_infoframe_pack_only() - write HDMI AVI infoframe to binary buffer * @frame: HDMI AVI infoframe * @buffer: destination buffer * @size: size of buffer @@ -82,20 +110,22 @@ EXPORT_SYMBOL(hdmi_avi_infoframe_init); * Returns the number of bytes packed into the binary buffer or a negative * error code on failure. */ -ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, - size_t size) +ssize_t hdmi_avi_infoframe_pack_only(const struct hdmi_avi_infoframe *frame, + void *buffer, size_t size) { u8 *ptr = buffer; size_t length; + int ret; + + ret = hdmi_avi_infoframe_check_only(frame); + if (ret) + return ret; length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; if (size < length) return -ENOSPC; - if (frame->picture_aspect > HDMI_PICTURE_ASPECT_16_9) - return -EINVAL; - memset(buffer, 0, size); ptr[0] = frame->type; @@ -152,6 +182,36 @@ ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, return length; } +EXPORT_SYMBOL(hdmi_avi_infoframe_pack_only); + +/** + * hdmi_avi_infoframe_pack() - check a HDMI AVI infoframe, + * and write it to binary buffer + * @frame: HDMI AVI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_avi_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_avi_infoframe_pack_only(frame, buffer, size); +} EXPORT_SYMBOL(hdmi_avi_infoframe_pack); /** @@ -178,8 +238,33 @@ int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, } EXPORT_SYMBOL(hdmi_spd_infoframe_init); +static int hdmi_spd_infoframe_check_only(const struct hdmi_spd_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_SPD || + frame->version != 1 || + frame->length != HDMI_SPD_INFOFRAME_SIZE) + return -EINVAL; + + return 0; +} + /** - * hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer + * hdmi_spd_infoframe_check() - check a HDMI SPD infoframe + * @frame: HDMI SPD infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_spd_infoframe_check(struct hdmi_spd_infoframe *frame) +{ + return hdmi_spd_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_spd_infoframe_check); + +/** + * hdmi_spd_infoframe_pack_only() - write HDMI SPD infoframe to binary buffer * @frame: HDMI SPD infoframe * @buffer: destination buffer * @size: size of buffer @@ -192,11 +277,16 @@ EXPORT_SYMBOL(hdmi_spd_infoframe_init); * Returns the number of bytes packed into the binary buffer or a negative * error code on failure. */ -ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, - size_t size) +ssize_t hdmi_spd_infoframe_pack_only(const struct hdmi_spd_infoframe *frame, + void *buffer, size_t size) { u8 *ptr = buffer; size_t length; + int ret; + + ret = hdmi_spd_infoframe_check_only(frame); + if (ret) + return ret; length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; @@ -222,6 +312,36 @@ ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, return length; } +EXPORT_SYMBOL(hdmi_spd_infoframe_pack_only); + +/** + * hdmi_spd_infoframe_pack() - check a HDMI SPD infoframe, + * and write it to binary buffer + * @frame: HDMI SPD infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_spd_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_spd_infoframe_pack_only(frame, buffer, size); +} EXPORT_SYMBOL(hdmi_spd_infoframe_pack); /** @@ -242,8 +362,33 @@ int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame) } EXPORT_SYMBOL(hdmi_audio_infoframe_init); +static int hdmi_audio_infoframe_check_only(const struct hdmi_audio_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_AUDIO || + frame->version != 1 || + frame->length != HDMI_AUDIO_INFOFRAME_SIZE) + return -EINVAL; + + return 0; +} + /** - * hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer + * hdmi_audio_infoframe_check() - check a HDMI audio infoframe + * @frame: HDMI audio infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame) +{ + return hdmi_audio_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_audio_infoframe_check); + +/** + * hdmi_audio_infoframe_pack_only() - write HDMI audio infoframe to binary buffer * @frame: HDMI audio infoframe * @buffer: destination buffer * @size: size of buffer @@ -256,12 +401,17 @@ EXPORT_SYMBOL(hdmi_audio_infoframe_init); * Returns the number of bytes packed into the binary buffer or a negative * error code on failure. */ -ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, - void *buffer, size_t size) +ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame, + void *buffer, size_t size) { unsigned char channels; u8 *ptr = buffer; size_t length; + int ret; + + ret = hdmi_audio_infoframe_check_only(frame); + if (ret) + return ret; length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; @@ -297,6 +447,36 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, return length; } +EXPORT_SYMBOL(hdmi_audio_infoframe_pack_only); + +/** + * hdmi_audio_infoframe_pack() - check a HDMI Audio infoframe, + * and write it to binary buffer + * @frame: HDMI Audio infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_audio_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_audio_infoframe_pack_only(frame, buffer, size); +} EXPORT_SYMBOL(hdmi_audio_infoframe_pack); /** @@ -319,6 +499,7 @@ int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame) * value */ frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID; + frame->length = 4; return 0; } @@ -335,8 +516,42 @@ static int hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *fram return 4; } +static int hdmi_vendor_infoframe_check_only(const struct hdmi_vendor_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_VENDOR || + frame->version != 1 || + frame->oui != HDMI_IEEE_OUI) + return -EINVAL; + + /* only one of those can be supplied */ + if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) + return -EINVAL; + + if (frame->length != hdmi_vendor_infoframe_length(frame)) + return -EINVAL; + + return 0; +} + +/** + * hdmi_vendor_infoframe_check() - check a HDMI vendor infoframe + * @frame: HDMI infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_vendor_infoframe_check(struct hdmi_vendor_infoframe *frame) +{ + frame->length = hdmi_vendor_infoframe_length(frame); + + return hdmi_vendor_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_vendor_infoframe_check); + /** - * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary buffer + * hdmi_vendor_infoframe_pack_only() - write a HDMI vendor infoframe to binary buffer * @frame: HDMI infoframe * @buffer: destination buffer * @size: size of buffer @@ -349,17 +564,16 @@ static int hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *fram * Returns the number of bytes packed into the binary buffer or a negative * error code on failure. */ -ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, - void *buffer, size_t size) +ssize_t hdmi_vendor_infoframe_pack_only(const struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size) { u8 *ptr = buffer; size_t length; + int ret; - /* only one of those can be supplied */ - if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) - return -EINVAL; - - frame->length = hdmi_vendor_infoframe_length(frame); + ret = hdmi_vendor_infoframe_check_only(frame); + if (ret) + return ret; length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; @@ -394,24 +608,134 @@ ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, return length; } +EXPORT_SYMBOL(hdmi_vendor_infoframe_pack_only); + +/** + * hdmi_vendor_infoframe_pack() - check a HDMI Vendor infoframe, + * and write it to binary buffer + * @frame: HDMI Vendor infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_vendor_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_vendor_infoframe_pack_only(frame, buffer, size); +} EXPORT_SYMBOL(hdmi_vendor_infoframe_pack); +static int +hdmi_vendor_any_infoframe_check_only(const union hdmi_vendor_any_infoframe *frame) +{ + if (frame->any.type != HDMI_INFOFRAME_TYPE_VENDOR || + frame->any.version != 1) + return -EINVAL; + + return 0; +} + +/* + * hdmi_vendor_any_infoframe_check() - check a vendor infoframe + */ +static int +hdmi_vendor_any_infoframe_check(union hdmi_vendor_any_infoframe *frame) +{ + int ret; + + ret = hdmi_vendor_any_infoframe_check_only(frame); + if (ret) + return ret; + + /* we only know about HDMI vendor infoframes */ + if (frame->any.oui != HDMI_IEEE_OUI) + return -EINVAL; + + return hdmi_vendor_infoframe_check(&frame->hdmi); +} + /* - * hdmi_vendor_any_infoframe_pack() - write a vendor infoframe to binary buffer + * hdmi_vendor_any_infoframe_pack_only() - write a vendor infoframe to binary buffer */ static ssize_t -hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame, - void *buffer, size_t size) +hdmi_vendor_any_infoframe_pack_only(const union hdmi_vendor_any_infoframe *frame, + void *buffer, size_t size) { + int ret; + + ret = hdmi_vendor_any_infoframe_check_only(frame); + if (ret) + return ret; + /* we only know about HDMI vendor infoframes */ if (frame->any.oui != HDMI_IEEE_OUI) return -EINVAL; - return hdmi_vendor_infoframe_pack(&frame->hdmi, buffer, size); + return hdmi_vendor_infoframe_pack_only(&frame->hdmi, buffer, size); +} + +/* + * hdmi_vendor_any_infoframe_pack() - check a vendor infoframe, + * and write it to binary buffer + */ +static ssize_t +hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_vendor_any_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_vendor_any_infoframe_pack_only(frame, buffer, size); } /** - * hdmi_infoframe_pack() - write a HDMI infoframe to binary buffer + * hdmi_infoframe_check() - check a HDMI infoframe + * @frame: HDMI infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int +hdmi_infoframe_check(union hdmi_infoframe *frame) +{ + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + return hdmi_avi_infoframe_check(&frame->avi); + case HDMI_INFOFRAME_TYPE_SPD: + return hdmi_spd_infoframe_check(&frame->spd); + case HDMI_INFOFRAME_TYPE_AUDIO: + return hdmi_audio_infoframe_check(&frame->audio); + case HDMI_INFOFRAME_TYPE_VENDOR: + return hdmi_vendor_any_infoframe_check(&frame->vendor); + default: + WARN(1, "Bad infoframe type %d\n", frame->any.type); + return -EINVAL; + } +} +EXPORT_SYMBOL(hdmi_infoframe_check); + +/** + * hdmi_infoframe_pack_only() - write a HDMI infoframe to binary buffer * @frame: HDMI infoframe * @buffer: destination buffer * @size: size of buffer @@ -425,7 +749,56 @@ hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame, * error code on failure. */ ssize_t -hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size) +hdmi_infoframe_pack_only(const union hdmi_infoframe *frame, void *buffer, size_t size) +{ + ssize_t length; + + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + length = hdmi_avi_infoframe_pack_only(&frame->avi, + buffer, size); + break; + case HDMI_INFOFRAME_TYPE_SPD: + length = hdmi_spd_infoframe_pack_only(&frame->spd, + buffer, size); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + length = hdmi_audio_infoframe_pack_only(&frame->audio, + buffer, size); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + length = hdmi_vendor_any_infoframe_pack_only(&frame->vendor, + buffer, size); + break; + default: + WARN(1, "Bad infoframe type %d\n", frame->any.type); + length = -EINVAL; + } + + return length; +} +EXPORT_SYMBOL(hdmi_infoframe_pack_only); + +/** + * hdmi_infoframe_pack() - check a HDMI infoframe, + * and write it to binary buffer + * @frame: HDMI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t +hdmi_infoframe_pack(union hdmi_infoframe *frame, + void *buffer, size_t size) { ssize_t length; @@ -471,7 +844,7 @@ static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type) static void hdmi_infoframe_log_header(const char *level, struct device *dev, - struct hdmi_any_infoframe *frame) + const struct hdmi_any_infoframe *frame) { hdmi_log("HDMI infoframe: %s, version %u, length %u\n", hdmi_infoframe_type_get_name(frame->type), @@ -673,10 +1046,10 @@ hdmi_content_type_get_name(enum hdmi_content_type content_type) */ static void hdmi_avi_infoframe_log(const char *level, struct device *dev, - struct hdmi_avi_infoframe *frame) + const struct hdmi_avi_infoframe *frame) { hdmi_infoframe_log_header(level, dev, - (struct hdmi_any_infoframe *)frame); + (const struct hdmi_any_infoframe *)frame); hdmi_log(" colorspace: %s\n", hdmi_colorspace_get_name(frame->colorspace)); @@ -750,12 +1123,12 @@ static const char *hdmi_spd_sdi_get_name(enum hdmi_spd_sdi sdi) */ static void hdmi_spd_infoframe_log(const char *level, struct device *dev, - struct hdmi_spd_infoframe *frame) + const struct hdmi_spd_infoframe *frame) { u8 buf[17]; hdmi_infoframe_log_header(level, dev, - (struct hdmi_any_infoframe *)frame); + (const struct hdmi_any_infoframe *)frame); memset(buf, 0, sizeof(buf)); @@ -886,10 +1259,10 @@ hdmi_audio_coding_type_ext_get_name(enum hdmi_audio_coding_type_ext ctx) */ static void hdmi_audio_infoframe_log(const char *level, struct device *dev, - struct hdmi_audio_infoframe *frame) + const struct hdmi_audio_infoframe *frame) { hdmi_infoframe_log_header(level, dev, - (struct hdmi_any_infoframe *)frame); + (const struct hdmi_any_infoframe *)frame); if (frame->channels) hdmi_log(" channels: %u\n", frame->channels - 1); @@ -949,12 +1322,12 @@ hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct) static void hdmi_vendor_any_infoframe_log(const char *level, struct device *dev, - union hdmi_vendor_any_infoframe *frame) + const union hdmi_vendor_any_infoframe *frame) { - struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + const struct hdmi_vendor_infoframe *hvf = &frame->hdmi; hdmi_infoframe_log_header(level, dev, - (struct hdmi_any_infoframe *)frame); + (const struct hdmi_any_infoframe *)frame); if (frame->any.oui != HDMI_IEEE_OUI) { hdmi_log(" not a HDMI vendor infoframe\n"); @@ -984,7 +1357,7 @@ hdmi_vendor_any_infoframe_log(const char *level, */ void hdmi_infoframe_log(const char *level, struct device *dev, - union hdmi_infoframe *frame) + const union hdmi_infoframe *frame) { switch (frame->any.type) { case HDMI_INFOFRAME_TYPE_AVI: @@ -1005,8 +1378,9 @@ EXPORT_SYMBOL(hdmi_infoframe_log); /** * hdmi_avi_infoframe_unpack() - unpack binary buffer to a HDMI AVI infoframe - * @buffer: source buffer * @frame: HDMI AVI infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary @buffer into a structured * @frame of the HDMI Auxiliary Video (AVI) information frame. @@ -1016,11 +1390,14 @@ EXPORT_SYMBOL(hdmi_infoframe_log); * Returns 0 on success or a negative error code on failure. */ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, - void *buffer) + const void *buffer, size_t size) { - u8 *ptr = buffer; + const u8 *ptr = buffer; int ret; + if (size < HDMI_INFOFRAME_SIZE(AVI)) + return -EINVAL; + if (ptr[0] != HDMI_INFOFRAME_TYPE_AVI || ptr[1] != 2 || ptr[2] != HDMI_AVI_INFOFRAME_SIZE) @@ -1068,8 +1445,9 @@ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, /** * hdmi_spd_infoframe_unpack() - unpack binary buffer to a HDMI SPD infoframe - * @buffer: source buffer * @frame: HDMI SPD infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary @buffer into a structured * @frame of the HDMI Source Product Description (SPD) information frame. @@ -1079,11 +1457,14 @@ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, * Returns 0 on success or a negative error code on failure. */ static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, - void *buffer) + const void *buffer, size_t size) { - u8 *ptr = buffer; + const u8 *ptr = buffer; int ret; + if (size < HDMI_INFOFRAME_SIZE(SPD)) + return -EINVAL; + if (ptr[0] != HDMI_INFOFRAME_TYPE_SPD || ptr[1] != 1 || ptr[2] != HDMI_SPD_INFOFRAME_SIZE) { @@ -1106,8 +1487,9 @@ static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, /** * hdmi_audio_infoframe_unpack() - unpack binary buffer to a HDMI AUDIO infoframe - * @buffer: source buffer * @frame: HDMI Audio infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary @buffer into a structured * @frame of the HDMI Audio information frame. @@ -1117,11 +1499,14 @@ static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, * Returns 0 on success or a negative error code on failure. */ static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, - void *buffer) + const void *buffer, size_t size) { - u8 *ptr = buffer; + const u8 *ptr = buffer; int ret; + if (size < HDMI_INFOFRAME_SIZE(AUDIO)) + return -EINVAL; + if (ptr[0] != HDMI_INFOFRAME_TYPE_AUDIO || ptr[1] != 1 || ptr[2] != HDMI_AUDIO_INFOFRAME_SIZE) { @@ -1151,8 +1536,9 @@ static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, /** * hdmi_vendor_infoframe_unpack() - unpack binary buffer to a HDMI vendor infoframe - * @buffer: source buffer * @frame: HDMI Vendor infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary @buffer into a structured * @frame of the HDMI Vendor information frame. @@ -1163,14 +1549,17 @@ static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, */ static int hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, - void *buffer) + const void *buffer, size_t size) { - u8 *ptr = buffer; + const u8 *ptr = buffer; size_t length; int ret; u8 hdmi_video_format; struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + if (size < HDMI_INFOFRAME_HEADER_SIZE) + return -EINVAL; + if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR || ptr[1] != 1 || (ptr[2] != 4 && ptr[2] != 5 && ptr[2] != 6)) @@ -1178,6 +1567,9 @@ hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, length = ptr[2]; + if (size < HDMI_INFOFRAME_HEADER_SIZE + length) + return -EINVAL; + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_HEADER_SIZE + length) != 0) return -EINVAL; @@ -1224,8 +1616,9 @@ hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, /** * hdmi_infoframe_unpack() - unpack binary buffer to a HDMI infoframe - * @buffer: source buffer * @frame: HDMI infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary buffer @buffer into a structured * @frame of a HDMI infoframe. @@ -1234,23 +1627,27 @@ hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, * * Returns 0 on success or a negative error code on failure. */ -int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer) +int hdmi_infoframe_unpack(union hdmi_infoframe *frame, + const void *buffer, size_t size) { int ret; - u8 *ptr = buffer; + const u8 *ptr = buffer; + + if (size < HDMI_INFOFRAME_HEADER_SIZE) + return -EINVAL; switch (ptr[0]) { case HDMI_INFOFRAME_TYPE_AVI: - ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer); + ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer, size); break; case HDMI_INFOFRAME_TYPE_SPD: - ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer); + ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer, size); break; case HDMI_INFOFRAME_TYPE_AUDIO: - ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer); + ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer, size); break; case HDMI_INFOFRAME_TYPE_VENDOR: - ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer); + ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer, size); break; default: ret = -EINVAL; diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index ccb5aa8468e0..9c56412bb2cf 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -133,6 +133,7 @@ struct dw_hdmi_plat_data { const struct dw_hdmi_phy_ops *phy_ops; const char *phy_name; void *phy_data; + unsigned int phy_force_vendor; /* Synopsys PHY support */ const struct dw_hdmi_mpll_config *mpll_cfg; diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h index d9c6d549f971..48a671e782ca 100644 --- a/include/drm/bridge/dw_mipi_dsi.h +++ b/include/drm/bridge/dw_mipi_dsi.h @@ -19,6 +19,13 @@ struct dw_mipi_dsi_phy_ops { unsigned int *lane_mbps); }; +struct dw_mipi_dsi_host_ops { + int (*attach)(void *priv_data, + struct mipi_dsi_device *dsi); + int (*detach)(void *priv_data, + struct mipi_dsi_device *dsi); +}; + struct dw_mipi_dsi_plat_data { void __iomem *base; unsigned int max_data_lanes; @@ -27,6 +34,7 @@ struct dw_mipi_dsi_plat_data { const struct drm_display_mode *mode); const struct dw_mipi_dsi_phy_ops *phy_ops; + const struct dw_mipi_dsi_host_ops *host_ops; void *priv_data; }; @@ -35,10 +43,8 @@ struct dw_mipi_dsi *dw_mipi_dsi_probe(struct platform_device *pdev, const struct dw_mipi_dsi_plat_data *plat_data); void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi); -struct dw_mipi_dsi *dw_mipi_dsi_bind(struct platform_device *pdev, - struct drm_encoder *encoder, - const struct dw_mipi_dsi_plat_data - *plat_data); +int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder); void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi); +void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave); #endif /* __DW_MIPI_DSI__ */ diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 05350424a4d3..514beb2d483a 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -110,4 +110,10 @@ static inline bool drm_can_sleep(void) return true; } +#if defined(CONFIG_DRM_DEBUG_SELFTEST_MODULE) +#define EXPORT_SYMBOL_FOR_TESTS_ONLY(x) EXPORT_SYMBOL(x) +#else +#define EXPORT_SYMBOL_FOR_TESTS_ONLY(x) +#endif + #endif diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 1e810e0b7664..f9b35834c45d 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -265,7 +265,6 @@ struct __drm_private_objs_state { * struct drm_atomic_state - the global state object for atomic updates * @ref: count of all references to this state (will not be freed until zero) * @dev: parent DRM device - * @allow_modeset: allow full modeset * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics * @async_update: hint for asynchronous plane update * @planes: pointer to array of structures with per-plane data @@ -284,6 +283,15 @@ struct drm_atomic_state { struct kref ref; struct drm_device *dev; + + /** + * @allow_modeset: + * + * Allow full modeset. This is used by the ATOMIC IOCTL handler to + * implement the DRM_MODE_ATOMIC_ALLOW_MODESET flag. Drivers should + * never consult this flag, instead looking at the output of + * drm_atomic_crtc_needs_modeset(). + */ bool allow_modeset : 1; bool legacy_cursor_update : 1; bool async_update : 1; diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 657af7b39379..25ca0097563e 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -31,6 +31,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_modeset_helper.h> +#include <drm/drm_atomic_state_helper.h> #include <drm/drm_util.h> struct drm_atomic_state; @@ -144,51 +145,6 @@ int drm_atomic_helper_page_flip_target( uint32_t flags, uint32_t target, struct drm_modeset_acquire_ctx *ctx); -struct drm_encoder * -drm_atomic_helper_best_encoder(struct drm_connector *connector); - -/* default implementations for state handling */ -void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); -void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, - struct drm_crtc_state *state); -struct drm_crtc_state * -drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc); -void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state); -void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, - struct drm_crtc_state *state); - -void __drm_atomic_helper_plane_reset(struct drm_plane *plane, - struct drm_plane_state *state); -void drm_atomic_helper_plane_reset(struct drm_plane *plane); -void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, - struct drm_plane_state *state); -struct drm_plane_state * -drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane); -void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state); -void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state); - -void __drm_atomic_helper_connector_reset(struct drm_connector *connector, - struct drm_connector_state *conn_state); -void drm_atomic_helper_connector_reset(struct drm_connector *connector); -void -__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, - struct drm_connector_state *state); -struct drm_connector_state * -drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector); -struct drm_atomic_state * -drm_atomic_helper_duplicate_state(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx); -void -__drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state); -void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, - struct drm_connector_state *state); -int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, - u16 *red, u16 *green, u16 *blue, - uint32_t size, - struct drm_modeset_acquire_ctx *ctx); -void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, - struct drm_private_state *state); /** * drm_atomic_crtc_for_each_plane - iterate over planes currently attached to CRTC diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h new file mode 100644 index 000000000000..5b82ccfdb502 --- /dev/null +++ b/include/drm/drm_atomic_state_helper.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 Intel Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rob Clark <robdclark@gmail.com> + * Daniel Vetter <daniel.vetter@ffwll.ch> + */ + +#include <linux/types.h> + +struct drm_crtc; +struct drm_crtc_state; +struct drm_plane; +struct drm_plane_state; +struct drm_connector; +struct drm_connector_state; +struct drm_private_obj; +struct drm_private_state; +struct drm_modeset_acquire_ctx; +struct drm_device; + +void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); +void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +struct drm_crtc_state * +drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc); +void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state); +void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); + +void __drm_atomic_helper_plane_reset(struct drm_plane *plane, + struct drm_plane_state *state); +void drm_atomic_helper_plane_reset(struct drm_plane *plane); +void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, + struct drm_plane_state *state); +struct drm_plane_state * +drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane); +void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state); +void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state); + +void __drm_atomic_helper_connector_reset(struct drm_connector *connector, + struct drm_connector_state *conn_state); +void drm_atomic_helper_connector_reset(struct drm_connector *connector); +void +__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, + struct drm_connector_state *state); +struct drm_connector_state * +drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector); +struct drm_atomic_state * +drm_atomic_helper_duplicate_state(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx); +void +__drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state); +void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state); +int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, + uint32_t size, + struct drm_modeset_acquire_ctx *ctx); +void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, + struct drm_private_state *state); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 9ccad6b062f2..dd0552cb7472 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1133,6 +1133,7 @@ int drm_connector_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, int connector_type); +void drm_connector_attach_edid_property(struct drm_connector *connector); int drm_connector_register(struct drm_connector *connector); void drm_connector_unregister(struct drm_connector *connector); int drm_connector_attach_encoder(struct drm_connector *connector, diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 6914633037a5..d65f034843ce 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -57,12 +57,6 @@ int drm_helper_connector_dpms(struct drm_connector *connector, int mode); void drm_helper_resume_force_mode(struct drm_device *dev); -int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb); -int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb); - /* drm_probe_helper.c */ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 2a3843f248cf..9ad98e8d9ede 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -685,6 +685,8 @@ # define DP_EDP_12 0x01 # define DP_EDP_13 0x02 # define DP_EDP_14 0x03 +# define DP_EDP_14a 0x04 /* eDP 1.4a */ +# define DP_EDP_14b 0x05 /* eDP 1.4b */ #define DP_EDP_GENERAL_CAP_1 0x701 # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0) diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 7f78d26a0766..59f005b419cf 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -409,7 +409,6 @@ struct drm_dp_payload { struct drm_dp_mst_topology_state { struct drm_private_state base; int avail_slots; - struct drm_atomic_state *state; struct drm_dp_mst_topology_mgr *mgr; }; @@ -498,11 +497,6 @@ struct drm_dp_mst_topology_mgr { int pbn_div; /** - * @state: State information for topology manager - */ - struct drm_dp_mst_topology_state *state; - - /** * @funcs: Atomic helper callbacks */ const struct drm_private_state_funcs *funcs; diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index 4a65f0d155b0..8dbbe1eece1b 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h @@ -26,8 +26,6 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma); void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma); void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma); -void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma *fbdev_cma, - bool state); struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, unsigned int plane); diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 26485acc51d7..84ac79219e4c 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -164,14 +164,14 @@ struct drm_file { * See also the :ref:`section on primary nodes and authentication * <drm_primary_node>`. */ - unsigned authenticated :1; + bool authenticated; /** * @stereo_allowed: * * True when the client has asked us to expose stereo 3D mode flags. */ - unsigned stereo_allowed :1; + bool stereo_allowed; /** * @universal_planes: @@ -179,10 +179,10 @@ struct drm_file { * True if client understands CRTC primary planes and cursor planes * in the plane list. Automatically set when @atomic is set. */ - unsigned universal_planes:1; + bool universal_planes; /** @atomic: True if client understands atomic properties. */ - unsigned atomic:1; + bool atomic; /** * @aspect_ratio_allowed: @@ -190,14 +190,14 @@ struct drm_file { * True, if client can handle picture aspect ratios, and has requested * to pass this information along with the mode. */ - unsigned aspect_ratio_allowed:1; + bool aspect_ratio_allowed; /** * @writeback_connectors: * * True if client understands writeback connectors */ - unsigned writeback_connectors:1; + bool writeback_connectors; /** * @is_master: @@ -208,7 +208,7 @@ struct drm_file { * See also the :ref:`section on primary nodes and authentication * <drm_primary_node>`. */ - unsigned is_master:1; + bool is_master; /** * @master: diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index 865ef60c17af..bcb389f04618 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -52,25 +52,86 @@ struct drm_mode_fb_cmd2; /** * struct drm_format_info - information about a DRM format - * @format: 4CC format identifier (DRM_FORMAT_*) - * @depth: Color depth (number of bits per pixel excluding padding bits), - * valid for a subset of RGB formats only. This is a legacy field, do not - * use in new code and set to 0 for new formats. - * @num_planes: Number of color planes (1 to 3) - * @cpp: Number of bytes per pixel (per plane) - * @hsub: Horizontal chroma subsampling factor - * @vsub: Vertical chroma subsampling factor - * @has_alpha: Does the format embeds an alpha component? - * @is_yuv: Is it a YUV format? */ struct drm_format_info { + /** @format: 4CC format identifier (DRM_FORMAT_*) */ u32 format; + + /** + * @depth: + * + * Color depth (number of bits per pixel excluding padding bits), + * valid for a subset of RGB formats only. This is a legacy field, do + * not use in new code and set to 0 for new formats. + */ u8 depth; + + /** @num_planes: Number of color planes (1 to 3) */ u8 num_planes; - u8 cpp[3]; + + union { + /** + * @cpp: + * + * Number of bytes per pixel (per plane), this is aliased with + * @char_per_block. It is deprecated in favour of using the + * triplet @char_per_block, @block_w, @block_h for better + * describing the pixel format. + */ + u8 cpp[3]; + + /** + * @char_per_block: + * + * Number of bytes per block (per plane), where blocks are + * defined as a rectangle of pixels which are stored next to + * each other in a byte aligned memory region. Together with + * @block_w and @block_h this is used to properly describe tiles + * in tiled formats or to describe groups of pixels in packed + * formats for which the memory needed for a single pixel is not + * byte aligned. + * + * @cpp has been kept for historical reasons because there are + * a lot of places in drivers where it's used. In drm core for + * generic code paths the preferred way is to use + * @char_per_block, drm_format_info_block_width() and + * drm_format_info_block_height() which allows handling both + * block and non-block formats in the same way. + * + * For formats that are intended to be used only with non-linear + * modifiers both @cpp and @char_per_block must be 0 in the + * generic format table. Drivers could supply accurate + * information from their drm_mode_config.get_format_info hook + * if they want the core to be validating the pitch. + */ + u8 char_per_block[3]; + }; + + /** + * @block_w: + * + * Block width in pixels, this is intended to be accessed through + * drm_format_info_block_width() + */ + u8 block_w[3]; + + /** + * @block_h: + * + * Block height in pixels, this is intended to be accessed through + * drm_format_info_block_height() + */ + u8 block_h[3]; + + /** @hsub: Horizontal chroma subsampling factor */ u8 hsub; + /** @vsub: Vertical chroma subsampling factor */ u8 vsub; + + /** @has_alpha: Does the format embeds an alpha component? */ bool has_alpha; + + /** @is_yuv: Is it a YUV format? */ bool is_yuv; }; @@ -96,6 +157,12 @@ int drm_format_horz_chroma_subsampling(uint32_t format); int drm_format_vert_chroma_subsampling(uint32_t format); int drm_format_plane_width(int width, uint32_t format, int plane); int drm_format_plane_height(int height, uint32_t format, int plane); +unsigned int drm_format_info_block_width(const struct drm_format_info *info, + int plane); +unsigned int drm_format_info_block_height(const struct drm_format_info *info, + int plane); +uint64_t drm_format_info_min_pitch(const struct drm_format_info *info, + int plane, unsigned int buffer_width); const char *drm_get_format_name(uint32_t format, struct drm_format_name_buf *buf); #endif /* __DRM_FOURCC_H__ */ diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 4fef19064b0f..491528f48cfb 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -168,6 +168,12 @@ struct mipi_dsi_device_info { * @format: pixel format for video mode * @lanes: number of active data lanes * @mode_flags: DSI operation mode related flags + * @hs_rate: maximum lane frequency for high speed mode in hertz, this should + * be set to the real limits of the hardware, zero is only accepted for + * legacy drivers + * @lp_rate: maximum lane frequency for low power mode in hertz, this should + * be set to the real limits of the hardware, zero is only accepted for + * legacy drivers */ struct mipi_dsi_device { struct mipi_dsi_host *host; @@ -178,6 +184,8 @@ struct mipi_dsi_device { unsigned int lanes; enum mipi_dsi_pixel_format format; unsigned long mode_flags; + unsigned long hs_rate; + unsigned long lp_rate; }; #define MIPI_DSI_MODULE_PREFIX "mipi-dsi:" diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 928e4172a0bb..5dbeabdbaf91 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -52,6 +52,12 @@ struct drm_mode_config_funcs { * requested metadata, but most of that is left to the driver. See * &struct drm_mode_fb_cmd2 for details. * + * To validate the pixel format and modifier drivers can use + * drm_any_plane_has_format() to make sure at least one plane supports + * the requested values. Note that the driver must first determine the + * actual modifier used if the request doesn't have it specified, + * ie. when (@mode_cmd->flags & DRM_MODE_FB_MODIFIERS) == 0. + * * If the parameters are deemed valid and the backing storage objects in * the underlying memory manager all exist, then the driver allocates * a new &drm_framebuffer structure, subclassed to contain @@ -809,6 +815,13 @@ struct drm_mode_config { /* dumb ioctl parameters */ uint32_t preferred_depth, prefer_shadow; + + /** + * @quirk_addfb_prefer_xbgr_30bpp: + * + * Special hack for legacy ADDFB to keep nouveau userspace happy. Should + * only ever be set by the nouveau kernel driver. + */ bool quirk_addfb_prefer_xbgr_30bpp; /** diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 0a0834bef8bd..3701f56c3362 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -798,5 +798,7 @@ static inline struct drm_plane *drm_plane_find(struct drm_device *dev, #define drm_for_each_plane(plane, dev) \ list_for_each_entry(plane, &(dev)->mode_config.plane_list, head) +bool drm_any_plane_has_format(struct drm_device *dev, + u32 format, u64 modifier); #endif diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index 26cee2934781..331ebd60b3a3 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -38,42 +38,7 @@ */ #define DRM_PLANE_HELPER_NO_SCALING (1<<16) -int drm_plane_helper_check_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_rect *src, - struct drm_rect *dest, - unsigned int rotation, - int min_scale, - int max_scale, - bool can_position, - bool can_update_disabled, - bool *visible); -int drm_primary_helper_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - struct drm_modeset_acquire_ctx *ctx); -int drm_primary_helper_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx); void drm_primary_helper_destroy(struct drm_plane *plane); extern const struct drm_plane_funcs drm_primary_helper_funcs; -int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - struct drm_modeset_acquire_ctx *ctx); -int drm_plane_helper_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx); - -/* For use by drm_crtc_helper.c */ -int drm_plane_helper_commit(struct drm_plane *plane, - struct drm_plane_state *plane_state, - struct drm_framebuffer *old_fb); #endif diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h index d716d653b096..e2032fbc0f08 100644 --- a/include/drm/drm_prime.h +++ b/include/drm/drm_prime.h @@ -93,9 +93,6 @@ void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, enum dma_data_direction dir); void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf); void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr); -void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num); -void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_num, - void *addr); int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma); int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h index 5b9efff35d6d..4a0a80d658c7 100644 --- a/include/drm/drm_property.h +++ b/include/drm/drm_property.h @@ -153,7 +153,8 @@ struct drm_property { * userspace. The kernel is allowed to update the value of these * properties. This is generally used to expose probe state to * userspace, e.g. the EDID, or the connector path property on DP - * MST sinks. + * MST sinks. Kernel can update the value of an immutable property + * by calling drm_object_property_set_value(). */ uint32_t flags; diff --git a/include/drm/drm_syncobj.h b/include/drm/drm_syncobj.h index 425432b85a87..29244cbcd05e 100644 --- a/include/drm/drm_syncobj.h +++ b/include/drm/drm_syncobj.h @@ -30,10 +30,15 @@ struct drm_syncobj_cb; +enum drm_syncobj_type { + DRM_SYNCOBJ_TYPE_BINARY, + DRM_SYNCOBJ_TYPE_TIMELINE +}; + /** * struct drm_syncobj - sync object. * - * This structure defines a generic sync object which wraps a &dma_fence. + * This structure defines a generic sync object which is timeline based. */ struct drm_syncobj { /** @@ -41,21 +46,42 @@ struct drm_syncobj { */ struct kref refcount; /** - * @fence: - * NULL or a pointer to the fence bound to this object. - * - * This field should not be used directly. Use drm_syncobj_fence_get() - * and drm_syncobj_replace_fence() instead. + * @type: indicate syncobj type + */ + enum drm_syncobj_type type; + /** + * @wq: wait signal operation work queue + */ + wait_queue_head_t wq; + /** + * @timeline_context: fence context used by timeline */ - struct dma_fence __rcu *fence; + u64 timeline_context; /** - * @cb_list: List of callbacks to call when the &fence gets replaced. + * @timeline: syncobj timeline value, which indicates point is signaled. */ + u64 timeline; + /** + * @signal_point: which indicates the latest signaler point. + */ + u64 signal_point; + /** + * @signal_pt_list: signaler point list. + */ + struct list_head signal_pt_list; + + /** + * @cb_list: List of callbacks to call when the &fence gets replaced. + */ struct list_head cb_list; /** - * @lock: Protects &cb_list and write-locks &fence. + * @pt_lock: Protects pt list. */ - spinlock_t lock; + spinlock_t pt_lock; + /** + * @cb_mutex: Protects syncobj cb list. + */ + struct mutex cb_mutex; /** * @file: A file backing for this syncobj. */ @@ -68,7 +94,7 @@ typedef void (*drm_syncobj_func_t)(struct drm_syncobj *syncobj, /** * struct drm_syncobj_cb - callback for drm_syncobj_add_callback * @node: used by drm_syncob_add_callback to append this struct to - * &drm_syncobj.cb_list + * &drm_syncobj.cb_list * @func: drm_syncobj_func_t to call * * This struct will be initialized by drm_syncobj_add_callback, additional @@ -106,35 +132,12 @@ drm_syncobj_put(struct drm_syncobj *obj) kref_put(&obj->refcount, drm_syncobj_free); } -/** - * drm_syncobj_fence_get - get a reference to a fence in a sync object - * @syncobj: sync object. - * - * This acquires additional reference to &drm_syncobj.fence contained in @obj, - * if not NULL. It is illegal to call this without already holding a reference. - * No locks required. - * - * Returns: - * Either the fence of @obj or NULL if there's none. - */ -static inline struct dma_fence * -drm_syncobj_fence_get(struct drm_syncobj *syncobj) -{ - struct dma_fence *fence; - - rcu_read_lock(); - fence = dma_fence_get_rcu_safe(&syncobj->fence); - rcu_read_unlock(); - - return fence; -} - struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, u32 handle); void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, u64 point, struct dma_fence *fence); int drm_syncobj_find_fence(struct drm_file *file_private, - u32 handle, u64 point, + u32 handle, u64 point, u64 flags, struct dma_fence **fence); void drm_syncobj_free(struct kref *kref); int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, @@ -142,5 +145,7 @@ int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, int drm_syncobj_get_handle(struct drm_file *file_private, struct drm_syncobj *syncobj, u32 *handle); int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd); +int drm_syncobj_search_fence(struct drm_syncobj *syncobj, u64 point, u64 flags, + struct dma_fence **fence); #endif diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index d25a9603ab57..6ad9630d4f48 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -95,7 +95,7 @@ struct drm_vblank_crtc { /** * @queue: Wait queue for vblank waiters. */ - wait_queue_head_t queue; /**< VBLANK wait queue */ + wait_queue_head_t queue; /** * @disable_timer: Disable timer for the delayed vblank disabling * hysteresis logic. Vblank disabling is controlled through the @@ -107,7 +107,7 @@ struct drm_vblank_crtc { /** * @seqlock: Protect vblank count and time. */ - seqlock_t seqlock; /* protects vblank count and time */ + seqlock_t seqlock; /** * @count: Current software vblank counter. @@ -123,7 +123,7 @@ struct drm_vblank_crtc { * this refcount reaches 0 can the hardware interrupt be disabled using * @disable_timer. */ - atomic_t refcount; /* number of users of vblank interruptsper crtc */ + atomic_t refcount; /** * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. */ @@ -136,7 +136,7 @@ struct drm_vblank_crtc { * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly * save and restore the vblank count. */ - unsigned int inmodeset; /* Display driver is setting mode */ + unsigned int inmodeset; /** * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this * structure. diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 4f3febc0f971..d2bacf502429 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -163,6 +163,9 @@ struct hdmi_avi_infoframe { int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame); ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, size_t size); +ssize_t hdmi_avi_infoframe_pack_only(const struct hdmi_avi_infoframe *frame, + void *buffer, size_t size); +int hdmi_avi_infoframe_check(struct hdmi_avi_infoframe *frame); enum hdmi_spd_sdi { HDMI_SPD_SDI_UNKNOWN, @@ -194,6 +197,9 @@ int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, const char *vendor, const char *product); ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, size_t size); +ssize_t hdmi_spd_infoframe_pack_only(const struct hdmi_spd_infoframe *frame, + void *buffer, size_t size); +int hdmi_spd_infoframe_check(struct hdmi_spd_infoframe *frame); enum hdmi_audio_coding_type { HDMI_AUDIO_CODING_TYPE_STREAM, @@ -272,6 +278,9 @@ struct hdmi_audio_infoframe { int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame); ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, void *buffer, size_t size); +ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame, + void *buffer, size_t size); +int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame); enum hdmi_3d_structure { HDMI_3D_STRUCTURE_INVALID = -1, @@ -299,6 +308,9 @@ struct hdmi_vendor_infoframe { int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame); ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, void *buffer, size_t size); +ssize_t hdmi_vendor_infoframe_pack_only(const struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size); +int hdmi_vendor_infoframe_check(struct hdmi_vendor_infoframe *frame); union hdmi_vendor_any_infoframe { struct { @@ -330,10 +342,14 @@ union hdmi_infoframe { struct hdmi_audio_infoframe audio; }; -ssize_t -hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size); -int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer); +ssize_t hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, + size_t size); +ssize_t hdmi_infoframe_pack_only(const union hdmi_infoframe *frame, + void *buffer, size_t size); +int hdmi_infoframe_check(union hdmi_infoframe *frame); +int hdmi_infoframe_unpack(union hdmi_infoframe *frame, + const void *buffer, size_t size); void hdmi_infoframe_log(const char *level, struct device *dev, - union hdmi_infoframe *frame); + const union hdmi_infoframe *frame); #endif /* _DRM_HDMI_H */ diff --git a/include/linux/reservation.h b/include/linux/reservation.h index 02166e815afb..2f0ffca35780 100644 --- a/include/linux/reservation.h +++ b/include/linux/reservation.h @@ -68,7 +68,6 @@ struct reservation_object_list { * @seq: sequence count for managing RCU read-side synchronization * @fence_excl: the exclusive fence, if there is one currently * @fence: list of current shared fences - * @staged: staged copy of shared fences for RCU updates */ struct reservation_object { struct ww_mutex lock; @@ -76,7 +75,6 @@ struct reservation_object { struct dma_fence __rcu *fence_excl; struct reservation_object_list __rcu *fence; - struct reservation_object_list *staged; }; #define reservation_object_held(obj) lockdep_is_held(&(obj)->lock.base) @@ -95,7 +93,6 @@ reservation_object_init(struct reservation_object *obj) __seqcount_init(&obj->seq, reservation_seqcount_string, &reservation_seqcount_class); RCU_INIT_POINTER(obj->fence, NULL); RCU_INIT_POINTER(obj->fence_excl, NULL); - obj->staged = NULL; } /** @@ -124,7 +121,6 @@ reservation_object_fini(struct reservation_object *obj) kfree(fobj); } - kfree(obj->staged); ww_mutex_destroy(&obj->lock); } @@ -218,6 +214,11 @@ reservation_object_trylock(struct reservation_object *obj) static inline void reservation_object_unlock(struct reservation_object *obj) { +#ifdef CONFIG_DEBUG_MUTEXES + /* Test shared fence slot reservation */ + if (obj->fence) + obj->fence->shared_max = obj->fence->shared_count; +#endif ww_mutex_unlock(&obj->lock); } @@ -265,7 +266,8 @@ reservation_object_get_excl_rcu(struct reservation_object *obj) return fence; } -int reservation_object_reserve_shared(struct reservation_object *obj); +int reservation_object_reserve_shared(struct reservation_object *obj, + unsigned int num_fences); void reservation_object_add_shared_fence(struct reservation_object *obj, struct dma_fence *fence); diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 300f336633f2..cebdb2541eb7 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -717,6 +717,7 @@ struct drm_prime_handle { struct drm_syncobj_create { __u32 handle; #define DRM_SYNCOBJ_CREATE_SIGNALED (1 << 0) +#define DRM_SYNCOBJ_CREATE_TYPE_TIMELINE (1 << 1) __u32 flags; }; diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 0cd40ebfa1b1..e7e48f1f4a74 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -153,6 +153,20 @@ extern "C" { #define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ /* + * packed YCbCr420 2x2 tiled formats + * first 64 bits will contain Y,Cb,Cr components for a 2x2 tile + */ +/* [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_Y0L0 fourcc_code('Y', '0', 'L', '0') +/* [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_X0L0 fourcc_code('X', '0', 'L', '0') + +/* [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_Y0L2 fourcc_code('Y', '0', 'L', '2') +/* [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_X0L2 fourcc_code('X', '0', 'L', '2') + +/* * 2 plane RGB + A * index 0 = RGB plane, same format as the corresponding non _A8 format has * index 1 = A plane, [7:0] A diff --git a/include/uapi/drm/v3d_drm.h b/include/uapi/drm/v3d_drm.h index 7b6627783608..f446656d00b1 100644 --- a/include/uapi/drm/v3d_drm.h +++ b/include/uapi/drm/v3d_drm.h @@ -58,6 +58,11 @@ struct drm_v3d_submit_cl { * coordinate shader to determine where primitives land on the screen, * then writes out the state updates and draw calls necessary per tile * to the tile allocation BO. + * + * This BCL will block on any previous BCL submitted on the + * same FD, but not on any RCL or BCLs submitted by other + * clients -- that is left up to the submitter to control + * using in_sync_bcl if necessary. */ __u32 bcl_start; @@ -69,6 +74,11 @@ struct drm_v3d_submit_cl { * This is the second set of commands executed, which will either * execute the tiles that have been set up by the BCL, or a fixed set * of tiles (in the case of RCL-only blits). + * + * This RCL will block on this submit's BCL, and any previous + * RCL submitted on the same FD, but not on any RCL or BCLs + * submitted by other clients -- that is left up to the + * submitter to control using in_sync_rcl if necessary. */ __u32 rcl_start; |