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. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAlvi0eAACgkQ/lWMcqZw E8PI3g//TIlMmbrUc6BrWCNnz4YpJ+mC2dq7HMYB4yf5QeXTYrRDqK6syakbGcbe FGYxt+21REfyQ9IQPdQslmVIUqJZuMBFSvLjXnZvybJSllE23CAXG0hEwHTuPYiF yzxmYwlYOxVlW0nnB3fEDM8BfbWyMYR03c4sobPgiAGoBd+CJif/BtKEwranYrRx 7rZh8PnrCPGAnewmYux6U4zkOpWyjUp5t3kqpNRJDxPfxpa991yvUJX3DxTFFr9b nqYNp3F3fkdowYuJj2eH/uBNd17TouzITGQxIZWEsJfFvB+2Awp7KgRCT8nUAt0I vELbADsy3QwZlQp1F2FaVwfGbmHr41F+Vsq9coUt4vPaiyT3vCW0KGCeal1dKyYf +S8UXIijkoGXm0RqxkbkJsG7AYSIzG+NQm6W+9tnQg6CwpQb2wqU3YCPQWqtFHqM Tz/EW+JqG5Wl1aHXVEbnSajtqT2ooskwHfy81iwNqaGwVy+ZSLIZpqC91Hk9SyZ9 HBDuKWSzqEqXWf7nwbOTm0umQ9mk8+I41k+dyqc2fq9z/gySqKd32eC3aLa0/p3y 6bngvu1TT6jNhRdduwxgl/Y5cnQp/Zg9wYRKmAjViZtooaWj8p2o45AufGz1rplR BdYVUOPofVVD9ShwxayWzuocFW/HbgYc7FHHgKUFgFBO5iC/A2s= =UV0m -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
d7563c55ef
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -59,12 +59,6 @@ Implementing Asynchronous Atomic Commit
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
|
||||
:doc: implementing nonblocking commit
|
||||
|
||||
Atomic State Reset and Initialization
|
||||
-------------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
|
||||
:doc: atomic state reset and initialization
|
||||
|
||||
Helper Functions Reference
|
||||
--------------------------
|
||||
|
||||
|
@ -74,6 +68,21 @@ Helper Functions Reference
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
|
||||
:export:
|
||||
|
||||
Atomic State Reset and Initialization
|
||||
-------------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_atomic_state_helper.c
|
||||
:doc: atomic state reset and initialization
|
||||
|
||||
Atomic State Helper Reference
|
||||
-----------------------------
|
||||
|
||||
.. kernel-doc:: include/drm/drm_atomic_state_helper.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_atomic_state_helper.c
|
||||
:export:
|
||||
|
||||
Simple KMS Helper Reference
|
||||
===========================
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
==============
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
13
MAINTAINERS
13
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
|
||||
|
|
|
@ -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;
|
||||
for (i = 0, j = 0, k = max; i < (old ? old->shared_count : 0); ++i) {
|
||||
struct dma_fence *fence;
|
||||
|
||||
check = rcu_dereference_protected(old->shared[i],
|
||||
reservation_object_held(obj));
|
||||
|
||||
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 @@ reservation_object_add_shared_replace(struct reservation_object *obj,
|
|||
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 @@ int reservation_object_copy_fences(struct reservation_object *dst,
|
|||
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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -556,7 +556,6 @@ static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev,
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
count = drm_add_modes_noedid(connector, 8192, 8192);
|
||||
drm_set_preferred_mode(connector, defx, defy);
|
||||
if (bochs->edid)
|
||||
count = drm_add_edid_modes(connector, bochs->edid);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -230,9 +230,20 @@ 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);
|
||||
|
||||
|
|
|
@ -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 @@ int drm_atomic_helper_page_flip_target(struct drm_crtc *crtc,
|
|||
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);
|
||||
|
|
|
@ -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);
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
@ -294,6 +292,24 @@ int drm_connector_init(struct drm_device *dev,
|
|||
}
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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."),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -433,34 +433,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
|
||||
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void drm_syncobj_remove_callback(struct drm_syncobj *syncobj,
|
||||
struct drm_syncobj_cb *cb)
|
||||
{
|
||||
mutex_lock(&syncobj->cb_mutex);
|
||||
list_del_init(&cb->node);
|
||||
mutex_unlock(&syncobj->cb_mutex);
|
||||
}
|
||||
|
||||
static void drm_syncobj_init(struct drm_syncobj *syncobj)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void drm_syncobj_add_callback(struct drm_syncobj *syncobj,
|
||||
struct drm_syncobj_cb *cb,
|
||||
drm_syncobj_func_t func)
|
||||
static void drm_syncobj_garbage_collection(struct drm_syncobj *syncobj)
|
||||
{
|
||||
spin_lock(&syncobj->lock);
|
||||
drm_syncobj_add_callback_locked(syncobj, cb, func);
|
||||
spin_unlock(&syncobj->lock);
|
||||
}
|
||||
struct drm_syncobj_signal_pt *signal_pt, *tmp, *tail_pt;
|
||||
|
||||
void drm_syncobj_remove_callback(struct drm_syncobj *syncobj,
|
||||
struct drm_syncobj_cb *cb)
|
||||
{
|
||||
spin_lock(&syncobj->lock);
|
||||
list_del_init(&cb->node);
|
||||
spin_unlock(&syncobj->lock);
|
||||
}
|
||||
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;
|
||||
u64 pt_value = point;
|
||||
|
||||
if (fence)
|
||||
dma_fence_get(fence);
|
||||
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);
|
||||
|
||||
spin_lock(&syncobj->lock);
|
||||
|
||||
old_fence = rcu_dereference_protected(syncobj->fence,
|
||||
lockdep_is_held(&syncobj->lock));
|
||||
rcu_assign_pointer(syncobj->fence, fence);
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
if (!syncobj)
|
||||
return -ENOENT;
|
||||
|
||||
*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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
|
|||
|
||||
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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
||||
|
|
|
@ -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",
|
||||
|
@ -2341,6 +2469,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,
|
||||
|
@ -2380,12 +2511,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,
|
||||
|
@ -2401,6 +2541,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,
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef H_QXL_DEV
|
||||
#define H_QXL_DEV
|
||||
|
||||
|
|
|
@ -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 @@ static int qdev_crtc_init(struct drm_device *dev, int crtc_id)
|
|||
|
||||
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 = {
|
||||
};
|
||||
|
||||
|
|
|
@ -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 @@ 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)
|
||||
{
|
||||
/*
|
||||
* 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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -134,9 +134,9 @@ static int qxlfb_create_pinned_object(struct qxl_device *qdev,
|
|||
*/
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue