diff options
author | Dave Airlie <airlied@redhat.com> | 2018-09-06 19:34:04 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-09-06 20:44:35 -0400 |
commit | f5169a17af4ee5170587fd76d76aaa72ae4b864a (patch) | |
tree | 923164e6b95de08874c2d87c8b1af5767042d53c | |
parent | 57361846b52bc686112da6ca5368d11210796804 (diff) | |
parent | 3ee22b769fd761c98eeaceab49153c3eb7612821 (diff) |
Merge tag 'drm-misc-next-2018-09-05' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 4.20:
UAPI Changes:
- Add userspace dma-buf device to turn memfd regions into dma-bufs (Gerd)
- Add per-plane blend mode property (Lowry)
- Change in drm_fourcc.h is documentation only (Brian)
Cross-subsystem Changes:
- None
Core Changes:
- Remove user logspam and useless lock in vma_offset_mgr destroy (Chris)
- Add get/verify_crc_source for improved crc source selection (Mahesh)
- Add __drm_atomic_helper_plane_reset to reduce copypasta (Alexandru)
Driver Changes:
- various: Replance ref/unref calls with drm_dev_get/put (Thomas)
- bridge: Add driver for TI SN65DSI86 chip (Sandeep)
- rockchip: Add PX30 support (Sandy)
- sun4i: Add support for R40 TCON (Jernej)
- vkms: Continued building out vkms, added gem support (Haneen)Driver Changes:
- various: fbdev: Wrap remove_conflicting_framebuffers with resource_len
accessors to remove a bunch of cargo-cult (Michał)
- rockchip: Add rgb output iface support + fixes (Sandy/Heiko)
- nouveau/amdgpu: Add cec-over-aux support (Hans)
- sun4i: Add support for Allwinner A64 (Jagan)
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Cc: Heiko Stuebner <heiko@sntech.de>
Cc: Sandy Huang <hjc@rock-chips.com>
Cc: Hans Verkuil <hans.verkuil@cisco.com>
Cc: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Sean Paul <sean@poorly.run>
Link: https://patchwork.freedesktop.org/patch/msgid/20180905202210.GA95199@art_vandelay
130 files changed, 4522 insertions, 684 deletions
diff --git a/Documentation/devicetree/bindings/display/atmel/hlcdc-dc.txt b/Documentation/devicetree/bindings/display/atmel/hlcdc-dc.txt index 82f2acb3d374..0398aec488ac 100644 --- a/Documentation/devicetree/bindings/display/atmel/hlcdc-dc.txt +++ b/Documentation/devicetree/bindings/display/atmel/hlcdc-dc.txt | |||
@@ -15,6 +15,13 @@ Required children nodes: | |||
15 | to external devices using the OF graph reprensentation (see ../graph.txt). | 15 | to external devices using the OF graph reprensentation (see ../graph.txt). |
16 | At least one port node is required. | 16 | At least one port node is required. |
17 | 17 | ||
18 | Optional properties in grandchild nodes: | ||
19 | Any endpoint grandchild node may specify a desired video interface | ||
20 | according to ../../media/video-interfaces.txt, specifically | ||
21 | - bus-width: recognized values are <12>, <16>, <18> and <24>, and | ||
22 | override any output mode selection heuristic, forcing "rgb444", | ||
23 | "rgb565", "rgb666" and "rgb888" respectively. | ||
24 | |||
18 | Example: | 25 | Example: |
19 | 26 | ||
20 | hlcdc: hlcdc@f0030000 { | 27 | hlcdc: hlcdc@f0030000 { |
@@ -50,3 +57,19 @@ Example: | |||
50 | #pwm-cells = <3>; | 57 | #pwm-cells = <3>; |
51 | }; | 58 | }; |
52 | }; | 59 | }; |
60 | |||
61 | Example 2: With a video interface override to force rgb565; as above | ||
62 | but with these changes/additions: | ||
63 | |||
64 | &hlcdc { | ||
65 | hlcdc-display-controller { | ||
66 | pinctrl-names = "default"; | ||
67 | pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb565>; | ||
68 | |||
69 | port@0 { | ||
70 | hlcdc_panel_output: endpoint@0 { | ||
71 | bus-width = <16>; | ||
72 | }; | ||
73 | }; | ||
74 | }; | ||
75 | }; | ||
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt index fd39ad34c383..50220190c203 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt | |||
@@ -22,7 +22,13 @@ among others. | |||
22 | 22 | ||
23 | Required properties: | 23 | Required properties: |
24 | 24 | ||
25 | - compatible: Must be "lvds-encoder" | 25 | - compatible: Must be one or more of the following |
26 | - "ti,ds90c185" for the TI DS90C185 FPD-Link Serializer | ||
27 | - "lvds-encoder" for a generic LVDS encoder device | ||
28 | |||
29 | When compatible with the generic version, nodes must list the | ||
30 | device-specific version corresponding to the device first | ||
31 | followed by the generic version. | ||
26 | 32 | ||
27 | Required nodes: | 33 | Required nodes: |
28 | 34 | ||
diff --git a/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.txt b/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.txt new file mode 100644 index 000000000000..0a3fbb53a16e --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.txt | |||
@@ -0,0 +1,87 @@ | |||
1 | SN65DSI86 DSI to eDP bridge chip | ||
2 | -------------------------------- | ||
3 | |||
4 | This is the binding for Texas Instruments SN65DSI86 bridge. | ||
5 | http://www.ti.com/general/docs/lit/getliterature.tsp?genericPartNumber=sn65dsi86&fileType=pdf | ||
6 | |||
7 | Required properties: | ||
8 | - compatible: Must be "ti,sn65dsi86" | ||
9 | - reg: i2c address of the chip, 0x2d as per datasheet | ||
10 | - enable-gpios: gpio specification for bridge_en pin (active high) | ||
11 | |||
12 | - vccio-supply: A 1.8V supply that powers up the digital IOs. | ||
13 | - vpll-supply: A 1.8V supply that powers up the displayport PLL. | ||
14 | - vcca-supply: A 1.2V supply that powers up the analog circuits. | ||
15 | - vcc-supply: A 1.2V supply that powers up the digital core. | ||
16 | |||
17 | Optional properties: | ||
18 | - interrupts-extended: Specifier for the SN65DSI86 interrupt line. | ||
19 | |||
20 | - gpio-controller: Marks the device has a GPIO controller. | ||
21 | - #gpio-cells : Should be two. The first cell is the pin number and | ||
22 | the second cell is used to specify flags. | ||
23 | See ../../gpio/gpio.txt for more information. | ||
24 | - #pwm-cells : Should be one. See ../../pwm/pwm.txt for description of | ||
25 | the cell formats. | ||
26 | |||
27 | - clock-names: should be "refclk" | ||
28 | - clocks: Specification for input reference clock. The reference | ||
29 | clock rate must be 12 MHz, 19.2 MHz, 26 MHz, 27 MHz or 38.4 MHz. | ||
30 | |||
31 | - data-lanes: See ../../media/video-interface.txt | ||
32 | - lane-polarities: See ../../media/video-interface.txt | ||
33 | |||
34 | - suspend-gpios: specification for GPIO1 pin on bridge (active low) | ||
35 | |||
36 | Required nodes: | ||
37 | This device has two video ports. Their connections are modelled using the | ||
38 | OF graph bindings specified in Documentation/devicetree/bindings/graph.txt. | ||
39 | |||
40 | - Video port 0 for DSI input | ||
41 | - Video port 1 for eDP output | ||
42 | |||
43 | Example | ||
44 | ------- | ||
45 | |||
46 | edp-bridge@2d { | ||
47 | compatible = "ti,sn65dsi86"; | ||
48 | #address-cells = <1>; | ||
49 | #size-cells = <0>; | ||
50 | reg = <0x2d>; | ||
51 | |||
52 | enable-gpios = <&msmgpio 33 GPIO_ACTIVE_HIGH>; | ||
53 | suspend-gpios = <&msmgpio 34 GPIO_ACTIVE_LOW>; | ||
54 | |||
55 | interrupts-extended = <&gpio3 4 IRQ_TYPE_EDGE_FALLING>; | ||
56 | |||
57 | vccio-supply = <&pm8916_l17>; | ||
58 | vcca-supply = <&pm8916_l6>; | ||
59 | vpll-supply = <&pm8916_l17>; | ||
60 | vcc-supply = <&pm8916_l6>; | ||
61 | |||
62 | clock-names = "refclk"; | ||
63 | clocks = <&input_refclk>; | ||
64 | |||
65 | ports { | ||
66 | #address-cells = <1>; | ||
67 | #size-cells = <0>; | ||
68 | |||
69 | port@0 { | ||
70 | reg = <0>; | ||
71 | |||
72 | edp_bridge_in: endpoint { | ||
73 | remote-endpoint = <&dsi_out>; | ||
74 | }; | ||
75 | }; | ||
76 | |||
77 | port@1 { | ||
78 | reg = <1>; | ||
79 | |||
80 | edp_bridge_out: endpoint { | ||
81 | data-lanes = <2 1 3 0>; | ||
82 | lane-polarities = <0 1 0 1>; | ||
83 | remote-endpoint = <&edp_panel_in>; | ||
84 | }; | ||
85 | }; | ||
86 | }; | ||
87 | } | ||
diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt new file mode 100644 index 000000000000..8f9abf28a8fa --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358764.txt | |||
@@ -0,0 +1,35 @@ | |||
1 | TC358764 MIPI-DSI to LVDS panel bridge | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: "toshiba,tc358764" | ||
5 | - reg: the virtual channel number of a DSI peripheral | ||
6 | - vddc-supply: core voltage supply, 1.2V | ||
7 | - vddio-supply: I/O voltage supply, 1.8V or 3.3V | ||
8 | - vddlvds-supply: LVDS1/2 voltage supply, 3.3V | ||
9 | - reset-gpios: a GPIO spec for the reset pin | ||
10 | |||
11 | The device node can contain following 'port' child nodes, | ||
12 | according to the OF graph bindings defined in [1]: | ||
13 | 0: DSI Input, not required, if the bridge is DSI controlled | ||
14 | 1: LVDS Output, mandatory | ||
15 | |||
16 | [1]: Documentation/devicetree/bindings/media/video-interfaces.txt | ||
17 | |||
18 | Example: | ||
19 | |||
20 | bridge@0 { | ||
21 | reg = <0>; | ||
22 | compatible = "toshiba,tc358764"; | ||
23 | vddc-supply = <&vcc_1v2_reg>; | ||
24 | vddio-supply = <&vcc_1v8_reg>; | ||
25 | vddlvds-supply = <&vcc_3v3_reg>; | ||
26 | reset-gpios = <&gpd1 6 GPIO_ACTIVE_LOW>; | ||
27 | #address-cells = <1>; | ||
28 | #size-cells = <0>; | ||
29 | port@1 { | ||
30 | reg = <1>; | ||
31 | lvds_ep: endpoint { | ||
32 | remote-endpoint = <&panel_ep>; | ||
33 | }; | ||
34 | }; | ||
35 | }; | ||
diff --git a/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt b/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt index 973c27273772..a336599f6c03 100644 --- a/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt +++ b/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt | |||
@@ -16,7 +16,7 @@ The following assumes that only a single peripheral is connected to a DSI | |||
16 | host. Experience shows that this is true for the large majority of setups. | 16 | host. Experience shows that this is true for the large majority of setups. |
17 | 17 | ||
18 | DSI host | 18 | DSI host |
19 | -------- | 19 | ======== |
20 | 20 | ||
21 | In addition to the standard properties and those defined by the parent bus of | 21 | In addition to the standard properties and those defined by the parent bus of |
22 | a DSI host, the following properties apply to a node representing a DSI host. | 22 | a DSI host, the following properties apply to a node representing a DSI host. |
@@ -29,12 +29,24 @@ Required properties: | |||
29 | - #size-cells: Should be 0. There are cases where it makes sense to use a | 29 | - #size-cells: Should be 0. There are cases where it makes sense to use a |
30 | different value here. See below. | 30 | different value here. See below. |
31 | 31 | ||
32 | Optional properties: | ||
33 | - clock-master: boolean. Should be enabled if the host is being used in | ||
34 | conjunction with another DSI host to drive the same peripheral. Hardware | ||
35 | supporting such a configuration generally requires the data on both the busses | ||
36 | to be driven by the same clock. Only the DSI host instance controlling this | ||
37 | clock should contain this property. | ||
38 | |||
32 | DSI peripheral | 39 | DSI peripheral |
33 | -------------- | 40 | ============== |
41 | |||
42 | Peripherals with DSI as control bus, or no control bus | ||
43 | ------------------------------------------------------ | ||
34 | 44 | ||
35 | Peripherals are represented as child nodes of the DSI host's node. Properties | 45 | Peripherals with the DSI bus as the primary control bus, or peripherals with |
36 | described here apply to all DSI peripherals, but individual bindings may want | 46 | no control bus but use the DSI bus to transmit pixel data are represented |
37 | to define additional, device-specific properties. | 47 | as child nodes of the DSI host's node. Properties described here apply to all |
48 | DSI peripherals, but individual bindings may want to define additional, | ||
49 | device-specific properties. | ||
38 | 50 | ||
39 | Required properties: | 51 | Required properties: |
40 | - reg: The virtual channel number of a DSI peripheral. Must be in the range | 52 | - reg: The virtual channel number of a DSI peripheral. Must be in the range |
@@ -49,9 +61,37 @@ case two alternative representations can be chosen: | |||
49 | property is the number of the first virtual channel and the second cell is | 61 | property is the number of the first virtual channel and the second cell is |
50 | the number of consecutive virtual channels. | 62 | the number of consecutive virtual channels. |
51 | 63 | ||
52 | Example | 64 | Peripherals with a different control bus |
53 | ------- | 65 | ---------------------------------------- |
54 | 66 | ||
67 | There are peripherals that have I2C/SPI (or some other non-DSI bus) as the | ||
68 | primary control bus, but are also connected to a DSI bus (mostly for the data | ||
69 | path). Connections between such peripherals and a DSI host can be represented | ||
70 | using the graph bindings [1], [2]. | ||
71 | |||
72 | Peripherals that support dual channel DSI | ||
73 | ----------------------------------------- | ||
74 | |||
75 | Peripherals with higher bandwidth requirements can be connected to 2 DSI | ||
76 | busses. Each DSI bus/channel drives some portion of the pixel data (generally | ||
77 | left/right half of each line of the display, or even/odd lines of the display). | ||
78 | The graph bindings should be used to represent the multiple DSI busses that are | ||
79 | connected to this peripheral. Each DSI host's output endpoint can be linked to | ||
80 | an input endpoint of the DSI peripheral. | ||
81 | |||
82 | [1] Documentation/devicetree/bindings/graph.txt | ||
83 | [2] Documentation/devicetree/bindings/media/video-interfaces.txt | ||
84 | |||
85 | Examples | ||
86 | ======== | ||
87 | - (1), (2) and (3) are examples of a DSI host and peripheral on the DSI bus | ||
88 | with different virtual channel configurations. | ||
89 | - (4) is an example of a peripheral on a I2C control bus connected to a | ||
90 | DSI host using of-graph bindings. | ||
91 | - (5) is an example of 2 DSI hosts driving a dual-channel DSI peripheral, | ||
92 | which uses I2C as its primary control bus. | ||
93 | |||
94 | 1) | ||
55 | dsi-host { | 95 | dsi-host { |
56 | ... | 96 | ... |
57 | 97 | ||
@@ -67,6 +107,7 @@ Example | |||
67 | ... | 107 | ... |
68 | }; | 108 | }; |
69 | 109 | ||
110 | 2) | ||
70 | dsi-host { | 111 | dsi-host { |
71 | ... | 112 | ... |
72 | 113 | ||
@@ -82,6 +123,7 @@ Example | |||
82 | ... | 123 | ... |
83 | }; | 124 | }; |
84 | 125 | ||
126 | 3) | ||
85 | dsi-host { | 127 | dsi-host { |
86 | ... | 128 | ... |
87 | 129 | ||
@@ -96,3 +138,98 @@ Example | |||
96 | 138 | ||
97 | ... | 139 | ... |
98 | }; | 140 | }; |
141 | |||
142 | 4) | ||
143 | i2c-host { | ||
144 | ... | ||
145 | |||
146 | dsi-bridge@35 { | ||
147 | compatible = "..."; | ||
148 | reg = <0x35>; | ||
149 | |||
150 | ports { | ||
151 | ... | ||
152 | |||
153 | port { | ||
154 | bridge_mipi_in: endpoint { | ||
155 | remote-endpoint = <&host_mipi_out>; | ||
156 | }; | ||
157 | }; | ||
158 | }; | ||
159 | }; | ||
160 | }; | ||
161 | |||
162 | dsi-host { | ||
163 | ... | ||
164 | |||
165 | ports { | ||
166 | ... | ||
167 | |||
168 | port { | ||
169 | host_mipi_out: endpoint { | ||
170 | remote-endpoint = <&bridge_mipi_in>; | ||
171 | }; | ||
172 | }; | ||
173 | }; | ||
174 | }; | ||
175 | |||
176 | 5) | ||
177 | i2c-host { | ||
178 | dsi-bridge@35 { | ||
179 | compatible = "..."; | ||
180 | reg = <0x35>; | ||
181 | |||
182 | ports { | ||
183 | #address-cells = <1>; | ||
184 | #size-cells = <0>; | ||
185 | |||
186 | port@0 { | ||
187 | reg = <0>; | ||
188 | dsi0_in: endpoint { | ||
189 | remote-endpoint = <&dsi0_out>; | ||
190 | }; | ||
191 | }; | ||
192 | |||
193 | port@1 { | ||
194 | reg = <1>; | ||
195 | dsi1_in: endpoint { | ||
196 | remote-endpoint = <&dsi1_out>; | ||
197 | }; | ||
198 | }; | ||
199 | }; | ||
200 | }; | ||
201 | }; | ||
202 | |||
203 | dsi0-host { | ||
204 | ... | ||
205 | |||
206 | /* | ||
207 | * this DSI instance drives the clock for both the host | ||
208 | * controllers | ||
209 | */ | ||
210 | clock-master; | ||
211 | |||
212 | ports { | ||
213 | ... | ||
214 | |||
215 | port { | ||
216 | dsi0_out: endpoint { | ||
217 | remote-endpoint = <&dsi0_in>; | ||
218 | }; | ||
219 | }; | ||
220 | }; | ||
221 | }; | ||
222 | |||
223 | dsi1-host { | ||
224 | ... | ||
225 | |||
226 | ports { | ||
227 | ... | ||
228 | |||
229 | port { | ||
230 | dsi1_out: endpoint { | ||
231 | remote-endpoint = <&dsi1_in>; | ||
232 | }; | ||
233 | }; | ||
234 | }; | ||
235 | }; | ||
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt index eeda3597011e..b79e5769f0ae 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt | |||
@@ -8,6 +8,9 @@ Required properties: | |||
8 | - compatible: value should be one of the following | 8 | - compatible: value should be one of the following |
9 | "rockchip,rk3036-vop"; | 9 | "rockchip,rk3036-vop"; |
10 | "rockchip,rk3126-vop"; | 10 | "rockchip,rk3126-vop"; |
11 | "rockchip,px30-vop-lit"; | ||
12 | "rockchip,px30-vop-big"; | ||
13 | "rockchip,rk3188-vop"; | ||
11 | "rockchip,rk3288-vop"; | 14 | "rockchip,rk3288-vop"; |
12 | "rockchip,rk3368-vop"; | 15 | "rockchip,rk3368-vop"; |
13 | "rockchip,rk3366-vop"; | 16 | "rockchip,rk3366-vop"; |
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index f8773ecb7525..0bbb5d47f228 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | |||
@@ -78,6 +78,7 @@ Required properties: | |||
78 | 78 | ||
79 | - compatible: value must be one of: | 79 | - compatible: value must be one of: |
80 | * "allwinner,sun8i-a83t-dw-hdmi" | 80 | * "allwinner,sun8i-a83t-dw-hdmi" |
81 | * "allwinner,sun50i-a64-dw-hdmi", "allwinner,sun8i-a83t-dw-hdmi" | ||
81 | - reg: base address and size of memory-mapped region | 82 | - reg: base address and size of memory-mapped region |
82 | - reg-io-width: See dw_hdmi.txt. Shall be 1. | 83 | - reg-io-width: See dw_hdmi.txt. Shall be 1. |
83 | - interrupts: HDMI interrupt number | 84 | - interrupts: HDMI interrupt number |
@@ -96,6 +97,9 @@ Required properties: | |||
96 | first port should be the input endpoint. The second should be the | 97 | first port should be the input endpoint. The second should be the |
97 | output, usually to an HDMI connector. | 98 | output, usually to an HDMI connector. |
98 | 99 | ||
100 | Optional properties: | ||
101 | - hvcc-supply: the VCC power supply of the controller | ||
102 | |||
99 | DWC HDMI PHY | 103 | DWC HDMI PHY |
100 | ------------ | 104 | ------------ |
101 | 105 | ||
@@ -151,6 +155,8 @@ Required properties: | |||
151 | * allwinner,sun8i-v3s-tcon | 155 | * allwinner,sun8i-v3s-tcon |
152 | * allwinner,sun9i-a80-tcon-lcd | 156 | * allwinner,sun9i-a80-tcon-lcd |
153 | * allwinner,sun9i-a80-tcon-tv | 157 | * allwinner,sun9i-a80-tcon-tv |
158 | * "allwinner,sun50i-a64-tcon-lcd", "allwinner,sun8i-a83t-tcon-lcd" | ||
159 | * "allwinner,sun50i-a64-tcon-tv", "allwinner,sun8i-a83t-tcon-tv" | ||
154 | - reg: base address and size of memory-mapped region | 160 | - reg: base address and size of memory-mapped region |
155 | - interrupts: interrupt associated to this IP | 161 | - interrupts: interrupt associated to this IP |
156 | - clocks: phandles to the clocks feeding the TCON. | 162 | - clocks: phandles to the clocks feeding the TCON. |
@@ -370,6 +376,8 @@ Required properties: | |||
370 | * allwinner,sun8i-a83t-de2-mixer-1 | 376 | * allwinner,sun8i-a83t-de2-mixer-1 |
371 | * allwinner,sun8i-h3-de2-mixer-0 | 377 | * allwinner,sun8i-h3-de2-mixer-0 |
372 | * allwinner,sun8i-v3s-de2-mixer | 378 | * allwinner,sun8i-v3s-de2-mixer |
379 | * allwinner,sun50i-a64-de2-mixer-0 | ||
380 | * allwinner,sun50i-a64-de2-mixer-1 | ||
373 | - reg: base address and size of the memory-mapped region. | 381 | - reg: base address and size of the memory-mapped region. |
374 | - clocks: phandles to the clocks feeding the mixer | 382 | - clocks: phandles to the clocks feeding the mixer |
375 | * bus: the mixer interface clock | 383 | * bus: the mixer interface clock |
@@ -403,6 +411,7 @@ Required properties: | |||
403 | * allwinner,sun8i-r40-display-engine | 411 | * allwinner,sun8i-r40-display-engine |
404 | * allwinner,sun8i-v3s-display-engine | 412 | * allwinner,sun8i-v3s-display-engine |
405 | * allwinner,sun9i-a80-display-engine | 413 | * allwinner,sun9i-a80-display-engine |
414 | * allwinner,sun50i-a64-display-engine | ||
406 | 415 | ||
407 | - allwinner,pipelines: list of phandle to the display engine | 416 | - allwinner,pipelines: list of phandle to the display engine |
408 | frontends (DE 1.0) or mixers (DE 2.0) available. | 417 | frontends (DE 1.0) or mixers (DE 2.0) available. |
diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 5dee6b8a4c12..f8f5bf11a6ca 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst | |||
@@ -323,6 +323,12 @@ Frame Buffer Functions Reference | |||
323 | DRM Format Handling | 323 | DRM Format Handling |
324 | =================== | 324 | =================== |
325 | 325 | ||
326 | .. kernel-doc:: include/uapi/drm/drm_fourcc.h | ||
327 | :doc: overview | ||
328 | |||
329 | Format Functions Reference | ||
330 | -------------------------- | ||
331 | |||
326 | .. kernel-doc:: include/drm/drm_fourcc.h | 332 | .. kernel-doc:: include/drm/drm_fourcc.h |
327 | :internal: | 333 | :internal: |
328 | 334 | ||
diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index 21b6b72a9ba8..c3ae888b92ef 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst | |||
@@ -297,7 +297,7 @@ made up of several fields, the more interesting ones being: | |||
297 | struct vm_operations_struct { | 297 | struct vm_operations_struct { |
298 | void (*open)(struct vm_area_struct * area); | 298 | void (*open)(struct vm_area_struct * area); |
299 | void (*close)(struct vm_area_struct * area); | 299 | void (*close)(struct vm_area_struct * area); |
300 | int (*fault)(struct vm_fault *vmf); | 300 | vm_fault_t (*fault)(struct vm_fault *vmf); |
301 | }; | 301 | }; |
302 | 302 | ||
303 | 303 | ||
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 13a7c999c04a..f2ac672eb766 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt | |||
@@ -272,6 +272,7 @@ Code Seq#(hex) Include File Comments | |||
272 | 't' 90-91 linux/toshiba.h toshiba and toshiba_acpi SMM | 272 | 't' 90-91 linux/toshiba.h toshiba and toshiba_acpi SMM |
273 | 'u' 00-1F linux/smb_fs.h gone | 273 | 'u' 00-1F linux/smb_fs.h gone |
274 | 'u' 20-3F linux/uvcvideo.h USB video class host driver | 274 | 'u' 20-3F linux/uvcvideo.h USB video class host driver |
275 | 'u' 40-4f linux/udmabuf.h userspace dma-buf misc device | ||
275 | 'v' 00-1F linux/ext2_fs.h conflict! | 276 | 'v' 00-1F linux/ext2_fs.h conflict! |
276 | 'v' 00-1F linux/fs.h conflict! | 277 | 'v' 00-1F linux/fs.h conflict! |
277 | 'v' 00-0F linux/sonypi.h conflict! | 278 | 'v' 00-0F linux/sonypi.h conflict! |
diff --git a/MAINTAINERS b/MAINTAINERS index 9ad052aeac39..029baa270a11 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -15343,6 +15343,14 @@ F: arch/x86/um/ | |||
15343 | F: fs/hostfs/ | 15343 | F: fs/hostfs/ |
15344 | F: fs/hppfs/ | 15344 | F: fs/hppfs/ |
15345 | 15345 | ||
15346 | USERSPACE DMA BUFFER DRIVER | ||
15347 | M: Gerd Hoffmann <kraxel@redhat.com> | ||
15348 | S: Maintained | ||
15349 | L: dri-devel@lists.freedesktop.org | ||
15350 | F: drivers/dma-buf/udmabuf.c | ||
15351 | F: include/uapi/linux/udmabuf.h | ||
15352 | T: git git://anongit.freedesktop.org/drm/drm-misc | ||
15353 | |||
15346 | USERSPACE I/O (UIO) | 15354 | USERSPACE I/O (UIO) |
15347 | M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 15355 | M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
15348 | S: Maintained | 15356 | S: Maintained |
diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index ed3b785bae37..338129eb126f 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig | |||
@@ -30,4 +30,12 @@ config SW_SYNC | |||
30 | WARNING: improper use of this can result in deadlocking kernel | 30 | WARNING: improper use of this can result in deadlocking kernel |
31 | drivers from userspace. Intended for test and debug only. | 31 | drivers from userspace. Intended for test and debug only. |
32 | 32 | ||
33 | config UDMABUF | ||
34 | bool "userspace dmabuf misc driver" | ||
35 | default n | ||
36 | depends on DMA_SHARED_BUFFER | ||
37 | help | ||
38 | A driver to let userspace turn memfd regions into dma-bufs. | ||
39 | Qemu can use this to create host dmabufs for guest framebuffers. | ||
40 | |||
33 | endmenu | 41 | endmenu |
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index c33bf8863147..0913a6ccab5a 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile | |||
@@ -1,3 +1,4 @@ | |||
1 | obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o | 1 | obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o |
2 | obj-$(CONFIG_SYNC_FILE) += sync_file.o | 2 | obj-$(CONFIG_SYNC_FILE) += sync_file.o |
3 | obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o | 3 | obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o |
4 | obj-$(CONFIG_UDMABUF) += udmabuf.o | ||
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 13884474d158..02f7f9a89979 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c | |||
@@ -405,7 +405,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) | |||
405 | || !exp_info->ops->map_dma_buf | 405 | || !exp_info->ops->map_dma_buf |
406 | || !exp_info->ops->unmap_dma_buf | 406 | || !exp_info->ops->unmap_dma_buf |
407 | || !exp_info->ops->release | 407 | || !exp_info->ops->release |
408 | || !exp_info->ops->map | ||
409 | || !exp_info->ops->mmap)) { | 408 | || !exp_info->ops->mmap)) { |
410 | return ERR_PTR(-EINVAL); | 409 | return ERR_PTR(-EINVAL); |
411 | } | 410 | } |
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c new file mode 100644 index 000000000000..2e8502250afe --- /dev/null +++ b/drivers/dma-buf/udmabuf.c | |||
@@ -0,0 +1,288 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <linux/init.h> | ||
3 | #include <linux/module.h> | ||
4 | #include <linux/device.h> | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/slab.h> | ||
7 | #include <linux/miscdevice.h> | ||
8 | #include <linux/dma-buf.h> | ||
9 | #include <linux/highmem.h> | ||
10 | #include <linux/cred.h> | ||
11 | #include <linux/shmem_fs.h> | ||
12 | #include <linux/memfd.h> | ||
13 | |||
14 | #include <uapi/linux/udmabuf.h> | ||
15 | |||
16 | struct udmabuf { | ||
17 | u32 pagecount; | ||
18 | struct page **pages; | ||
19 | }; | ||
20 | |||
21 | static int udmabuf_vm_fault(struct vm_fault *vmf) | ||
22 | { | ||
23 | struct vm_area_struct *vma = vmf->vma; | ||
24 | struct udmabuf *ubuf = vma->vm_private_data; | ||
25 | |||
26 | if (WARN_ON(vmf->pgoff >= ubuf->pagecount)) | ||
27 | return VM_FAULT_SIGBUS; | ||
28 | |||
29 | vmf->page = ubuf->pages[vmf->pgoff]; | ||
30 | get_page(vmf->page); | ||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | static const struct vm_operations_struct udmabuf_vm_ops = { | ||
35 | .fault = udmabuf_vm_fault, | ||
36 | }; | ||
37 | |||
38 | static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) | ||
39 | { | ||
40 | struct udmabuf *ubuf = buf->priv; | ||
41 | |||
42 | if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0) | ||
43 | return -EINVAL; | ||
44 | |||
45 | vma->vm_ops = &udmabuf_vm_ops; | ||
46 | vma->vm_private_data = ubuf; | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static struct sg_table *map_udmabuf(struct dma_buf_attachment *at, | ||
51 | enum dma_data_direction direction) | ||
52 | { | ||
53 | struct udmabuf *ubuf = at->dmabuf->priv; | ||
54 | struct sg_table *sg; | ||
55 | |||
56 | sg = kzalloc(sizeof(*sg), GFP_KERNEL); | ||
57 | if (!sg) | ||
58 | goto err1; | ||
59 | if (sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, | ||
60 | 0, ubuf->pagecount << PAGE_SHIFT, | ||
61 | GFP_KERNEL) < 0) | ||
62 | goto err2; | ||
63 | if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) | ||
64 | goto err3; | ||
65 | |||
66 | return sg; | ||
67 | |||
68 | err3: | ||
69 | sg_free_table(sg); | ||
70 | err2: | ||
71 | kfree(sg); | ||
72 | err1: | ||
73 | return ERR_PTR(-ENOMEM); | ||
74 | } | ||
75 | |||
76 | static void unmap_udmabuf(struct dma_buf_attachment *at, | ||
77 | struct sg_table *sg, | ||
78 | enum dma_data_direction direction) | ||
79 | { | ||
80 | sg_free_table(sg); | ||
81 | kfree(sg); | ||
82 | } | ||
83 | |||
84 | static void release_udmabuf(struct dma_buf *buf) | ||
85 | { | ||
86 | struct udmabuf *ubuf = buf->priv; | ||
87 | pgoff_t pg; | ||
88 | |||
89 | for (pg = 0; pg < ubuf->pagecount; pg++) | ||
90 | put_page(ubuf->pages[pg]); | ||
91 | kfree(ubuf->pages); | ||
92 | kfree(ubuf); | ||
93 | } | ||
94 | |||
95 | static void *kmap_udmabuf(struct dma_buf *buf, unsigned long page_num) | ||
96 | { | ||
97 | struct udmabuf *ubuf = buf->priv; | ||
98 | struct page *page = ubuf->pages[page_num]; | ||
99 | |||
100 | return kmap(page); | ||
101 | } | ||
102 | |||
103 | static void kunmap_udmabuf(struct dma_buf *buf, unsigned long page_num, | ||
104 | void *vaddr) | ||
105 | { | ||
106 | kunmap(vaddr); | ||
107 | } | ||
108 | |||
109 | static struct dma_buf_ops udmabuf_ops = { | ||
110 | .map_dma_buf = map_udmabuf, | ||
111 | .unmap_dma_buf = unmap_udmabuf, | ||
112 | .release = release_udmabuf, | ||
113 | .map = kmap_udmabuf, | ||
114 | .unmap = kunmap_udmabuf, | ||
115 | .mmap = mmap_udmabuf, | ||
116 | }; | ||
117 | |||
118 | #define SEALS_WANTED (F_SEAL_SHRINK) | ||
119 | #define SEALS_DENIED (F_SEAL_WRITE) | ||
120 | |||
121 | static long udmabuf_create(struct udmabuf_create_list *head, | ||
122 | struct udmabuf_create_item *list) | ||
123 | { | ||
124 | DEFINE_DMA_BUF_EXPORT_INFO(exp_info); | ||
125 | struct file *memfd = NULL; | ||
126 | struct udmabuf *ubuf; | ||
127 | struct dma_buf *buf; | ||
128 | pgoff_t pgoff, pgcnt, pgidx, pgbuf; | ||
129 | struct page *page; | ||
130 | int seals, ret = -EINVAL; | ||
131 | u32 i, flags; | ||
132 | |||
133 | ubuf = kzalloc(sizeof(struct udmabuf), GFP_KERNEL); | ||
134 | if (!ubuf) | ||
135 | return -ENOMEM; | ||
136 | |||
137 | for (i = 0; i < head->count; i++) { | ||
138 | if (!IS_ALIGNED(list[i].offset, PAGE_SIZE)) | ||
139 | goto err_free_ubuf; | ||
140 | if (!IS_ALIGNED(list[i].size, PAGE_SIZE)) | ||
141 | goto err_free_ubuf; | ||
142 | ubuf->pagecount += list[i].size >> PAGE_SHIFT; | ||
143 | } | ||
144 | ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(struct page *), | ||
145 | GFP_KERNEL); | ||
146 | if (!ubuf->pages) { | ||
147 | ret = -ENOMEM; | ||
148 | goto err_free_ubuf; | ||
149 | } | ||
150 | |||
151 | pgbuf = 0; | ||
152 | for (i = 0; i < head->count; i++) { | ||
153 | memfd = fget(list[i].memfd); | ||
154 | if (!memfd) | ||
155 | goto err_put_pages; | ||
156 | if (!shmem_mapping(file_inode(memfd)->i_mapping)) | ||
157 | goto err_put_pages; | ||
158 | seals = memfd_fcntl(memfd, F_GET_SEALS, 0); | ||
159 | if (seals == -EINVAL || | ||
160 | (seals & SEALS_WANTED) != SEALS_WANTED || | ||
161 | (seals & SEALS_DENIED) != 0) | ||
162 | goto err_put_pages; | ||
163 | pgoff = list[i].offset >> PAGE_SHIFT; | ||
164 | pgcnt = list[i].size >> PAGE_SHIFT; | ||
165 | for (pgidx = 0; pgidx < pgcnt; pgidx++) { | ||
166 | page = shmem_read_mapping_page( | ||
167 | file_inode(memfd)->i_mapping, pgoff + pgidx); | ||
168 | if (IS_ERR(page)) { | ||
169 | ret = PTR_ERR(page); | ||
170 | goto err_put_pages; | ||
171 | } | ||
172 | ubuf->pages[pgbuf++] = page; | ||
173 | } | ||
174 | fput(memfd); | ||
175 | } | ||
176 | memfd = NULL; | ||
177 | |||
178 | exp_info.ops = &udmabuf_ops; | ||
179 | exp_info.size = ubuf->pagecount << PAGE_SHIFT; | ||
180 | exp_info.priv = ubuf; | ||
181 | |||
182 | buf = dma_buf_export(&exp_info); | ||
183 | if (IS_ERR(buf)) { | ||
184 | ret = PTR_ERR(buf); | ||
185 | goto err_put_pages; | ||
186 | } | ||
187 | |||
188 | flags = 0; | ||
189 | if (head->flags & UDMABUF_FLAGS_CLOEXEC) | ||
190 | flags |= O_CLOEXEC; | ||
191 | return dma_buf_fd(buf, flags); | ||
192 | |||
193 | err_put_pages: | ||
194 | while (pgbuf > 0) | ||
195 | put_page(ubuf->pages[--pgbuf]); | ||
196 | err_free_ubuf: | ||
197 | if (memfd) | ||
198 | fput(memfd); | ||
199 | kfree(ubuf->pages); | ||
200 | kfree(ubuf); | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | static long udmabuf_ioctl_create(struct file *filp, unsigned long arg) | ||
205 | { | ||
206 | struct udmabuf_create create; | ||
207 | struct udmabuf_create_list head; | ||
208 | struct udmabuf_create_item list; | ||
209 | |||
210 | if (copy_from_user(&create, (void __user *)arg, | ||
211 | sizeof(struct udmabuf_create))) | ||
212 | return -EFAULT; | ||
213 | |||
214 | head.flags = create.flags; | ||
215 | head.count = 1; | ||
216 | list.memfd = create.memfd; | ||
217 | list.offset = create.offset; | ||
218 | list.size = create.size; | ||
219 | |||
220 | return udmabuf_create(&head, &list); | ||
221 | } | ||
222 | |||
223 | static long udmabuf_ioctl_create_list(struct file *filp, unsigned long arg) | ||
224 | { | ||
225 | struct udmabuf_create_list head; | ||
226 | struct udmabuf_create_item *list; | ||
227 | int ret = -EINVAL; | ||
228 | u32 lsize; | ||
229 | |||
230 | if (copy_from_user(&head, (void __user *)arg, sizeof(head))) | ||
231 | return -EFAULT; | ||
232 | if (head.count > 1024) | ||
233 | return -EINVAL; | ||
234 | lsize = sizeof(struct udmabuf_create_item) * head.count; | ||
235 | list = memdup_user((void __user *)(arg + sizeof(head)), lsize); | ||
236 | if (IS_ERR(list)) | ||
237 | return PTR_ERR(list); | ||
238 | |||
239 | ret = udmabuf_create(&head, list); | ||
240 | kfree(list); | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | static long udmabuf_ioctl(struct file *filp, unsigned int ioctl, | ||
245 | unsigned long arg) | ||
246 | { | ||
247 | long ret; | ||
248 | |||
249 | switch (ioctl) { | ||
250 | case UDMABUF_CREATE: | ||
251 | ret = udmabuf_ioctl_create(filp, arg); | ||
252 | break; | ||
253 | case UDMABUF_CREATE_LIST: | ||
254 | ret = udmabuf_ioctl_create_list(filp, arg); | ||
255 | break; | ||
256 | default: | ||
257 | ret = -EINVAL; | ||
258 | break; | ||
259 | } | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | static const struct file_operations udmabuf_fops = { | ||
264 | .owner = THIS_MODULE, | ||
265 | .unlocked_ioctl = udmabuf_ioctl, | ||
266 | }; | ||
267 | |||
268 | static struct miscdevice udmabuf_misc = { | ||
269 | .minor = MISC_DYNAMIC_MINOR, | ||
270 | .name = "udmabuf", | ||
271 | .fops = &udmabuf_fops, | ||
272 | }; | ||
273 | |||
274 | static int __init udmabuf_dev_init(void) | ||
275 | { | ||
276 | return misc_register(&udmabuf_misc); | ||
277 | } | ||
278 | |||
279 | static void __exit udmabuf_dev_exit(void) | ||
280 | { | ||
281 | misc_deregister(&udmabuf_misc); | ||
282 | } | ||
283 | |||
284 | module_init(udmabuf_dev_init) | ||
285 | module_exit(udmabuf_dev_exit) | ||
286 | |||
287 | MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); | ||
288 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 8843a06360fa..6870909da926 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | |||
@@ -785,28 +785,6 @@ MODULE_DEVICE_TABLE(pci, pciidlist); | |||
785 | 785 | ||
786 | static struct drm_driver kms_driver; | 786 | static struct drm_driver kms_driver; |
787 | 787 | ||
788 | static int amdgpu_kick_out_firmware_fb(struct pci_dev *pdev) | ||
789 | { | ||
790 | struct apertures_struct *ap; | ||
791 | bool primary = false; | ||
792 | |||
793 | ap = alloc_apertures(1); | ||
794 | if (!ap) | ||
795 | return -ENOMEM; | ||
796 | |||
797 | ap->ranges[0].base = pci_resource_start(pdev, 0); | ||
798 | ap->ranges[0].size = pci_resource_len(pdev, 0); | ||
799 | |||
800 | #ifdef CONFIG_X86 | ||
801 | primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; | ||
802 | #endif | ||
803 | drm_fb_helper_remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary); | ||
804 | kfree(ap); | ||
805 | |||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | |||
810 | static int amdgpu_pci_probe(struct pci_dev *pdev, | 788 | static int amdgpu_pci_probe(struct pci_dev *pdev, |
811 | const struct pci_device_id *ent) | 789 | const struct pci_device_id *ent) |
812 | { | 790 | { |
@@ -834,7 +812,7 @@ static int amdgpu_pci_probe(struct pci_dev *pdev, | |||
834 | return ret; | 812 | return ret; |
835 | 813 | ||
836 | /* Get rid of things like offb */ | 814 | /* Get rid of things like offb */ |
837 | ret = amdgpu_kick_out_firmware_fb(pdev); | 815 | ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "amdgpudrmfb"); |
838 | if (ret) | 816 | if (ret) |
839 | return ret; | 817 | return ret; |
840 | 818 | ||
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 800f481a6995..af6adffba788 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | |||
@@ -896,6 +896,7 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) | |||
896 | aconnector->dc_sink = sink; | 896 | aconnector->dc_sink = sink; |
897 | if (sink->dc_edid.length == 0) { | 897 | if (sink->dc_edid.length == 0) { |
898 | aconnector->edid = NULL; | 898 | aconnector->edid = NULL; |
899 | drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); | ||
899 | } else { | 900 | } else { |
900 | aconnector->edid = | 901 | aconnector->edid = |
901 | (struct edid *) sink->dc_edid.raw_edid; | 902 | (struct edid *) sink->dc_edid.raw_edid; |
@@ -903,10 +904,13 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) | |||
903 | 904 | ||
904 | drm_connector_update_edid_property(connector, | 905 | drm_connector_update_edid_property(connector, |
905 | aconnector->edid); | 906 | aconnector->edid); |
907 | drm_dp_cec_set_edid(&aconnector->dm_dp_aux.aux, | ||
908 | aconnector->edid); | ||
906 | } | 909 | } |
907 | amdgpu_dm_add_sink_to_freesync_module(connector, aconnector->edid); | 910 | amdgpu_dm_add_sink_to_freesync_module(connector, aconnector->edid); |
908 | 911 | ||
909 | } else { | 912 | } else { |
913 | drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); | ||
910 | amdgpu_dm_remove_sink_from_freesync_module(connector); | 914 | amdgpu_dm_remove_sink_from_freesync_module(connector); |
911 | drm_connector_update_edid_property(connector, NULL); | 915 | drm_connector_update_edid_property(connector, NULL); |
912 | aconnector->num_modes = 0; | 916 | aconnector->num_modes = 0; |
@@ -1061,8 +1065,10 @@ static void handle_hpd_rx_irq(void *param) | |||
1061 | (dc_link->type == dc_connection_mst_branch)) | 1065 | (dc_link->type == dc_connection_mst_branch)) |
1062 | dm_handle_hpd_rx_irq(aconnector); | 1066 | dm_handle_hpd_rx_irq(aconnector); |
1063 | 1067 | ||
1064 | if (dc_link->type != dc_connection_mst_branch) | 1068 | if (dc_link->type != dc_connection_mst_branch) { |
1069 | drm_dp_cec_irq(&aconnector->dm_dp_aux.aux); | ||
1065 | mutex_unlock(&aconnector->hpd_lock); | 1070 | mutex_unlock(&aconnector->hpd_lock); |
1071 | } | ||
1066 | } | 1072 | } |
1067 | 1073 | ||
1068 | static void register_hpd_handlers(struct amdgpu_device *adev) | 1074 | static void register_hpd_handlers(struct amdgpu_device *adev) |
@@ -2595,6 +2601,7 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = { | |||
2595 | .atomic_duplicate_state = dm_crtc_duplicate_state, | 2601 | .atomic_duplicate_state = dm_crtc_duplicate_state, |
2596 | .atomic_destroy_state = dm_crtc_destroy_state, | 2602 | .atomic_destroy_state = dm_crtc_destroy_state, |
2597 | .set_crc_source = amdgpu_dm_crtc_set_crc_source, | 2603 | .set_crc_source = amdgpu_dm_crtc_set_crc_source, |
2604 | .verify_crc_source = amdgpu_dm_crtc_verify_crc_source, | ||
2598 | .enable_vblank = dm_enable_vblank, | 2605 | .enable_vblank = dm_enable_vblank, |
2599 | .disable_vblank = dm_disable_vblank, | 2606 | .disable_vblank = dm_disable_vblank, |
2600 | }; | 2607 | }; |
@@ -2730,6 +2737,7 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector) | |||
2730 | dm->backlight_dev = NULL; | 2737 | dm->backlight_dev = NULL; |
2731 | } | 2738 | } |
2732 | #endif | 2739 | #endif |
2740 | drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux); | ||
2733 | drm_connector_unregister(connector); | 2741 | drm_connector_unregister(connector); |
2734 | drm_connector_cleanup(connector); | 2742 | drm_connector_cleanup(connector); |
2735 | kfree(connector); | 2743 | kfree(connector); |
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index a29dc35954c9..54056d180003 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | |||
@@ -258,11 +258,14 @@ amdgpu_dm_remove_sink_from_freesync_module(struct drm_connector *connector); | |||
258 | 258 | ||
259 | /* amdgpu_dm_crc.c */ | 259 | /* amdgpu_dm_crc.c */ |
260 | #ifdef CONFIG_DEBUG_FS | 260 | #ifdef CONFIG_DEBUG_FS |
261 | int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name, | 261 | int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name); |
262 | size_t *values_cnt); | 262 | int amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, |
263 | const char *src_name, | ||
264 | size_t *values_cnt); | ||
263 | void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc); | 265 | void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc); |
264 | #else | 266 | #else |
265 | #define amdgpu_dm_crtc_set_crc_source NULL | 267 | #define amdgpu_dm_crtc_set_crc_source NULL |
268 | #define amdgpu_dm_crtc_verify_crc_source NULL | ||
266 | #define amdgpu_dm_crtc_handle_crc_irq(x) | 269 | #define amdgpu_dm_crtc_handle_crc_irq(x) |
267 | #endif | 270 | #endif |
268 | 271 | ||
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c index 9bfb040352e9..01fc5717b657 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c | |||
@@ -46,8 +46,23 @@ static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source) | |||
46 | return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID; | 46 | return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID; |
47 | } | 47 | } |
48 | 48 | ||
49 | int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name, | 49 | int |
50 | size_t *values_cnt) | 50 | amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name, |
51 | size_t *values_cnt) | ||
52 | { | ||
53 | enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name); | ||
54 | |||
55 | if (source < 0) { | ||
56 | DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n", | ||
57 | src_name, crtc->index); | ||
58 | return -EINVAL; | ||
59 | } | ||
60 | |||
61 | *values_cnt = 3; | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) | ||
51 | { | 66 | { |
52 | struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state); | 67 | struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state); |
53 | struct dc_stream_state *stream_state = crtc_state->stream; | 68 | struct dc_stream_state *stream_state = crtc_state->stream; |
@@ -83,7 +98,6 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name, | |||
83 | return -EINVAL; | 98 | return -EINVAL; |
84 | } | 99 | } |
85 | 100 | ||
86 | *values_cnt = 3; | ||
87 | /* Reset crc_skipped on dm state */ | 101 | /* Reset crc_skipped on dm state */ |
88 | crtc_state->crc_skip_count = 0; | 102 | crtc_state->crc_skip_count = 0; |
89 | return 0; | 103 | return 0; |
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 9a300732ba37..18a3a6e5ffa0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c | |||
@@ -496,6 +496,8 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, | |||
496 | aconnector->dm_dp_aux.ddc_service = aconnector->dc_link->ddc; | 496 | aconnector->dm_dp_aux.ddc_service = aconnector->dc_link->ddc; |
497 | 497 | ||
498 | drm_dp_aux_register(&aconnector->dm_dp_aux.aux); | 498 | drm_dp_aux_register(&aconnector->dm_dp_aux.aux); |
499 | drm_dp_cec_register_connector(&aconnector->dm_dp_aux.aux, | ||
500 | aconnector->base.name, dm->adev->dev); | ||
499 | aconnector->mst_mgr.cbs = &dm_mst_cbs; | 501 | aconnector->mst_mgr.cbs = &dm_mst_cbs; |
500 | drm_dp_mst_topology_mgr_init( | 502 | drm_dp_mst_topology_mgr_init( |
501 | &aconnector->mst_mgr, | 503 | &aconnector->mst_mgr, |
diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 29409a65d864..49c37f6dd63e 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c | |||
@@ -78,11 +78,8 @@ static void malidp_plane_reset(struct drm_plane *plane) | |||
78 | kfree(state); | 78 | kfree(state); |
79 | plane->state = NULL; | 79 | plane->state = NULL; |
80 | state = kzalloc(sizeof(*state), GFP_KERNEL); | 80 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
81 | if (state) { | 81 | if (state) |
82 | state->base.plane = plane; | 82 | __drm_atomic_helper_plane_reset(plane, &state->base); |
83 | state->base.rotation = DRM_MODE_ROTATE_0; | ||
84 | plane->state = &state->base; | ||
85 | } | ||
86 | } | 83 | } |
87 | 84 | ||
88 | static struct | 85 | static struct |
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index d73281095fac..9e34bce089d0 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | |||
@@ -101,18 +101,34 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) | |||
101 | (adj->crtc_hdisplay - 1) | | 101 | (adj->crtc_hdisplay - 1) | |
102 | ((adj->crtc_vdisplay - 1) << 16)); | 102 | ((adj->crtc_vdisplay - 1) << 16)); |
103 | 103 | ||
104 | cfg = 0; | 104 | cfg = ATMEL_HLCDC_CLKSEL; |
105 | 105 | ||
106 | prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); | 106 | prate = 2 * clk_get_rate(crtc->dc->hlcdc->sys_clk); |
107 | mode_rate = adj->crtc_clock * 1000; | 107 | mode_rate = adj->crtc_clock * 1000; |
108 | if ((prate / 2) < mode_rate) { | ||
109 | prate *= 2; | ||
110 | cfg |= ATMEL_HLCDC_CLKSEL; | ||
111 | } | ||
112 | 108 | ||
113 | div = DIV_ROUND_UP(prate, mode_rate); | 109 | div = DIV_ROUND_UP(prate, mode_rate); |
114 | if (div < 2) | 110 | if (div < 2) { |
115 | div = 2; | 111 | div = 2; |
112 | } else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) { | ||
113 | /* The divider ended up too big, try a lower base rate. */ | ||
114 | cfg &= ~ATMEL_HLCDC_CLKSEL; | ||
115 | prate /= 2; | ||
116 | div = DIV_ROUND_UP(prate, mode_rate); | ||
117 | if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) | ||
118 | div = ATMEL_HLCDC_CLKDIV_MASK; | ||
119 | } else { | ||
120 | int div_low = prate / mode_rate; | ||
121 | |||
122 | if (div_low >= 2 && | ||
123 | ((prate / div_low - mode_rate) < | ||
124 | 10 * (mode_rate - prate / div))) | ||
125 | /* | ||
126 | * At least 10 times better when using a higher | ||
127 | * frequency than requested, instead of a lower. | ||
128 | * So, go with that. | ||
129 | */ | ||
130 | div = div_low; | ||
131 | } | ||
116 | 132 | ||
117 | cfg |= ATMEL_HLCDC_CLKDIV(div); | 133 | cfg |= ATMEL_HLCDC_CLKDIV(div); |
118 | 134 | ||
@@ -226,6 +242,55 @@ static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c, | |||
226 | #define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) | 242 | #define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) |
227 | #define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) | 243 | #define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) |
228 | 244 | ||
245 | static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state) | ||
246 | { | ||
247 | struct drm_connector *connector = state->connector; | ||
248 | struct drm_display_info *info = &connector->display_info; | ||
249 | struct drm_encoder *encoder; | ||
250 | unsigned int supported_fmts = 0; | ||
251 | int j; | ||
252 | |||
253 | encoder = state->best_encoder; | ||
254 | if (!encoder) | ||
255 | encoder = connector->encoder; | ||
256 | |||
257 | switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { | ||
258 | case 0: | ||
259 | break; | ||
260 | case MEDIA_BUS_FMT_RGB444_1X12: | ||
261 | return ATMEL_HLCDC_RGB444_OUTPUT; | ||
262 | case MEDIA_BUS_FMT_RGB565_1X16: | ||
263 | return ATMEL_HLCDC_RGB565_OUTPUT; | ||
264 | case MEDIA_BUS_FMT_RGB666_1X18: | ||
265 | return ATMEL_HLCDC_RGB666_OUTPUT; | ||
266 | case MEDIA_BUS_FMT_RGB888_1X24: | ||
267 | return ATMEL_HLCDC_RGB888_OUTPUT; | ||
268 | default: | ||
269 | return -EINVAL; | ||
270 | } | ||
271 | |||
272 | for (j = 0; j < info->num_bus_formats; j++) { | ||
273 | switch (info->bus_formats[j]) { | ||
274 | case MEDIA_BUS_FMT_RGB444_1X12: | ||
275 | supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; | ||
276 | break; | ||
277 | case MEDIA_BUS_FMT_RGB565_1X16: | ||
278 | supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; | ||
279 | break; | ||
280 | case MEDIA_BUS_FMT_RGB666_1X18: | ||
281 | supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; | ||
282 | break; | ||
283 | case MEDIA_BUS_FMT_RGB888_1X24: | ||
284 | supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; | ||
285 | break; | ||
286 | default: | ||
287 | break; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | return supported_fmts; | ||
292 | } | ||
293 | |||
229 | static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) | 294 | static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) |
230 | { | 295 | { |
231 | unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; | 296 | unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; |
@@ -238,31 +303,12 @@ static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) | |||
238 | crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); | 303 | crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); |
239 | 304 | ||
240 | for_each_new_connector_in_state(state->state, connector, cstate, i) { | 305 | for_each_new_connector_in_state(state->state, connector, cstate, i) { |
241 | struct drm_display_info *info = &connector->display_info; | ||
242 | unsigned int supported_fmts = 0; | 306 | unsigned int supported_fmts = 0; |
243 | int j; | ||
244 | 307 | ||
245 | if (!cstate->crtc) | 308 | if (!cstate->crtc) |
246 | continue; | 309 | continue; |
247 | 310 | ||
248 | for (j = 0; j < info->num_bus_formats; j++) { | 311 | supported_fmts = atmel_hlcdc_connector_output_mode(cstate); |
249 | switch (info->bus_formats[j]) { | ||
250 | case MEDIA_BUS_FMT_RGB444_1X12: | ||
251 | supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; | ||
252 | break; | ||
253 | case MEDIA_BUS_FMT_RGB565_1X16: | ||
254 | supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; | ||
255 | break; | ||
256 | case MEDIA_BUS_FMT_RGB666_1X18: | ||
257 | supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; | ||
258 | break; | ||
259 | case MEDIA_BUS_FMT_RGB888_1X24: | ||
260 | supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; | ||
261 | break; | ||
262 | default: | ||
263 | break; | ||
264 | } | ||
265 | } | ||
266 | 312 | ||
267 | if (crtc->dc->desc->conflicting_output_formats) | 313 | if (crtc->dc->desc->conflicting_output_formats) |
268 | output_fmts &= supported_fmts; | 314 | output_fmts &= supported_fmts; |
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 60c937f42114..4cc1e03f0aee 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | |||
@@ -441,5 +441,6 @@ void atmel_hlcdc_crtc_irq(struct drm_crtc *c); | |||
441 | int atmel_hlcdc_crtc_create(struct drm_device *dev); | 441 | int atmel_hlcdc_crtc_create(struct drm_device *dev); |
442 | 442 | ||
443 | int atmel_hlcdc_create_outputs(struct drm_device *dev); | 443 | int atmel_hlcdc_create_outputs(struct drm_device *dev); |
444 | int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder); | ||
444 | 445 | ||
445 | #endif /* DRM_ATMEL_HLCDC_H */ | 446 | #endif /* DRM_ATMEL_HLCDC_H */ |
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 8db51fb131db..f73d8a92274e 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | |||
@@ -27,33 +27,94 @@ | |||
27 | 27 | ||
28 | #include "atmel_hlcdc_dc.h" | 28 | #include "atmel_hlcdc_dc.h" |
29 | 29 | ||
30 | struct atmel_hlcdc_rgb_output { | ||
31 | struct drm_encoder encoder; | ||
32 | int bus_fmt; | ||
33 | }; | ||
34 | |||
30 | static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { | 35 | static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { |
31 | .destroy = drm_encoder_cleanup, | 36 | .destroy = drm_encoder_cleanup, |
32 | }; | 37 | }; |
33 | 38 | ||
39 | static struct atmel_hlcdc_rgb_output * | ||
40 | atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder) | ||
41 | { | ||
42 | return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); | ||
43 | } | ||
44 | |||
45 | int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder) | ||
46 | { | ||
47 | struct atmel_hlcdc_rgb_output *output; | ||
48 | |||
49 | output = atmel_hlcdc_encoder_to_rgb_output(encoder); | ||
50 | |||
51 | return output->bus_fmt; | ||
52 | } | ||
53 | |||
54 | static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep) | ||
55 | { | ||
56 | u32 bus_width; | ||
57 | int ret; | ||
58 | |||
59 | ret = of_property_read_u32(ep, "bus-width", &bus_width); | ||
60 | if (ret == -EINVAL) | ||
61 | return 0; | ||
62 | if (ret) | ||
63 | return ret; | ||
64 | |||
65 | switch (bus_width) { | ||
66 | case 12: | ||
67 | return MEDIA_BUS_FMT_RGB444_1X12; | ||
68 | case 16: | ||
69 | return MEDIA_BUS_FMT_RGB565_1X16; | ||
70 | case 18: | ||
71 | return MEDIA_BUS_FMT_RGB666_1X18; | ||
72 | case 24: | ||
73 | return MEDIA_BUS_FMT_RGB888_1X24; | ||
74 | default: | ||
75 | return -EINVAL; | ||
76 | } | ||
77 | } | ||
78 | |||
34 | static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) | 79 | static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) |
35 | { | 80 | { |
36 | struct drm_encoder *encoder; | 81 | struct atmel_hlcdc_rgb_output *output; |
82 | struct device_node *ep; | ||
37 | struct drm_panel *panel; | 83 | struct drm_panel *panel; |
38 | struct drm_bridge *bridge; | 84 | struct drm_bridge *bridge; |
39 | int ret; | 85 | int ret; |
40 | 86 | ||
87 | ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint); | ||
88 | if (!ep) | ||
89 | return -ENODEV; | ||
90 | |||
41 | ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint, | 91 | ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint, |
42 | &panel, &bridge); | 92 | &panel, &bridge); |
43 | if (ret) | 93 | if (ret) { |
94 | of_node_put(ep); | ||
44 | return ret; | 95 | return ret; |
96 | } | ||
45 | 97 | ||
46 | encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL); | 98 | output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); |
47 | if (!encoder) | 99 | if (!output) { |
100 | of_node_put(ep); | ||
101 | return -ENOMEM; | ||
102 | } | ||
103 | |||
104 | output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); | ||
105 | of_node_put(ep); | ||
106 | if (output->bus_fmt < 0) { | ||
107 | dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint); | ||
48 | return -EINVAL; | 108 | return -EINVAL; |
109 | } | ||
49 | 110 | ||
50 | ret = drm_encoder_init(dev, encoder, | 111 | ret = drm_encoder_init(dev, &output->encoder, |
51 | &atmel_hlcdc_panel_encoder_funcs, | 112 | &atmel_hlcdc_panel_encoder_funcs, |
52 | DRM_MODE_ENCODER_NONE, NULL); | 113 | DRM_MODE_ENCODER_NONE, NULL); |
53 | if (ret) | 114 | if (ret) |
54 | return ret; | 115 | return ret; |
55 | 116 | ||
56 | encoder->possible_crtcs = 0x1; | 117 | output->encoder.possible_crtcs = 0x1; |
57 | 118 | ||
58 | if (panel) { | 119 | if (panel) { |
59 | bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); | 120 | bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); |
@@ -62,7 +123,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) | |||
62 | } | 123 | } |
63 | 124 | ||
64 | if (bridge) { | 125 | if (bridge) { |
65 | ret = drm_bridge_attach(encoder, bridge, NULL); | 126 | ret = drm_bridge_attach(&output->encoder, bridge, NULL); |
66 | if (!ret) | 127 | if (!ret) |
67 | return 0; | 128 | return 0; |
68 | 129 | ||
@@ -70,7 +131,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) | |||
70 | drm_panel_bridge_remove(bridge); | 131 | drm_panel_bridge_remove(bridge); |
71 | } | 132 | } |
72 | 133 | ||
73 | drm_encoder_cleanup(encoder); | 134 | drm_encoder_cleanup(&output->encoder); |
74 | 135 | ||
75 | return ret; | 136 | return ret; |
76 | } | 137 | } |
@@ -78,12 +139,23 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) | |||
78 | int atmel_hlcdc_create_outputs(struct drm_device *dev) | 139 | int atmel_hlcdc_create_outputs(struct drm_device *dev) |
79 | { | 140 | { |
80 | int endpoint, ret = 0; | 141 | int endpoint, ret = 0; |
142 | int attached = 0; | ||
81 | 143 | ||
82 | for (endpoint = 0; !ret; endpoint++) | 144 | /* |
145 | * Always scan the first few endpoints even if we get -ENODEV, | ||
146 | * but keep going after that as long as we keep getting hits. | ||
147 | */ | ||
148 | for (endpoint = 0; !ret || endpoint < 4; endpoint++) { | ||
83 | ret = atmel_hlcdc_attach_endpoint(dev, endpoint); | 149 | ret = atmel_hlcdc_attach_endpoint(dev, endpoint); |
150 | if (ret == -ENODEV) | ||
151 | continue; | ||
152 | if (ret) | ||
153 | break; | ||
154 | attached++; | ||
155 | } | ||
84 | 156 | ||
85 | /* At least one device was successfully attached.*/ | 157 | /* At least one device was successfully attached.*/ |
86 | if (ret == -ENODEV && endpoint) | 158 | if (ret == -ENODEV && attached) |
87 | return 0; | 159 | return 0; |
88 | 160 | ||
89 | return ret; | 161 | return ret; |
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 04440064b9b7..9330a076e15a 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | |||
@@ -942,10 +942,7 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p) | |||
942 | "Failed to allocate initial plane state\n"); | 942 | "Failed to allocate initial plane state\n"); |
943 | return; | 943 | return; |
944 | } | 944 | } |
945 | 945 | __drm_atomic_helper_plane_reset(p, &state->base); | |
946 | p->state = &state->base; | ||
947 | p->state->alpha = DRM_BLEND_ALPHA_OPAQUE; | ||
948 | p->state->plane = p; | ||
949 | } | 946 | } |
950 | } | 947 | } |
951 | 948 | ||
diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index 7b20318483e4..c61b40c72b62 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c | |||
@@ -143,22 +143,6 @@ static const struct dev_pm_ops bochs_pm_ops = { | |||
143 | /* ---------------------------------------------------------------------- */ | 143 | /* ---------------------------------------------------------------------- */ |
144 | /* pci interface */ | 144 | /* pci interface */ |
145 | 145 | ||
146 | static int bochs_kick_out_firmware_fb(struct pci_dev *pdev) | ||
147 | { | ||
148 | struct apertures_struct *ap; | ||
149 | |||
150 | ap = alloc_apertures(1); | ||
151 | if (!ap) | ||
152 | return -ENOMEM; | ||
153 | |||
154 | ap->ranges[0].base = pci_resource_start(pdev, 0); | ||
155 | ap->ranges[0].size = pci_resource_len(pdev, 0); | ||
156 | drm_fb_helper_remove_conflicting_framebuffers(ap, "bochsdrmfb", false); | ||
157 | kfree(ap); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int bochs_pci_probe(struct pci_dev *pdev, | 146 | static int bochs_pci_probe(struct pci_dev *pdev, |
163 | const struct pci_device_id *ent) | 147 | const struct pci_device_id *ent) |
164 | { | 148 | { |
@@ -171,7 +155,7 @@ static int bochs_pci_probe(struct pci_dev *pdev, | |||
171 | return -ENOMEM; | 155 | return -ENOMEM; |
172 | } | 156 | } |
173 | 157 | ||
174 | ret = bochs_kick_out_firmware_fb(pdev); | 158 | ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "bochsdrmfb"); |
175 | if (ret) | 159 | if (ret) |
176 | return ret; | 160 | return ret; |
177 | 161 | ||
diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c index 39cd08416773..c9c7097030ca 100644 --- a/drivers/gpu/drm/bochs/bochs_mm.c +++ b/drivers/gpu/drm/bochs/bochs_mm.c | |||
@@ -430,7 +430,7 @@ static void bochs_bo_unref(struct bochs_bo **bo) | |||
430 | return; | 430 | return; |
431 | 431 | ||
432 | tbo = &((*bo)->bo); | 432 | tbo = &((*bo)->bo); |
433 | ttm_bo_unref(&tbo); | 433 | ttm_bo_put(tbo); |
434 | *bo = NULL; | 434 | *bo = NULL; |
435 | } | 435 | } |
436 | 436 | ||
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index bf6cad6c9178..9eeb8ef0b174 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig | |||
@@ -112,6 +112,14 @@ config DRM_THINE_THC63LVD1024 | |||
112 | ---help--- | 112 | ---help--- |
113 | Thine THC63LVD1024 LVDS/parallel converter driver. | 113 | Thine THC63LVD1024 LVDS/parallel converter driver. |
114 | 114 | ||
115 | config DRM_TOSHIBA_TC358764 | ||
116 | tristate "TC358764 DSI/LVDS bridge" | ||
117 | depends on DRM && DRM_PANEL | ||
118 | depends on OF | ||
119 | select DRM_MIPI_DSI | ||
120 | help | ||
121 | Toshiba TC358764 DSI/LVDS bridge driver. | ||
122 | |||
115 | config DRM_TOSHIBA_TC358767 | 123 | config DRM_TOSHIBA_TC358767 |
116 | tristate "Toshiba TC358767 eDP bridge" | 124 | tristate "Toshiba TC358767 eDP bridge" |
117 | depends on OF | 125 | depends on OF |
@@ -128,6 +136,16 @@ config DRM_TI_TFP410 | |||
128 | ---help--- | 136 | ---help--- |
129 | Texas Instruments TFP410 DVI/HDMI Transmitter driver | 137 | Texas Instruments TFP410 DVI/HDMI Transmitter driver |
130 | 138 | ||
139 | config DRM_TI_SN65DSI86 | ||
140 | tristate "TI SN65DSI86 DSI to eDP bridge" | ||
141 | depends on OF | ||
142 | select DRM_KMS_HELPER | ||
143 | select REGMAP_I2C | ||
144 | select DRM_PANEL | ||
145 | select DRM_MIPI_DSI | ||
146 | help | ||
147 | Texas Instruments SN65DSI86 DSI to eDP Bridge driver | ||
148 | |||
131 | source "drivers/gpu/drm/bridge/analogix/Kconfig" | 149 | source "drivers/gpu/drm/bridge/analogix/Kconfig" |
132 | 150 | ||
133 | source "drivers/gpu/drm/bridge/adv7511/Kconfig" | 151 | source "drivers/gpu/drm/bridge/adv7511/Kconfig" |
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 35f88d48ec20..4934fcf5a6f8 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile | |||
@@ -10,8 +10,10 @@ obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o | |||
10 | obj-$(CONFIG_DRM_SII902X) += sii902x.o | 10 | obj-$(CONFIG_DRM_SII902X) += sii902x.o |
11 | obj-$(CONFIG_DRM_SII9234) += sii9234.o | 11 | obj-$(CONFIG_DRM_SII9234) += sii9234.o |
12 | obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o | 12 | obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o |
13 | obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o | ||
13 | obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o | 14 | obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o |
14 | obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ | 15 | obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ |
15 | obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ | 16 | obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ |
17 | obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o | ||
16 | obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o | 18 | obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o |
17 | obj-y += synopsys/ | 19 | obj-y += synopsys/ |
diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile index 5dad97d920be..3e1b1e3d9533 100644 --- a/drivers/gpu/drm/bridge/synopsys/Makefile +++ b/drivers/gpu/drm/bridge/synopsys/Makefile | |||
@@ -1,5 +1,3 @@ | |||
1 | #ccflags-y := -Iinclude/drm | ||
2 | |||
3 | obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o | 1 | obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o |
4 | obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o | 2 | obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o |
5 | obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o | 3 | obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o |
diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c new file mode 100644 index 000000000000..ee6b98efa9c2 --- /dev/null +++ b/drivers/gpu/drm/bridge/tc358764.c | |||
@@ -0,0 +1,499 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2018 Samsung Electronics Co., Ltd | ||
4 | * | ||
5 | * Authors: | ||
6 | * Andrzej Hajda <a.hajda@samsung.com> | ||
7 | * Maciej Purski <m.purski@samsung.com> | ||
8 | */ | ||
9 | |||
10 | #include <drm/drm_atomic_helper.h> | ||
11 | #include <drm/drm_crtc.h> | ||
12 | #include <drm/drm_crtc_helper.h> | ||
13 | #include <drm/drm_fb_helper.h> | ||
14 | #include <drm/drm_mipi_dsi.h> | ||
15 | #include <drm/drm_of.h> | ||
16 | #include <drm/drm_panel.h> | ||
17 | #include <drm/drmP.h> | ||
18 | #include <linux/gpio/consumer.h> | ||
19 | #include <linux/of_graph.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <video/mipi_display.h> | ||
22 | |||
23 | #define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) | ||
24 | #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) | ||
25 | |||
26 | /* PPI layer registers */ | ||
27 | #define PPI_STARTPPI 0x0104 /* START control bit */ | ||
28 | #define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */ | ||
29 | #define PPI_LANEENABLE 0x0134 /* Enables each lane */ | ||
30 | #define PPI_TX_RX_TA 0x013C /* BTA timing parameters */ | ||
31 | #define PPI_D0S_CLRSIPOCOUNT 0x0164 /* Assertion timer for Lane 0 */ | ||
32 | #define PPI_D1S_CLRSIPOCOUNT 0x0168 /* Assertion timer for Lane 1 */ | ||
33 | #define PPI_D2S_CLRSIPOCOUNT 0x016C /* Assertion timer for Lane 2 */ | ||
34 | #define PPI_D3S_CLRSIPOCOUNT 0x0170 /* Assertion timer for Lane 3 */ | ||
35 | #define PPI_START_FUNCTION 1 | ||
36 | |||
37 | /* DSI layer registers */ | ||
38 | #define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */ | ||
39 | #define DSI_LANEENABLE 0x0210 /* Enables each lane */ | ||
40 | #define DSI_RX_START 1 | ||
41 | |||
42 | /* Video path registers */ | ||
43 | #define VP_CTRL 0x0450 /* Video Path Control */ | ||
44 | #define VP_CTRL_MSF(v) FLD_VAL(v, 0, 0) /* Magic square in RGB666 */ | ||
45 | #define VP_CTRL_VTGEN(v) FLD_VAL(v, 4, 4) /* Use chip clock for timing */ | ||
46 | #define VP_CTRL_EVTMODE(v) FLD_VAL(v, 5, 5) /* Event mode */ | ||
47 | #define VP_CTRL_RGB888(v) FLD_VAL(v, 8, 8) /* RGB888 mode */ | ||
48 | #define VP_CTRL_VSDELAY(v) FLD_VAL(v, 31, 20) /* VSYNC delay */ | ||
49 | #define VP_CTRL_HSPOL BIT(17) /* Polarity of HSYNC signal */ | ||
50 | #define VP_CTRL_DEPOL BIT(18) /* Polarity of DE signal */ | ||
51 | #define VP_CTRL_VSPOL BIT(19) /* Polarity of VSYNC signal */ | ||
52 | #define VP_HTIM1 0x0454 /* Horizontal Timing Control 1 */ | ||
53 | #define VP_HTIM1_HBP(v) FLD_VAL(v, 24, 16) | ||
54 | #define VP_HTIM1_HSYNC(v) FLD_VAL(v, 8, 0) | ||
55 | #define VP_HTIM2 0x0458 /* Horizontal Timing Control 2 */ | ||
56 | #define VP_HTIM2_HFP(v) FLD_VAL(v, 24, 16) | ||
57 | #define VP_HTIM2_HACT(v) FLD_VAL(v, 10, 0) | ||
58 | #define VP_VTIM1 0x045C /* Vertical Timing Control 1 */ | ||
59 | #define VP_VTIM1_VBP(v) FLD_VAL(v, 23, 16) | ||
60 | #define VP_VTIM1_VSYNC(v) FLD_VAL(v, 7, 0) | ||
61 | #define VP_VTIM2 0x0460 /* Vertical Timing Control 2 */ | ||
62 | #define VP_VTIM2_VFP(v) FLD_VAL(v, 23, 16) | ||
63 | #define VP_VTIM2_VACT(v) FLD_VAL(v, 10, 0) | ||
64 | #define VP_VFUEN 0x0464 /* Video Frame Timing Update Enable */ | ||
65 | |||
66 | /* LVDS registers */ | ||
67 | #define LV_MX0003 0x0480 /* Mux input bit 0 to 3 */ | ||
68 | #define LV_MX0407 0x0484 /* Mux input bit 4 to 7 */ | ||
69 | #define LV_MX0811 0x0488 /* Mux input bit 8 to 11 */ | ||
70 | #define LV_MX1215 0x048C /* Mux input bit 12 to 15 */ | ||
71 | #define LV_MX1619 0x0490 /* Mux input bit 16 to 19 */ | ||
72 | #define LV_MX2023 0x0494 /* Mux input bit 20 to 23 */ | ||
73 | #define LV_MX2427 0x0498 /* Mux input bit 24 to 27 */ | ||
74 | #define LV_MX(b0, b1, b2, b3) (FLD_VAL(b0, 4, 0) | FLD_VAL(b1, 12, 8) | \ | ||
75 | FLD_VAL(b2, 20, 16) | FLD_VAL(b3, 28, 24)) | ||
76 | |||
77 | /* Input bit numbers used in mux registers */ | ||
78 | enum { | ||
79 | LVI_R0, | ||
80 | LVI_R1, | ||
81 | LVI_R2, | ||
82 | LVI_R3, | ||
83 | LVI_R4, | ||
84 | LVI_R5, | ||
85 | LVI_R6, | ||
86 | LVI_R7, | ||
87 | LVI_G0, | ||
88 | LVI_G1, | ||
89 | LVI_G2, | ||
90 | LVI_G3, | ||
91 | LVI_G4, | ||
92 | LVI_G5, | ||
93 | LVI_G6, | ||
94 | LVI_G7, | ||
95 | LVI_B0, | ||
96 | LVI_B1, | ||
97 | LVI_B2, | ||
98 | LVI_B3, | ||
99 | LVI_B4, | ||
100 | LVI_B5, | ||
101 | LVI_B6, | ||
102 | LVI_B7, | ||
103 | LVI_HS, | ||
104 | LVI_VS, | ||
105 | LVI_DE, | ||
106 | LVI_L0 | ||
107 | }; | ||
108 | |||
109 | #define LV_CFG 0x049C /* LVDS Configuration */ | ||
110 | #define LV_PHY0 0x04A0 /* LVDS PHY 0 */ | ||
111 | #define LV_PHY0_RST(v) FLD_VAL(v, 22, 22) /* PHY reset */ | ||
112 | #define LV_PHY0_IS(v) FLD_VAL(v, 15, 14) | ||
113 | #define LV_PHY0_ND(v) FLD_VAL(v, 4, 0) /* Frequency range select */ | ||
114 | #define LV_PHY0_PRBS_ON(v) FLD_VAL(v, 20, 16) /* Clock/Data Flag pins */ | ||
115 | |||
116 | /* System registers */ | ||
117 | #define SYS_RST 0x0504 /* System Reset */ | ||
118 | #define SYS_ID 0x0580 /* System ID */ | ||
119 | |||
120 | #define SYS_RST_I2CS BIT(0) /* Reset I2C-Slave controller */ | ||
121 | #define SYS_RST_I2CM BIT(1) /* Reset I2C-Master controller */ | ||
122 | #define SYS_RST_LCD BIT(2) /* Reset LCD controller */ | ||
123 | #define SYS_RST_BM BIT(3) /* Reset Bus Management controller */ | ||
124 | #define SYS_RST_DSIRX BIT(4) /* Reset DSI-RX and App controller */ | ||
125 | #define SYS_RST_REG BIT(5) /* Reset Register module */ | ||
126 | |||
127 | #define LPX_PERIOD 2 | ||
128 | #define TTA_SURE 3 | ||
129 | #define TTA_GET 0x20000 | ||
130 | |||
131 | /* Lane enable PPI and DSI register bits */ | ||
132 | #define LANEENABLE_CLEN BIT(0) | ||
133 | #define LANEENABLE_L0EN BIT(1) | ||
134 | #define LANEENABLE_L1EN BIT(2) | ||
135 | #define LANEENABLE_L2EN BIT(3) | ||
136 | #define LANEENABLE_L3EN BIT(4) | ||
137 | |||
138 | /* LVCFG fields */ | ||
139 | #define LV_CFG_LVEN BIT(0) | ||
140 | #define LV_CFG_LVDLINK BIT(1) | ||
141 | #define LV_CFG_CLKPOL1 BIT(2) | ||
142 | #define LV_CFG_CLKPOL2 BIT(3) | ||
143 | |||
144 | static const char * const tc358764_supplies[] = { | ||
145 | "vddc", "vddio", "vddlvds" | ||
146 | }; | ||
147 | |||
148 | struct tc358764 { | ||
149 | struct device *dev; | ||
150 | struct drm_bridge bridge; | ||
151 | struct drm_connector connector; | ||
152 | struct regulator_bulk_data supplies[ARRAY_SIZE(tc358764_supplies)]; | ||
153 | struct gpio_desc *gpio_reset; | ||
154 | struct drm_panel *panel; | ||
155 | int error; | ||
156 | }; | ||
157 | |||
158 | static int tc358764_clear_error(struct tc358764 *ctx) | ||
159 | { | ||
160 | int ret = ctx->error; | ||
161 | |||
162 | ctx->error = 0; | ||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | static void tc358764_read(struct tc358764 *ctx, u16 addr, u32 *val) | ||
167 | { | ||
168 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | ||
169 | ssize_t ret; | ||
170 | |||
171 | if (ctx->error) | ||
172 | return; | ||
173 | |||
174 | cpu_to_le16s(&addr); | ||
175 | ret = mipi_dsi_generic_read(dsi, &addr, sizeof(addr), val, sizeof(*val)); | ||
176 | if (ret >= 0) | ||
177 | le32_to_cpus(val); | ||
178 | |||
179 | dev_dbg(ctx->dev, "read: %d, addr: %d\n", addr, *val); | ||
180 | } | ||
181 | |||
182 | static void tc358764_write(struct tc358764 *ctx, u16 addr, u32 val) | ||
183 | { | ||
184 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | ||
185 | ssize_t ret; | ||
186 | u8 data[6]; | ||
187 | |||
188 | if (ctx->error) | ||
189 | return; | ||
190 | |||
191 | data[0] = addr; | ||
192 | data[1] = addr >> 8; | ||
193 | data[2] = val; | ||
194 | data[3] = val >> 8; | ||
195 | data[4] = val >> 16; | ||
196 | data[5] = val >> 24; | ||
197 | |||
198 | ret = mipi_dsi_generic_write(dsi, data, sizeof(data)); | ||
199 | if (ret < 0) | ||
200 | ctx->error = ret; | ||
201 | } | ||
202 | |||
203 | static inline struct tc358764 *bridge_to_tc358764(struct drm_bridge *bridge) | ||
204 | { | ||
205 | return container_of(bridge, struct tc358764, bridge); | ||
206 | } | ||
207 | |||
208 | static inline | ||
209 | struct tc358764 *connector_to_tc358764(struct drm_connector *connector) | ||
210 | { | ||
211 | return container_of(connector, struct tc358764, connector); | ||
212 | } | ||
213 | |||
214 | static int tc358764_init(struct tc358764 *ctx) | ||
215 | { | ||
216 | u32 v = 0; | ||
217 | |||
218 | tc358764_read(ctx, SYS_ID, &v); | ||
219 | if (ctx->error) | ||
220 | return tc358764_clear_error(ctx); | ||
221 | dev_info(ctx->dev, "ID: %#x\n", v); | ||
222 | |||
223 | /* configure PPI counters */ | ||
224 | tc358764_write(ctx, PPI_TX_RX_TA, TTA_GET | TTA_SURE); | ||
225 | tc358764_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD); | ||
226 | tc358764_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5); | ||
227 | tc358764_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5); | ||
228 | tc358764_write(ctx, PPI_D2S_CLRSIPOCOUNT, 5); | ||
229 | tc358764_write(ctx, PPI_D3S_CLRSIPOCOUNT, 5); | ||
230 | |||
231 | /* enable four data lanes and clock lane */ | ||
232 | tc358764_write(ctx, PPI_LANEENABLE, LANEENABLE_L3EN | LANEENABLE_L2EN | | ||
233 | LANEENABLE_L1EN | LANEENABLE_L0EN | LANEENABLE_CLEN); | ||
234 | tc358764_write(ctx, DSI_LANEENABLE, LANEENABLE_L3EN | LANEENABLE_L2EN | | ||
235 | LANEENABLE_L1EN | LANEENABLE_L0EN | LANEENABLE_CLEN); | ||
236 | |||
237 | /* start */ | ||
238 | tc358764_write(ctx, PPI_STARTPPI, PPI_START_FUNCTION); | ||
239 | tc358764_write(ctx, DSI_STARTDSI, DSI_RX_START); | ||
240 | |||
241 | /* configure video path */ | ||
242 | tc358764_write(ctx, VP_CTRL, VP_CTRL_VSDELAY(15) | VP_CTRL_RGB888(1) | | ||
243 | VP_CTRL_EVTMODE(1) | VP_CTRL_HSPOL | VP_CTRL_VSPOL); | ||
244 | |||
245 | /* reset PHY */ | ||
246 | tc358764_write(ctx, LV_PHY0, LV_PHY0_RST(1) | | ||
247 | LV_PHY0_PRBS_ON(4) | LV_PHY0_IS(2) | LV_PHY0_ND(6)); | ||
248 | tc358764_write(ctx, LV_PHY0, LV_PHY0_PRBS_ON(4) | LV_PHY0_IS(2) | | ||
249 | LV_PHY0_ND(6)); | ||
250 | |||
251 | /* reset bridge */ | ||
252 | tc358764_write(ctx, SYS_RST, SYS_RST_LCD); | ||
253 | |||
254 | /* set bit order */ | ||
255 | tc358764_write(ctx, LV_MX0003, LV_MX(LVI_R0, LVI_R1, LVI_R2, LVI_R3)); | ||
256 | tc358764_write(ctx, LV_MX0407, LV_MX(LVI_R4, LVI_R7, LVI_R5, LVI_G0)); | ||
257 | tc358764_write(ctx, LV_MX0811, LV_MX(LVI_G1, LVI_G2, LVI_G6, LVI_G7)); | ||
258 | tc358764_write(ctx, LV_MX1215, LV_MX(LVI_G3, LVI_G4, LVI_G5, LVI_B0)); | ||
259 | tc358764_write(ctx, LV_MX1619, LV_MX(LVI_B6, LVI_B7, LVI_B1, LVI_B2)); | ||
260 | tc358764_write(ctx, LV_MX2023, LV_MX(LVI_B3, LVI_B4, LVI_B5, LVI_L0)); | ||
261 | tc358764_write(ctx, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_R6)); | ||
262 | tc358764_write(ctx, LV_CFG, LV_CFG_CLKPOL2 | LV_CFG_CLKPOL1 | | ||
263 | LV_CFG_LVEN); | ||
264 | |||
265 | return tc358764_clear_error(ctx); | ||
266 | } | ||
267 | |||
268 | static void tc358764_reset(struct tc358764 *ctx) | ||
269 | { | ||
270 | gpiod_set_value(ctx->gpio_reset, 1); | ||
271 | usleep_range(1000, 2000); | ||
272 | gpiod_set_value(ctx->gpio_reset, 0); | ||
273 | usleep_range(1000, 2000); | ||
274 | } | ||
275 | |||
276 | static int tc358764_get_modes(struct drm_connector *connector) | ||
277 | { | ||
278 | struct tc358764 *ctx = connector_to_tc358764(connector); | ||
279 | |||
280 | return drm_panel_get_modes(ctx->panel); | ||
281 | } | ||
282 | |||
283 | static const | ||
284 | struct drm_connector_helper_funcs tc358764_connector_helper_funcs = { | ||
285 | .get_modes = tc358764_get_modes, | ||
286 | }; | ||
287 | |||
288 | static const struct drm_connector_funcs tc358764_connector_funcs = { | ||
289 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
290 | .destroy = drm_connector_cleanup, | ||
291 | .reset = drm_atomic_helper_connector_reset, | ||
292 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||
293 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||
294 | }; | ||
295 | |||
296 | static void tc358764_disable(struct drm_bridge *bridge) | ||
297 | { | ||
298 | struct tc358764 *ctx = bridge_to_tc358764(bridge); | ||
299 | int ret = drm_panel_disable(bridge_to_tc358764(bridge)->panel); | ||
300 | |||
301 | if (ret < 0) | ||
302 | dev_err(ctx->dev, "error disabling panel (%d)\n", ret); | ||
303 | } | ||
304 | |||
305 | static void tc358764_post_disable(struct drm_bridge *bridge) | ||
306 | { | ||
307 | struct tc358764 *ctx = bridge_to_tc358764(bridge); | ||
308 | int ret; | ||
309 | |||
310 | ret = drm_panel_unprepare(ctx->panel); | ||
311 | if (ret < 0) | ||
312 | dev_err(ctx->dev, "error unpreparing panel (%d)\n", ret); | ||
313 | tc358764_reset(ctx); | ||
314 | usleep_range(10000, 15000); | ||
315 | ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); | ||
316 | if (ret < 0) | ||
317 | dev_err(ctx->dev, "error disabling regulators (%d)\n", ret); | ||
318 | } | ||
319 | |||
320 | static void tc358764_pre_enable(struct drm_bridge *bridge) | ||
321 | { | ||
322 | struct tc358764 *ctx = bridge_to_tc358764(bridge); | ||
323 | int ret; | ||
324 | |||
325 | ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); | ||
326 | if (ret < 0) | ||
327 | dev_err(ctx->dev, "error enabling regulators (%d)\n", ret); | ||
328 | usleep_range(10000, 15000); | ||
329 | tc358764_reset(ctx); | ||
330 | ret = tc358764_init(ctx); | ||
331 | if (ret < 0) | ||
332 | dev_err(ctx->dev, "error initializing bridge (%d)\n", ret); | ||
333 | ret = drm_panel_prepare(ctx->panel); | ||
334 | if (ret < 0) | ||
335 | dev_err(ctx->dev, "error preparing panel (%d)\n", ret); | ||
336 | } | ||
337 | |||
338 | static void tc358764_enable(struct drm_bridge *bridge) | ||
339 | { | ||
340 | struct tc358764 *ctx = bridge_to_tc358764(bridge); | ||
341 | int ret = drm_panel_enable(ctx->panel); | ||
342 | |||
343 | if (ret < 0) | ||
344 | dev_err(ctx->dev, "error enabling panel (%d)\n", ret); | ||
345 | } | ||
346 | |||
347 | static int tc358764_attach(struct drm_bridge *bridge) | ||
348 | { | ||
349 | struct tc358764 *ctx = bridge_to_tc358764(bridge); | ||
350 | struct drm_device *drm = bridge->dev; | ||
351 | int ret; | ||
352 | |||
353 | ctx->connector.polled = DRM_CONNECTOR_POLL_HPD; | ||
354 | ret = drm_connector_init(drm, &ctx->connector, | ||
355 | &tc358764_connector_funcs, | ||
356 | DRM_MODE_CONNECTOR_LVDS); | ||
357 | if (ret) { | ||
358 | DRM_ERROR("Failed to initialize connector\n"); | ||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | drm_connector_helper_add(&ctx->connector, | ||
363 | &tc358764_connector_helper_funcs); | ||
364 | drm_connector_attach_encoder(&ctx->connector, bridge->encoder); | ||
365 | drm_panel_attach(ctx->panel, &ctx->connector); | ||
366 | ctx->connector.funcs->reset(&ctx->connector); | ||
367 | drm_fb_helper_add_one_connector(drm->fb_helper, &ctx->connector); | ||
368 | drm_connector_register(&ctx->connector); | ||
369 | |||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static void tc358764_detach(struct drm_bridge *bridge) | ||
374 | { | ||
375 | struct tc358764 *ctx = bridge_to_tc358764(bridge); | ||
376 | struct drm_device *drm = bridge->dev; | ||
377 | |||
378 | drm_connector_unregister(&ctx->connector); | ||
379 | drm_fb_helper_remove_one_connector(drm->fb_helper, &ctx->connector); | ||
380 | drm_panel_detach(ctx->panel); | ||
381 | ctx->panel = NULL; | ||
382 | drm_connector_unreference(&ctx->connector); | ||
383 | } | ||
384 | |||
385 | static const struct drm_bridge_funcs tc358764_bridge_funcs = { | ||
386 | .disable = tc358764_disable, | ||
387 | .post_disable = tc358764_post_disable, | ||
388 | .enable = tc358764_enable, | ||
389 | .pre_enable = tc358764_pre_enable, | ||
390 | .attach = tc358764_attach, | ||
391 | .detach = tc358764_detach, | ||
392 | }; | ||
393 | |||
394 | static int tc358764_parse_dt(struct tc358764 *ctx) | ||
395 | { | ||
396 | struct device *dev = ctx->dev; | ||
397 | int ret; | ||
398 | |||
399 | ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); | ||
400 | if (IS_ERR(ctx->gpio_reset)) { | ||
401 | dev_err(dev, "no reset GPIO pin provided\n"); | ||
402 | return PTR_ERR(ctx->gpio_reset); | ||
403 | } | ||
404 | |||
405 | ret = drm_of_find_panel_or_bridge(ctx->dev->of_node, 1, 0, &ctx->panel, | ||
406 | NULL); | ||
407 | if (ret && ret != -EPROBE_DEFER) | ||
408 | dev_err(dev, "cannot find panel (%d)\n", ret); | ||
409 | |||
410 | return ret; | ||
411 | } | ||
412 | |||
413 | static int tc358764_configure_regulators(struct tc358764 *ctx) | ||
414 | { | ||
415 | int i, ret; | ||
416 | |||
417 | for (i = 0; i < ARRAY_SIZE(ctx->supplies); ++i) | ||
418 | ctx->supplies[i].supply = tc358764_supplies[i]; | ||
419 | |||
420 | ret = devm_regulator_bulk_get(ctx->dev, ARRAY_SIZE(ctx->supplies), | ||
421 | ctx->supplies); | ||
422 | if (ret < 0) | ||
423 | dev_err(ctx->dev, "failed to get regulators: %d\n", ret); | ||
424 | |||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | static int tc358764_probe(struct mipi_dsi_device *dsi) | ||
429 | { | ||
430 | struct device *dev = &dsi->dev; | ||
431 | struct tc358764 *ctx; | ||
432 | int ret; | ||
433 | |||
434 | ctx = devm_kzalloc(dev, sizeof(struct tc358764), GFP_KERNEL); | ||
435 | if (!ctx) | ||
436 | return -ENOMEM; | ||
437 | |||
438 | mipi_dsi_set_drvdata(dsi, ctx); | ||
439 | |||
440 | ctx->dev = dev; | ||
441 | |||
442 | dsi->lanes = 4; | ||
443 | dsi->format = MIPI_DSI_FMT_RGB888; | ||
444 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | ||
445 | | MIPI_DSI_MODE_VIDEO_AUTO_VERT | MIPI_DSI_MODE_LPM; | ||
446 | |||
447 | ret = tc358764_parse_dt(ctx); | ||
448 | if (ret < 0) | ||
449 | return ret; | ||
450 | |||
451 | ret = tc358764_configure_regulators(ctx); | ||
452 | if (ret < 0) | ||
453 | return ret; | ||
454 | |||
455 | ctx->bridge.funcs = &tc358764_bridge_funcs; | ||
456 | ctx->bridge.of_node = dev->of_node; | ||
457 | |||
458 | drm_bridge_add(&ctx->bridge); | ||
459 | |||
460 | ret = mipi_dsi_attach(dsi); | ||
461 | if (ret < 0) { | ||
462 | drm_bridge_remove(&ctx->bridge); | ||
463 | dev_err(dev, "failed to attach dsi\n"); | ||
464 | } | ||
465 | |||
466 | return ret; | ||
467 | } | ||
468 | |||
469 | static int tc358764_remove(struct mipi_dsi_device *dsi) | ||
470 | { | ||
471 | struct tc358764 *ctx = mipi_dsi_get_drvdata(dsi); | ||
472 | |||
473 | mipi_dsi_detach(dsi); | ||
474 | drm_bridge_remove(&ctx->bridge); | ||
475 | |||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static const struct of_device_id tc358764_of_match[] = { | ||
480 | { .compatible = "toshiba,tc358764" }, | ||
481 | { } | ||
482 | }; | ||
483 | MODULE_DEVICE_TABLE(of, tc358764_of_match); | ||
484 | |||
485 | static struct mipi_dsi_driver tc358764_driver = { | ||
486 | .probe = tc358764_probe, | ||
487 | .remove = tc358764_remove, | ||
488 | .driver = { | ||
489 | .name = "tc358764", | ||
490 | .owner = THIS_MODULE, | ||
491 | .of_match_table = tc358764_of_match, | ||
492 | }, | ||
493 | }; | ||
494 | module_mipi_dsi_driver(tc358764_driver); | ||
495 | |||
496 | MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); | ||
497 | MODULE_AUTHOR("Maciej Purski <m.purski@samsung.com>"); | ||
498 | MODULE_DESCRIPTION("MIPI-DSI based Driver for TC358764 DSI/LVDS Bridge"); | ||
499 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c new file mode 100644 index 000000000000..f8a931cf3665 --- /dev/null +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c | |||
@@ -0,0 +1,779 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | #include <drm/drmP.h> | ||
7 | #include <drm/drm_atomic.h> | ||
8 | #include <drm/drm_atomic_helper.h> | ||
9 | #include <drm/drm_crtc_helper.h> | ||
10 | #include <drm/drm_dp_helper.h> | ||
11 | #include <drm/drm_mipi_dsi.h> | ||
12 | #include <drm/drm_of.h> | ||
13 | #include <drm/drm_panel.h> | ||
14 | #include <linux/clk.h> | ||
15 | #include <linux/gpio/consumer.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/iopoll.h> | ||
18 | #include <linux/of_graph.h> | ||
19 | #include <linux/pm_runtime.h> | ||
20 | #include <linux/regmap.h> | ||
21 | #include <linux/regulator/consumer.h> | ||
22 | |||
23 | #define SN_DEVICE_REV_REG 0x08 | ||
24 | #define SN_DPPLL_SRC_REG 0x0A | ||
25 | #define DPPLL_CLK_SRC_DSICLK BIT(0) | ||
26 | #define REFCLK_FREQ_MASK GENMASK(3, 1) | ||
27 | #define REFCLK_FREQ(x) ((x) << 1) | ||
28 | #define DPPLL_SRC_DP_PLL_LOCK BIT(7) | ||
29 | #define SN_PLL_ENABLE_REG 0x0D | ||
30 | #define SN_DSI_LANES_REG 0x10 | ||
31 | #define CHA_DSI_LANES_MASK GENMASK(4, 3) | ||
32 | #define CHA_DSI_LANES(x) ((x) << 3) | ||
33 | #define SN_DSIA_CLK_FREQ_REG 0x12 | ||
34 | #define SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG 0x20 | ||
35 | #define SN_CHA_VERTICAL_DISPLAY_SIZE_LOW_REG 0x24 | ||
36 | #define SN_CHA_HSYNC_PULSE_WIDTH_LOW_REG 0x2C | ||
37 | #define SN_CHA_HSYNC_PULSE_WIDTH_HIGH_REG 0x2D | ||
38 | #define CHA_HSYNC_POLARITY BIT(7) | ||
39 | #define SN_CHA_VSYNC_PULSE_WIDTH_LOW_REG 0x30 | ||
40 | #define SN_CHA_VSYNC_PULSE_WIDTH_HIGH_REG 0x31 | ||
41 | #define CHA_VSYNC_POLARITY BIT(7) | ||
42 | #define SN_CHA_HORIZONTAL_BACK_PORCH_REG 0x34 | ||
43 | #define SN_CHA_VERTICAL_BACK_PORCH_REG 0x36 | ||
44 | #define SN_CHA_HORIZONTAL_FRONT_PORCH_REG 0x38 | ||
45 | #define SN_CHA_VERTICAL_FRONT_PORCH_REG 0x3A | ||
46 | #define SN_ENH_FRAME_REG 0x5A | ||
47 | #define VSTREAM_ENABLE BIT(3) | ||
48 | #define SN_DATA_FORMAT_REG 0x5B | ||
49 | #define SN_HPD_DISABLE_REG 0x5C | ||
50 | #define HPD_DISABLE BIT(0) | ||
51 | #define SN_AUX_WDATA_REG(x) (0x64 + (x)) | ||
52 | #define SN_AUX_ADDR_19_16_REG 0x74 | ||
53 | #define SN_AUX_ADDR_15_8_REG 0x75 | ||
54 | #define SN_AUX_ADDR_7_0_REG 0x76 | ||
55 | #define SN_AUX_LENGTH_REG 0x77 | ||
56 | #define SN_AUX_CMD_REG 0x78 | ||
57 | #define AUX_CMD_SEND BIT(1) | ||
58 | #define AUX_CMD_REQ(x) ((x) << 4) | ||
59 | #define SN_AUX_RDATA_REG(x) (0x79 + (x)) | ||
60 | #define SN_SSC_CONFIG_REG 0x93 | ||
61 | #define DP_NUM_LANES_MASK GENMASK(5, 4) | ||
62 | #define DP_NUM_LANES(x) ((x) << 4) | ||
63 | #define SN_DATARATE_CONFIG_REG 0x94 | ||
64 | #define DP_DATARATE_MASK GENMASK(7, 5) | ||
65 | #define DP_DATARATE(x) ((x) << 5) | ||
66 | #define SN_ML_TX_MODE_REG 0x96 | ||
67 | #define ML_TX_MAIN_LINK_OFF 0 | ||
68 | #define ML_TX_NORMAL_MODE BIT(0) | ||
69 | #define SN_AUX_CMD_STATUS_REG 0xF4 | ||
70 | #define AUX_IRQ_STATUS_AUX_RPLY_TOUT BIT(3) | ||
71 | #define AUX_IRQ_STATUS_AUX_SHORT BIT(5) | ||
72 | #define AUX_IRQ_STATUS_NAT_I2C_FAIL BIT(6) | ||
73 | |||
74 | #define MIN_DSI_CLK_FREQ_MHZ 40 | ||
75 | |||
76 | /* fudge factor required to account for 8b/10b encoding */ | ||
77 | #define DP_CLK_FUDGE_NUM 10 | ||
78 | #define DP_CLK_FUDGE_DEN 8 | ||
79 | |||
80 | /* Matches DP_AUX_MAX_PAYLOAD_BYTES (for now) */ | ||
81 | #define SN_AUX_MAX_PAYLOAD_BYTES 16 | ||
82 | |||
83 | #define SN_REGULATOR_SUPPLY_NUM 4 | ||
84 | |||
85 | struct ti_sn_bridge { | ||
86 | struct device *dev; | ||
87 | struct regmap *regmap; | ||
88 | struct drm_dp_aux aux; | ||
89 | struct drm_bridge bridge; | ||
90 | struct drm_connector connector; | ||
91 | struct device_node *host_node; | ||
92 | struct mipi_dsi_device *dsi; | ||
93 | struct clk *refclk; | ||
94 | struct drm_panel *panel; | ||
95 | struct gpio_desc *enable_gpio; | ||
96 | struct regulator_bulk_data supplies[SN_REGULATOR_SUPPLY_NUM]; | ||
97 | }; | ||
98 | |||
99 | static const struct regmap_range ti_sn_bridge_volatile_ranges[] = { | ||
100 | { .range_min = 0, .range_max = 0xFF }, | ||
101 | }; | ||
102 | |||
103 | static const struct regmap_access_table ti_sn_bridge_volatile_table = { | ||
104 | .yes_ranges = ti_sn_bridge_volatile_ranges, | ||
105 | .n_yes_ranges = ARRAY_SIZE(ti_sn_bridge_volatile_ranges), | ||
106 | }; | ||
107 | |||
108 | static const struct regmap_config ti_sn_bridge_regmap_config = { | ||
109 | .reg_bits = 8, | ||
110 | .val_bits = 8, | ||
111 | .volatile_table = &ti_sn_bridge_volatile_table, | ||
112 | .cache_type = REGCACHE_NONE, | ||
113 | }; | ||
114 | |||
115 | static void ti_sn_bridge_write_u16(struct ti_sn_bridge *pdata, | ||
116 | unsigned int reg, u16 val) | ||
117 | { | ||
118 | regmap_write(pdata->regmap, reg, val & 0xFF); | ||
119 | regmap_write(pdata->regmap, reg + 1, val >> 8); | ||
120 | } | ||
121 | |||
122 | static int __maybe_unused ti_sn_bridge_resume(struct device *dev) | ||
123 | { | ||
124 | struct ti_sn_bridge *pdata = dev_get_drvdata(dev); | ||
125 | int ret; | ||
126 | |||
127 | ret = regulator_bulk_enable(SN_REGULATOR_SUPPLY_NUM, pdata->supplies); | ||
128 | if (ret) { | ||
129 | DRM_ERROR("failed to enable supplies %d\n", ret); | ||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | gpiod_set_value(pdata->enable_gpio, 1); | ||
134 | |||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | static int __maybe_unused ti_sn_bridge_suspend(struct device *dev) | ||
139 | { | ||
140 | struct ti_sn_bridge *pdata = dev_get_drvdata(dev); | ||
141 | int ret; | ||
142 | |||
143 | gpiod_set_value(pdata->enable_gpio, 0); | ||
144 | |||
145 | ret = regulator_bulk_disable(SN_REGULATOR_SUPPLY_NUM, pdata->supplies); | ||
146 | if (ret) | ||
147 | DRM_ERROR("failed to disable supplies %d\n", ret); | ||
148 | |||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | static const struct dev_pm_ops ti_sn_bridge_pm_ops = { | ||
153 | SET_RUNTIME_PM_OPS(ti_sn_bridge_suspend, ti_sn_bridge_resume, NULL) | ||
154 | }; | ||
155 | |||
156 | /* Connector funcs */ | ||
157 | static struct ti_sn_bridge * | ||
158 | connector_to_ti_sn_bridge(struct drm_connector *connector) | ||
159 | { | ||
160 | return container_of(connector, struct ti_sn_bridge, connector); | ||
161 | } | ||
162 | |||
163 | static int ti_sn_bridge_connector_get_modes(struct drm_connector *connector) | ||
164 | { | ||
165 | struct ti_sn_bridge *pdata = connector_to_ti_sn_bridge(connector); | ||
166 | |||
167 | return drm_panel_get_modes(pdata->panel); | ||
168 | } | ||
169 | |||
170 | static enum drm_mode_status | ||
171 | ti_sn_bridge_connector_mode_valid(struct drm_connector *connector, | ||
172 | struct drm_display_mode *mode) | ||
173 | { | ||
174 | /* maximum supported resolution is 4K at 60 fps */ | ||
175 | if (mode->clock > 594000) | ||
176 | return MODE_CLOCK_HIGH; | ||
177 | |||
178 | return MODE_OK; | ||
179 | } | ||
180 | |||
181 | static struct drm_connector_helper_funcs ti_sn_bridge_connector_helper_funcs = { | ||
182 | .get_modes = ti_sn_bridge_connector_get_modes, | ||
183 | .mode_valid = ti_sn_bridge_connector_mode_valid, | ||
184 | }; | ||
185 | |||
186 | static enum drm_connector_status | ||
187 | ti_sn_bridge_connector_detect(struct drm_connector *connector, bool force) | ||
188 | { | ||
189 | /** | ||
190 | * TODO: Currently if drm_panel is present, then always | ||
191 | * return the status as connected. Need to add support to detect | ||
192 | * device state for hot pluggable scenarios. | ||
193 | */ | ||
194 | return connector_status_connected; | ||
195 | } | ||
196 | |||
197 | static const struct drm_connector_funcs ti_sn_bridge_connector_funcs = { | ||
198 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
199 | .detect = ti_sn_bridge_connector_detect, | ||
200 | .destroy = drm_connector_cleanup, | ||
201 | .reset = drm_atomic_helper_connector_reset, | ||
202 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||
203 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||
204 | }; | ||
205 | |||
206 | static struct ti_sn_bridge *bridge_to_ti_sn_bridge(struct drm_bridge *bridge) | ||
207 | { | ||
208 | return container_of(bridge, struct ti_sn_bridge, bridge); | ||
209 | } | ||
210 | |||
211 | static int ti_sn_bridge_parse_regulators(struct ti_sn_bridge *pdata) | ||
212 | { | ||
213 | unsigned int i; | ||
214 | const char * const ti_sn_bridge_supply_names[] = { | ||
215 | "vcca", "vcc", "vccio", "vpll", | ||
216 | }; | ||
217 | |||
218 | for (i = 0; i < SN_REGULATOR_SUPPLY_NUM; i++) | ||
219 | pdata->supplies[i].supply = ti_sn_bridge_supply_names[i]; | ||
220 | |||
221 | return devm_regulator_bulk_get(pdata->dev, SN_REGULATOR_SUPPLY_NUM, | ||
222 | pdata->supplies); | ||
223 | } | ||
224 | |||
225 | static int ti_sn_bridge_attach(struct drm_bridge *bridge) | ||
226 | { | ||
227 | int ret, val; | ||
228 | struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); | ||
229 | struct mipi_dsi_host *host; | ||
230 | struct mipi_dsi_device *dsi; | ||
231 | const struct mipi_dsi_device_info info = { .type = "ti_sn_bridge", | ||
232 | .channel = 0, | ||
233 | .node = NULL, | ||
234 | }; | ||
235 | |||
236 | ret = drm_connector_init(bridge->dev, &pdata->connector, | ||
237 | &ti_sn_bridge_connector_funcs, | ||
238 | DRM_MODE_CONNECTOR_eDP); | ||
239 | if (ret) { | ||
240 | DRM_ERROR("Failed to initialize connector with drm\n"); | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | drm_connector_helper_add(&pdata->connector, | ||
245 | &ti_sn_bridge_connector_helper_funcs); | ||
246 | drm_connector_attach_encoder(&pdata->connector, bridge->encoder); | ||
247 | |||
248 | /* | ||
249 | * TODO: ideally finding host resource and dsi dev registration needs | ||
250 | * to be done in bridge probe. But some existing DSI host drivers will | ||
251 | * wait for any of the drm_bridge/drm_panel to get added to the global | ||
252 | * bridge/panel list, before completing their probe. So if we do the | ||
253 | * dsi dev registration part in bridge probe, before populating in | ||
254 | * the global bridge list, then it will cause deadlock as dsi host probe | ||
255 | * will never complete, neither our bridge probe. So keeping it here | ||
256 | * will satisfy most of the existing host drivers. Once the host driver | ||
257 | * is fixed we can move the below code to bridge probe safely. | ||
258 | */ | ||
259 | host = of_find_mipi_dsi_host_by_node(pdata->host_node); | ||
260 | if (!host) { | ||
261 | DRM_ERROR("failed to find dsi host\n"); | ||
262 | ret = -ENODEV; | ||
263 | goto err_dsi_host; | ||
264 | } | ||
265 | |||
266 | dsi = mipi_dsi_device_register_full(host, &info); | ||
267 | if (IS_ERR(dsi)) { | ||
268 | DRM_ERROR("failed to create dsi device\n"); | ||
269 | ret = PTR_ERR(dsi); | ||
270 | goto err_dsi_host; | ||
271 | } | ||
272 | |||
273 | /* TODO: setting to 4 lanes always for now */ | ||
274 | dsi->lanes = 4; | ||
275 | dsi->format = MIPI_DSI_FMT_RGB888; | ||
276 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | | ||
277 | MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE; | ||
278 | |||
279 | /* check if continuous dsi clock is required or not */ | ||
280 | pm_runtime_get_sync(pdata->dev); | ||
281 | regmap_read(pdata->regmap, SN_DPPLL_SRC_REG, &val); | ||
282 | pm_runtime_put(pdata->dev); | ||
283 | if (!(val & DPPLL_CLK_SRC_DSICLK)) | ||
284 | dsi->mode_flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS; | ||
285 | |||
286 | ret = mipi_dsi_attach(dsi); | ||
287 | if (ret < 0) { | ||
288 | DRM_ERROR("failed to attach dsi to host\n"); | ||
289 | goto err_dsi_attach; | ||
290 | } | ||
291 | pdata->dsi = dsi; | ||
292 | |||
293 | /* attach panel to bridge */ | ||
294 | drm_panel_attach(pdata->panel, &pdata->connector); | ||
295 | |||
296 | return 0; | ||
297 | |||
298 | err_dsi_attach: | ||
299 | mipi_dsi_device_unregister(dsi); | ||
300 | err_dsi_host: | ||
301 | drm_connector_cleanup(&pdata->connector); | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | static void ti_sn_bridge_disable(struct drm_bridge *bridge) | ||
306 | { | ||
307 | struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); | ||
308 | |||
309 | drm_panel_disable(pdata->panel); | ||
310 | |||
311 | /* disable video stream */ | ||
312 | regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, VSTREAM_ENABLE, 0); | ||
313 | /* semi auto link training mode OFF */ | ||
314 | regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0); | ||
315 | /* disable DP PLL */ | ||
316 | regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 0); | ||
317 | |||
318 | drm_panel_unprepare(pdata->panel); | ||
319 | } | ||
320 | |||
321 | static u32 ti_sn_bridge_get_dsi_freq(struct ti_sn_bridge *pdata) | ||
322 | { | ||
323 | u32 bit_rate_khz, clk_freq_khz; | ||
324 | struct drm_display_mode *mode = | ||
325 | &pdata->bridge.encoder->crtc->state->adjusted_mode; | ||
326 | |||
327 | bit_rate_khz = mode->clock * | ||
328 | mipi_dsi_pixel_format_to_bpp(pdata->dsi->format); | ||
329 | clk_freq_khz = bit_rate_khz / (pdata->dsi->lanes * 2); | ||
330 | |||
331 | return clk_freq_khz; | ||
332 | } | ||
333 | |||
334 | /* clk frequencies supported by bridge in Hz in case derived from REFCLK pin */ | ||
335 | static const u32 ti_sn_bridge_refclk_lut[] = { | ||
336 | 12000000, | ||
337 | 19200000, | ||
338 | 26000000, | ||
339 | 27000000, | ||
340 | 38400000, | ||
341 | }; | ||
342 | |||
343 | /* clk frequencies supported by bridge in Hz in case derived from DACP/N pin */ | ||
344 | static const u32 ti_sn_bridge_dsiclk_lut[] = { | ||
345 | 468000000, | ||
346 | 384000000, | ||
347 | 416000000, | ||
348 | 486000000, | ||
349 | 460800000, | ||
350 | }; | ||
351 | |||
352 | static void ti_sn_bridge_set_refclk_freq(struct ti_sn_bridge *pdata) | ||
353 | { | ||
354 | int i; | ||
355 | u32 refclk_rate; | ||
356 | const u32 *refclk_lut; | ||
357 | size_t refclk_lut_size; | ||
358 | |||
359 | if (pdata->refclk) { | ||
360 | refclk_rate = clk_get_rate(pdata->refclk); | ||
361 | refclk_lut = ti_sn_bridge_refclk_lut; | ||
362 | refclk_lut_size = ARRAY_SIZE(ti_sn_bridge_refclk_lut); | ||
363 | clk_prepare_enable(pdata->refclk); | ||
364 | } else { | ||
365 | refclk_rate = ti_sn_bridge_get_dsi_freq(pdata) * 1000; | ||
366 | refclk_lut = ti_sn_bridge_dsiclk_lut; | ||
367 | refclk_lut_size = ARRAY_SIZE(ti_sn_bridge_dsiclk_lut); | ||
368 | } | ||
369 | |||
370 | /* for i equals to refclk_lut_size means default frequency */ | ||
371 | for (i = 0; i < refclk_lut_size; i++) | ||
372 | if (refclk_lut[i] == refclk_rate) | ||
373 | break; | ||
374 | |||
375 | regmap_update_bits(pdata->regmap, SN_DPPLL_SRC_REG, REFCLK_FREQ_MASK, | ||
376 | REFCLK_FREQ(i)); | ||
377 | } | ||
378 | |||
379 | /** | ||
380 | * LUT index corresponds to register value and | ||
381 | * LUT values corresponds to dp data rate supported | ||
382 | * by the bridge in Mbps unit. | ||
383 | */ | ||
384 | static const unsigned int ti_sn_bridge_dp_rate_lut[] = { | ||
385 | 0, 1620, 2160, 2430, 2700, 3240, 4320, 5400 | ||
386 | }; | ||
387 | |||
388 | static void ti_sn_bridge_set_dsi_dp_rate(struct ti_sn_bridge *pdata) | ||
389 | { | ||
390 | unsigned int bit_rate_mhz, clk_freq_mhz, dp_rate_mhz; | ||
391 | unsigned int val, i; | ||
392 | struct drm_display_mode *mode = | ||
393 | &pdata->bridge.encoder->crtc->state->adjusted_mode; | ||
394 | |||
395 | /* set DSIA clk frequency */ | ||
396 | bit_rate_mhz = (mode->clock / 1000) * | ||
397 | mipi_dsi_pixel_format_to_bpp(pdata->dsi->format); | ||
398 | clk_freq_mhz = bit_rate_mhz / (pdata->dsi->lanes * 2); | ||
399 | |||
400 | /* for each increment in val, frequency increases by 5MHz */ | ||
401 | val = (MIN_DSI_CLK_FREQ_MHZ / 5) + | ||
402 | (((clk_freq_mhz - MIN_DSI_CLK_FREQ_MHZ) / 5) & 0xFF); | ||
403 | regmap_write(pdata->regmap, SN_DSIA_CLK_FREQ_REG, val); | ||
404 | |||
405 | /* set DP data rate */ | ||
406 | dp_rate_mhz = ((bit_rate_mhz / pdata->dsi->lanes) * DP_CLK_FUDGE_NUM) / | ||
407 | DP_CLK_FUDGE_DEN; | ||
408 | for (i = 0; i < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1; i++) | ||
409 | if (ti_sn_bridge_dp_rate_lut[i] > dp_rate_mhz) | ||
410 | break; | ||
411 | |||
412 | regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG, | ||
413 | DP_DATARATE_MASK, DP_DATARATE(i)); | ||
414 | } | ||
415 | |||
416 | static void ti_sn_bridge_set_video_timings(struct ti_sn_bridge *pdata) | ||
417 | { | ||
418 | struct drm_display_mode *mode = | ||
419 | &pdata->bridge.encoder->crtc->state->adjusted_mode; | ||
420 | u8 hsync_polarity = 0, vsync_polarity = 0; | ||
421 | |||
422 | if (mode->flags & DRM_MODE_FLAG_PHSYNC) | ||
423 | hsync_polarity = CHA_HSYNC_POLARITY; | ||
424 | if (mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
425 | vsync_polarity = CHA_VSYNC_POLARITY; | ||
426 | |||
427 | ti_sn_bridge_write_u16(pdata, SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG, | ||
428 | mode->hdisplay); | ||
429 | ti_sn_bridge_write_u16(pdata, SN_CHA_VERTICAL_DISPLAY_SIZE_LOW_REG, | ||
430 | mode->vdisplay); | ||
431 | regmap_write(pdata->regmap, SN_CHA_HSYNC_PULSE_WIDTH_LOW_REG, | ||
432 | (mode->hsync_end - mode->hsync_start) & 0xFF); | ||
433 | regmap_write(pdata->regmap, SN_CHA_HSYNC_PULSE_WIDTH_HIGH_REG, | ||
434 | (((mode->hsync_end - mode->hsync_start) >> 8) & 0x7F) | | ||
435 | hsync_polarity); | ||
436 | regmap_write(pdata->regmap, SN_CHA_VSYNC_PULSE_WIDTH_LOW_REG, | ||
437 | (mode->vsync_end - mode->vsync_start) & 0xFF); | ||
438 | regmap_write(pdata->regmap, SN_CHA_VSYNC_PULSE_WIDTH_HIGH_REG, | ||
439 | (((mode->vsync_end - mode->vsync_start) >> 8) & 0x7F) | | ||
440 | vsync_polarity); | ||
441 | |||
442 | regmap_write(pdata->regmap, SN_CHA_HORIZONTAL_BACK_PORCH_REG, | ||
443 | (mode->htotal - mode->hsync_end) & 0xFF); | ||
444 | regmap_write(pdata->regmap, SN_CHA_VERTICAL_BACK_PORCH_REG, | ||
445 | (mode->vtotal - mode->vsync_end) & 0xFF); | ||
446 | |||
447 | regmap_write(pdata->regmap, SN_CHA_HORIZONTAL_FRONT_PORCH_REG, | ||
448 | (mode->hsync_start - mode->hdisplay) & 0xFF); | ||
449 | regmap_write(pdata->regmap, SN_CHA_VERTICAL_FRONT_PORCH_REG, | ||
450 | (mode->vsync_start - mode->vdisplay) & 0xFF); | ||
451 | |||
452 | usleep_range(10000, 10500); /* 10ms delay recommended by spec */ | ||
453 | } | ||
454 | |||
455 | static void ti_sn_bridge_enable(struct drm_bridge *bridge) | ||
456 | { | ||
457 | struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); | ||
458 | unsigned int val; | ||
459 | int ret; | ||
460 | |||
461 | /* | ||
462 | * FIXME: | ||
463 | * This 70ms was found necessary by experimentation. If it's not | ||
464 | * present, link training fails. It seems like it can go anywhere from | ||
465 | * pre_enable() up to semi-auto link training initiation below. | ||
466 | * | ||
467 | * Neither the datasheet for the bridge nor the panel tested mention a | ||
468 | * delay of this magnitude in the timing requirements. So for now, add | ||
469 | * the mystery delay until someone figures out a better fix. | ||
470 | */ | ||
471 | msleep(70); | ||
472 | |||
473 | /* DSI_A lane config */ | ||
474 | val = CHA_DSI_LANES(4 - pdata->dsi->lanes); | ||
475 | regmap_update_bits(pdata->regmap, SN_DSI_LANES_REG, | ||
476 | CHA_DSI_LANES_MASK, val); | ||
477 | |||
478 | /* DP lane config */ | ||
479 | val = DP_NUM_LANES(pdata->dsi->lanes - 1); | ||
480 | regmap_update_bits(pdata->regmap, SN_SSC_CONFIG_REG, DP_NUM_LANES_MASK, | ||
481 | val); | ||
482 | |||
483 | /* set dsi/dp clk frequency value */ | ||
484 | ti_sn_bridge_set_dsi_dp_rate(pdata); | ||
485 | |||
486 | /* enable DP PLL */ | ||
487 | regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 1); | ||
488 | |||
489 | ret = regmap_read_poll_timeout(pdata->regmap, SN_DPPLL_SRC_REG, val, | ||
490 | val & DPPLL_SRC_DP_PLL_LOCK, 1000, | ||
491 | 50 * 1000); | ||
492 | if (ret) { | ||
493 | DRM_ERROR("DP_PLL_LOCK polling failed (%d)\n", ret); | ||
494 | return; | ||
495 | } | ||
496 | |||
497 | /** | ||
498 | * The SN65DSI86 only supports ASSR Display Authentication method and | ||
499 | * this method is enabled by default. An eDP panel must support this | ||
500 | * authentication method. We need to enable this method in the eDP panel | ||
501 | * at DisplayPort address 0x0010A prior to link training. | ||
502 | */ | ||
503 | drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET, | ||
504 | DP_ALTERNATE_SCRAMBLER_RESET_ENABLE); | ||
505 | |||
506 | /* Semi auto link training mode */ | ||
507 | regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0x0A); | ||
508 | ret = regmap_read_poll_timeout(pdata->regmap, SN_ML_TX_MODE_REG, val, | ||
509 | val == ML_TX_MAIN_LINK_OFF || | ||
510 | val == ML_TX_NORMAL_MODE, 1000, | ||
511 | 500 * 1000); | ||
512 | if (ret) { | ||
513 | DRM_ERROR("Training complete polling failed (%d)\n", ret); | ||
514 | return; | ||
515 | } else if (val == ML_TX_MAIN_LINK_OFF) { | ||
516 | DRM_ERROR("Link training failed, link is off\n"); | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | /* config video parameters */ | ||
521 | ti_sn_bridge_set_video_timings(pdata); | ||
522 | |||
523 | /* enable video stream */ | ||
524 | regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, VSTREAM_ENABLE, | ||
525 | VSTREAM_ENABLE); | ||
526 | |||
527 | drm_panel_enable(pdata->panel); | ||
528 | } | ||
529 | |||
530 | static void ti_sn_bridge_pre_enable(struct drm_bridge *bridge) | ||
531 | { | ||
532 | struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); | ||
533 | |||
534 | pm_runtime_get_sync(pdata->dev); | ||
535 | |||
536 | /* configure bridge ref_clk */ | ||
537 | ti_sn_bridge_set_refclk_freq(pdata); | ||
538 | |||
539 | /* in case drm_panel is connected then HPD is not supported */ | ||
540 | regmap_update_bits(pdata->regmap, SN_HPD_DISABLE_REG, HPD_DISABLE, | ||
541 | HPD_DISABLE); | ||
542 | |||
543 | drm_panel_prepare(pdata->panel); | ||
544 | } | ||
545 | |||
546 | static void ti_sn_bridge_post_disable(struct drm_bridge *bridge) | ||
547 | { | ||
548 | struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); | ||
549 | |||
550 | if (pdata->refclk) | ||
551 | clk_disable_unprepare(pdata->refclk); | ||
552 | |||
553 | pm_runtime_put_sync(pdata->dev); | ||
554 | } | ||
555 | |||
556 | static const struct drm_bridge_funcs ti_sn_bridge_funcs = { | ||
557 | .attach = ti_sn_bridge_attach, | ||
558 | .pre_enable = ti_sn_bridge_pre_enable, | ||
559 | .enable = ti_sn_bridge_enable, | ||
560 | .disable = ti_sn_bridge_disable, | ||
561 | .post_disable = ti_sn_bridge_post_disable, | ||
562 | }; | ||
563 | |||
564 | static struct ti_sn_bridge *aux_to_ti_sn_bridge(struct drm_dp_aux *aux) | ||
565 | { | ||
566 | return container_of(aux, struct ti_sn_bridge, aux); | ||
567 | } | ||
568 | |||
569 | static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux, | ||
570 | struct drm_dp_aux_msg *msg) | ||
571 | { | ||
572 | struct ti_sn_bridge *pdata = aux_to_ti_sn_bridge(aux); | ||
573 | u32 request = msg->request & ~DP_AUX_I2C_MOT; | ||
574 | u32 request_val = AUX_CMD_REQ(msg->request); | ||
575 | u8 *buf = (u8 *)msg->buffer; | ||
576 | unsigned int val; | ||
577 | int ret, i; | ||
578 | |||
579 | if (msg->size > SN_AUX_MAX_PAYLOAD_BYTES) | ||
580 | return -EINVAL; | ||
581 | |||
582 | switch (request) { | ||
583 | case DP_AUX_NATIVE_WRITE: | ||
584 | case DP_AUX_I2C_WRITE: | ||
585 | case DP_AUX_NATIVE_READ: | ||
586 | case DP_AUX_I2C_READ: | ||
587 | regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val); | ||
588 | break; | ||
589 | default: | ||
590 | return -EINVAL; | ||
591 | } | ||
592 | |||
593 | regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, | ||
594 | (msg->address >> 16) & 0xF); | ||
595 | regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG, | ||
596 | (msg->address >> 8) & 0xFF); | ||
597 | regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, msg->address & 0xFF); | ||
598 | |||
599 | regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, msg->size); | ||
600 | |||
601 | if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE) { | ||
602 | for (i = 0; i < msg->size; i++) | ||
603 | regmap_write(pdata->regmap, SN_AUX_WDATA_REG(i), | ||
604 | buf[i]); | ||
605 | } | ||
606 | |||
607 | regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val | AUX_CMD_SEND); | ||
608 | |||
609 | ret = regmap_read_poll_timeout(pdata->regmap, SN_AUX_CMD_REG, val, | ||
610 | !(val & AUX_CMD_SEND), 200, | ||
611 | 50 * 1000); | ||
612 | if (ret) | ||
613 | return ret; | ||
614 | |||
615 | ret = regmap_read(pdata->regmap, SN_AUX_CMD_STATUS_REG, &val); | ||
616 | if (ret) | ||
617 | return ret; | ||
618 | else if ((val & AUX_IRQ_STATUS_NAT_I2C_FAIL) | ||
619 | || (val & AUX_IRQ_STATUS_AUX_RPLY_TOUT) | ||
620 | || (val & AUX_IRQ_STATUS_AUX_SHORT)) | ||
621 | return -ENXIO; | ||
622 | |||
623 | if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE) | ||
624 | return msg->size; | ||
625 | |||
626 | for (i = 0; i < msg->size; i++) { | ||
627 | unsigned int val; | ||
628 | ret = regmap_read(pdata->regmap, SN_AUX_RDATA_REG(i), | ||
629 | &val); | ||
630 | if (ret) | ||
631 | return ret; | ||
632 | |||
633 | WARN_ON(val & ~0xFF); | ||
634 | buf[i] = (u8)(val & 0xFF); | ||
635 | } | ||
636 | |||
637 | return msg->size; | ||
638 | } | ||
639 | |||
640 | static int ti_sn_bridge_parse_dsi_host(struct ti_sn_bridge *pdata) | ||
641 | { | ||
642 | struct device_node *np = pdata->dev->of_node; | ||
643 | |||
644 | pdata->host_node = of_graph_get_remote_node(np, 0, 0); | ||
645 | |||
646 | if (!pdata->host_node) { | ||
647 | DRM_ERROR("remote dsi host node not found\n"); | ||
648 | return -ENODEV; | ||
649 | } | ||
650 | |||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static int ti_sn_bridge_probe(struct i2c_client *client, | ||
655 | const struct i2c_device_id *id) | ||
656 | { | ||
657 | struct ti_sn_bridge *pdata; | ||
658 | int ret; | ||
659 | |||
660 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
661 | DRM_ERROR("device doesn't support I2C\n"); | ||
662 | return -ENODEV; | ||
663 | } | ||
664 | |||
665 | pdata = devm_kzalloc(&client->dev, sizeof(struct ti_sn_bridge), | ||
666 | GFP_KERNEL); | ||
667 | if (!pdata) | ||
668 | return -ENOMEM; | ||
669 | |||
670 | pdata->regmap = devm_regmap_init_i2c(client, | ||
671 | &ti_sn_bridge_regmap_config); | ||
672 | if (IS_ERR(pdata->regmap)) { | ||
673 | DRM_ERROR("regmap i2c init failed\n"); | ||
674 | return PTR_ERR(pdata->regmap); | ||
675 | } | ||
676 | |||
677 | pdata->dev = &client->dev; | ||
678 | |||
679 | ret = drm_of_find_panel_or_bridge(pdata->dev->of_node, 1, 0, | ||
680 | &pdata->panel, NULL); | ||
681 | if (ret) { | ||
682 | DRM_ERROR("could not find any panel node\n"); | ||
683 | return ret; | ||
684 | } | ||
685 | |||
686 | dev_set_drvdata(&client->dev, pdata); | ||
687 | |||
688 | pdata->enable_gpio = devm_gpiod_get(pdata->dev, "enable", | ||
689 | GPIOD_OUT_LOW); | ||
690 | if (IS_ERR(pdata->enable_gpio)) { | ||
691 | DRM_ERROR("failed to get enable gpio from DT\n"); | ||
692 | ret = PTR_ERR(pdata->enable_gpio); | ||
693 | return ret; | ||
694 | } | ||
695 | |||
696 | ret = ti_sn_bridge_parse_regulators(pdata); | ||
697 | if (ret) { | ||
698 | DRM_ERROR("failed to parse regulators\n"); | ||
699 | return ret; | ||
700 | } | ||
701 | |||
702 | pdata->refclk = devm_clk_get(pdata->dev, "refclk"); | ||
703 | if (IS_ERR(pdata->refclk)) { | ||
704 | ret = PTR_ERR(pdata->refclk); | ||
705 | if (ret == -EPROBE_DEFER) | ||
706 | return ret; | ||
707 | DRM_DEBUG_KMS("refclk not found\n"); | ||
708 | pdata->refclk = NULL; | ||
709 | } | ||
710 | |||
711 | ret = ti_sn_bridge_parse_dsi_host(pdata); | ||
712 | if (ret) | ||
713 | return ret; | ||
714 | |||
715 | pm_runtime_enable(pdata->dev); | ||
716 | |||
717 | i2c_set_clientdata(client, pdata); | ||
718 | |||
719 | pdata->aux.name = "ti-sn65dsi86-aux"; | ||
720 | pdata->aux.dev = pdata->dev; | ||
721 | pdata->aux.transfer = ti_sn_aux_transfer; | ||
722 | drm_dp_aux_register(&pdata->aux); | ||
723 | |||
724 | pdata->bridge.funcs = &ti_sn_bridge_funcs; | ||
725 | pdata->bridge.of_node = client->dev.of_node; | ||
726 | |||
727 | drm_bridge_add(&pdata->bridge); | ||
728 | |||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | static int ti_sn_bridge_remove(struct i2c_client *client) | ||
733 | { | ||
734 | struct ti_sn_bridge *pdata = i2c_get_clientdata(client); | ||
735 | |||
736 | if (!pdata) | ||
737 | return -EINVAL; | ||
738 | |||
739 | of_node_put(pdata->host_node); | ||
740 | |||
741 | pm_runtime_disable(pdata->dev); | ||
742 | |||
743 | if (pdata->dsi) { | ||
744 | mipi_dsi_detach(pdata->dsi); | ||
745 | mipi_dsi_device_unregister(pdata->dsi); | ||
746 | } | ||
747 | |||
748 | drm_bridge_remove(&pdata->bridge); | ||
749 | |||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | static struct i2c_device_id ti_sn_bridge_id[] = { | ||
754 | { "ti,sn65dsi86", 0}, | ||
755 | {}, | ||
756 | }; | ||
757 | MODULE_DEVICE_TABLE(i2c, ti_sn_bridge_id); | ||
758 | |||
759 | static const struct of_device_id ti_sn_bridge_match_table[] = { | ||
760 | {.compatible = "ti,sn65dsi86"}, | ||
761 | {}, | ||
762 | }; | ||
763 | MODULE_DEVICE_TABLE(of, ti_sn_bridge_match_table); | ||
764 | |||
765 | static struct i2c_driver ti_sn_bridge_driver = { | ||
766 | .driver = { | ||
767 | .name = "ti_sn65dsi86", | ||
768 | .of_match_table = ti_sn_bridge_match_table, | ||
769 | .pm = &ti_sn_bridge_pm_ops, | ||
770 | }, | ||
771 | .probe = ti_sn_bridge_probe, | ||
772 | .remove = ti_sn_bridge_remove, | ||
773 | .id_table = ti_sn_bridge_id, | ||
774 | }; | ||
775 | module_i2c_driver(ti_sn_bridge_driver); | ||
776 | |||
777 | MODULE_AUTHOR("Sandeep Panda <spanda@codeaurora.org>"); | ||
778 | MODULE_DESCRIPTION("sn65dsi86 DSI to eDP bridge driver"); | ||
779 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 69c4e352dd78..db40b77c7f7c 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c | |||
@@ -16,11 +16,11 @@ | |||
16 | #include "cirrus_drv.h" | 16 | #include "cirrus_drv.h" |
17 | 17 | ||
18 | int cirrus_modeset = -1; | 18 | int cirrus_modeset = -1; |
19 | int cirrus_bpp = 24; | 19 | int cirrus_bpp = 16; |
20 | 20 | ||
21 | MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); | 21 | MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); |
22 | module_param_named(modeset, cirrus_modeset, int, 0400); | 22 | module_param_named(modeset, cirrus_modeset, int, 0400); |
23 | MODULE_PARM_DESC(bpp, "Max bits-per-pixel (default:24)"); | 23 | MODULE_PARM_DESC(bpp, "Max bits-per-pixel (default:16)"); |
24 | module_param_named(bpp, cirrus_bpp, int, 0400); | 24 | module_param_named(bpp, cirrus_bpp, int, 0400); |
25 | 25 | ||
26 | /* | 26 | /* |
@@ -42,33 +42,12 @@ static const struct pci_device_id pciidlist[] = { | |||
42 | }; | 42 | }; |
43 | 43 | ||
44 | 44 | ||
45 | static int cirrus_kick_out_firmware_fb(struct pci_dev *pdev) | ||
46 | { | ||
47 | struct apertures_struct *ap; | ||
48 | bool primary = false; | ||
49 | |||
50 | ap = alloc_apertures(1); | ||
51 | if (!ap) | ||
52 | return -ENOMEM; | ||
53 | |||
54 | ap->ranges[0].base = pci_resource_start(pdev, 0); | ||
55 | ap->ranges[0].size = pci_resource_len(pdev, 0); | ||
56 | |||
57 | #ifdef CONFIG_X86 | ||
58 | primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; | ||
59 | #endif | ||
60 | drm_fb_helper_remove_conflicting_framebuffers(ap, "cirrusdrmfb", primary); | ||
61 | kfree(ap); | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int cirrus_pci_probe(struct pci_dev *pdev, | 45 | static int cirrus_pci_probe(struct pci_dev *pdev, |
67 | const struct pci_device_id *ent) | 46 | const struct pci_device_id *ent) |
68 | { | 47 | { |
69 | int ret; | 48 | int ret; |
70 | 49 | ||
71 | ret = cirrus_kick_out_firmware_fb(pdev); | 50 | ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "cirrusdrmfb"); |
72 | if (ret) | 51 | if (ret) |
73 | return ret; | 52 | return ret; |
74 | 53 | ||
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index ce9db7aab225..a29f87e98d9d 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h | |||
@@ -146,7 +146,7 @@ struct cirrus_device { | |||
146 | 146 | ||
147 | struct cirrus_fbdev { | 147 | struct cirrus_fbdev { |
148 | struct drm_fb_helper helper; | 148 | struct drm_fb_helper helper; |
149 | struct drm_framebuffer gfb; | 149 | struct drm_framebuffer *gfb; |
150 | void *sysram; | 150 | void *sysram; |
151 | int size; | 151 | int size; |
152 | int x1, y1, x2, y2; /* dirty rect */ | 152 | int x1, y1, x2, y2; /* dirty rect */ |
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index b643ac92801c..68ab1821e15b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c | |||
@@ -22,14 +22,14 @@ static void cirrus_dirty_update(struct cirrus_fbdev *afbdev, | |||
22 | struct drm_gem_object *obj; | 22 | struct drm_gem_object *obj; |
23 | struct cirrus_bo *bo; | 23 | struct cirrus_bo *bo; |
24 | int src_offset, dst_offset; | 24 | int src_offset, dst_offset; |
25 | int bpp = afbdev->gfb.format->cpp[0]; | 25 | int bpp = afbdev->gfb->format->cpp[0]; |
26 | int ret = -EBUSY; | 26 | int ret = -EBUSY; |
27 | bool unmap = false; | 27 | bool unmap = false; |
28 | bool store_for_later = false; | 28 | bool store_for_later = false; |
29 | int x2, y2; | 29 | int x2, y2; |
30 | unsigned long flags; | 30 | unsigned long flags; |
31 | 31 | ||
32 | obj = afbdev->gfb.obj[0]; | 32 | obj = afbdev->gfb->obj[0]; |
33 | bo = gem_to_cirrus_bo(obj); | 33 | bo = gem_to_cirrus_bo(obj); |
34 | 34 | ||
35 | /* | 35 | /* |
@@ -82,7 +82,7 @@ static void cirrus_dirty_update(struct cirrus_fbdev *afbdev, | |||
82 | } | 82 | } |
83 | for (i = y; i < y + height; i++) { | 83 | for (i = y; i < y + height; i++) { |
84 | /* assume equal stride for now */ | 84 | /* assume equal stride for now */ |
85 | src_offset = dst_offset = i * afbdev->gfb.pitches[0] + (x * bpp); | 85 | src_offset = dst_offset = i * afbdev->gfb->pitches[0] + (x * bpp); |
86 | memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp); | 86 | memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp); |
87 | 87 | ||
88 | } | 88 | } |
@@ -192,23 +192,26 @@ static int cirrusfb_create(struct drm_fb_helper *helper, | |||
192 | return -ENOMEM; | 192 | return -ENOMEM; |
193 | 193 | ||
194 | info = drm_fb_helper_alloc_fbi(helper); | 194 | info = drm_fb_helper_alloc_fbi(helper); |
195 | if (IS_ERR(info)) | 195 | if (IS_ERR(info)) { |
196 | return PTR_ERR(info); | 196 | ret = PTR_ERR(info); |
197 | goto err_vfree; | ||
198 | } | ||
197 | 199 | ||
198 | info->par = gfbdev; | 200 | info->par = gfbdev; |
199 | 201 | ||
200 | ret = cirrus_framebuffer_init(cdev->dev, &gfbdev->gfb, &mode_cmd, gobj); | 202 | fb = kzalloc(sizeof(*fb), GFP_KERNEL); |
203 | if (!fb) { | ||
204 | ret = -ENOMEM; | ||
205 | goto err_drm_gem_object_put_unlocked; | ||
206 | } | ||
207 | |||
208 | ret = cirrus_framebuffer_init(cdev->dev, fb, &mode_cmd, gobj); | ||
201 | if (ret) | 209 | if (ret) |
202 | return ret; | 210 | goto err_kfree; |
203 | 211 | ||
204 | gfbdev->sysram = sysram; | 212 | gfbdev->sysram = sysram; |
205 | gfbdev->size = size; | 213 | gfbdev->size = size; |
206 | 214 | gfbdev->gfb = fb; | |
207 | fb = &gfbdev->gfb; | ||
208 | if (!fb) { | ||
209 | DRM_INFO("fb is NULL\n"); | ||
210 | return -EINVAL; | ||
211 | } | ||
212 | 215 | ||
213 | /* setup helper */ | 216 | /* setup helper */ |
214 | gfbdev->helper.fb = fb; | 217 | gfbdev->helper.fb = fb; |
@@ -241,24 +244,27 @@ static int cirrusfb_create(struct drm_fb_helper *helper, | |||
241 | DRM_INFO(" pitch is %d\n", fb->pitches[0]); | 244 | DRM_INFO(" pitch is %d\n", fb->pitches[0]); |
242 | 245 | ||
243 | return 0; | 246 | return 0; |
247 | |||
248 | err_kfree: | ||
249 | kfree(fb); | ||
250 | err_drm_gem_object_put_unlocked: | ||
251 | drm_gem_object_put_unlocked(gobj); | ||
252 | err_vfree: | ||
253 | vfree(sysram); | ||
254 | return ret; | ||
244 | } | 255 | } |
245 | 256 | ||
246 | static int cirrus_fbdev_destroy(struct drm_device *dev, | 257 | static int cirrus_fbdev_destroy(struct drm_device *dev, |
247 | struct cirrus_fbdev *gfbdev) | 258 | struct cirrus_fbdev *gfbdev) |
248 | { | 259 | { |
249 | struct drm_framebuffer *gfb = &gfbdev->gfb; | 260 | struct drm_framebuffer *gfb = gfbdev->gfb; |
250 | 261 | ||
251 | drm_fb_helper_unregister_fbi(&gfbdev->helper); | 262 | drm_fb_helper_unregister_fbi(&gfbdev->helper); |
252 | 263 | ||
253 | if (gfb->obj[0]) { | ||
254 | drm_gem_object_put_unlocked(gfb->obj[0]); | ||
255 | gfb->obj[0] = NULL; | ||
256 | } | ||
257 | |||
258 | vfree(gfbdev->sysram); | 264 | vfree(gfbdev->sysram); |
259 | drm_fb_helper_fini(&gfbdev->helper); | 265 | drm_fb_helper_fini(&gfbdev->helper); |
260 | drm_framebuffer_unregister_private(gfb); | 266 | if (gfb) |
261 | drm_framebuffer_cleanup(gfb); | 267 | drm_framebuffer_put(gfb); |
262 | 268 | ||
263 | return 0; | 269 | return 0; |
264 | } | 270 | } |
@@ -271,7 +277,6 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) | |||
271 | { | 277 | { |
272 | struct cirrus_fbdev *gfbdev; | 278 | struct cirrus_fbdev *gfbdev; |
273 | int ret; | 279 | int ret; |
274 | int bpp_sel = 24; | ||
275 | 280 | ||
276 | /*bpp_sel = 8;*/ | 281 | /*bpp_sel = 8;*/ |
277 | gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL); | 282 | gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL); |
@@ -296,7 +301,7 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) | |||
296 | /* disable all the possible outputs/crtcs before entering KMS mode */ | 301 | /* disable all the possible outputs/crtcs before entering KMS mode */ |
297 | drm_helper_disable_unused_functions(cdev->dev); | 302 | drm_helper_disable_unused_functions(cdev->dev); |
298 | 303 | ||
299 | return drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel); | 304 | return drm_fb_helper_initial_config(&gfbdev->helper, cirrus_bpp); |
300 | } | 305 | } |
301 | 306 | ||
302 | void cirrus_fbdev_fini(struct cirrus_device *cdev) | 307 | void cirrus_fbdev_fini(struct cirrus_device *cdev) |
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 60d54e10a34d..57f8fe6d020b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c | |||
@@ -269,7 +269,7 @@ static void cirrus_bo_unref(struct cirrus_bo **bo) | |||
269 | return; | 269 | return; |
270 | 270 | ||
271 | tbo = &((*bo)->bo); | 271 | tbo = &((*bo)->bo); |
272 | ttm_bo_unref(&tbo); | 272 | ttm_bo_put(tbo); |
273 | *bo = NULL; | 273 | *bo = NULL; |
274 | } | 274 | } |
275 | 275 | ||
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 336bfda40125..ed7dcf212a34 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c | |||
@@ -127,7 +127,7 @@ static int cirrus_crtc_do_set_base(struct drm_crtc *crtc, | |||
127 | return ret; | 127 | return ret; |
128 | } | 128 | } |
129 | 129 | ||
130 | if (&cdev->mode_info.gfbdev->gfb == crtc->primary->fb) { | 130 | if (cdev->mode_info.gfbdev->gfb == crtc->primary->fb) { |
131 | /* if pushing console in kmap it */ | 131 | /* if pushing console in kmap it */ |
132 | ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); | 132 | ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); |
133 | if (ret) | 133 | if (ret) |
@@ -512,7 +512,7 @@ int cirrus_modeset_init(struct cirrus_device *cdev) | |||
512 | cdev->dev->mode_config.max_height = CIRRUS_MAX_FB_HEIGHT; | 512 | cdev->dev->mode_config.max_height = CIRRUS_MAX_FB_HEIGHT; |
513 | 513 | ||
514 | cdev->dev->mode_config.fb_base = cdev->mc.vram_base; | 514 | cdev->dev->mode_config.fb_base = cdev->mc.vram_base; |
515 | cdev->dev->mode_config.preferred_depth = 24; | 515 | cdev->dev->mode_config.preferred_depth = cirrus_bpp; |
516 | /* don't prefer a shadow on virt GPU */ | 516 | /* don't prefer a shadow on virt GPU */ |
517 | cdev->dev->mode_config.prefer_shadow = 0; | 517 | cdev->dev->mode_config.prefer_shadow = 0; |
518 | 518 | ||
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3eb061e11e2e..d0478abc01bd 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c | |||
@@ -895,6 +895,8 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, | |||
895 | state->src_h = val; | 895 | state->src_h = val; |
896 | } else if (property == plane->alpha_property) { | 896 | } else if (property == plane->alpha_property) { |
897 | state->alpha = val; | 897 | state->alpha = val; |
898 | } else if (property == plane->blend_mode_property) { | ||
899 | state->pixel_blend_mode = val; | ||
898 | } else if (property == plane->rotation_property) { | 900 | } else if (property == plane->rotation_property) { |
899 | if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) { | 901 | if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) { |
900 | DRM_DEBUG_ATOMIC("[PLANE:%d:%s] bad rotation bitmask: 0x%llx\n", | 902 | DRM_DEBUG_ATOMIC("[PLANE:%d:%s] bad rotation bitmask: 0x%llx\n", |
@@ -968,6 +970,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane, | |||
968 | *val = state->src_h; | 970 | *val = state->src_h; |
969 | } else if (property == plane->alpha_property) { | 971 | } else if (property == plane->alpha_property) { |
970 | *val = state->alpha; | 972 | *val = state->alpha; |
973 | } else if (property == plane->blend_mode_property) { | ||
974 | *val = state->pixel_blend_mode; | ||
971 | } else if (property == plane->rotation_property) { | 975 | } else if (property == plane->rotation_property) { |
972 | *val = state->rotation; | 976 | *val = state->rotation; |
973 | } else if (property == plane->zpos_property) { | 977 | } else if (property == plane->zpos_property) { |
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 80be74df7ba6..2c23a48482da 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c | |||
@@ -3555,6 +3555,29 @@ void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, | |||
3555 | EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); | 3555 | EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); |
3556 | 3556 | ||
3557 | /** | 3557 | /** |
3558 | * __drm_atomic_helper_plane_reset - resets planes state to default values | ||
3559 | * @plane: plane object, must not be NULL | ||
3560 | * @state: atomic plane state, must not be NULL | ||
3561 | * | ||
3562 | * Initializes plane state to default. This is useful for drivers that subclass | ||
3563 | * the plane state. | ||
3564 | */ | ||
3565 | void __drm_atomic_helper_plane_reset(struct drm_plane *plane, | ||
3566 | struct drm_plane_state *state) | ||
3567 | { | ||
3568 | state->plane = plane; | ||
3569 | state->rotation = DRM_MODE_ROTATE_0; | ||
3570 | |||
3571 | /* Reset the alpha value to fully opaque if it matters */ | ||
3572 | if (plane->alpha_property) | ||
3573 | state->alpha = plane->alpha_property->values[1]; | ||
3574 | state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; | ||
3575 | |||
3576 | plane->state = state; | ||
3577 | } | ||
3578 | EXPORT_SYMBOL(__drm_atomic_helper_plane_reset); | ||
3579 | |||
3580 | /** | ||
3558 | * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes | 3581 | * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes |
3559 | * @plane: drm plane | 3582 | * @plane: drm plane |
3560 | * | 3583 | * |
@@ -3568,15 +3591,8 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) | |||
3568 | 3591 | ||
3569 | kfree(plane->state); | 3592 | kfree(plane->state); |
3570 | plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); | 3593 | plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); |
3571 | 3594 | if (plane->state) | |
3572 | if (plane->state) { | 3595 | __drm_atomic_helper_plane_reset(plane, plane->state); |
3573 | plane->state->plane = plane; | ||
3574 | plane->state->rotation = DRM_MODE_ROTATE_0; | ||
3575 | |||
3576 | /* Reset the alpha value to fully opaque if it matters */ | ||
3577 | if (plane->alpha_property) | ||
3578 | plane->state->alpha = plane->alpha_property->values[1]; | ||
3579 | } | ||
3580 | } | 3596 | } |
3581 | EXPORT_SYMBOL(drm_atomic_helper_plane_reset); | 3597 | EXPORT_SYMBOL(drm_atomic_helper_plane_reset); |
3582 | 3598 | ||
diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index a16a74d7e15e..402b62d3f072 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c | |||
@@ -107,6 +107,52 @@ | |||
107 | * planes. Without this property the primary plane is always below the cursor | 107 | * planes. Without this property the primary plane is always below the cursor |
108 | * plane, and ordering between all other planes is undefined. | 108 | * plane, and ordering between all other planes is undefined. |
109 | * | 109 | * |
110 | * pixel blend mode: | ||
111 | * Pixel blend mode is set up with drm_plane_create_blend_mode_property(). | ||
112 | * It adds a blend mode for alpha blending equation selection, describing | ||
113 | * how the pixels from the current plane are composited with the | ||
114 | * background. | ||
115 | * | ||
116 | * Three alpha blending equations are defined: | ||
117 | * | ||
118 | * "None": | ||
119 | * Blend formula that ignores the pixel alpha:: | ||
120 | * | ||
121 | * out.rgb = plane_alpha * fg.rgb + | ||
122 | * (1 - plane_alpha) * bg.rgb | ||
123 | * | ||
124 | * "Pre-multiplied": | ||
125 | * Blend formula that assumes the pixel color values | ||
126 | * have been already pre-multiplied with the alpha | ||
127 | * channel values:: | ||
128 | * | ||
129 | * out.rgb = plane_alpha * fg.rgb + | ||
130 | * (1 - (plane_alpha * fg.alpha)) * bg.rgb | ||
131 | * | ||
132 | * "Coverage": | ||
133 | * Blend formula that assumes the pixel color values have not | ||
134 | * been pre-multiplied and will do so when blending them to the | ||
135 | * background color values:: | ||
136 | * | ||
137 | * out.rgb = plane_alpha * fg.alpha * fg.rgb + | ||
138 | * (1 - (plane_alpha * fg.alpha)) * bg.rgb | ||
139 | * | ||
140 | * Using the following symbols: | ||
141 | * | ||
142 | * "fg.rgb": | ||
143 | * Each of the RGB component values from the plane's pixel | ||
144 | * "fg.alpha": | ||
145 | * Alpha component value from the plane's pixel. If the plane's | ||
146 | * pixel format has no alpha component, then this is assumed to be | ||
147 | * 1.0. In these cases, this property has no effect, as all three | ||
148 | * equations become equivalent. | ||
149 | * "bg.rgb": | ||
150 | * Each of the RGB component values from the background | ||
151 | * "plane_alpha": | ||
152 | * Plane alpha value set by the plane "alpha" property. If the | ||
153 | * plane does not expose the "alpha" property, then this is | ||
154 | * assumed to be 1.0 | ||
155 | * | ||
110 | * Note that all the property extensions described here apply either to the | 156 | * Note that all the property extensions described here apply either to the |
111 | * plane or the CRTC (e.g. for the background color, which currently is not | 157 | * plane or the CRTC (e.g. for the background color, which currently is not |
112 | * exposed and assumed to be black). | 158 | * exposed and assumed to be black). |
@@ -448,3 +494,80 @@ int drm_atomic_normalize_zpos(struct drm_device *dev, | |||
448 | return 0; | 494 | return 0; |
449 | } | 495 | } |
450 | EXPORT_SYMBOL(drm_atomic_normalize_zpos); | 496 | EXPORT_SYMBOL(drm_atomic_normalize_zpos); |
497 | |||
498 | /** | ||
499 | * drm_plane_create_blend_mode_property - create a new blend mode property | ||
500 | * @plane: drm plane | ||
501 | * @supported_modes: bitmask of supported modes, must include | ||
502 | * BIT(DRM_MODE_BLEND_PREMULTI). Current DRM assumption is | ||
503 | * that alpha is premultiplied, and old userspace can break if | ||
504 | * the property defaults to anything else. | ||
505 | * | ||
506 | * This creates a new property describing the blend mode. | ||
507 | * | ||
508 | * The property exposed to userspace is an enumeration property (see | ||
509 | * drm_property_create_enum()) called "pixel blend mode" and has the | ||
510 | * following enumeration values: | ||
511 | * | ||
512 | * "None": | ||
513 | * Blend formula that ignores the pixel alpha. | ||
514 | * | ||
515 | * "Pre-multiplied": | ||
516 | * Blend formula that assumes the pixel color values have been already | ||
517 | * pre-multiplied with the alpha channel values. | ||
518 | * | ||
519 | * "Coverage": | ||
520 | * Blend formula that assumes the pixel color values have not been | ||
521 | * pre-multiplied and will do so when blending them to the background color | ||
522 | * values. | ||
523 | * | ||
524 | * RETURNS: | ||
525 | * Zero for success or -errno | ||
526 | */ | ||
527 | int drm_plane_create_blend_mode_property(struct drm_plane *plane, | ||
528 | unsigned int supported_modes) | ||
529 | { | ||
530 | struct drm_device *dev = plane->dev; | ||
531 | struct drm_property *prop; | ||
532 | static const struct drm_prop_enum_list props[] = { | ||
533 | { DRM_MODE_BLEND_PIXEL_NONE, "None" }, | ||
534 | { DRM_MODE_BLEND_PREMULTI, "Pre-multiplied" }, | ||
535 | { DRM_MODE_BLEND_COVERAGE, "Coverage" }, | ||
536 | }; | ||
537 | unsigned int valid_mode_mask = BIT(DRM_MODE_BLEND_PIXEL_NONE) | | ||
538 | BIT(DRM_MODE_BLEND_PREMULTI) | | ||
539 | BIT(DRM_MODE_BLEND_COVERAGE); | ||
540 | int i; | ||
541 | |||
542 | if (WARN_ON((supported_modes & ~valid_mode_mask) || | ||
543 | ((supported_modes & BIT(DRM_MODE_BLEND_PREMULTI)) == 0))) | ||
544 | return -EINVAL; | ||
545 | |||
546 | prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, | ||
547 | "pixel blend mode", | ||
548 | hweight32(supported_modes)); | ||
549 | if (!prop) | ||
550 | return -ENOMEM; | ||
551 | |||
552 | for (i = 0; i < ARRAY_SIZE(props); i++) { | ||
553 | int ret; | ||
554 | |||
555 | if (!(BIT(props[i].type) & supported_modes)) | ||
556 | continue; | ||
557 | |||
558 | ret = drm_property_add_enum(prop, props[i].type, | ||
559 | props[i].name); | ||
560 | |||
561 | if (ret) { | ||
562 | drm_property_destroy(dev, prop); | ||
563 | |||
564 | return ret; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | drm_object_attach_property(&plane->base, prop, DRM_MODE_BLEND_PREMULTI); | ||
569 | plane->blend_mode_property = prop; | ||
570 | |||
571 | return 0; | ||
572 | } | ||
573 | EXPORT_SYMBOL(drm_plane_create_blend_mode_property); | ||
diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index 99961192bf03..00e743153e94 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c | |||
@@ -68,8 +68,29 @@ static int crc_control_show(struct seq_file *m, void *data) | |||
68 | { | 68 | { |
69 | struct drm_crtc *crtc = m->private; | 69 | struct drm_crtc *crtc = m->private; |
70 | 70 | ||
71 | seq_printf(m, "%s\n", crtc->crc.source); | 71 | if (crtc->funcs->get_crc_sources) { |
72 | size_t count; | ||
73 | const char *const *sources = crtc->funcs->get_crc_sources(crtc, | ||
74 | &count); | ||
75 | size_t values_cnt; | ||
76 | int i; | ||
77 | |||
78 | if (count == 0 || !sources) | ||
79 | goto out; | ||
80 | |||
81 | for (i = 0; i < count; i++) | ||
82 | if (!crtc->funcs->verify_crc_source(crtc, sources[i], | ||
83 | &values_cnt)) { | ||
84 | if (strcmp(sources[i], crtc->crc.source)) | ||
85 | seq_printf(m, "%s\n", sources[i]); | ||
86 | else | ||
87 | seq_printf(m, "%s*\n", sources[i]); | ||
88 | } | ||
89 | } | ||
90 | return 0; | ||
72 | 91 | ||
92 | out: | ||
93 | seq_printf(m, "%s*\n", crtc->crc.source); | ||
73 | return 0; | 94 | return 0; |
74 | } | 95 | } |
75 | 96 | ||
@@ -87,6 +108,8 @@ static ssize_t crc_control_write(struct file *file, const char __user *ubuf, | |||
87 | struct drm_crtc *crtc = m->private; | 108 | struct drm_crtc *crtc = m->private; |
88 | struct drm_crtc_crc *crc = &crtc->crc; | 109 | struct drm_crtc_crc *crc = &crtc->crc; |
89 | char *source; | 110 | char *source; |
111 | size_t values_cnt; | ||
112 | int ret; | ||
90 | 113 | ||
91 | if (len == 0) | 114 | if (len == 0) |
92 | return 0; | 115 | return 0; |
@@ -104,6 +127,10 @@ static ssize_t crc_control_write(struct file *file, const char __user *ubuf, | |||
104 | if (source[len] == '\n') | 127 | if (source[len] == '\n') |
105 | source[len] = '\0'; | 128 | source[len] = '\0'; |
106 | 129 | ||
130 | ret = crtc->funcs->verify_crc_source(crtc, source, &values_cnt); | ||
131 | if (ret) | ||
132 | return ret; | ||
133 | |||
107 | spin_lock_irq(&crc->lock); | 134 | spin_lock_irq(&crc->lock); |
108 | 135 | ||
109 | if (crc->opened) { | 136 | if (crc->opened) { |
@@ -168,57 +195,41 @@ static int crtc_crc_open(struct inode *inode, struct file *filep) | |||
168 | return ret; | 195 | return ret; |
169 | } | 196 | } |
170 | 197 | ||
171 | spin_lock_irq(&crc->lock); | 198 | ret = crtc->funcs->verify_crc_source(crtc, crc->source, &values_cnt); |
172 | if (!crc->opened) | ||
173 | crc->opened = true; | ||
174 | else | ||
175 | ret = -EBUSY; | ||
176 | spin_unlock_irq(&crc->lock); | ||
177 | |||
178 | if (ret) | 199 | if (ret) |
179 | return ret; | 200 | return ret; |
180 | 201 | ||
181 | ret = crtc->funcs->set_crc_source(crtc, crc->source, &values_cnt); | 202 | if (WARN_ON(values_cnt > DRM_MAX_CRC_NR)) |
182 | if (ret) | 203 | return -EINVAL; |
183 | goto err; | ||
184 | |||
185 | if (WARN_ON(values_cnt > DRM_MAX_CRC_NR)) { | ||
186 | ret = -EINVAL; | ||
187 | goto err_disable; | ||
188 | } | ||
189 | 204 | ||
190 | if (WARN_ON(values_cnt == 0)) { | 205 | if (WARN_ON(values_cnt == 0)) |
191 | ret = -EINVAL; | 206 | return -EINVAL; |
192 | goto err_disable; | ||
193 | } | ||
194 | 207 | ||
195 | entries = kcalloc(DRM_CRC_ENTRIES_NR, sizeof(*entries), GFP_KERNEL); | 208 | entries = kcalloc(DRM_CRC_ENTRIES_NR, sizeof(*entries), GFP_KERNEL); |
196 | if (!entries) { | 209 | if (!entries) |
197 | ret = -ENOMEM; | 210 | return -ENOMEM; |
198 | goto err_disable; | ||
199 | } | ||
200 | 211 | ||
201 | spin_lock_irq(&crc->lock); | 212 | spin_lock_irq(&crc->lock); |
202 | crc->entries = entries; | 213 | if (!crc->opened) { |
203 | crc->values_cnt = values_cnt; | 214 | crc->opened = true; |
204 | 215 | crc->entries = entries; | |
205 | /* | 216 | crc->values_cnt = values_cnt; |
206 | * Only return once we got a first frame, so userspace doesn't have to | 217 | } else { |
207 | * guess when this particular piece of HW will be ready to start | 218 | ret = -EBUSY; |
208 | * generating CRCs. | 219 | } |
209 | */ | ||
210 | ret = wait_event_interruptible_lock_irq(crc->wq, | ||
211 | crtc_crc_data_count(crc), | ||
212 | crc->lock); | ||
213 | spin_unlock_irq(&crc->lock); | 220 | spin_unlock_irq(&crc->lock); |
214 | 221 | ||
222 | if (ret) { | ||
223 | kfree(entries); | ||
224 | return ret; | ||
225 | } | ||
226 | |||
227 | ret = crtc->funcs->set_crc_source(crtc, crc->source); | ||
215 | if (ret) | 228 | if (ret) |
216 | goto err_disable; | 229 | goto err; |
217 | 230 | ||
218 | return 0; | 231 | return 0; |
219 | 232 | ||
220 | err_disable: | ||
221 | crtc->funcs->set_crc_source(crtc, NULL, &values_cnt); | ||
222 | err: | 233 | err: |
223 | spin_lock_irq(&crc->lock); | 234 | spin_lock_irq(&crc->lock); |
224 | crtc_crc_cleanup(crc); | 235 | crtc_crc_cleanup(crc); |
@@ -230,9 +241,8 @@ static int crtc_crc_release(struct inode *inode, struct file *filep) | |||
230 | { | 241 | { |
231 | struct drm_crtc *crtc = filep->f_inode->i_private; | 242 | struct drm_crtc *crtc = filep->f_inode->i_private; |
232 | struct drm_crtc_crc *crc = &crtc->crc; | 243 | struct drm_crtc_crc *crc = &crtc->crc; |
233 | size_t values_cnt; | ||
234 | 244 | ||
235 | crtc->funcs->set_crc_source(crtc, NULL, &values_cnt); | 245 | crtc->funcs->set_crc_source(crtc, NULL); |
236 | 246 | ||
237 | spin_lock_irq(&crc->lock); | 247 | spin_lock_irq(&crc->lock); |
238 | crtc_crc_cleanup(crc); | 248 | crtc_crc_cleanup(crc); |
@@ -338,7 +348,7 @@ int drm_debugfs_crtc_crc_add(struct drm_crtc *crtc) | |||
338 | { | 348 | { |
339 | struct dentry *crc_ent, *ent; | 349 | struct dentry *crc_ent, *ent; |
340 | 350 | ||
341 | if (!crtc->funcs->set_crc_source) | 351 | if (!crtc->funcs->set_crc_source || !crtc->funcs->verify_crc_source) |
342 | return 0; | 352 | return 0; |
343 | 353 | ||
344 | crc_ent = debugfs_create_dir("crc", crtc->debugfs_entry); | 354 | crc_ent = debugfs_create_dir("crc", crtc->debugfs_entry); |
diff --git a/drivers/gpu/drm/drm_dp_cec.c b/drivers/gpu/drm/drm_dp_cec.c index 988513346e9c..8a718f85079a 100644 --- a/drivers/gpu/drm/drm_dp_cec.c +++ b/drivers/gpu/drm/drm_dp_cec.c | |||
@@ -16,7 +16,9 @@ | |||
16 | * here. Quite a few active (mini-)DP-to-HDMI or USB-C-to-HDMI adapters | 16 | * here. Quite a few active (mini-)DP-to-HDMI or USB-C-to-HDMI adapters |
17 | * have a converter chip that supports CEC-Tunneling-over-AUX (usually the | 17 | * have a converter chip that supports CEC-Tunneling-over-AUX (usually the |
18 | * Parade PS176), but they do not wire up the CEC pin, thus making CEC | 18 | * Parade PS176), but they do not wire up the CEC pin, thus making CEC |
19 | * useless. | 19 | * useless. Note that MegaChips 2900-based adapters appear to have good |
20 | * support for CEC tunneling. Those adapters that I have tested using | ||
21 | * this chipset all have the CEC line connected. | ||
20 | * | 22 | * |
21 | * Sadly there is no way for this driver to know this. What happens is | 23 | * Sadly there is no way for this driver to know this. What happens is |
22 | * that a /dev/cecX device is created that is isolated and unable to see | 24 | * that a /dev/cecX device is created that is isolated and unable to see |
@@ -238,6 +240,10 @@ void drm_dp_cec_irq(struct drm_dp_aux *aux) | |||
238 | u8 cec_irq; | 240 | u8 cec_irq; |
239 | int ret; | 241 | int ret; |
240 | 242 | ||
243 | /* No transfer function was set, so not a DP connector */ | ||
244 | if (!aux->transfer) | ||
245 | return; | ||
246 | |||
241 | mutex_lock(&aux->cec.lock); | 247 | mutex_lock(&aux->cec.lock); |
242 | if (!aux->cec.adap) | 248 | if (!aux->cec.adap) |
243 | goto unlock; | 249 | goto unlock; |
@@ -293,6 +299,10 @@ void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid) | |||
293 | unsigned int num_las = 1; | 299 | unsigned int num_las = 1; |
294 | u8 cap; | 300 | u8 cap; |
295 | 301 | ||
302 | /* No transfer function was set, so not a DP connector */ | ||
303 | if (!aux->transfer) | ||
304 | return; | ||
305 | |||
296 | #ifndef CONFIG_MEDIA_CEC_RC | 306 | #ifndef CONFIG_MEDIA_CEC_RC |
297 | /* | 307 | /* |
298 | * CEC_CAP_RC is part of CEC_CAP_DEFAULTS, but it is stripped by | 308 | * CEC_CAP_RC is part of CEC_CAP_DEFAULTS, but it is stripped by |
@@ -361,6 +371,10 @@ EXPORT_SYMBOL(drm_dp_cec_set_edid); | |||
361 | */ | 371 | */ |
362 | void drm_dp_cec_unset_edid(struct drm_dp_aux *aux) | 372 | void drm_dp_cec_unset_edid(struct drm_dp_aux *aux) |
363 | { | 373 | { |
374 | /* No transfer function was set, so not a DP connector */ | ||
375 | if (!aux->transfer) | ||
376 | return; | ||
377 | |||
364 | cancel_delayed_work_sync(&aux->cec.unregister_work); | 378 | cancel_delayed_work_sync(&aux->cec.unregister_work); |
365 | 379 | ||
366 | mutex_lock(&aux->cec.lock); | 380 | mutex_lock(&aux->cec.lock); |
@@ -404,6 +418,8 @@ void drm_dp_cec_register_connector(struct drm_dp_aux *aux, const char *name, | |||
404 | struct device *parent) | 418 | struct device *parent) |
405 | { | 419 | { |
406 | WARN_ON(aux->cec.adap); | 420 | WARN_ON(aux->cec.adap); |
421 | if (WARN_ON(!aux->transfer)) | ||
422 | return; | ||
407 | aux->cec.name = name; | 423 | aux->cec.name = name; |
408 | aux->cec.parent = parent; | 424 | aux->cec.parent = parent; |
409 | INIT_DELAYED_WORK(&aux->cec.unregister_work, | 425 | INIT_DELAYED_WORK(&aux->cec.unregister_work, |
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 0cccbcb2d03e..8c6b9fd89f8a 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c | |||
@@ -850,7 +850,8 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) | |||
850 | return ret; | 850 | return ret; |
851 | 851 | ||
852 | case DP_AUX_I2C_REPLY_NACK: | 852 | case DP_AUX_I2C_REPLY_NACK: |
853 | DRM_DEBUG_KMS("I2C nack (result=%d, size=%zu\n", ret, msg->size); | 853 | DRM_DEBUG_KMS("I2C nack (result=%d, size=%zu)\n", |
854 | ret, msg->size); | ||
854 | aux->i2c_nack_count++; | 855 | aux->i2c_nack_count++; |
855 | return -EREMOTEIO; | 856 | return -EREMOTEIO; |
856 | 857 | ||
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 7780567aa669..5ff1d79b86c4 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c | |||
@@ -439,6 +439,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx | |||
439 | if (idx > raw->curlen) | 439 | if (idx > raw->curlen) |
440 | goto fail_len; | 440 | goto fail_len; |
441 | repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; | 441 | repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; |
442 | idx++; | ||
442 | if (idx > raw->curlen) | 443 | if (idx > raw->curlen) |
443 | goto fail_len; | 444 | goto fail_len; |
444 | 445 | ||
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 9da36a6271d3..47e0e2f6642d 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c | |||
@@ -86,14 +86,21 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, | |||
86 | { | 86 | { |
87 | struct drm_gem_cma_object *obj; | 87 | struct drm_gem_cma_object *obj; |
88 | dma_addr_t paddr; | 88 | dma_addr_t paddr; |
89 | u8 h_div = 1, v_div = 1; | ||
89 | 90 | ||
90 | obj = drm_fb_cma_get_gem_obj(fb, plane); | 91 | obj = drm_fb_cma_get_gem_obj(fb, plane); |
91 | if (!obj) | 92 | if (!obj) |
92 | return 0; | 93 | return 0; |
93 | 94 | ||
94 | paddr = obj->paddr + fb->offsets[plane]; | 95 | paddr = obj->paddr + fb->offsets[plane]; |
95 | paddr += fb->format->cpp[plane] * (state->src_x >> 16); | 96 | |
96 | paddr += fb->pitches[plane] * (state->src_y >> 16); | 97 | if (plane > 0) { |
98 | h_div = fb->format->hsub; | ||
99 | v_div = fb->format->vsub; | ||
100 | } | ||
101 | |||
102 | paddr += (fb->format->cpp[plane] * (state->src_x >> 16)) / h_div; | ||
103 | paddr += (fb->pitches[plane] * (state->src_y >> 16)) / v_div; | ||
97 | 104 | ||
98 | return paddr; | 105 | return paddr; |
99 | } | 106 | } |
@@ -219,21 +226,6 @@ void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma) | |||
219 | EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event); | 226 | EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event); |
220 | 227 | ||
221 | /** | 228 | /** |
222 | * drm_fbdev_cma_set_suspend - wrapper around drm_fb_helper_set_suspend | ||
223 | * @fbdev_cma: The drm_fbdev_cma struct, may be NULL | ||
224 | * @state: desired state, zero to resume, non-zero to suspend | ||
225 | * | ||
226 | * Calls drm_fb_helper_set_suspend, which is a wrapper around | ||
227 | * fb_set_suspend implemented by fbdev core. | ||
228 | */ | ||
229 | void drm_fbdev_cma_set_suspend(struct drm_fbdev_cma *fbdev_cma, bool state) | ||
230 | { | ||
231 | if (fbdev_cma) | ||
232 | drm_fb_helper_set_suspend(&fbdev_cma->fb_helper, state); | ||
233 | } | ||
234 | EXPORT_SYMBOL(drm_fbdev_cma_set_suspend); | ||
235 | |||
236 | /** | ||
237 | * drm_fbdev_cma_set_suspend_unlocked - wrapper around | 229 | * drm_fbdev_cma_set_suspend_unlocked - wrapper around |
238 | * drm_fb_helper_set_suspend_unlocked | 230 | * drm_fb_helper_set_suspend_unlocked |
239 | * @fbdev_cma: The drm_fbdev_cma struct, may be NULL | 231 | * @fbdev_cma: The drm_fbdev_cma struct, may be NULL |
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 80a5115c3846..1d2ced882b66 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c | |||
@@ -436,7 +436,7 @@ struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj) | |||
436 | 436 | ||
437 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | 437 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); |
438 | if (!sgt) | 438 | if (!sgt) |
439 | return NULL; | 439 | return ERR_PTR(-ENOMEM); |
440 | 440 | ||
441 | ret = dma_get_sgtable(obj->dev->dev, sgt, cma_obj->vaddr, | 441 | ret = dma_get_sgtable(obj->dev->dev, sgt, cma_obj->vaddr, |
442 | cma_obj->paddr, obj->size); | 442 | cma_obj->paddr, obj->size); |
@@ -447,7 +447,7 @@ struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj) | |||
447 | 447 | ||
448 | out: | 448 | out: |
449 | kfree(sgt); | 449 | kfree(sgt); |
450 | return NULL; | 450 | return ERR_PTR(ret); |
451 | } | 451 | } |
452 | EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table); | 452 | EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table); |
453 | 453 | ||
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index b902361dee6e..0db486d10d1c 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c | |||
@@ -152,7 +152,9 @@ EXPORT_SYMBOL(drm_panel_detach); | |||
152 | * | 152 | * |
153 | * Return: A pointer to the panel registered for the specified device tree | 153 | * Return: A pointer to the panel registered for the specified device tree |
154 | * node or an ERR_PTR() if no panel matching the device tree node can be found. | 154 | * node or an ERR_PTR() if no panel matching the device tree node can be found. |
155 | * | ||
155 | * Possible error codes returned by this function: | 156 | * Possible error codes returned by this function: |
157 | * | ||
156 | * - EPROBE_DEFER: the panel device has not been probed yet, and the caller | 158 | * - EPROBE_DEFER: the panel device has not been probed yet, and the caller |
157 | * should retry later | 159 | * should retry later |
158 | * - ENODEV: the device is not available (status != "okay" or "ok") | 160 | * - ENODEV: the device is not available (status != "okay" or "ok") |
diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index adb3cb27d31e..3a8837c49639 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c | |||
@@ -120,14 +120,6 @@ static int drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, | |||
120 | return ret; | 120 | return ret; |
121 | } | 121 | } |
122 | 122 | ||
123 | /** | ||
124 | * drm_syncobj_add_callback - adds a callback to syncobj::cb_list | ||
125 | * @syncobj: Sync object to which to add the callback | ||
126 | * @cb: Callback to add | ||
127 | * @func: Func to use when initializing the drm_syncobj_cb struct | ||
128 | * | ||
129 | * This adds a callback to be called next time the fence is replaced | ||
130 | */ | ||
131 | void drm_syncobj_add_callback(struct drm_syncobj *syncobj, | 123 | void drm_syncobj_add_callback(struct drm_syncobj *syncobj, |
132 | struct drm_syncobj_cb *cb, | 124 | struct drm_syncobj_cb *cb, |
133 | drm_syncobj_func_t func) | 125 | drm_syncobj_func_t func) |
@@ -136,13 +128,7 @@ void drm_syncobj_add_callback(struct drm_syncobj *syncobj, | |||
136 | drm_syncobj_add_callback_locked(syncobj, cb, func); | 128 | drm_syncobj_add_callback_locked(syncobj, cb, func); |
137 | spin_unlock(&syncobj->lock); | 129 | spin_unlock(&syncobj->lock); |
138 | } | 130 | } |
139 | EXPORT_SYMBOL(drm_syncobj_add_callback); | ||
140 | 131 | ||
141 | /** | ||
142 | * drm_syncobj_add_callback - removes a callback to syncobj::cb_list | ||
143 | * @syncobj: Sync object from which to remove the callback | ||
144 | * @cb: Callback to remove | ||
145 | */ | ||
146 | void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, | 132 | void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, |
147 | struct drm_syncobj_cb *cb) | 133 | struct drm_syncobj_cb *cb) |
148 | { | 134 | { |
@@ -150,7 +136,6 @@ void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, | |||
150 | list_del_init(&cb->node); | 136 | list_del_init(&cb->node); |
151 | spin_unlock(&syncobj->lock); | 137 | spin_unlock(&syncobj->lock); |
152 | } | 138 | } |
153 | EXPORT_SYMBOL(drm_syncobj_remove_callback); | ||
154 | 139 | ||
155 | /** | 140 | /** |
156 | * drm_syncobj_replace_fence - replace fence in a sync object. | 141 | * drm_syncobj_replace_fence - replace fence in a sync object. |
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 28cdcf76b6f9..ec2dcfdac8ef 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c | |||
@@ -873,8 +873,8 @@ static void send_vblank_event(struct drm_device *dev, | |||
873 | * handler by calling drm_crtc_send_vblank_event() and make sure that there's no | 873 | * handler by calling drm_crtc_send_vblank_event() and make sure that there's no |
874 | * possible race with the hardware committing the atomic update. | 874 | * possible race with the hardware committing the atomic update. |
875 | * | 875 | * |
876 | * Caller must hold a vblank reference for the event @e, which will be dropped | 876 | * Caller must hold a vblank reference for the event @e acquired by a |
877 | * when the next vblank arrives. | 877 | * drm_crtc_vblank_get(), which will be dropped when the next vblank arrives. |
878 | */ | 878 | */ |
879 | void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, | 879 | void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, |
880 | struct drm_pending_vblank_event *e) | 880 | struct drm_pending_vblank_event *e) |
@@ -1541,7 +1541,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, | |||
1541 | if (vblwait->request.type & | 1541 | if (vblwait->request.type & |
1542 | ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | | 1542 | ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | |
1543 | _DRM_VBLANK_HIGH_CRTC_MASK)) { | 1543 | _DRM_VBLANK_HIGH_CRTC_MASK)) { |
1544 | DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", | 1544 | DRM_DEBUG("Unsupported type value 0x%x, supported mask 0x%x\n", |
1545 | vblwait->request.type, | 1545 | vblwait->request.type, |
1546 | (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | | 1546 | (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | |
1547 | _DRM_VBLANK_HIGH_CRTC_MASK)); | 1547 | _DRM_VBLANK_HIGH_CRTC_MASK)); |
diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index a6b2fe36b025..c5d0d2358301 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c | |||
@@ -103,10 +103,7 @@ EXPORT_SYMBOL(drm_vma_offset_manager_init); | |||
103 | */ | 103 | */ |
104 | void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr) | 104 | void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr) |
105 | { | 105 | { |
106 | /* take the lock to protect against buggy drivers */ | ||
107 | write_lock(&mgr->vm_lock); | ||
108 | drm_mm_takedown(&mgr->vm_addr_space_mm); | 106 | drm_mm_takedown(&mgr->vm_addr_space_mm); |
109 | write_unlock(&mgr->vm_lock); | ||
110 | } | 107 | } |
111 | EXPORT_SYMBOL(drm_vma_offset_manager_destroy); | 108 | EXPORT_SYMBOL(drm_vma_offset_manager_destroy); |
112 | 109 | ||
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 93d2f4000d2f..941b238bdcc9 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <linux/mm_types.h> | 24 | #include <linux/mm_types.h> |
25 | 25 | ||
26 | #include <drm/drmP.h> | 26 | #include <drm/drmP.h> |
27 | #include <drm/drm_global.h> | ||
28 | #include <drm/gma_drm.h> | 27 | #include <drm/gma_drm.h> |
29 | #include "psb_reg.h" | 28 | #include "psb_reg.h" |
30 | #include "psb_intel_drv.h" | 29 | #include "psb_intel_drv.h" |
diff --git a/drivers/gpu/drm/i915/i915_gem_clflush.c b/drivers/gpu/drm/i915/i915_gem_clflush.c index f5c570d35b2a..8e74c23cbd91 100644 --- a/drivers/gpu/drm/i915/i915_gem_clflush.c +++ b/drivers/gpu/drm/i915/i915_gem_clflush.c | |||
@@ -45,11 +45,6 @@ static const char *i915_clflush_get_timeline_name(struct dma_fence *fence) | |||
45 | return "clflush"; | 45 | return "clflush"; |
46 | } | 46 | } |
47 | 47 | ||
48 | static bool i915_clflush_enable_signaling(struct dma_fence *fence) | ||
49 | { | ||
50 | return true; | ||
51 | } | ||
52 | |||
53 | static void i915_clflush_release(struct dma_fence *fence) | 48 | static void i915_clflush_release(struct dma_fence *fence) |
54 | { | 49 | { |
55 | struct clflush *clflush = container_of(fence, typeof(*clflush), dma); | 50 | struct clflush *clflush = container_of(fence, typeof(*clflush), dma); |
@@ -63,8 +58,6 @@ static void i915_clflush_release(struct dma_fence *fence) | |||
63 | static const struct dma_fence_ops i915_clflush_ops = { | 58 | static const struct dma_fence_ops i915_clflush_ops = { |
64 | .get_driver_name = i915_clflush_get_driver_name, | 59 | .get_driver_name = i915_clflush_get_driver_name, |
65 | .get_timeline_name = i915_clflush_get_timeline_name, | 60 | .get_timeline_name = i915_clflush_get_timeline_name, |
66 | .enable_signaling = i915_clflush_enable_signaling, | ||
67 | .wait = dma_fence_default_wait, | ||
68 | .release = i915_clflush_release, | 61 | .release = i915_clflush_release, |
69 | }; | 62 | }; |
70 | 63 | ||
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4a3c8ee9a973..775968fa2049 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -12896,6 +12896,8 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { | |||
12896 | .atomic_duplicate_state = intel_crtc_duplicate_state, | 12896 | .atomic_duplicate_state = intel_crtc_duplicate_state, |
12897 | .atomic_destroy_state = intel_crtc_destroy_state, | 12897 | .atomic_destroy_state = intel_crtc_destroy_state, |
12898 | .set_crc_source = intel_crtc_set_crc_source, | 12898 | .set_crc_source = intel_crtc_set_crc_source, |
12899 | .verify_crc_source = intel_crtc_verify_crc_source, | ||
12900 | .get_crc_sources = intel_crtc_get_crc_sources, | ||
12899 | }; | 12901 | }; |
12900 | 12902 | ||
12901 | struct wait_rps_boost { | 12903 | struct wait_rps_boost { |
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8fc61e96754f..5f63e1a9c25b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h | |||
@@ -2172,12 +2172,17 @@ void lspcon_wait_pcon_mode(struct intel_lspcon *lspcon); | |||
2172 | 2172 | ||
2173 | /* intel_pipe_crc.c */ | 2173 | /* intel_pipe_crc.c */ |
2174 | #ifdef CONFIG_DEBUG_FS | 2174 | #ifdef CONFIG_DEBUG_FS |
2175 | int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name, | 2175 | int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name); |
2176 | size_t *values_cnt); | 2176 | int intel_crtc_verify_crc_source(struct drm_crtc *crtc, |
2177 | const char *source_name, size_t *values_cnt); | ||
2178 | const char *const *intel_crtc_get_crc_sources(struct drm_crtc *crtc, | ||
2179 | size_t *count); | ||
2177 | void intel_crtc_disable_pipe_crc(struct intel_crtc *crtc); | 2180 | void intel_crtc_disable_pipe_crc(struct intel_crtc *crtc); |
2178 | void intel_crtc_enable_pipe_crc(struct intel_crtc *crtc); | 2181 | void intel_crtc_enable_pipe_crc(struct intel_crtc *crtc); |
2179 | #else | 2182 | #else |
2180 | #define intel_crtc_set_crc_source NULL | 2183 | #define intel_crtc_set_crc_source NULL |
2184 | #define intel_crtc_verify_crc_source NULL | ||
2185 | #define intel_crtc_get_crc_sources NULL | ||
2181 | static inline void intel_crtc_disable_pipe_crc(struct intel_crtc *crtc) | 2186 | static inline void intel_crtc_disable_pipe_crc(struct intel_crtc *crtc) |
2182 | { | 2187 | { |
2183 | } | 2188 | } |
diff --git a/drivers/gpu/drm/i915/intel_pipe_crc.c b/drivers/gpu/drm/i915/intel_pipe_crc.c index 849e1b69ba73..f3c9010e332a 100644 --- a/drivers/gpu/drm/i915/intel_pipe_crc.c +++ b/drivers/gpu/drm/i915/intel_pipe_crc.c | |||
@@ -468,8 +468,122 @@ void intel_display_crc_init(struct drm_i915_private *dev_priv) | |||
468 | } | 468 | } |
469 | } | 469 | } |
470 | 470 | ||
471 | int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name, | 471 | static int i8xx_crc_source_valid(struct drm_i915_private *dev_priv, |
472 | size_t *values_cnt) | 472 | const enum intel_pipe_crc_source source) |
473 | { | ||
474 | switch (source) { | ||
475 | case INTEL_PIPE_CRC_SOURCE_PIPE: | ||
476 | case INTEL_PIPE_CRC_SOURCE_NONE: | ||
477 | return 0; | ||
478 | default: | ||
479 | return -EINVAL; | ||
480 | } | ||
481 | } | ||
482 | |||
483 | static int i9xx_crc_source_valid(struct drm_i915_private *dev_priv, | ||
484 | const enum intel_pipe_crc_source source) | ||
485 | { | ||
486 | switch (source) { | ||
487 | case INTEL_PIPE_CRC_SOURCE_PIPE: | ||
488 | case INTEL_PIPE_CRC_SOURCE_TV: | ||
489 | case INTEL_PIPE_CRC_SOURCE_DP_B: | ||
490 | case INTEL_PIPE_CRC_SOURCE_DP_C: | ||
491 | case INTEL_PIPE_CRC_SOURCE_DP_D: | ||
492 | case INTEL_PIPE_CRC_SOURCE_NONE: | ||
493 | return 0; | ||
494 | default: | ||
495 | return -EINVAL; | ||
496 | } | ||
497 | } | ||
498 | |||
499 | static int vlv_crc_source_valid(struct drm_i915_private *dev_priv, | ||
500 | const enum intel_pipe_crc_source source) | ||
501 | { | ||
502 | switch (source) { | ||
503 | case INTEL_PIPE_CRC_SOURCE_PIPE: | ||
504 | case INTEL_PIPE_CRC_SOURCE_DP_B: | ||
505 | case INTEL_PIPE_CRC_SOURCE_DP_C: | ||
506 | case INTEL_PIPE_CRC_SOURCE_DP_D: | ||
507 | case INTEL_PIPE_CRC_SOURCE_NONE: | ||
508 | return 0; | ||
509 | default: | ||
510 | return -EINVAL; | ||
511 | } | ||
512 | } | ||
513 | |||
514 | static int ilk_crc_source_valid(struct drm_i915_private *dev_priv, | ||
515 | const enum intel_pipe_crc_source source) | ||
516 | { | ||
517 | switch (source) { | ||
518 | case INTEL_PIPE_CRC_SOURCE_PIPE: | ||
519 | case INTEL_PIPE_CRC_SOURCE_PLANE1: | ||
520 | case INTEL_PIPE_CRC_SOURCE_PLANE2: | ||
521 | case INTEL_PIPE_CRC_SOURCE_NONE: | ||
522 | return 0; | ||
523 | default: | ||
524 | return -EINVAL; | ||
525 | } | ||
526 | } | ||
527 | |||
528 | static int ivb_crc_source_valid(struct drm_i915_private *dev_priv, | ||
529 | const enum intel_pipe_crc_source source) | ||
530 | { | ||
531 | switch (source) { | ||
532 | case INTEL_PIPE_CRC_SOURCE_PIPE: | ||
533 | case INTEL_PIPE_CRC_SOURCE_PLANE1: | ||
534 | case INTEL_PIPE_CRC_SOURCE_PLANE2: | ||
535 | case INTEL_PIPE_CRC_SOURCE_PF: | ||
536 | case INTEL_PIPE_CRC_SOURCE_NONE: | ||
537 | return 0; | ||
538 | default: | ||
539 | return -EINVAL; | ||
540 | } | ||
541 | } | ||
542 | |||
543 | static int | ||
544 | intel_is_valid_crc_source(struct drm_i915_private *dev_priv, | ||
545 | const enum intel_pipe_crc_source source) | ||
546 | { | ||
547 | if (IS_GEN2(dev_priv)) | ||
548 | return i8xx_crc_source_valid(dev_priv, source); | ||
549 | else if (INTEL_GEN(dev_priv) < 5) | ||
550 | return i9xx_crc_source_valid(dev_priv, source); | ||
551 | else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) | ||
552 | return vlv_crc_source_valid(dev_priv, source); | ||
553 | else if (IS_GEN5(dev_priv) || IS_GEN6(dev_priv)) | ||
554 | return ilk_crc_source_valid(dev_priv, source); | ||
555 | else | ||
556 | return ivb_crc_source_valid(dev_priv, source); | ||
557 | } | ||
558 | |||
559 | const char *const *intel_crtc_get_crc_sources(struct drm_crtc *crtc, | ||
560 | size_t *count) | ||
561 | { | ||
562 | *count = ARRAY_SIZE(pipe_crc_sources); | ||
563 | return pipe_crc_sources; | ||
564 | } | ||
565 | |||
566 | int intel_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, | ||
567 | size_t *values_cnt) | ||
568 | { | ||
569 | struct drm_i915_private *dev_priv = to_i915(crtc->dev); | ||
570 | enum intel_pipe_crc_source source; | ||
571 | |||
572 | if (display_crc_ctl_parse_source(source_name, &source) < 0) { | ||
573 | DRM_DEBUG_DRIVER("unknown source %s\n", source_name); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | if (source == INTEL_PIPE_CRC_SOURCE_AUTO || | ||
578 | intel_is_valid_crc_source(dev_priv, source) == 0) { | ||
579 | *values_cnt = 5; | ||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | return -EINVAL; | ||
584 | } | ||
585 | |||
586 | int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name) | ||
473 | { | 587 | { |
474 | struct drm_i915_private *dev_priv = to_i915(crtc->dev); | 588 | struct drm_i915_private *dev_priv = to_i915(crtc->dev); |
475 | struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[crtc->index]; | 589 | struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[crtc->index]; |
@@ -508,7 +622,6 @@ int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name, | |||
508 | } | 622 | } |
509 | 623 | ||
510 | pipe_crc->skipped = 0; | 624 | pipe_crc->skipped = 0; |
511 | *values_cnt = 5; | ||
512 | 625 | ||
513 | out: | 626 | out: |
514 | intel_display_power_put(dev_priv, power_domain); | 627 | intel_display_power_put(dev_priv, power_domain); |
diff --git a/drivers/gpu/drm/i915/selftests/i915_sw_fence.c b/drivers/gpu/drm/i915/selftests/i915_sw_fence.c index 570e325af93e..cdbc8f134e5e 100644 --- a/drivers/gpu/drm/i915/selftests/i915_sw_fence.c +++ b/drivers/gpu/drm/i915/selftests/i915_sw_fence.c | |||
@@ -611,17 +611,9 @@ static const char *mock_name(struct dma_fence *fence) | |||
611 | return "mock"; | 611 | return "mock"; |
612 | } | 612 | } |
613 | 613 | ||
614 | static bool mock_enable_signaling(struct dma_fence *fence) | ||
615 | { | ||
616 | return true; | ||
617 | } | ||
618 | |||
619 | static const struct dma_fence_ops mock_fence_ops = { | 614 | static const struct dma_fence_ops mock_fence_ops = { |
620 | .get_driver_name = mock_name, | 615 | .get_driver_name = mock_name, |
621 | .get_timeline_name = mock_name, | 616 | .get_timeline_name = mock_name, |
622 | .enable_signaling = mock_enable_signaling, | ||
623 | .wait = dma_fence_default_wait, | ||
624 | .release = dma_fence_free, | ||
625 | }; | 617 | }; |
626 | 618 | ||
627 | static DEFINE_SPINLOCK(mock_fence_lock); | 619 | static DEFINE_SPINLOCK(mock_fence_lock); |
diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 203f247d4854..40605fdf0e33 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c | |||
@@ -281,16 +281,13 @@ static void ipu_plane_state_reset(struct drm_plane *plane) | |||
281 | ipu_state = to_ipu_plane_state(plane->state); | 281 | ipu_state = to_ipu_plane_state(plane->state); |
282 | __drm_atomic_helper_plane_destroy_state(plane->state); | 282 | __drm_atomic_helper_plane_destroy_state(plane->state); |
283 | kfree(ipu_state); | 283 | kfree(ipu_state); |
284 | plane->state = NULL; | ||
284 | } | 285 | } |
285 | 286 | ||
286 | ipu_state = kzalloc(sizeof(*ipu_state), GFP_KERNEL); | 287 | ipu_state = kzalloc(sizeof(*ipu_state), GFP_KERNEL); |
287 | 288 | ||
288 | if (ipu_state) { | 289 | if (ipu_state) |
289 | ipu_state->base.plane = plane; | 290 | __drm_atomic_helper_plane_reset(plane, &ipu_state->base); |
290 | ipu_state->base.rotation = DRM_MODE_ROTATE_0; | ||
291 | } | ||
292 | |||
293 | plane->state = &ipu_state->base; | ||
294 | } | 291 | } |
295 | 292 | ||
296 | static struct drm_plane_state * | 293 | static struct drm_plane_state * |
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 74cdde2ee474..ac6af4bd9df6 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c | |||
@@ -42,29 +42,10 @@ static const struct pci_device_id pciidlist[] = { | |||
42 | 42 | ||
43 | MODULE_DEVICE_TABLE(pci, pciidlist); | 43 | MODULE_DEVICE_TABLE(pci, pciidlist); |
44 | 44 | ||
45 | static void mgag200_kick_out_firmware_fb(struct pci_dev *pdev) | ||
46 | { | ||
47 | struct apertures_struct *ap; | ||
48 | bool primary = false; | ||
49 | |||
50 | ap = alloc_apertures(1); | ||
51 | if (!ap) | ||
52 | return; | ||
53 | |||
54 | ap->ranges[0].base = pci_resource_start(pdev, 0); | ||
55 | ap->ranges[0].size = pci_resource_len(pdev, 0); | ||
56 | |||
57 | #ifdef CONFIG_X86 | ||
58 | primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; | ||
59 | #endif | ||
60 | drm_fb_helper_remove_conflicting_framebuffers(ap, "mgag200drmfb", primary); | ||
61 | kfree(ap); | ||
62 | } | ||
63 | |||
64 | 45 | ||
65 | static int mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | 46 | static int mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
66 | { | 47 | { |
67 | mgag200_kick_out_firmware_fb(pdev); | 48 | drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "mgag200drmfb"); |
68 | 49 | ||
69 | return drm_get_pci_dev(pdev, ent, &driver); | 50 | return drm_get_pci_dev(pdev, ent, &driver); |
70 | } | 51 | } |
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 780f983b0294..79d54103d470 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c | |||
@@ -124,20 +124,11 @@ static int mga_probe_vram(struct mga_device *mdev, void __iomem *mem) | |||
124 | static int mga_vram_init(struct mga_device *mdev) | 124 | static int mga_vram_init(struct mga_device *mdev) |
125 | { | 125 | { |
126 | void __iomem *mem; | 126 | void __iomem *mem; |
127 | struct apertures_struct *aper = alloc_apertures(1); | ||
128 | if (!aper) | ||
129 | return -ENOMEM; | ||
130 | 127 | ||
131 | /* BAR 0 is VRAM */ | 128 | /* BAR 0 is VRAM */ |
132 | mdev->mc.vram_base = pci_resource_start(mdev->dev->pdev, 0); | 129 | mdev->mc.vram_base = pci_resource_start(mdev->dev->pdev, 0); |
133 | mdev->mc.vram_window = pci_resource_len(mdev->dev->pdev, 0); | 130 | mdev->mc.vram_window = pci_resource_len(mdev->dev->pdev, 0); |
134 | 131 | ||
135 | aper->ranges[0].base = mdev->mc.vram_base; | ||
136 | aper->ranges[0].size = mdev->mc.vram_window; | ||
137 | |||
138 | drm_fb_helper_remove_conflicting_framebuffers(aper, "mgafb", true); | ||
139 | kfree(aper); | ||
140 | |||
141 | if (!devm_request_mem_region(mdev->dev->dev, mdev->mc.vram_base, mdev->mc.vram_window, | 132 | if (!devm_request_mem_region(mdev->dev->dev, mdev->mc.vram_base, mdev->mc.vram_window, |
142 | "mgadrmfb_vram")) { | 133 | "mgadrmfb_vram")) { |
143 | DRM_ERROR("can't reserve VRAM\n"); | 134 | DRM_ERROR("can't reserve VRAM\n"); |
diff --git a/drivers/gpu/drm/msm/msm_fence.c b/drivers/gpu/drm/msm/msm_fence.c index 349c12f670eb..77263cf97b20 100644 --- a/drivers/gpu/drm/msm/msm_fence.c +++ b/drivers/gpu/drm/msm/msm_fence.c | |||
@@ -119,11 +119,6 @@ static const char *msm_fence_get_timeline_name(struct dma_fence *fence) | |||
119 | return f->fctx->name; | 119 | return f->fctx->name; |
120 | } | 120 | } |
121 | 121 | ||
122 | static bool msm_fence_enable_signaling(struct dma_fence *fence) | ||
123 | { | ||
124 | return true; | ||
125 | } | ||
126 | |||
127 | static bool msm_fence_signaled(struct dma_fence *fence) | 122 | static bool msm_fence_signaled(struct dma_fence *fence) |
128 | { | 123 | { |
129 | struct msm_fence *f = to_msm_fence(fence); | 124 | struct msm_fence *f = to_msm_fence(fence); |
@@ -133,10 +128,7 @@ static bool msm_fence_signaled(struct dma_fence *fence) | |||
133 | static const struct dma_fence_ops msm_fence_ops = { | 128 | static const struct dma_fence_ops msm_fence_ops = { |
134 | .get_driver_name = msm_fence_get_driver_name, | 129 | .get_driver_name = msm_fence_get_driver_name, |
135 | .get_timeline_name = msm_fence_get_timeline_name, | 130 | .get_timeline_name = msm_fence_get_timeline_name, |
136 | .enable_signaling = msm_fence_enable_signaling, | ||
137 | .signaled = msm_fence_signaled, | 131 | .signaled = msm_fence_signaled, |
138 | .wait = dma_fence_default_wait, | ||
139 | .release = dma_fence_free, | ||
140 | }; | 132 | }; |
141 | 133 | ||
142 | struct dma_fence * | 134 | struct dma_fence * |
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 51932c72334e..eb4f766b5958 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c | |||
@@ -400,8 +400,10 @@ nouveau_connector_destroy(struct drm_connector *connector) | |||
400 | kfree(nv_connector->edid); | 400 | kfree(nv_connector->edid); |
401 | drm_connector_unregister(connector); | 401 | drm_connector_unregister(connector); |
402 | drm_connector_cleanup(connector); | 402 | drm_connector_cleanup(connector); |
403 | if (nv_connector->aux.transfer) | 403 | if (nv_connector->aux.transfer) { |
404 | drm_dp_cec_unregister_connector(&nv_connector->aux); | ||
404 | drm_dp_aux_unregister(&nv_connector->aux); | 405 | drm_dp_aux_unregister(&nv_connector->aux); |
406 | } | ||
405 | kfree(connector); | 407 | kfree(connector); |
406 | } | 408 | } |
407 | 409 | ||
@@ -608,6 +610,7 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) | |||
608 | 610 | ||
609 | nouveau_connector_set_encoder(connector, nv_encoder); | 611 | nouveau_connector_set_encoder(connector, nv_encoder); |
610 | conn_status = connector_status_connected; | 612 | conn_status = connector_status_connected; |
613 | drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid); | ||
611 | goto out; | 614 | goto out; |
612 | } | 615 | } |
613 | 616 | ||
@@ -1108,11 +1111,14 @@ nouveau_connector_hotplug(struct nvif_notify *notify) | |||
1108 | 1111 | ||
1109 | if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { | 1112 | if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { |
1110 | NV_DEBUG(drm, "service %s\n", name); | 1113 | NV_DEBUG(drm, "service %s\n", name); |
1114 | drm_dp_cec_irq(&nv_connector->aux); | ||
1111 | if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) | 1115 | if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) |
1112 | nv50_mstm_service(nv_encoder->dp.mstm); | 1116 | nv50_mstm_service(nv_encoder->dp.mstm); |
1113 | } else { | 1117 | } else { |
1114 | bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); | 1118 | bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); |
1115 | 1119 | ||
1120 | if (!plugged) | ||
1121 | drm_dp_cec_unset_edid(&nv_connector->aux); | ||
1116 | NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); | 1122 | NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); |
1117 | if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) { | 1123 | if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) { |
1118 | if (!plugged) | 1124 | if (!plugged) |
@@ -1302,7 +1308,6 @@ nouveau_connector_create(struct drm_device *dev, int index) | |||
1302 | kfree(nv_connector); | 1308 | kfree(nv_connector); |
1303 | return ERR_PTR(ret); | 1309 | return ERR_PTR(ret); |
1304 | } | 1310 | } |
1305 | |||
1306 | funcs = &nouveau_connector_funcs; | 1311 | funcs = &nouveau_connector_funcs; |
1307 | break; | 1312 | break; |
1308 | default: | 1313 | default: |
@@ -1356,6 +1361,14 @@ nouveau_connector_create(struct drm_device *dev, int index) | |||
1356 | break; | 1361 | break; |
1357 | } | 1362 | } |
1358 | 1363 | ||
1364 | switch (type) { | ||
1365 | case DRM_MODE_CONNECTOR_DisplayPort: | ||
1366 | case DRM_MODE_CONNECTOR_eDP: | ||
1367 | drm_dp_cec_register_connector(&nv_connector->aux, | ||
1368 | connector->name, dev->dev); | ||
1369 | break; | ||
1370 | } | ||
1371 | |||
1359 | ret = nvif_notify_init(&disp->disp.object, nouveau_connector_hotplug, | 1372 | ret = nvif_notify_init(&disp->disp.object, nouveau_connector_hotplug, |
1360 | true, NV04_DISP_NTFY_CONN, | 1373 | true, NV04_DISP_NTFY_CONN, |
1361 | &(struct nvif_notify_conn_req_v0) { | 1374 | &(struct nvif_notify_conn_req_v0) { |
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 412d49bc6e56..99be61ddeb75 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c | |||
@@ -526,6 +526,5 @@ static const struct dma_fence_ops nouveau_fence_ops_uevent = { | |||
526 | .get_timeline_name = nouveau_fence_get_timeline_name, | 526 | .get_timeline_name = nouveau_fence_get_timeline_name, |
527 | .enable_signaling = nouveau_fence_enable_signaling, | 527 | .enable_signaling = nouveau_fence_enable_signaling, |
528 | .signaled = nouveau_fence_is_signaled, | 528 | .signaled = nouveau_fence_is_signaled, |
529 | .wait = dma_fence_default_wait, | ||
530 | .release = nouveau_fence_release | 529 | .release = nouveau_fence_release |
531 | }; | 530 | }; |
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 0570c6826bff..01704a7f07cb 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c | |||
@@ -37,7 +37,8 @@ static bool qxl_head_enabled(struct qxl_head *head) | |||
37 | return head->width && head->height; | 37 | return head->width && head->height; |
38 | } | 38 | } |
39 | 39 | ||
40 | static void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count) | 40 | static int qxl_alloc_client_monitors_config(struct qxl_device *qdev, |
41 | unsigned int count) | ||
41 | { | 42 | { |
42 | if (qdev->client_monitors_config && | 43 | if (qdev->client_monitors_config && |
43 | count > qdev->client_monitors_config->count) { | 44 | count > qdev->client_monitors_config->count) { |
@@ -49,15 +50,17 @@ static void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned c | |||
49 | sizeof(struct qxl_monitors_config) + | 50 | sizeof(struct qxl_monitors_config) + |
50 | sizeof(struct qxl_head) * count, GFP_KERNEL); | 51 | sizeof(struct qxl_head) * count, GFP_KERNEL); |
51 | if (!qdev->client_monitors_config) | 52 | if (!qdev->client_monitors_config) |
52 | return; | 53 | return -ENOMEM; |
53 | } | 54 | } |
54 | qdev->client_monitors_config->count = count; | 55 | qdev->client_monitors_config->count = count; |
56 | return 0; | ||
55 | } | 57 | } |
56 | 58 | ||
57 | enum { | 59 | enum { |
58 | MONITORS_CONFIG_MODIFIED, | 60 | MONITORS_CONFIG_MODIFIED, |
59 | MONITORS_CONFIG_UNCHANGED, | 61 | MONITORS_CONFIG_UNCHANGED, |
60 | MONITORS_CONFIG_BAD_CRC, | 62 | MONITORS_CONFIG_BAD_CRC, |
63 | MONITORS_CONFIG_ERROR, | ||
61 | }; | 64 | }; |
62 | 65 | ||
63 | static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) | 66 | static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) |
@@ -87,7 +90,10 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) | |||
87 | && (num_monitors != qdev->client_monitors_config->count)) { | 90 | && (num_monitors != qdev->client_monitors_config->count)) { |
88 | status = MONITORS_CONFIG_MODIFIED; | 91 | status = MONITORS_CONFIG_MODIFIED; |
89 | } | 92 | } |
90 | qxl_alloc_client_monitors_config(qdev, num_monitors); | 93 | if (qxl_alloc_client_monitors_config(qdev, num_monitors)) { |
94 | status = MONITORS_CONFIG_ERROR; | ||
95 | return status; | ||
96 | } | ||
91 | /* we copy max from the client but it isn't used */ | 97 | /* we copy max from the client but it isn't used */ |
92 | qdev->client_monitors_config->max_allowed = | 98 | qdev->client_monitors_config->max_allowed = |
93 | qdev->monitors_config->max_allowed; | 99 | qdev->monitors_config->max_allowed; |
@@ -161,6 +167,10 @@ void qxl_display_read_client_monitors_config(struct qxl_device *qdev) | |||
161 | break; | 167 | break; |
162 | udelay(5); | 168 | udelay(5); |
163 | } | 169 | } |
170 | if (status == MONITORS_CONFIG_ERROR) { | ||
171 | DRM_DEBUG_KMS("ignoring client monitors config: error"); | ||
172 | return; | ||
173 | } | ||
164 | if (status == MONITORS_CONFIG_BAD_CRC) { | 174 | if (status == MONITORS_CONFIG_BAD_CRC) { |
165 | DRM_DEBUG_KMS("ignoring client monitors config: bad crc"); | 175 | DRM_DEBUG_KMS("ignoring client monitors config: bad crc"); |
166 | return; | 176 | return; |
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 2445e75cf7ea..13c8a662f9b4 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c | |||
@@ -119,7 +119,7 @@ qxl_pci_remove(struct pci_dev *pdev) | |||
119 | 119 | ||
120 | dev->dev_private = NULL; | 120 | dev->dev_private = NULL; |
121 | kfree(qdev); | 121 | kfree(qdev); |
122 | drm_dev_unref(dev); | 122 | drm_dev_put(dev); |
123 | } | 123 | } |
124 | 124 | ||
125 | static const struct file_operations qxl_fops = { | 125 | static const struct file_operations qxl_fops = { |
@@ -136,20 +136,11 @@ static int qxl_drm_freeze(struct drm_device *dev) | |||
136 | { | 136 | { |
137 | struct pci_dev *pdev = dev->pdev; | 137 | struct pci_dev *pdev = dev->pdev; |
138 | struct qxl_device *qdev = dev->dev_private; | 138 | struct qxl_device *qdev = dev->dev_private; |
139 | struct drm_crtc *crtc; | 139 | int ret; |
140 | |||
141 | drm_kms_helper_poll_disable(dev); | ||
142 | |||
143 | console_lock(); | ||
144 | qxl_fbdev_set_suspend(qdev, 1); | ||
145 | console_unlock(); | ||
146 | 140 | ||
147 | /* unpin the front buffers */ | 141 | ret = drm_mode_config_helper_suspend(dev); |
148 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 142 | if (ret) |
149 | const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | 143 | return ret; |
150 | if (crtc->enabled) | ||
151 | (*crtc_funcs->disable)(crtc); | ||
152 | } | ||
153 | 144 | ||
154 | qxl_destroy_monitors_object(qdev); | 145 | qxl_destroy_monitors_object(qdev); |
155 | qxl_surf_evict(qdev); | 146 | qxl_surf_evict(qdev); |
@@ -175,14 +166,7 @@ static int qxl_drm_resume(struct drm_device *dev, bool thaw) | |||
175 | } | 166 | } |
176 | 167 | ||
177 | qxl_create_monitors_object(qdev); | 168 | qxl_create_monitors_object(qdev); |
178 | drm_helper_resume_force_mode(dev); | 169 | return drm_mode_config_helper_resume(dev); |
179 | |||
180 | console_lock(); | ||
181 | qxl_fbdev_set_suspend(qdev, 0); | ||
182 | console_unlock(); | ||
183 | |||
184 | drm_kms_helper_poll_enable(dev); | ||
185 | return 0; | ||
186 | } | 170 | } |
187 | 171 | ||
188 | static int qxl_pm_suspend(struct device *dev) | 172 | static int qxl_pm_suspend(struct device *dev) |
diff --git a/drivers/gpu/drm/qxl/qxl_gem.c b/drivers/gpu/drm/qxl/qxl_gem.c index f5c1e7872e92..89606c819d82 100644 --- a/drivers/gpu/drm/qxl/qxl_gem.c +++ b/drivers/gpu/drm/qxl/qxl_gem.c | |||
@@ -40,7 +40,7 @@ void qxl_gem_object_free(struct drm_gem_object *gobj) | |||
40 | qxl_surface_evict(qdev, qobj, false); | 40 | qxl_surface_evict(qdev, qobj, false); |
41 | 41 | ||
42 | tbo = &qobj->tbo; | 42 | tbo = &qobj->tbo; |
43 | ttm_bo_unref(&tbo); | 43 | ttm_bo_put(tbo); |
44 | } | 44 | } |
45 | 45 | ||
46 | int qxl_gem_object_create(struct qxl_device *qdev, int size, | 46 | int qxl_gem_object_create(struct qxl_device *qdev, int size, |
diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index 771250aed78d..e25c589d5f50 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c | |||
@@ -102,8 +102,10 @@ int qxl_device_init(struct qxl_device *qdev, | |||
102 | int r, sb; | 102 | int r, sb; |
103 | 103 | ||
104 | r = drm_dev_init(&qdev->ddev, drv, &pdev->dev); | 104 | r = drm_dev_init(&qdev->ddev, drv, &pdev->dev); |
105 | if (r) | 105 | if (r) { |
106 | return r; | 106 | pr_err("Unable to init drm dev"); |
107 | goto error; | ||
108 | } | ||
107 | 109 | ||
108 | qdev->ddev.pdev = pdev; | 110 | qdev->ddev.pdev = pdev; |
109 | pci_set_drvdata(pdev, &qdev->ddev); | 111 | pci_set_drvdata(pdev, &qdev->ddev); |
@@ -121,6 +123,11 @@ int qxl_device_init(struct qxl_device *qdev, | |||
121 | qdev->io_base = pci_resource_start(pdev, 3); | 123 | qdev->io_base = pci_resource_start(pdev, 3); |
122 | 124 | ||
123 | qdev->vram_mapping = io_mapping_create_wc(qdev->vram_base, pci_resource_len(pdev, 0)); | 125 | qdev->vram_mapping = io_mapping_create_wc(qdev->vram_base, pci_resource_len(pdev, 0)); |
126 | if (!qdev->vram_mapping) { | ||
127 | pr_err("Unable to create vram_mapping"); | ||
128 | r = -ENOMEM; | ||
129 | goto error; | ||
130 | } | ||
124 | 131 | ||
125 | if (pci_resource_len(pdev, 4) > 0) { | 132 | if (pci_resource_len(pdev, 4) > 0) { |
126 | /* 64bit surface bar present */ | 133 | /* 64bit surface bar present */ |
@@ -139,6 +146,11 @@ int qxl_device_init(struct qxl_device *qdev, | |||
139 | qdev->surface_mapping = | 146 | qdev->surface_mapping = |
140 | io_mapping_create_wc(qdev->surfaceram_base, | 147 | io_mapping_create_wc(qdev->surfaceram_base, |
141 | qdev->surfaceram_size); | 148 | qdev->surfaceram_size); |
149 | if (!qdev->surface_mapping) { | ||
150 | pr_err("Unable to create surface_mapping"); | ||
151 | r = -ENOMEM; | ||
152 | goto vram_mapping_free; | ||
153 | } | ||
142 | } | 154 | } |
143 | 155 | ||
144 | DRM_DEBUG_KMS("qxl: vram %llx-%llx(%dM %dk), surface %llx-%llx(%dM %dk, %s)\n", | 156 | DRM_DEBUG_KMS("qxl: vram %llx-%llx(%dM %dk), surface %llx-%llx(%dM %dk, %s)\n", |
@@ -155,20 +167,29 @@ int qxl_device_init(struct qxl_device *qdev, | |||
155 | qdev->rom = ioremap(qdev->rom_base, qdev->rom_size); | 167 | qdev->rom = ioremap(qdev->rom_base, qdev->rom_size); |
156 | if (!qdev->rom) { | 168 | if (!qdev->rom) { |
157 | pr_err("Unable to ioremap ROM\n"); | 169 | pr_err("Unable to ioremap ROM\n"); |
158 | return -ENOMEM; | 170 | r = -ENOMEM; |
171 | goto surface_mapping_free; | ||
159 | } | 172 | } |
160 | 173 | ||
161 | qxl_check_device(qdev); | 174 | if (!qxl_check_device(qdev)) { |
175 | r = -ENODEV; | ||
176 | goto surface_mapping_free; | ||
177 | } | ||
162 | 178 | ||
163 | r = qxl_bo_init(qdev); | 179 | r = qxl_bo_init(qdev); |
164 | if (r) { | 180 | if (r) { |
165 | DRM_ERROR("bo init failed %d\n", r); | 181 | DRM_ERROR("bo init failed %d\n", r); |
166 | return r; | 182 | goto rom_unmap; |
167 | } | 183 | } |
168 | 184 | ||
169 | qdev->ram_header = ioremap(qdev->vram_base + | 185 | qdev->ram_header = ioremap(qdev->vram_base + |
170 | qdev->rom->ram_header_offset, | 186 | qdev->rom->ram_header_offset, |
171 | sizeof(*qdev->ram_header)); | 187 | sizeof(*qdev->ram_header)); |
188 | if (!qdev->ram_header) { | ||
189 | DRM_ERROR("Unable to ioremap RAM header\n"); | ||
190 | r = -ENOMEM; | ||
191 | goto bo_fini; | ||
192 | } | ||
172 | 193 | ||
173 | qdev->command_ring = qxl_ring_create(&(qdev->ram_header->cmd_ring_hdr), | 194 | qdev->command_ring = qxl_ring_create(&(qdev->ram_header->cmd_ring_hdr), |
174 | sizeof(struct qxl_command), | 195 | sizeof(struct qxl_command), |
@@ -176,6 +197,11 @@ int qxl_device_init(struct qxl_device *qdev, | |||
176 | qdev->io_base + QXL_IO_NOTIFY_CMD, | 197 | qdev->io_base + QXL_IO_NOTIFY_CMD, |
177 | false, | 198 | false, |
178 | &qdev->display_event); | 199 | &qdev->display_event); |
200 | if (!qdev->command_ring) { | ||
201 | DRM_ERROR("Unable to create command ring\n"); | ||
202 | r = -ENOMEM; | ||
203 | goto ram_header_unmap; | ||
204 | } | ||
179 | 205 | ||
180 | qdev->cursor_ring = qxl_ring_create( | 206 | qdev->cursor_ring = qxl_ring_create( |
181 | &(qdev->ram_header->cursor_ring_hdr), | 207 | &(qdev->ram_header->cursor_ring_hdr), |
@@ -185,12 +211,23 @@ int qxl_device_init(struct qxl_device *qdev, | |||
185 | false, | 211 | false, |
186 | &qdev->cursor_event); | 212 | &qdev->cursor_event); |
187 | 213 | ||
214 | if (!qdev->cursor_ring) { | ||
215 | DRM_ERROR("Unable to create cursor ring\n"); | ||
216 | r = -ENOMEM; | ||
217 | goto command_ring_free; | ||
218 | } | ||
219 | |||
188 | qdev->release_ring = qxl_ring_create( | 220 | qdev->release_ring = qxl_ring_create( |
189 | &(qdev->ram_header->release_ring_hdr), | 221 | &(qdev->ram_header->release_ring_hdr), |
190 | sizeof(uint64_t), | 222 | sizeof(uint64_t), |
191 | QXL_RELEASE_RING_SIZE, 0, true, | 223 | QXL_RELEASE_RING_SIZE, 0, true, |
192 | NULL); | 224 | NULL); |
193 | 225 | ||
226 | if (!qdev->release_ring) { | ||
227 | DRM_ERROR("Unable to create release ring\n"); | ||
228 | r = -ENOMEM; | ||
229 | goto cursor_ring_free; | ||
230 | } | ||
194 | /* TODO - slot initialization should happen on reset. where is our | 231 | /* TODO - slot initialization should happen on reset. where is our |
195 | * reset handler? */ | 232 | * reset handler? */ |
196 | qdev->n_mem_slots = qdev->rom->slots_end; | 233 | qdev->n_mem_slots = qdev->rom->slots_end; |
@@ -203,6 +240,12 @@ int qxl_device_init(struct qxl_device *qdev, | |||
203 | kmalloc_array(qdev->n_mem_slots, sizeof(struct qxl_memslot), | 240 | kmalloc_array(qdev->n_mem_slots, sizeof(struct qxl_memslot), |
204 | GFP_KERNEL); | 241 | GFP_KERNEL); |
205 | 242 | ||
243 | if (!qdev->mem_slots) { | ||
244 | DRM_ERROR("Unable to alloc mem slots\n"); | ||
245 | r = -ENOMEM; | ||
246 | goto release_ring_free; | ||
247 | } | ||
248 | |||
206 | idr_init(&qdev->release_idr); | 249 | idr_init(&qdev->release_idr); |
207 | spin_lock_init(&qdev->release_idr_lock); | 250 | spin_lock_init(&qdev->release_idr_lock); |
208 | spin_lock_init(&qdev->release_lock); | 251 | spin_lock_init(&qdev->release_lock); |
@@ -218,8 +261,10 @@ int qxl_device_init(struct qxl_device *qdev, | |||
218 | 261 | ||
219 | /* must initialize irq before first async io - slot creation */ | 262 | /* must initialize irq before first async io - slot creation */ |
220 | r = qxl_irq_init(qdev); | 263 | r = qxl_irq_init(qdev); |
221 | if (r) | 264 | if (r) { |
222 | return r; | 265 | DRM_ERROR("Unable to init qxl irq\n"); |
266 | goto mem_slots_free; | ||
267 | } | ||
223 | 268 | ||
224 | /* | 269 | /* |
225 | * Note that virtual is surface0. We rely on the single ioremap done | 270 | * Note that virtual is surface0. We rely on the single ioremap done |
@@ -243,6 +288,27 @@ int qxl_device_init(struct qxl_device *qdev, | |||
243 | INIT_WORK(&qdev->gc_work, qxl_gc_work); | 288 | INIT_WORK(&qdev->gc_work, qxl_gc_work); |
244 | 289 | ||
245 | return 0; | 290 | return 0; |
291 | |||
292 | mem_slots_free: | ||
293 | kfree(qdev->mem_slots); | ||
294 | release_ring_free: | ||
295 | qxl_ring_free(qdev->release_ring); | ||
296 | cursor_ring_free: | ||
297 | qxl_ring_free(qdev->cursor_ring); | ||
298 | command_ring_free: | ||
299 | qxl_ring_free(qdev->command_ring); | ||
300 | ram_header_unmap: | ||
301 | iounmap(qdev->ram_header); | ||
302 | bo_fini: | ||
303 | qxl_bo_fini(qdev); | ||
304 | rom_unmap: | ||
305 | iounmap(qdev->rom); | ||
306 | surface_mapping_free: | ||
307 | io_mapping_free(qdev->surface_mapping); | ||
308 | vram_mapping_free: | ||
309 | io_mapping_free(qdev->vram_mapping); | ||
310 | error: | ||
311 | return r; | ||
246 | } | 312 | } |
247 | 313 | ||
248 | void qxl_device_fini(struct qxl_device *qdev) | 314 | void qxl_device_fini(struct qxl_device *qdev) |
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 2a7977a23b31..99c63eeb2866 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c | |||
@@ -316,27 +316,6 @@ static struct drm_driver kms_driver; | |||
316 | 316 | ||
317 | bool radeon_device_is_virtual(void); | 317 | bool radeon_device_is_virtual(void); |
318 | 318 | ||
319 | static int radeon_kick_out_firmware_fb(struct pci_dev *pdev) | ||
320 | { | ||
321 | struct apertures_struct *ap; | ||
322 | bool primary = false; | ||
323 | |||
324 | ap = alloc_apertures(1); | ||
325 | if (!ap) | ||
326 | return -ENOMEM; | ||
327 | |||
328 | ap->ranges[0].base = pci_resource_start(pdev, 0); | ||
329 | ap->ranges[0].size = pci_resource_len(pdev, 0); | ||
330 | |||
331 | #ifdef CONFIG_X86 | ||
332 | primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; | ||
333 | #endif | ||
334 | drm_fb_helper_remove_conflicting_framebuffers(ap, "radeondrmfb", primary); | ||
335 | kfree(ap); | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static int radeon_pci_probe(struct pci_dev *pdev, | 319 | static int radeon_pci_probe(struct pci_dev *pdev, |
341 | const struct pci_device_id *ent) | 320 | const struct pci_device_id *ent) |
342 | { | 321 | { |
@@ -346,7 +325,7 @@ static int radeon_pci_probe(struct pci_dev *pdev, | |||
346 | return -EPROBE_DEFER; | 325 | return -EPROBE_DEFER; |
347 | 326 | ||
348 | /* Get rid of things like offb */ | 327 | /* Get rid of things like offb */ |
349 | ret = radeon_kick_out_firmware_fb(pdev); | 328 | ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "radeondrmfb"); |
350 | if (ret) | 329 | if (ret) |
351 | return ret; | 330 | return ret; |
352 | 331 | ||
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 15dc9caa128b..8a9e5e6f16b4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c | |||
@@ -691,6 +691,65 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = { | |||
691 | .atomic_disable = rcar_du_crtc_atomic_disable, | 691 | .atomic_disable = rcar_du_crtc_atomic_disable, |
692 | }; | 692 | }; |
693 | 693 | ||
694 | static void rcar_du_crtc_crc_init(struct rcar_du_crtc *rcrtc) | ||
695 | { | ||
696 | struct rcar_du_device *rcdu = rcrtc->group->dev; | ||
697 | const char **sources; | ||
698 | unsigned int count; | ||
699 | int i = -1; | ||
700 | |||
701 | /* CRC available only on Gen3 HW. */ | ||
702 | if (rcdu->info->gen < 3) | ||
703 | return; | ||
704 | |||
705 | /* Reserve 1 for "auto" source. */ | ||
706 | count = rcrtc->vsp->num_planes + 1; | ||
707 | |||
708 | sources = kmalloc_array(count, sizeof(*sources), GFP_KERNEL); | ||
709 | if (!sources) | ||
710 | return; | ||
711 | |||
712 | sources[0] = kstrdup("auto", GFP_KERNEL); | ||
713 | if (!sources[0]) | ||
714 | goto error; | ||
715 | |||
716 | for (i = 0; i < rcrtc->vsp->num_planes; ++i) { | ||
717 | struct drm_plane *plane = &rcrtc->vsp->planes[i].plane; | ||
718 | char name[16]; | ||
719 | |||
720 | sprintf(name, "plane%u", plane->base.id); | ||
721 | sources[i + 1] = kstrdup(name, GFP_KERNEL); | ||
722 | if (!sources[i + 1]) | ||
723 | goto error; | ||
724 | } | ||
725 | |||
726 | rcrtc->sources = sources; | ||
727 | rcrtc->sources_count = count; | ||
728 | return; | ||
729 | |||
730 | error: | ||
731 | while (i >= 0) { | ||
732 | kfree(sources[i]); | ||
733 | i--; | ||
734 | } | ||
735 | kfree(sources); | ||
736 | } | ||
737 | |||
738 | static void rcar_du_crtc_crc_cleanup(struct rcar_du_crtc *rcrtc) | ||
739 | { | ||
740 | unsigned int i; | ||
741 | |||
742 | if (!rcrtc->sources) | ||
743 | return; | ||
744 | |||
745 | for (i = 0; i < rcrtc->sources_count; i++) | ||
746 | kfree(rcrtc->sources[i]); | ||
747 | kfree(rcrtc->sources); | ||
748 | |||
749 | rcrtc->sources = NULL; | ||
750 | rcrtc->sources_count = 0; | ||
751 | } | ||
752 | |||
694 | static struct drm_crtc_state * | 753 | static struct drm_crtc_state * |
695 | rcar_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc) | 754 | rcar_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc) |
696 | { | 755 | { |
@@ -717,6 +776,15 @@ static void rcar_du_crtc_atomic_destroy_state(struct drm_crtc *crtc, | |||
717 | kfree(to_rcar_crtc_state(state)); | 776 | kfree(to_rcar_crtc_state(state)); |
718 | } | 777 | } |
719 | 778 | ||
779 | static void rcar_du_crtc_cleanup(struct drm_crtc *crtc) | ||
780 | { | ||
781 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); | ||
782 | |||
783 | rcar_du_crtc_crc_cleanup(rcrtc); | ||
784 | |||
785 | return drm_crtc_cleanup(crtc); | ||
786 | } | ||
787 | |||
720 | static void rcar_du_crtc_reset(struct drm_crtc *crtc) | 788 | static void rcar_du_crtc_reset(struct drm_crtc *crtc) |
721 | { | 789 | { |
722 | struct rcar_du_crtc_state *state; | 790 | struct rcar_du_crtc_state *state; |
@@ -756,17 +824,11 @@ static void rcar_du_crtc_disable_vblank(struct drm_crtc *crtc) | |||
756 | rcrtc->vblank_enable = false; | 824 | rcrtc->vblank_enable = false; |
757 | } | 825 | } |
758 | 826 | ||
759 | static int rcar_du_crtc_set_crc_source(struct drm_crtc *crtc, | 827 | static int rcar_du_crtc_parse_crc_source(struct rcar_du_crtc *rcrtc, |
760 | const char *source_name, | 828 | const char *source_name, |
761 | size_t *values_cnt) | 829 | enum vsp1_du_crc_source *source) |
762 | { | 830 | { |
763 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); | 831 | unsigned int index; |
764 | struct drm_modeset_acquire_ctx ctx; | ||
765 | struct drm_crtc_state *crtc_state; | ||
766 | struct drm_atomic_state *state; | ||
767 | enum vsp1_du_crc_source source; | ||
768 | unsigned int index = 0; | ||
769 | unsigned int i; | ||
770 | int ret; | 832 | int ret; |
771 | 833 | ||
772 | /* | 834 | /* |
@@ -774,31 +836,72 @@ static int rcar_du_crtc_set_crc_source(struct drm_crtc *crtc, | |||
774 | * CRC on an input plane (%u is the plane ID), and "auto" to compute the | 836 | * CRC on an input plane (%u is the plane ID), and "auto" to compute the |
775 | * CRC on the composer (VSP) output. | 837 | * CRC on the composer (VSP) output. |
776 | */ | 838 | */ |
839 | |||
777 | if (!source_name) { | 840 | if (!source_name) { |
778 | source = VSP1_DU_CRC_NONE; | 841 | *source = VSP1_DU_CRC_NONE; |
842 | return 0; | ||
779 | } else if (!strcmp(source_name, "auto")) { | 843 | } else if (!strcmp(source_name, "auto")) { |
780 | source = VSP1_DU_CRC_OUTPUT; | 844 | *source = VSP1_DU_CRC_OUTPUT; |
845 | return 0; | ||
781 | } else if (strstarts(source_name, "plane")) { | 846 | } else if (strstarts(source_name, "plane")) { |
782 | source = VSP1_DU_CRC_PLANE; | 847 | unsigned int i; |
848 | |||
849 | *source = VSP1_DU_CRC_PLANE; | ||
783 | 850 | ||
784 | ret = kstrtouint(source_name + strlen("plane"), 10, &index); | 851 | ret = kstrtouint(source_name + strlen("plane"), 10, &index); |
785 | if (ret < 0) | 852 | if (ret < 0) |
786 | return ret; | 853 | return ret; |
787 | 854 | ||
788 | for (i = 0; i < rcrtc->vsp->num_planes; ++i) { | 855 | for (i = 0; i < rcrtc->vsp->num_planes; ++i) { |
789 | if (index == rcrtc->vsp->planes[i].plane.base.id) { | 856 | if (index == rcrtc->vsp->planes[i].plane.base.id) |
790 | index = i; | 857 | return i; |
791 | break; | ||
792 | } | ||
793 | } | 858 | } |
859 | } | ||
794 | 860 | ||
795 | if (i >= rcrtc->vsp->num_planes) | 861 | return -EINVAL; |
796 | return -EINVAL; | 862 | } |
797 | } else { | 863 | |
864 | static int rcar_du_crtc_verify_crc_source(struct drm_crtc *crtc, | ||
865 | const char *source_name, | ||
866 | size_t *values_cnt) | ||
867 | { | ||
868 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); | ||
869 | enum vsp1_du_crc_source source; | ||
870 | |||
871 | if (rcar_du_crtc_parse_crc_source(rcrtc, source_name, &source) < 0) { | ||
872 | DRM_DEBUG_DRIVER("unknown source %s\n", source_name); | ||
798 | return -EINVAL; | 873 | return -EINVAL; |
799 | } | 874 | } |
800 | 875 | ||
801 | *values_cnt = 1; | 876 | *values_cnt = 1; |
877 | return 0; | ||
878 | } | ||
879 | |||
880 | const char *const *rcar_du_crtc_get_crc_sources(struct drm_crtc *crtc, | ||
881 | size_t *count) | ||
882 | { | ||
883 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); | ||
884 | |||
885 | *count = rcrtc->sources_count; | ||
886 | return rcrtc->sources; | ||
887 | } | ||
888 | |||
889 | static int rcar_du_crtc_set_crc_source(struct drm_crtc *crtc, | ||
890 | const char *source_name) | ||
891 | { | ||
892 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); | ||
893 | struct drm_modeset_acquire_ctx ctx; | ||
894 | struct drm_crtc_state *crtc_state; | ||
895 | struct drm_atomic_state *state; | ||
896 | enum vsp1_du_crc_source source; | ||
897 | unsigned int index; | ||
898 | int ret; | ||
899 | |||
900 | ret = rcar_du_crtc_parse_crc_source(rcrtc, source_name, &source); | ||
901 | if (ret < 0) | ||
902 | return ret; | ||
903 | |||
904 | index = ret; | ||
802 | 905 | ||
803 | /* Perform an atomic commit to set the CRC source. */ | 906 | /* Perform an atomic commit to set the CRC source. */ |
804 | drm_modeset_acquire_init(&ctx, 0); | 907 | drm_modeset_acquire_init(&ctx, 0); |
@@ -853,7 +956,7 @@ static const struct drm_crtc_funcs crtc_funcs_gen2 = { | |||
853 | 956 | ||
854 | static const struct drm_crtc_funcs crtc_funcs_gen3 = { | 957 | static const struct drm_crtc_funcs crtc_funcs_gen3 = { |
855 | .reset = rcar_du_crtc_reset, | 958 | .reset = rcar_du_crtc_reset, |
856 | .destroy = drm_crtc_cleanup, | 959 | .destroy = rcar_du_crtc_cleanup, |
857 | .set_config = drm_atomic_helper_set_config, | 960 | .set_config = drm_atomic_helper_set_config, |
858 | .page_flip = drm_atomic_helper_page_flip, | 961 | .page_flip = drm_atomic_helper_page_flip, |
859 | .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state, | 962 | .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state, |
@@ -861,6 +964,8 @@ static const struct drm_crtc_funcs crtc_funcs_gen3 = { | |||
861 | .enable_vblank = rcar_du_crtc_enable_vblank, | 964 | .enable_vblank = rcar_du_crtc_enable_vblank, |
862 | .disable_vblank = rcar_du_crtc_disable_vblank, | 965 | .disable_vblank = rcar_du_crtc_disable_vblank, |
863 | .set_crc_source = rcar_du_crtc_set_crc_source, | 966 | .set_crc_source = rcar_du_crtc_set_crc_source, |
967 | .verify_crc_source = rcar_du_crtc_verify_crc_source, | ||
968 | .get_crc_sources = rcar_du_crtc_get_crc_sources, | ||
864 | }; | 969 | }; |
865 | 970 | ||
866 | /* ----------------------------------------------------------------------------- | 971 | /* ----------------------------------------------------------------------------- |
@@ -999,5 +1104,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex, | |||
999 | return ret; | 1104 | return ret; |
1000 | } | 1105 | } |
1001 | 1106 | ||
1107 | rcar_du_crtc_crc_init(rcrtc); | ||
1108 | |||
1002 | return 0; | 1109 | return 0; |
1003 | } | 1110 | } |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 7680cb2636c8..592c79993e08 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h | |||
@@ -67,6 +67,9 @@ struct rcar_du_crtc { | |||
67 | struct rcar_du_group *group; | 67 | struct rcar_du_group *group; |
68 | struct rcar_du_vsp *vsp; | 68 | struct rcar_du_vsp *vsp; |
69 | unsigned int vsp_pipe; | 69 | unsigned int vsp_pipe; |
70 | |||
71 | const char *const *sources; | ||
72 | unsigned int sources_count; | ||
70 | }; | 73 | }; |
71 | 74 | ||
72 | #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) | 75 | #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index c20f7ed48c8d..8861e715c248 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c | |||
@@ -690,14 +690,12 @@ static void rcar_du_plane_reset(struct drm_plane *plane) | |||
690 | if (state == NULL) | 690 | if (state == NULL) |
691 | return; | 691 | return; |
692 | 692 | ||
693 | __drm_atomic_helper_plane_reset(plane, &state->state); | ||
694 | |||
693 | state->hwindex = -1; | 695 | state->hwindex = -1; |
694 | state->source = RCAR_DU_PLANE_MEMORY; | 696 | state->source = RCAR_DU_PLANE_MEMORY; |
695 | state->colorkey = RCAR_DU_COLORKEY_NONE; | 697 | state->colorkey = RCAR_DU_COLORKEY_NONE; |
696 | state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; | 698 | state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; |
697 | |||
698 | plane->state = &state->state; | ||
699 | plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE; | ||
700 | plane->state->plane = plane; | ||
701 | } | 699 | } |
702 | 700 | ||
703 | static int rcar_du_plane_atomic_set_property(struct drm_plane *plane, | 701 | static int rcar_du_plane_atomic_set_property(struct drm_plane *plane, |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index 72eebeda518e..45eb777a16a4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c | |||
@@ -346,11 +346,8 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane) | |||
346 | if (state == NULL) | 346 | if (state == NULL) |
347 | return; | 347 | return; |
348 | 348 | ||
349 | state->state.alpha = DRM_BLEND_ALPHA_OPAQUE; | 349 | __drm_atomic_helper_plane_reset(plane, &state->state); |
350 | state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; | 350 | state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; |
351 | |||
352 | plane->state = &state->state; | ||
353 | plane->state->plane = plane; | ||
354 | } | 351 | } |
355 | 352 | ||
356 | static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = { | 353 | static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = { |
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 0ccc76217ee4..26438d45732b 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig | |||
@@ -8,6 +8,7 @@ config DRM_ROCKCHIP | |||
8 | select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP | 8 | select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP |
9 | select DRM_DW_HDMI if ROCKCHIP_DW_HDMI | 9 | select DRM_DW_HDMI if ROCKCHIP_DW_HDMI |
10 | select DRM_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI | 10 | select DRM_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI |
11 | select DRM_RGB if ROCKCHIP_RGB | ||
11 | select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC | 12 | select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC |
12 | help | 13 | help |
13 | Choose this option if you have a Rockchip soc chipset. | 14 | Choose this option if you have a Rockchip soc chipset. |
@@ -23,7 +24,7 @@ config ROCKCHIP_ANALOGIX_DP | |||
23 | help | 24 | help |
24 | This selects support for Rockchip SoC specific extensions | 25 | This selects support for Rockchip SoC specific extensions |
25 | for the Analogix Core DP driver. If you want to enable DP | 26 | for the Analogix Core DP driver. If you want to enable DP |
26 | on RK3288 based SoC, you should selet this option. | 27 | on RK3288 or RK3399 based SoC, you should select this option. |
27 | 28 | ||
28 | config ROCKCHIP_CDN_DP | 29 | config ROCKCHIP_CDN_DP |
29 | bool "Rockchip cdn DP" | 30 | bool "Rockchip cdn DP" |
@@ -39,16 +40,16 @@ config ROCKCHIP_DW_HDMI | |||
39 | help | 40 | help |
40 | This selects support for Rockchip SoC specific extensions | 41 | This selects support for Rockchip SoC specific extensions |
41 | for the Synopsys DesignWare HDMI driver. If you want to | 42 | for the Synopsys DesignWare HDMI driver. If you want to |
42 | enable HDMI on RK3288 based SoC, you should selet this | 43 | enable HDMI on RK3288 or RK3399 based SoC, you should select |
43 | option. | 44 | this option. |
44 | 45 | ||
45 | config ROCKCHIP_DW_MIPI_DSI | 46 | config ROCKCHIP_DW_MIPI_DSI |
46 | bool "Rockchip specific extensions for Synopsys DW MIPI DSI" | 47 | bool "Rockchip specific extensions for Synopsys DW MIPI DSI" |
47 | help | 48 | help |
48 | This selects support for Rockchip SoC specific extensions | 49 | This selects support for Rockchip SoC specific extensions |
49 | for the Synopsys DesignWare HDMI driver. If you want to | 50 | for the Synopsys DesignWare HDMI driver. If you want to |
50 | enable MIPI DSI on RK3288 based SoC, you should selet this | 51 | enable MIPI DSI on RK3288 or RK3399 based SoC, you should |
51 | option. | 52 | select this option. |
52 | 53 | ||
53 | config ROCKCHIP_INNO_HDMI | 54 | config ROCKCHIP_INNO_HDMI |
54 | bool "Rockchip specific extensions for Innosilicon HDMI" | 55 | bool "Rockchip specific extensions for Innosilicon HDMI" |
@@ -66,4 +67,14 @@ config ROCKCHIP_LVDS | |||
66 | Rockchip rk3288 SoC has LVDS TX Controller can be used, and it | 67 | Rockchip rk3288 SoC has LVDS TX Controller can be used, and it |
67 | support LVDS, rgb, dual LVDS output mode. say Y to enable its | 68 | support LVDS, rgb, dual LVDS output mode. say Y to enable its |
68 | driver. | 69 | driver. |
70 | |||
71 | config ROCKCHIP_RGB | ||
72 | bool "Rockchip RGB support" | ||
73 | depends on DRM_ROCKCHIP | ||
74 | depends on PINCTRL | ||
75 | help | ||
76 | Choose this option to enable support for Rockchip RGB output. | ||
77 | Some Rockchip CRTCs, like rv1108, can directly output parallel | ||
78 | and serial RGB format to panel or connect to a conversion chip. | ||
79 | say Y to enable its driver. | ||
69 | endif | 80 | endif |
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a314e2109e76..868263ff0302 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile | |||
@@ -14,5 +14,6 @@ rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o | |||
14 | rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o | 14 | rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o |
15 | rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o | 15 | rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o |
16 | rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o | 16 | rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o |
17 | rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o | ||
17 | 18 | ||
18 | obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o | 19 | obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o |
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index f814d37b1db2..5864cb452c5c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/pm_runtime.h> | 24 | #include <linux/pm_runtime.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/of_graph.h> | 26 | #include <linux/of_graph.h> |
27 | #include <linux/of_platform.h> | ||
27 | #include <linux/component.h> | 28 | #include <linux/component.h> |
28 | #include <linux/console.h> | 29 | #include <linux/console.h> |
29 | #include <linux/iommu.h> | 30 | #include <linux/iommu.h> |
@@ -184,7 +185,7 @@ err_mode_config_cleanup: | |||
184 | err_free: | 185 | err_free: |
185 | drm_dev->dev_private = NULL; | 186 | drm_dev->dev_private = NULL; |
186 | dev_set_drvdata(dev, NULL); | 187 | dev_set_drvdata(dev, NULL); |
187 | drm_dev_unref(drm_dev); | 188 | drm_dev_put(drm_dev); |
188 | return ret; | 189 | return ret; |
189 | } | 190 | } |
190 | 191 | ||
@@ -204,7 +205,7 @@ static void rockchip_drm_unbind(struct device *dev) | |||
204 | 205 | ||
205 | drm_dev->dev_private = NULL; | 206 | drm_dev->dev_private = NULL; |
206 | dev_set_drvdata(dev, NULL); | 207 | dev_set_drvdata(dev, NULL); |
207 | drm_dev_unref(drm_dev); | 208 | drm_dev_put(drm_dev); |
208 | } | 209 | } |
209 | 210 | ||
210 | static const struct file_operations rockchip_drm_driver_fops = { | 211 | static const struct file_operations rockchip_drm_driver_fops = { |
@@ -243,60 +244,18 @@ static struct drm_driver rockchip_drm_driver = { | |||
243 | }; | 244 | }; |
244 | 245 | ||
245 | #ifdef CONFIG_PM_SLEEP | 246 | #ifdef CONFIG_PM_SLEEP |
246 | static void rockchip_drm_fb_suspend(struct drm_device *drm) | ||
247 | { | ||
248 | struct rockchip_drm_private *priv = drm->dev_private; | ||
249 | |||
250 | console_lock(); | ||
251 | drm_fb_helper_set_suspend(&priv->fbdev_helper, 1); | ||
252 | console_unlock(); | ||
253 | } | ||
254 | |||
255 | static void rockchip_drm_fb_resume(struct drm_device *drm) | ||
256 | { | ||
257 | struct rockchip_drm_private *priv = drm->dev_private; | ||
258 | |||
259 | console_lock(); | ||
260 | drm_fb_helper_set_suspend(&priv->fbdev_helper, 0); | ||
261 | console_unlock(); | ||
262 | } | ||
263 | |||
264 | static int rockchip_drm_sys_suspend(struct device *dev) | 247 | static int rockchip_drm_sys_suspend(struct device *dev) |
265 | { | 248 | { |
266 | struct drm_device *drm = dev_get_drvdata(dev); | 249 | struct drm_device *drm = dev_get_drvdata(dev); |
267 | struct rockchip_drm_private *priv; | ||
268 | |||
269 | if (!drm) | ||
270 | return 0; | ||
271 | 250 | ||
272 | drm_kms_helper_poll_disable(drm); | 251 | return drm_mode_config_helper_suspend(drm); |
273 | rockchip_drm_fb_suspend(drm); | ||
274 | |||
275 | priv = drm->dev_private; | ||
276 | priv->state = drm_atomic_helper_suspend(drm); | ||
277 | if (IS_ERR(priv->state)) { | ||
278 | rockchip_drm_fb_resume(drm); | ||
279 | drm_kms_helper_poll_enable(drm); | ||
280 | return PTR_ERR(priv->state); | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | } | 252 | } |
285 | 253 | ||
286 | static int rockchip_drm_sys_resume(struct device *dev) | 254 | static int rockchip_drm_sys_resume(struct device *dev) |
287 | { | 255 | { |
288 | struct drm_device *drm = dev_get_drvdata(dev); | 256 | struct drm_device *drm = dev_get_drvdata(dev); |
289 | struct rockchip_drm_private *priv; | ||
290 | |||
291 | if (!drm) | ||
292 | return 0; | ||
293 | |||
294 | priv = drm->dev_private; | ||
295 | drm_atomic_helper_resume(drm, priv->state); | ||
296 | rockchip_drm_fb_resume(drm); | ||
297 | drm_kms_helper_poll_enable(drm); | ||
298 | 257 | ||
299 | return 0; | 258 | return drm_mode_config_helper_resume(drm); |
300 | } | 259 | } |
301 | #endif | 260 | #endif |
302 | 261 | ||
@@ -309,6 +268,53 @@ static const struct dev_pm_ops rockchip_drm_pm_ops = { | |||
309 | static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS]; | 268 | static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS]; |
310 | static int num_rockchip_sub_drivers; | 269 | static int num_rockchip_sub_drivers; |
311 | 270 | ||
271 | /* | ||
272 | * Check if a vop endpoint is leading to a rockchip subdriver or bridge. | ||
273 | * Should be called from the component bind stage of the drivers | ||
274 | * to ensure that all subdrivers are probed. | ||
275 | * | ||
276 | * @ep: endpoint of a rockchip vop | ||
277 | * | ||
278 | * returns true if subdriver, false if external bridge and -ENODEV | ||
279 | * if remote port does not contain a device. | ||
280 | */ | ||
281 | int rockchip_drm_endpoint_is_subdriver(struct device_node *ep) | ||
282 | { | ||
283 | struct device_node *node = of_graph_get_remote_port_parent(ep); | ||
284 | struct platform_device *pdev; | ||
285 | struct device_driver *drv; | ||
286 | int i; | ||
287 | |||
288 | if (!node) | ||
289 | return -ENODEV; | ||
290 | |||
291 | /* status disabled will prevent creation of platform-devices */ | ||
292 | pdev = of_find_device_by_node(node); | ||
293 | of_node_put(node); | ||
294 | if (!pdev) | ||
295 | return -ENODEV; | ||
296 | |||
297 | /* | ||
298 | * All rockchip subdrivers have probed at this point, so | ||
299 | * any device not having a driver now is an external bridge. | ||
300 | */ | ||
301 | drv = pdev->dev.driver; | ||
302 | if (!drv) { | ||
303 | platform_device_put(pdev); | ||
304 | return false; | ||
305 | } | ||
306 | |||
307 | for (i = 0; i < num_rockchip_sub_drivers; i++) { | ||
308 | if (rockchip_sub_drivers[i] == to_platform_driver(drv)) { | ||
309 | platform_device_put(pdev); | ||
310 | return true; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | platform_device_put(pdev); | ||
315 | return false; | ||
316 | } | ||
317 | |||
312 | static int compare_dev(struct device *dev, void *data) | 318 | static int compare_dev(struct device *dev, void *data) |
313 | { | 319 | { |
314 | return dev == (struct device *)data; | 320 | return dev == (struct device *)data; |
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 3a6ebfc26036..21a023a97bb8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h | |||
@@ -51,7 +51,6 @@ struct rockchip_crtc_state { | |||
51 | struct rockchip_drm_private { | 51 | struct rockchip_drm_private { |
52 | struct drm_fb_helper fbdev_helper; | 52 | struct drm_fb_helper fbdev_helper; |
53 | struct drm_gem_object *fbdev_bo; | 53 | struct drm_gem_object *fbdev_bo; |
54 | struct drm_atomic_state *state; | ||
55 | struct iommu_domain *domain; | 54 | struct iommu_domain *domain; |
56 | struct mutex mm_lock; | 55 | struct mutex mm_lock; |
57 | struct drm_mm mm; | 56 | struct drm_mm mm; |
@@ -65,6 +64,7 @@ void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, | |||
65 | struct device *dev); | 64 | struct device *dev); |
66 | int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout); | 65 | int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout); |
67 | 66 | ||
67 | int rockchip_drm_endpoint_is_subdriver(struct device_node *ep); | ||
68 | extern struct platform_driver cdn_dp_driver; | 68 | extern struct platform_driver cdn_dp_driver; |
69 | extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; | 69 | extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; |
70 | extern struct platform_driver dw_mipi_dsi_driver; | 70 | extern struct platform_driver dw_mipi_dsi_driver; |
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 1359e5c773e4..0c35a88e33dd 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/of_device.h> | 32 | #include <linux/of_device.h> |
33 | #include <linux/pm_runtime.h> | 33 | #include <linux/pm_runtime.h> |
34 | #include <linux/component.h> | 34 | #include <linux/component.h> |
35 | #include <linux/overflow.h> | ||
35 | 36 | ||
36 | #include <linux/reset.h> | 37 | #include <linux/reset.h> |
37 | #include <linux/delay.h> | 38 | #include <linux/delay.h> |
@@ -41,6 +42,7 @@ | |||
41 | #include "rockchip_drm_fb.h" | 42 | #include "rockchip_drm_fb.h" |
42 | #include "rockchip_drm_psr.h" | 43 | #include "rockchip_drm_psr.h" |
43 | #include "rockchip_drm_vop.h" | 44 | #include "rockchip_drm_vop.h" |
45 | #include "rockchip_rgb.h" | ||
44 | 46 | ||
45 | #define VOP_WIN_SET(x, win, name, v) \ | 47 | #define VOP_WIN_SET(x, win, name, v) \ |
46 | vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) | 48 | vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) |
@@ -92,6 +94,7 @@ struct vop_win { | |||
92 | struct vop *vop; | 94 | struct vop *vop; |
93 | }; | 95 | }; |
94 | 96 | ||
97 | struct rockchip_rgb; | ||
95 | struct vop { | 98 | struct vop { |
96 | struct drm_crtc crtc; | 99 | struct drm_crtc crtc; |
97 | struct device *dev; | 100 | struct device *dev; |
@@ -135,6 +138,9 @@ struct vop { | |||
135 | /* vop dclk reset */ | 138 | /* vop dclk reset */ |
136 | struct reset_control *dclk_rst; | 139 | struct reset_control *dclk_rst; |
137 | 140 | ||
141 | /* optional internal rgb encoder */ | ||
142 | struct rockchip_rgb *rgb; | ||
143 | |||
138 | struct vop_win win[]; | 144 | struct vop_win win[]; |
139 | }; | 145 | }; |
140 | 146 | ||
@@ -1111,7 +1117,7 @@ static struct drm_connector *vop_get_edp_connector(struct vop *vop) | |||
1111 | } | 1117 | } |
1112 | 1118 | ||
1113 | static int vop_crtc_set_crc_source(struct drm_crtc *crtc, | 1119 | static int vop_crtc_set_crc_source(struct drm_crtc *crtc, |
1114 | const char *source_name, size_t *values_cnt) | 1120 | const char *source_name) |
1115 | { | 1121 | { |
1116 | struct vop *vop = to_vop(crtc); | 1122 | struct vop *vop = to_vop(crtc); |
1117 | struct drm_connector *connector; | 1123 | struct drm_connector *connector; |
@@ -1121,8 +1127,6 @@ static int vop_crtc_set_crc_source(struct drm_crtc *crtc, | |||
1121 | if (!connector) | 1127 | if (!connector) |
1122 | return -EINVAL; | 1128 | return -EINVAL; |
1123 | 1129 | ||
1124 | *values_cnt = 3; | ||
1125 | |||
1126 | if (source_name && strcmp(source_name, "auto") == 0) | 1130 | if (source_name && strcmp(source_name, "auto") == 0) |
1127 | ret = analogix_dp_start_crc(connector); | 1131 | ret = analogix_dp_start_crc(connector); |
1128 | else if (!source_name) | 1132 | else if (!source_name) |
@@ -1132,9 +1136,28 @@ static int vop_crtc_set_crc_source(struct drm_crtc *crtc, | |||
1132 | 1136 | ||
1133 | return ret; | 1137 | return ret; |
1134 | } | 1138 | } |
1139 | |||
1140 | static int | ||
1141 | vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, | ||
1142 | size_t *values_cnt) | ||
1143 | { | ||
1144 | if (source_name && strcmp(source_name, "auto") != 0) | ||
1145 | return -EINVAL; | ||
1146 | |||
1147 | *values_cnt = 3; | ||
1148 | return 0; | ||
1149 | } | ||
1150 | |||
1135 | #else | 1151 | #else |
1136 | static int vop_crtc_set_crc_source(struct drm_crtc *crtc, | 1152 | static int vop_crtc_set_crc_source(struct drm_crtc *crtc, |
1137 | const char *source_name, size_t *values_cnt) | 1153 | const char *source_name) |
1154 | { | ||
1155 | return -ENODEV; | ||
1156 | } | ||
1157 | |||
1158 | static int | ||
1159 | vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, | ||
1160 | size_t *values_cnt) | ||
1138 | { | 1161 | { |
1139 | return -ENODEV; | 1162 | return -ENODEV; |
1140 | } | 1163 | } |
@@ -1150,6 +1173,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = { | |||
1150 | .enable_vblank = vop_crtc_enable_vblank, | 1173 | .enable_vblank = vop_crtc_enable_vblank, |
1151 | .disable_vblank = vop_crtc_disable_vblank, | 1174 | .disable_vblank = vop_crtc_disable_vblank, |
1152 | .set_crc_source = vop_crtc_set_crc_source, | 1175 | .set_crc_source = vop_crtc_set_crc_source, |
1176 | .verify_crc_source = vop_crtc_verify_crc_source, | ||
1153 | }; | 1177 | }; |
1154 | 1178 | ||
1155 | static void vop_fb_unref_worker(struct drm_flip_work *work, void *val) | 1179 | static void vop_fb_unref_worker(struct drm_flip_work *work, void *val) |
@@ -1561,7 +1585,6 @@ static int vop_bind(struct device *dev, struct device *master, void *data) | |||
1561 | struct drm_device *drm_dev = data; | 1585 | struct drm_device *drm_dev = data; |
1562 | struct vop *vop; | 1586 | struct vop *vop; |
1563 | struct resource *res; | 1587 | struct resource *res; |
1564 | size_t alloc_size; | ||
1565 | int ret, irq; | 1588 | int ret, irq; |
1566 | 1589 | ||
1567 | vop_data = of_device_get_match_data(dev); | 1590 | vop_data = of_device_get_match_data(dev); |
@@ -1569,8 +1592,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data) | |||
1569 | return -ENODEV; | 1592 | return -ENODEV; |
1570 | 1593 | ||
1571 | /* Allocate vop struct and its vop_win array */ | 1594 | /* Allocate vop struct and its vop_win array */ |
1572 | alloc_size = sizeof(*vop) + sizeof(*vop->win) * vop_data->win_size; | 1595 | vop = devm_kzalloc(dev, struct_size(vop, win, vop_data->win_size), |
1573 | vop = devm_kzalloc(dev, alloc_size, GFP_KERNEL); | 1596 | GFP_KERNEL); |
1574 | if (!vop) | 1597 | if (!vop) |
1575 | return -ENOMEM; | 1598 | return -ENOMEM; |
1576 | 1599 | ||
@@ -1620,6 +1643,14 @@ static int vop_bind(struct device *dev, struct device *master, void *data) | |||
1620 | if (ret) | 1643 | if (ret) |
1621 | goto err_disable_pm_runtime; | 1644 | goto err_disable_pm_runtime; |
1622 | 1645 | ||
1646 | if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) { | ||
1647 | vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev); | ||
1648 | if (IS_ERR(vop->rgb)) { | ||
1649 | ret = PTR_ERR(vop->rgb); | ||
1650 | goto err_disable_pm_runtime; | ||
1651 | } | ||
1652 | } | ||
1653 | |||
1623 | return 0; | 1654 | return 0; |
1624 | 1655 | ||
1625 | err_disable_pm_runtime: | 1656 | err_disable_pm_runtime: |
@@ -1632,6 +1663,9 @@ static void vop_unbind(struct device *dev, struct device *master, void *data) | |||
1632 | { | 1663 | { |
1633 | struct vop *vop = dev_get_drvdata(dev); | 1664 | struct vop *vop = dev_get_drvdata(dev); |
1634 | 1665 | ||
1666 | if (vop->rgb) | ||
1667 | rockchip_rgb_fini(vop->rgb); | ||
1668 | |||
1635 | pm_runtime_disable(dev); | 1669 | pm_runtime_disable(dev); |
1636 | vop_destroy_crtc(vop); | 1670 | vop_destroy_crtc(vop); |
1637 | 1671 | ||
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index fcb91041a666..fd5765dfd637 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h | |||
@@ -162,6 +162,7 @@ struct vop_data { | |||
162 | unsigned int win_size; | 162 | unsigned int win_size; |
163 | 163 | ||
164 | #define VOP_FEATURE_OUTPUT_RGB10 BIT(0) | 164 | #define VOP_FEATURE_OUTPUT_RGB10 BIT(0) |
165 | #define VOP_FEATURE_INTERNAL_RGB BIT(1) | ||
165 | u64 feature; | 166 | u64 feature; |
166 | }; | 167 | }; |
167 | 168 | ||
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c new file mode 100644 index 000000000000..96ac1458a59c --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c | |||
@@ -0,0 +1,173 @@ | |||
1 | //SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd | ||
4 | * Author: | ||
5 | * Sandy Huang <hjc@rock-chips.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <drm/drmP.h> | ||
18 | #include <drm/drm_atomic_helper.h> | ||
19 | #include <drm/drm_crtc_helper.h> | ||
20 | #include <drm/drm_dp_helper.h> | ||
21 | #include <drm/drm_panel.h> | ||
22 | #include <drm/drm_of.h> | ||
23 | |||
24 | #include <linux/component.h> | ||
25 | #include <linux/of_graph.h> | ||
26 | |||
27 | #include "rockchip_drm_drv.h" | ||
28 | #include "rockchip_drm_vop.h" | ||
29 | |||
30 | #define encoder_to_rgb(c) container_of(c, struct rockchip_rgb, encoder) | ||
31 | |||
32 | struct rockchip_rgb { | ||
33 | struct device *dev; | ||
34 | struct drm_device *drm_dev; | ||
35 | struct drm_bridge *bridge; | ||
36 | struct drm_encoder encoder; | ||
37 | int output_mode; | ||
38 | }; | ||
39 | |||
40 | static int | ||
41 | rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder, | ||
42 | struct drm_crtc_state *crtc_state, | ||
43 | struct drm_connector_state *conn_state) | ||
44 | { | ||
45 | struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); | ||
46 | struct drm_connector *connector = conn_state->connector; | ||
47 | struct drm_display_info *info = &connector->display_info; | ||
48 | u32 bus_format; | ||
49 | |||
50 | if (info->num_bus_formats) | ||
51 | bus_format = info->bus_formats[0]; | ||
52 | else | ||
53 | bus_format = MEDIA_BUS_FMT_RGB888_1X24; | ||
54 | |||
55 | switch (bus_format) { | ||
56 | case MEDIA_BUS_FMT_RGB666_1X18: | ||
57 | s->output_mode = ROCKCHIP_OUT_MODE_P666; | ||
58 | break; | ||
59 | case MEDIA_BUS_FMT_RGB565_1X16: | ||
60 | s->output_mode = ROCKCHIP_OUT_MODE_P565; | ||
61 | break; | ||
62 | case MEDIA_BUS_FMT_RGB888_1X24: | ||
63 | case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: | ||
64 | default: | ||
65 | s->output_mode = ROCKCHIP_OUT_MODE_P888; | ||
66 | break; | ||
67 | } | ||
68 | |||
69 | s->output_type = DRM_MODE_CONNECTOR_LVDS; | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static const | ||
75 | struct drm_encoder_helper_funcs rockchip_rgb_encoder_helper_funcs = { | ||
76 | .atomic_check = rockchip_rgb_encoder_atomic_check, | ||
77 | }; | ||
78 | |||
79 | static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = { | ||
80 | .destroy = drm_encoder_cleanup, | ||
81 | }; | ||
82 | |||
83 | struct rockchip_rgb *rockchip_rgb_init(struct device *dev, | ||
84 | struct drm_crtc *crtc, | ||
85 | struct drm_device *drm_dev) | ||
86 | { | ||
87 | struct rockchip_rgb *rgb; | ||
88 | struct drm_encoder *encoder; | ||
89 | struct device_node *port, *endpoint; | ||
90 | u32 endpoint_id; | ||
91 | int ret = 0, child_count = 0; | ||
92 | struct drm_panel *panel; | ||
93 | struct drm_bridge *bridge; | ||
94 | |||
95 | rgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL); | ||
96 | if (!rgb) | ||
97 | return ERR_PTR(-ENOMEM); | ||
98 | |||
99 | rgb->dev = dev; | ||
100 | rgb->drm_dev = drm_dev; | ||
101 | |||
102 | port = of_graph_get_port_by_id(dev->of_node, 0); | ||
103 | if (!port) | ||
104 | return ERR_PTR(-EINVAL); | ||
105 | |||
106 | for_each_child_of_node(port, endpoint) { | ||
107 | if (of_property_read_u32(endpoint, "reg", &endpoint_id)) | ||
108 | endpoint_id = 0; | ||
109 | |||
110 | if (rockchip_drm_endpoint_is_subdriver(endpoint) > 0) | ||
111 | continue; | ||
112 | |||
113 | child_count++; | ||
114 | ret = drm_of_find_panel_or_bridge(dev->of_node, 0, endpoint_id, | ||
115 | &panel, &bridge); | ||
116 | if (!ret) | ||
117 | break; | ||
118 | } | ||
119 | |||
120 | of_node_put(port); | ||
121 | |||
122 | /* if the rgb output is not connected to anything, just return */ | ||
123 | if (!child_count) | ||
124 | return NULL; | ||
125 | |||
126 | if (ret < 0) { | ||
127 | if (ret != -EPROBE_DEFER) | ||
128 | DRM_DEV_ERROR(dev, "failed to find panel or bridge %d\n", ret); | ||
129 | return ERR_PTR(ret); | ||
130 | } | ||
131 | |||
132 | encoder = &rgb->encoder; | ||
133 | encoder->possible_crtcs = drm_crtc_mask(crtc); | ||
134 | |||
135 | ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs, | ||
136 | DRM_MODE_ENCODER_NONE, NULL); | ||
137 | if (ret < 0) { | ||
138 | DRM_DEV_ERROR(drm_dev->dev, | ||
139 | "failed to initialize encoder: %d\n", ret); | ||
140 | return ERR_PTR(ret); | ||
141 | } | ||
142 | |||
143 | drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs); | ||
144 | |||
145 | if (panel) { | ||
146 | bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS); | ||
147 | if (IS_ERR(bridge)) | ||
148 | return ERR_CAST(bridge); | ||
149 | } | ||
150 | |||
151 | rgb->bridge = bridge; | ||
152 | |||
153 | ret = drm_bridge_attach(encoder, rgb->bridge, NULL); | ||
154 | if (ret) { | ||
155 | DRM_DEV_ERROR(drm_dev->dev, | ||
156 | "failed to attach bridge: %d\n", ret); | ||
157 | goto err_free_encoder; | ||
158 | } | ||
159 | |||
160 | return rgb; | ||
161 | |||
162 | err_free_encoder: | ||
163 | drm_encoder_cleanup(encoder); | ||
164 | return ERR_PTR(ret); | ||
165 | } | ||
166 | EXPORT_SYMBOL_GPL(rockchip_rgb_init); | ||
167 | |||
168 | void rockchip_rgb_fini(struct rockchip_rgb *rgb) | ||
169 | { | ||
170 | drm_panel_bridge_remove(rgb->bridge); | ||
171 | drm_encoder_cleanup(&rgb->encoder); | ||
172 | } | ||
173 | EXPORT_SYMBOL_GPL(rockchip_rgb_fini); | ||
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.h b/drivers/gpu/drm/rockchip/rockchip_rgb.h new file mode 100644 index 000000000000..38b52e63b2b0 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.h | |||
@@ -0,0 +1,33 @@ | |||
1 | //SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd | ||
4 | * Author: | ||
5 | * Sandy Huang <hjc@rock-chips.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #ifdef CONFIG_ROCKCHIP_RGB | ||
18 | struct rockchip_rgb *rockchip_rgb_init(struct device *dev, | ||
19 | struct drm_crtc *crtc, | ||
20 | struct drm_device *drm_dev); | ||
21 | void rockchip_rgb_fini(struct rockchip_rgb *rgb); | ||
22 | #else | ||
23 | static inline struct rockchip_rgb *rockchip_rgb_init(struct device *dev, | ||
24 | struct drm_crtc *crtc, | ||
25 | struct drm_device *drm_dev) | ||
26 | { | ||
27 | return NULL; | ||
28 | } | ||
29 | |||
30 | static inline void rockchip_rgb_fini(struct rockchip_rgb *rgb) | ||
31 | { | ||
32 | } | ||
33 | #endif | ||
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 08023d3ecb76..a6db3cd5544b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c | |||
@@ -177,6 +177,215 @@ static const struct vop_data rk3126_vop = { | |||
177 | .win_size = ARRAY_SIZE(rk3126_vop_win_data), | 177 | .win_size = ARRAY_SIZE(rk3126_vop_win_data), |
178 | }; | 178 | }; |
179 | 179 | ||
180 | static const int px30_vop_intrs[] = { | ||
181 | FS_INTR, | ||
182 | 0, 0, | ||
183 | LINE_FLAG_INTR, | ||
184 | 0, | ||
185 | BUS_ERROR_INTR, | ||
186 | 0, 0, | ||
187 | DSP_HOLD_VALID_INTR, | ||
188 | }; | ||
189 | |||
190 | static const struct vop_intr px30_intr = { | ||
191 | .intrs = px30_vop_intrs, | ||
192 | .nintrs = ARRAY_SIZE(px30_vop_intrs), | ||
193 | .line_flag_num[0] = VOP_REG(PX30_LINE_FLAG, 0xfff, 0), | ||
194 | .status = VOP_REG_MASK_SYNC(PX30_INTR_STATUS, 0xffff, 0), | ||
195 | .enable = VOP_REG_MASK_SYNC(PX30_INTR_EN, 0xffff, 0), | ||
196 | .clear = VOP_REG_MASK_SYNC(PX30_INTR_CLEAR, 0xffff, 0), | ||
197 | }; | ||
198 | |||
199 | static const struct vop_common px30_common = { | ||
200 | .standby = VOP_REG_SYNC(PX30_SYS_CTRL2, 0x1, 1), | ||
201 | .out_mode = VOP_REG(PX30_DSP_CTRL2, 0xf, 16), | ||
202 | .dsp_blank = VOP_REG(PX30_DSP_CTRL2, 0x1, 14), | ||
203 | .cfg_done = VOP_REG_SYNC(PX30_REG_CFG_DONE, 0x1, 0), | ||
204 | }; | ||
205 | |||
206 | static const struct vop_modeset px30_modeset = { | ||
207 | .htotal_pw = VOP_REG(PX30_DSP_HTOTAL_HS_END, 0x0fff0fff, 0), | ||
208 | .hact_st_end = VOP_REG(PX30_DSP_HACT_ST_END, 0x0fff0fff, 0), | ||
209 | .vtotal_pw = VOP_REG(PX30_DSP_VTOTAL_VS_END, 0x0fff0fff, 0), | ||
210 | .vact_st_end = VOP_REG(PX30_DSP_VACT_ST_END, 0x0fff0fff, 0), | ||
211 | }; | ||
212 | |||
213 | static const struct vop_output px30_output = { | ||
214 | .rgb_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0xf, 1), | ||
215 | .mipi_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0xf, 25), | ||
216 | .rgb_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 0), | ||
217 | .mipi_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 24), | ||
218 | }; | ||
219 | |||
220 | static const struct vop_scl_regs px30_win_scl = { | ||
221 | .scale_yrgb_x = VOP_REG(PX30_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), | ||
222 | .scale_yrgb_y = VOP_REG(PX30_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), | ||
223 | .scale_cbcr_x = VOP_REG(PX30_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), | ||
224 | .scale_cbcr_y = VOP_REG(PX30_WIN0_SCL_FACTOR_CBR, 0xffff, 16), | ||
225 | }; | ||
226 | |||
227 | static const struct vop_win_phy px30_win0_data = { | ||
228 | .scl = &px30_win_scl, | ||
229 | .data_formats = formats_win_full, | ||
230 | .nformats = ARRAY_SIZE(formats_win_full), | ||
231 | .enable = VOP_REG(PX30_WIN0_CTRL0, 0x1, 0), | ||
232 | .format = VOP_REG(PX30_WIN0_CTRL0, 0x7, 1), | ||
233 | .rb_swap = VOP_REG(PX30_WIN0_CTRL0, 0x1, 12), | ||
234 | .act_info = VOP_REG(PX30_WIN0_ACT_INFO, 0xffffffff, 0), | ||
235 | .dsp_info = VOP_REG(PX30_WIN0_DSP_INFO, 0xffffffff, 0), | ||
236 | .dsp_st = VOP_REG(PX30_WIN0_DSP_ST, 0xffffffff, 0), | ||
237 | .yrgb_mst = VOP_REG(PX30_WIN0_YRGB_MST0, 0xffffffff, 0), | ||
238 | .uv_mst = VOP_REG(PX30_WIN0_CBR_MST0, 0xffffffff, 0), | ||
239 | .yrgb_vir = VOP_REG(PX30_WIN0_VIR, 0x1fff, 0), | ||
240 | .uv_vir = VOP_REG(PX30_WIN0_VIR, 0x1fff, 16), | ||
241 | }; | ||
242 | |||
243 | static const struct vop_win_phy px30_win1_data = { | ||
244 | .data_formats = formats_win_lite, | ||
245 | .nformats = ARRAY_SIZE(formats_win_lite), | ||
246 | .enable = VOP_REG(PX30_WIN1_CTRL0, 0x1, 0), | ||
247 | .format = VOP_REG(PX30_WIN1_CTRL0, 0x7, 4), | ||
248 | .rb_swap = VOP_REG(PX30_WIN1_CTRL0, 0x1, 12), | ||
249 | .dsp_info = VOP_REG(PX30_WIN1_DSP_INFO, 0xffffffff, 0), | ||
250 | .dsp_st = VOP_REG(PX30_WIN1_DSP_ST, 0xffffffff, 0), | ||
251 | .yrgb_mst = VOP_REG(PX30_WIN1_MST, 0xffffffff, 0), | ||
252 | .yrgb_vir = VOP_REG(PX30_WIN1_VIR, 0x1fff, 0), | ||
253 | }; | ||
254 | |||
255 | static const struct vop_win_phy px30_win2_data = { | ||
256 | .data_formats = formats_win_lite, | ||
257 | .nformats = ARRAY_SIZE(formats_win_lite), | ||
258 | .gate = VOP_REG(PX30_WIN2_CTRL0, 0x1, 4), | ||
259 | .enable = VOP_REG(PX30_WIN2_CTRL0, 0x1, 0), | ||
260 | .format = VOP_REG(PX30_WIN2_CTRL0, 0x3, 5), | ||
261 | .rb_swap = VOP_REG(PX30_WIN2_CTRL0, 0x1, 20), | ||
262 | .dsp_info = VOP_REG(PX30_WIN2_DSP_INFO0, 0x0fff0fff, 0), | ||
263 | .dsp_st = VOP_REG(PX30_WIN2_DSP_ST0, 0x1fff1fff, 0), | ||
264 | .yrgb_mst = VOP_REG(PX30_WIN2_MST0, 0xffffffff, 0), | ||
265 | .yrgb_vir = VOP_REG(PX30_WIN2_VIR0_1, 0x1fff, 0), | ||
266 | }; | ||
267 | |||
268 | static const struct vop_win_data px30_vop_big_win_data[] = { | ||
269 | { .base = 0x00, .phy = &px30_win0_data, | ||
270 | .type = DRM_PLANE_TYPE_PRIMARY }, | ||
271 | { .base = 0x00, .phy = &px30_win1_data, | ||
272 | .type = DRM_PLANE_TYPE_OVERLAY }, | ||
273 | { .base = 0x00, .phy = &px30_win2_data, | ||
274 | .type = DRM_PLANE_TYPE_CURSOR }, | ||
275 | }; | ||
276 | |||
277 | static const struct vop_data px30_vop_big = { | ||
278 | .intr = &px30_intr, | ||
279 | .feature = VOP_FEATURE_INTERNAL_RGB, | ||
280 | .common = &px30_common, | ||
281 | .modeset = &px30_modeset, | ||
282 | .output = &px30_output, | ||
283 | .win = px30_vop_big_win_data, | ||
284 | .win_size = ARRAY_SIZE(px30_vop_big_win_data), | ||
285 | }; | ||
286 | |||
287 | static const struct vop_win_data px30_vop_lit_win_data[] = { | ||
288 | { .base = 0x00, .phy = &px30_win1_data, | ||
289 | .type = DRM_PLANE_TYPE_PRIMARY }, | ||
290 | }; | ||
291 | |||
292 | static const struct vop_data px30_vop_lit = { | ||
293 | .intr = &px30_intr, | ||
294 | .feature = VOP_FEATURE_INTERNAL_RGB, | ||
295 | .common = &px30_common, | ||
296 | .modeset = &px30_modeset, | ||
297 | .output = &px30_output, | ||
298 | .win = px30_vop_lit_win_data, | ||
299 | .win_size = ARRAY_SIZE(px30_vop_lit_win_data), | ||
300 | }; | ||
301 | |||
302 | static const struct vop_scl_regs rk3188_win_scl = { | ||
303 | .scale_yrgb_x = VOP_REG(RK3188_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), | ||
304 | .scale_yrgb_y = VOP_REG(RK3188_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), | ||
305 | .scale_cbcr_x = VOP_REG(RK3188_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), | ||
306 | .scale_cbcr_y = VOP_REG(RK3188_WIN0_SCL_FACTOR_CBR, 0xffff, 16), | ||
307 | }; | ||
308 | |||
309 | static const struct vop_win_phy rk3188_win0_data = { | ||
310 | .scl = &rk3188_win_scl, | ||
311 | .data_formats = formats_win_full, | ||
312 | .nformats = ARRAY_SIZE(formats_win_full), | ||
313 | .enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 0), | ||
314 | .format = VOP_REG(RK3188_SYS_CTRL, 0x7, 3), | ||
315 | .rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 15), | ||
316 | .act_info = VOP_REG(RK3188_WIN0_ACT_INFO, 0x1fff1fff, 0), | ||
317 | .dsp_info = VOP_REG(RK3188_WIN0_DSP_INFO, 0x0fff0fff, 0), | ||
318 | .dsp_st = VOP_REG(RK3188_WIN0_DSP_ST, 0x1fff1fff, 0), | ||
319 | .yrgb_mst = VOP_REG(RK3188_WIN0_YRGB_MST0, 0xffffffff, 0), | ||
320 | .uv_mst = VOP_REG(RK3188_WIN0_CBR_MST0, 0xffffffff, 0), | ||
321 | .yrgb_vir = VOP_REG(RK3188_WIN_VIR, 0x1fff, 0), | ||
322 | }; | ||
323 | |||
324 | static const struct vop_win_phy rk3188_win1_data = { | ||
325 | .data_formats = formats_win_lite, | ||
326 | .nformats = ARRAY_SIZE(formats_win_lite), | ||
327 | .enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 1), | ||
328 | .format = VOP_REG(RK3188_SYS_CTRL, 0x7, 6), | ||
329 | .rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 19), | ||
330 | /* no act_info on window1 */ | ||
331 | .dsp_info = VOP_REG(RK3188_WIN1_DSP_INFO, 0x07ff07ff, 0), | ||
332 | .dsp_st = VOP_REG(RK3188_WIN1_DSP_ST, 0x0fff0fff, 0), | ||
333 | .yrgb_mst = VOP_REG(RK3188_WIN1_MST, 0xffffffff, 0), | ||
334 | .yrgb_vir = VOP_REG(RK3188_WIN_VIR, 0x1fff, 16), | ||
335 | }; | ||
336 | |||
337 | static const struct vop_modeset rk3188_modeset = { | ||
338 | .htotal_pw = VOP_REG(RK3188_DSP_HTOTAL_HS_END, 0x0fff0fff, 0), | ||
339 | .hact_st_end = VOP_REG(RK3188_DSP_HACT_ST_END, 0x0fff0fff, 0), | ||
340 | .vtotal_pw = VOP_REG(RK3188_DSP_VTOTAL_VS_END, 0x0fff0fff, 0), | ||
341 | .vact_st_end = VOP_REG(RK3188_DSP_VACT_ST_END, 0x0fff0fff, 0), | ||
342 | }; | ||
343 | |||
344 | static const struct vop_output rk3188_output = { | ||
345 | .pin_pol = VOP_REG(RK3188_DSP_CTRL0, 0xf, 4), | ||
346 | }; | ||
347 | |||
348 | static const struct vop_common rk3188_common = { | ||
349 | .gate_en = VOP_REG(RK3188_SYS_CTRL, 0x1, 31), | ||
350 | .standby = VOP_REG(RK3188_SYS_CTRL, 0x1, 30), | ||
351 | .out_mode = VOP_REG(RK3188_DSP_CTRL0, 0xf, 0), | ||
352 | .cfg_done = VOP_REG(RK3188_REG_CFG_DONE, 0x1, 0), | ||
353 | .dsp_blank = VOP_REG(RK3188_DSP_CTRL1, 0x3, 24), | ||
354 | }; | ||
355 | |||
356 | static const struct vop_win_data rk3188_vop_win_data[] = { | ||
357 | { .base = 0x00, .phy = &rk3188_win0_data, | ||
358 | .type = DRM_PLANE_TYPE_PRIMARY }, | ||
359 | { .base = 0x00, .phy = &rk3188_win1_data, | ||
360 | .type = DRM_PLANE_TYPE_CURSOR }, | ||
361 | }; | ||
362 | |||
363 | static const int rk3188_vop_intrs[] = { | ||
364 | 0, | ||
365 | FS_INTR, | ||
366 | LINE_FLAG_INTR, | ||
367 | BUS_ERROR_INTR, | ||
368 | }; | ||
369 | |||
370 | static const struct vop_intr rk3188_vop_intr = { | ||
371 | .intrs = rk3188_vop_intrs, | ||
372 | .nintrs = ARRAY_SIZE(rk3188_vop_intrs), | ||
373 | .line_flag_num[0] = VOP_REG(RK3188_INT_STATUS, 0xfff, 12), | ||
374 | .status = VOP_REG(RK3188_INT_STATUS, 0xf, 0), | ||
375 | .enable = VOP_REG(RK3188_INT_STATUS, 0xf, 4), | ||
376 | .clear = VOP_REG(RK3188_INT_STATUS, 0xf, 8), | ||
377 | }; | ||
378 | |||
379 | static const struct vop_data rk3188_vop = { | ||
380 | .intr = &rk3188_vop_intr, | ||
381 | .common = &rk3188_common, | ||
382 | .modeset = &rk3188_modeset, | ||
383 | .output = &rk3188_output, | ||
384 | .win = rk3188_vop_win_data, | ||
385 | .win_size = ARRAY_SIZE(rk3188_vop_win_data), | ||
386 | .feature = VOP_FEATURE_INTERNAL_RGB, | ||
387 | }; | ||
388 | |||
180 | static const struct vop_scl_extension rk3288_win_full_scl_ext = { | 389 | static const struct vop_scl_extension rk3288_win_full_scl_ext = { |
181 | .cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31), | 390 | .cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31), |
182 | .cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30), | 391 | .cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30), |
@@ -541,6 +750,12 @@ static const struct of_device_id vop_driver_dt_match[] = { | |||
541 | .data = &rk3036_vop }, | 750 | .data = &rk3036_vop }, |
542 | { .compatible = "rockchip,rk3126-vop", | 751 | { .compatible = "rockchip,rk3126-vop", |
543 | .data = &rk3126_vop }, | 752 | .data = &rk3126_vop }, |
753 | { .compatible = "rockchip,px30-vop-big", | ||
754 | .data = &px30_vop_big }, | ||
755 | { .compatible = "rockchip,px30-vop-lit", | ||
756 | .data = &px30_vop_lit }, | ||
757 | { .compatible = "rockchip,rk3188-vop", | ||
758 | .data = &rk3188_vop }, | ||
544 | { .compatible = "rockchip,rk3288-vop", | 759 | { .compatible = "rockchip,rk3288-vop", |
545 | .data = &rk3288_vop }, | 760 | .data = &rk3288_vop }, |
546 | { .compatible = "rockchip,rk3368-vop", | 761 | { .compatible = "rockchip,rk3368-vop", |
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index f81b510ea99c..7348c68352ed 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h | |||
@@ -884,4 +884,103 @@ | |||
884 | #define RK3126_WIN1_DSP_ST 0x54 | 884 | #define RK3126_WIN1_DSP_ST 0x54 |
885 | /* rk3126 register definition end */ | 885 | /* rk3126 register definition end */ |
886 | 886 | ||
887 | /* px30 register definition */ | ||
888 | #define PX30_REG_CFG_DONE 0x00000 | ||
889 | #define PX30_VERSION 0x00004 | ||
890 | #define PX30_DSP_BG 0x00008 | ||
891 | #define PX30_MCU_CTRL 0x0000c | ||
892 | #define PX30_SYS_CTRL0 0x00010 | ||
893 | #define PX30_SYS_CTRL1 0x00014 | ||
894 | #define PX30_SYS_CTRL2 0x00018 | ||
895 | #define PX30_DSP_CTRL0 0x00020 | ||
896 | #define PX30_DSP_CTRL2 0x00028 | ||
897 | #define PX30_VOP_STATUS 0x0002c | ||
898 | #define PX30_LINE_FLAG 0x00030 | ||
899 | #define PX30_INTR_EN 0x00034 | ||
900 | #define PX30_INTR_CLEAR 0x00038 | ||
901 | #define PX30_INTR_STATUS 0x0003c | ||
902 | #define PX30_WIN0_CTRL0 0x00050 | ||
903 | #define PX30_WIN0_CTRL1 0x00054 | ||
904 | #define PX30_WIN0_COLOR_KEY 0x00058 | ||
905 | #define PX30_WIN0_VIR 0x0005c | ||
906 | #define PX30_WIN0_YRGB_MST0 0x00060 | ||
907 | #define PX30_WIN0_CBR_MST0 0x00064 | ||
908 | #define PX30_WIN0_ACT_INFO 0x00068 | ||
909 | #define PX30_WIN0_DSP_INFO 0x0006c | ||
910 | #define PX30_WIN0_DSP_ST 0x00070 | ||
911 | #define PX30_WIN0_SCL_FACTOR_YRGB 0x00074 | ||
912 | #define PX30_WIN0_SCL_FACTOR_CBR 0x00078 | ||
913 | #define PX30_WIN0_SCL_OFFSET 0x0007c | ||
914 | #define PX30_WIN0_ALPHA_CTRL 0x00080 | ||
915 | #define PX30_WIN1_CTRL0 0x00090 | ||
916 | #define PX30_WIN1_CTRL1 0x00094 | ||
917 | #define PX30_WIN1_VIR 0x00098 | ||
918 | #define PX30_WIN1_MST 0x000a0 | ||
919 | #define PX30_WIN1_DSP_INFO 0x000a4 | ||
920 | #define PX30_WIN1_DSP_ST 0x000a8 | ||
921 | #define PX30_WIN1_COLOR_KEY 0x000ac | ||
922 | #define PX30_WIN1_ALPHA_CTRL 0x000bc | ||
923 | #define PX30_HWC_CTRL0 0x000e0 | ||
924 | #define PX30_HWC_CTRL1 0x000e4 | ||
925 | #define PX30_HWC_MST 0x000e8 | ||
926 | #define PX30_HWC_DSP_ST 0x000ec | ||
927 | #define PX30_HWC_ALPHA_CTRL 0x000f0 | ||
928 | #define PX30_DSP_HTOTAL_HS_END 0x00100 | ||
929 | #define PX30_DSP_HACT_ST_END 0x00104 | ||
930 | #define PX30_DSP_VTOTAL_VS_END 0x00108 | ||
931 | #define PX30_DSP_VACT_ST_END 0x0010c | ||
932 | #define PX30_DSP_VS_ST_END_F1 0x00110 | ||
933 | #define PX30_DSP_VACT_ST_END_F1 0x00114 | ||
934 | #define PX30_BCSH_CTRL 0x00160 | ||
935 | #define PX30_BCSH_COL_BAR 0x00164 | ||
936 | #define PX30_BCSH_BCS 0x00168 | ||
937 | #define PX30_BCSH_H 0x0016c | ||
938 | #define PX30_FRC_LOWER01_0 0x00170 | ||
939 | #define PX30_FRC_LOWER01_1 0x00174 | ||
940 | #define PX30_FRC_LOWER10_0 0x00178 | ||
941 | #define PX30_FRC_LOWER10_1 0x0017c | ||
942 | #define PX30_FRC_LOWER11_0 0x00180 | ||
943 | #define PX30_FRC_LOWER11_1 0x00184 | ||
944 | #define PX30_MCU_RW_BYPASS_PORT 0x0018c | ||
945 | #define PX30_WIN2_CTRL0 0x00190 | ||
946 | #define PX30_WIN2_CTRL1 0x00194 | ||
947 | #define PX30_WIN2_VIR0_1 0x00198 | ||
948 | #define PX30_WIN2_VIR2_3 0x0019c | ||
949 | #define PX30_WIN2_MST0 0x001a0 | ||
950 | #define PX30_WIN2_DSP_INFO0 0x001a4 | ||
951 | #define PX30_WIN2_DSP_ST0 0x001a8 | ||
952 | #define PX30_WIN2_COLOR_KEY 0x001ac | ||
953 | #define PX30_WIN2_ALPHA_CTRL 0x001bc | ||
954 | #define PX30_BLANKING_VALUE 0x001f4 | ||
955 | #define PX30_FLAG_REG_FRM_VALID 0x001f8 | ||
956 | #define PX30_FLAG_REG 0x001fc | ||
957 | #define PX30_HWC_LUT_ADDR 0x00600 | ||
958 | #define PX30_GAMMA_LUT_ADDR 0x00a00 | ||
959 | /* px30 register definition end */ | ||
960 | |||
961 | /* rk3188 register definition */ | ||
962 | #define RK3188_SYS_CTRL 0x00 | ||
963 | #define RK3188_DSP_CTRL0 0x04 | ||
964 | #define RK3188_DSP_CTRL1 0x08 | ||
965 | #define RK3188_INT_STATUS 0x10 | ||
966 | #define RK3188_WIN0_YRGB_MST0 0x20 | ||
967 | #define RK3188_WIN0_CBR_MST0 0x24 | ||
968 | #define RK3188_WIN0_YRGB_MST1 0x28 | ||
969 | #define RK3188_WIN0_CBR_MST1 0x2c | ||
970 | #define RK3188_WIN_VIR 0x30 | ||
971 | #define RK3188_WIN0_ACT_INFO 0x34 | ||
972 | #define RK3188_WIN0_DSP_INFO 0x38 | ||
973 | #define RK3188_WIN0_DSP_ST 0x3c | ||
974 | #define RK3188_WIN0_SCL_FACTOR_YRGB 0x40 | ||
975 | #define RK3188_WIN0_SCL_FACTOR_CBR 0x44 | ||
976 | #define RK3188_WIN1_MST 0x4c | ||
977 | #define RK3188_WIN1_DSP_INFO 0x50 | ||
978 | #define RK3188_WIN1_DSP_ST 0x54 | ||
979 | #define RK3188_DSP_HTOTAL_HS_END 0x6c | ||
980 | #define RK3188_DSP_HACT_ST_END 0x70 | ||
981 | #define RK3188_DSP_VTOTAL_VS_END 0x74 | ||
982 | #define RK3188_DSP_VACT_ST_END 0x78 | ||
983 | #define RK3188_REG_CFG_DONE 0x90 | ||
984 | /* rk3188 register definition end */ | ||
985 | |||
887 | #endif /* _ROCKCHIP_VOP_REG_H */ | 986 | #endif /* _ROCKCHIP_VOP_REG_H */ |
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 49438337f70d..19b9b5ed1297 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c | |||
@@ -721,7 +721,6 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) | |||
721 | return 0; | 721 | return 0; |
722 | 722 | ||
723 | err_sysfs: | 723 | err_sysfs: |
724 | drm_bridge_remove(bridge); | ||
725 | return -EINVAL; | 724 | return -EINVAL; |
726 | } | 725 | } |
727 | 726 | ||
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 34cdc4644435..ccf718404a1c 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c | |||
@@ -1315,7 +1315,6 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) | |||
1315 | return 0; | 1315 | return 0; |
1316 | 1316 | ||
1317 | err_sysfs: | 1317 | err_sysfs: |
1318 | drm_bridge_remove(bridge); | ||
1319 | hdmi->drm_connector = NULL; | 1318 | hdmi->drm_connector = NULL; |
1320 | return -EINVAL; | 1319 | return -EINVAL; |
1321 | } | 1320 | } |
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index d7950b52a1fd..bf49c55b0f2c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c | |||
@@ -34,6 +34,9 @@ | |||
34 | struct sun4i_backend_quirks { | 34 | struct sun4i_backend_quirks { |
35 | /* backend <-> TCON muxing selection done in backend */ | 35 | /* backend <-> TCON muxing selection done in backend */ |
36 | bool needs_output_muxing; | 36 | bool needs_output_muxing; |
37 | |||
38 | /* alpha at the lowest z position is not always supported */ | ||
39 | bool supports_lowest_plane_alpha; | ||
37 | }; | 40 | }; |
38 | 41 | ||
39 | static const u32 sunxi_rgb2yuv_coef[12] = { | 42 | static const u32 sunxi_rgb2yuv_coef[12] = { |
@@ -60,32 +63,6 @@ static const u32 sunxi_bt601_yuv2rgb_coef[12] = { | |||
60 | 0x000004a7, 0x00000812, 0x00000000, 0x00002eb1, | 63 | 0x000004a7, 0x00000812, 0x00000000, 0x00002eb1, |
61 | }; | 64 | }; |
62 | 65 | ||
63 | static inline bool sun4i_backend_format_is_planar_yuv(uint32_t format) | ||
64 | { | ||
65 | switch (format) { | ||
66 | case DRM_FORMAT_YUV411: | ||
67 | case DRM_FORMAT_YUV422: | ||
68 | case DRM_FORMAT_YUV444: | ||
69 | return true; | ||
70 | default: | ||
71 | return false; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static inline bool sun4i_backend_format_is_packed_yuv422(uint32_t format) | ||
76 | { | ||
77 | switch (format) { | ||
78 | case DRM_FORMAT_YUYV: | ||
79 | case DRM_FORMAT_YVYU: | ||
80 | case DRM_FORMAT_UYVY: | ||
81 | case DRM_FORMAT_VYUY: | ||
82 | return true; | ||
83 | |||
84 | default: | ||
85 | return false; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine) | 66 | static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine) |
90 | { | 67 | { |
91 | int i; | 68 | int i; |
@@ -215,7 +192,8 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, | |||
215 | { | 192 | { |
216 | struct drm_plane_state *state = plane->state; | 193 | struct drm_plane_state *state = plane->state; |
217 | struct drm_framebuffer *fb = state->fb; | 194 | struct drm_framebuffer *fb = state->fb; |
218 | uint32_t format = fb->format->format; | 195 | const struct drm_format_info *format = fb->format; |
196 | const uint32_t fmt = format->format; | ||
219 | u32 val = SUN4I_BACKEND_IYUVCTL_EN; | 197 | u32 val = SUN4I_BACKEND_IYUVCTL_EN; |
220 | int i; | 198 | int i; |
221 | 199 | ||
@@ -233,16 +211,16 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, | |||
233 | SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN); | 211 | SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN); |
234 | 212 | ||
235 | /* TODO: Add support for the multi-planar YUV formats */ | 213 | /* TODO: Add support for the multi-planar YUV formats */ |
236 | if (sun4i_backend_format_is_packed_yuv422(format)) | 214 | if (format->num_planes == 1) |
237 | val |= SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422; | 215 | val |= SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422; |
238 | else | 216 | else |
239 | DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", format); | 217 | DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", fmt); |
240 | 218 | ||
241 | /* | 219 | /* |
242 | * Allwinner seems to list the pixel sequence from right to left, while | 220 | * Allwinner seems to list the pixel sequence from right to left, while |
243 | * DRM lists it from left to right. | 221 | * DRM lists it from left to right. |
244 | */ | 222 | */ |
245 | switch (format) { | 223 | switch (fmt) { |
246 | case DRM_FORMAT_YUYV: | 224 | case DRM_FORMAT_YUYV: |
247 | val |= SUN4I_BACKEND_IYUVCTL_FBPS_VYUY; | 225 | val |= SUN4I_BACKEND_IYUVCTL_FBPS_VYUY; |
248 | break; | 226 | break; |
@@ -257,7 +235,7 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend, | |||
257 | break; | 235 | break; |
258 | default: | 236 | default: |
259 | DRM_DEBUG_DRIVER("Unsupported YUV pixel sequence (0x%x)\n", | 237 | DRM_DEBUG_DRIVER("Unsupported YUV pixel sequence (0x%x)\n", |
260 | format); | 238 | fmt); |
261 | } | 239 | } |
262 | 240 | ||
263 | regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVCTL_REG, val); | 241 | regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVCTL_REG, val); |
@@ -457,12 +435,14 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, | |||
457 | struct drm_crtc_state *crtc_state) | 435 | struct drm_crtc_state *crtc_state) |
458 | { | 436 | { |
459 | struct drm_plane_state *plane_states[SUN4I_BACKEND_NUM_LAYERS] = { 0 }; | 437 | struct drm_plane_state *plane_states[SUN4I_BACKEND_NUM_LAYERS] = { 0 }; |
438 | struct sun4i_backend *backend = engine_to_sun4i_backend(engine); | ||
460 | struct drm_atomic_state *state = crtc_state->state; | 439 | struct drm_atomic_state *state = crtc_state->state; |
461 | struct drm_device *drm = state->dev; | 440 | struct drm_device *drm = state->dev; |
462 | struct drm_plane *plane; | 441 | struct drm_plane *plane; |
463 | unsigned int num_planes = 0; | 442 | unsigned int num_planes = 0; |
464 | unsigned int num_alpha_planes = 0; | 443 | unsigned int num_alpha_planes = 0; |
465 | unsigned int num_frontend_planes = 0; | 444 | unsigned int num_frontend_planes = 0; |
445 | unsigned int num_alpha_planes_max = 1; | ||
466 | unsigned int num_yuv_planes = 0; | 446 | unsigned int num_yuv_planes = 0; |
467 | unsigned int current_pipe = 0; | 447 | unsigned int current_pipe = 0; |
468 | unsigned int i; | 448 | unsigned int i; |
@@ -526,33 +506,40 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, | |||
526 | * the layer with the highest priority. | 506 | * the layer with the highest priority. |
527 | * | 507 | * |
528 | * The second step is the actual alpha blending, that takes | 508 | * The second step is the actual alpha blending, that takes |
529 | * the two pipes as input, and uses the eventual alpha | 509 | * the two pipes as input, and uses the potential alpha |
530 | * component to do the transparency between the two. | 510 | * component to do the transparency between the two. |
531 | * | 511 | * |
532 | * This two steps scenario makes us unable to guarantee a | 512 | * This two-step scenario makes us unable to guarantee a |
533 | * robust alpha blending between the 4 layers in all | 513 | * robust alpha blending between the 4 layers in all |
534 | * situations, since this means that we need to have one layer | 514 | * situations, since this means that we need to have one layer |
535 | * with alpha at the lowest position of our two pipes. | 515 | * with alpha at the lowest position of our two pipes. |
536 | * | 516 | * |
537 | * However, we cannot even do that, since the hardware has a | 517 | * However, we cannot even do that on every platform, since |
538 | * bug where the lowest plane of the lowest pipe (pipe 0, | 518 | * the hardware has a bug where the lowest plane of the lowest |
539 | * priority 0), if it has any alpha, will discard the pixel | 519 | * pipe (pipe 0, priority 0), if it has any alpha, will |
540 | * entirely and just display the pixels in the background | 520 | * discard the pixel data entirely and just display the pixels |
541 | * color (black by default). | 521 | * in the background color (black by default). |
542 | * | 522 | * |
543 | * This means that we effectively have only three valid | 523 | * This means that on the affected platforms, we effectively |
544 | * configurations with alpha, all of them with the alpha being | 524 | * have only three valid configurations with alpha, all of |
545 | * on pipe1 with the lowest position, which can be 1, 2 or 3 | 525 | * them with the alpha being on pipe1 with the lowest |
546 | * depending on the number of planes and their zpos. | 526 | * position, which can be 1, 2 or 3 depending on the number of |
527 | * planes and their zpos. | ||
547 | */ | 528 | */ |
548 | if (num_alpha_planes > SUN4I_BACKEND_NUM_ALPHA_LAYERS) { | 529 | |
530 | /* For platforms that are not affected by the issue described above. */ | ||
531 | if (backend->quirks->supports_lowest_plane_alpha) | ||
532 | num_alpha_planes_max++; | ||
533 | |||
534 | if (num_alpha_planes > num_alpha_planes_max) { | ||
549 | DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n"); | 535 | DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n"); |
550 | return -EINVAL; | 536 | return -EINVAL; |
551 | } | 537 | } |
552 | 538 | ||
553 | /* We can't have an alpha plane at the lowest position */ | 539 | /* We can't have an alpha plane at the lowest position */ |
554 | if (plane_states[0]->fb->format->has_alpha || | 540 | if (!backend->quirks->supports_lowest_plane_alpha && |
555 | (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE)) | 541 | (plane_states[0]->fb->format->has_alpha || |
542 | (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE))) | ||
556 | return -EINVAL; | 543 | return -EINVAL; |
557 | 544 | ||
558 | for (i = 1; i < num_planes; i++) { | 545 | for (i = 1; i < num_planes; i++) { |
@@ -876,6 +863,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, | |||
876 | : SUN4I_BACKEND_MODCTL_OUT_LCD0)); | 863 | : SUN4I_BACKEND_MODCTL_OUT_LCD0)); |
877 | } | 864 | } |
878 | 865 | ||
866 | backend->quirks = quirks; | ||
867 | |||
879 | return 0; | 868 | return 0; |
880 | 869 | ||
881 | err_disable_ram_clk: | 870 | err_disable_ram_clk: |
@@ -935,9 +924,11 @@ static const struct sun4i_backend_quirks sun6i_backend_quirks = { | |||
935 | 924 | ||
936 | static const struct sun4i_backend_quirks sun7i_backend_quirks = { | 925 | static const struct sun4i_backend_quirks sun7i_backend_quirks = { |
937 | .needs_output_muxing = true, | 926 | .needs_output_muxing = true, |
927 | .supports_lowest_plane_alpha = true, | ||
938 | }; | 928 | }; |
939 | 929 | ||
940 | static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { | 930 | static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { |
931 | .supports_lowest_plane_alpha = true, | ||
941 | }; | 932 | }; |
942 | 933 | ||
943 | static const struct sun4i_backend_quirks sun9i_backend_quirks = { | 934 | static const struct sun4i_backend_quirks sun9i_backend_quirks = { |
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 4caee0392fa4..e3d4c6035eb2 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h | |||
@@ -167,7 +167,6 @@ | |||
167 | #define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p))) | 167 | #define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p))) |
168 | 168 | ||
169 | #define SUN4I_BACKEND_NUM_LAYERS 4 | 169 | #define SUN4I_BACKEND_NUM_LAYERS 4 |
170 | #define SUN4I_BACKEND_NUM_ALPHA_LAYERS 1 | ||
171 | #define SUN4I_BACKEND_NUM_FRONTEND_LAYERS 1 | 170 | #define SUN4I_BACKEND_NUM_FRONTEND_LAYERS 1 |
172 | #define SUN4I_BACKEND_NUM_YUV_PLANES 1 | 171 | #define SUN4I_BACKEND_NUM_YUV_PLANES 1 |
173 | 172 | ||
@@ -187,6 +186,8 @@ struct sun4i_backend { | |||
187 | /* Protects against races in the frontend teardown */ | 186 | /* Protects against races in the frontend teardown */ |
188 | spinlock_t frontend_lock; | 187 | spinlock_t frontend_lock; |
189 | bool frontend_teardown; | 188 | bool frontend_teardown; |
189 | |||
190 | const struct sun4i_backend_quirks *quirks; | ||
190 | }; | 191 | }; |
191 | 192 | ||
192 | static inline struct sun4i_backend * | 193 | static inline struct sun4i_backend * |
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index dd19d674055c..1e41c3f5fd6d 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c | |||
@@ -61,22 +61,6 @@ static struct drm_driver sun4i_drv_driver = { | |||
61 | /* Frame Buffer Operations */ | 61 | /* Frame Buffer Operations */ |
62 | }; | 62 | }; |
63 | 63 | ||
64 | static void sun4i_remove_framebuffers(void) | ||
65 | { | ||
66 | struct apertures_struct *ap; | ||
67 | |||
68 | ap = alloc_apertures(1); | ||
69 | if (!ap) | ||
70 | return; | ||
71 | |||
72 | /* The framebuffer can be located anywhere in RAM */ | ||
73 | ap->ranges[0].base = 0; | ||
74 | ap->ranges[0].size = ~0; | ||
75 | |||
76 | drm_fb_helper_remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false); | ||
77 | kfree(ap); | ||
78 | } | ||
79 | |||
80 | static int sun4i_drv_bind(struct device *dev) | 64 | static int sun4i_drv_bind(struct device *dev) |
81 | { | 65 | { |
82 | struct drm_device *drm; | 66 | struct drm_device *drm; |
@@ -119,7 +103,7 @@ static int sun4i_drv_bind(struct device *dev) | |||
119 | drm->irq_enabled = true; | 103 | drm->irq_enabled = true; |
120 | 104 | ||
121 | /* Remove early framebuffers (ie. simplefb) */ | 105 | /* Remove early framebuffers (ie. simplefb) */ |
122 | sun4i_remove_framebuffers(); | 106 | drm_fb_helper_remove_conflicting_framebuffers(NULL, "sun4i-drm-fb", false); |
123 | 107 | ||
124 | /* Create our framebuffer */ | 108 | /* Create our framebuffer */ |
125 | ret = sun4i_framebuffer_init(drm); | 109 | ret = sun4i_framebuffer_init(drm); |
@@ -421,6 +405,7 @@ static const struct of_device_id sun4i_drv_of_table[] = { | |||
421 | { .compatible = "allwinner,sun8i-r40-display-engine" }, | 405 | { .compatible = "allwinner,sun8i-r40-display-engine" }, |
422 | { .compatible = "allwinner,sun8i-v3s-display-engine" }, | 406 | { .compatible = "allwinner,sun8i-v3s-display-engine" }, |
423 | { .compatible = "allwinner,sun9i-a80-display-engine" }, | 407 | { .compatible = "allwinner,sun9i-a80-display-engine" }, |
408 | { .compatible = "allwinner,sun50i-a64-display-engine" }, | ||
424 | { } | 409 | { } |
425 | }; | 410 | }; |
426 | MODULE_DEVICE_TABLE(of, sun4i_drv_of_table); | 411 | MODULE_DEVICE_TABLE(of, sun4i_drv_of_table); |
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c index 750ad24de1d7..78f77af8805a 100644 --- a/drivers/gpu/drm/sun4i/sun4i_layer.c +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c | |||
@@ -35,9 +35,7 @@ static void sun4i_backend_layer_reset(struct drm_plane *plane) | |||
35 | 35 | ||
36 | state = kzalloc(sizeof(*state), GFP_KERNEL); | 36 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
37 | if (state) { | 37 | if (state) { |
38 | plane->state = &state->state; | 38 | __drm_atomic_helper_plane_reset(plane, &state->state); |
39 | plane->state->plane = plane; | ||
40 | plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE; | ||
41 | plane->state->zpos = layer->id; | 39 | plane->state->zpos = layer->id; |
42 | } | 40 | } |
43 | } | 41 | } |
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 3fb084f802e2..0cebb2db5b99 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <drm/drm_encoder.h> | 17 | #include <drm/drm_encoder.h> |
18 | #include <drm/drm_modes.h> | 18 | #include <drm/drm_modes.h> |
19 | #include <drm/drm_of.h> | 19 | #include <drm/drm_of.h> |
20 | #include <drm/drm_panel.h> | ||
20 | 21 | ||
21 | #include <uapi/drm/drm_mode.h> | 22 | #include <uapi/drm/drm_mode.h> |
22 | 23 | ||
@@ -35,6 +36,7 @@ | |||
35 | #include "sun4i_rgb.h" | 36 | #include "sun4i_rgb.h" |
36 | #include "sun4i_tcon.h" | 37 | #include "sun4i_tcon.h" |
37 | #include "sun6i_mipi_dsi.h" | 38 | #include "sun6i_mipi_dsi.h" |
39 | #include "sun8i_tcon_top.h" | ||
38 | #include "sunxi_engine.h" | 40 | #include "sunxi_engine.h" |
39 | 41 | ||
40 | static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder) | 42 | static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder) |
@@ -474,6 +476,33 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, | |||
474 | if (mode->flags & DRM_MODE_FLAG_PVSYNC) | 476 | if (mode->flags & DRM_MODE_FLAG_PVSYNC) |
475 | val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; | 477 | val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; |
476 | 478 | ||
479 | /* | ||
480 | * On A20 and similar SoCs, the only way to achieve Positive Edge | ||
481 | * (Rising Edge), is setting dclk clock phase to 2/3(240°). | ||
482 | * By default TCON works in Negative Edge(Falling Edge), | ||
483 | * this is why phase is set to 0 in that case. | ||
484 | * Unfortunately there's no way to logically invert dclk through | ||
485 | * IO_POL register. | ||
486 | * The only acceptable way to work, triple checked with scope, | ||
487 | * is using clock phase set to 0° for Negative Edge and set to 240° | ||
488 | * for Positive Edge. | ||
489 | * On A33 and similar SoCs there would be a 90° phase option, | ||
490 | * but it divides also dclk by 2. | ||
491 | * Following code is a way to avoid quirks all around TCON | ||
492 | * and DOTCLOCK drivers. | ||
493 | */ | ||
494 | if (!IS_ERR(tcon->panel)) { | ||
495 | struct drm_panel *panel = tcon->panel; | ||
496 | struct drm_connector *connector = panel->connector; | ||
497 | struct drm_display_info display_info = connector->display_info; | ||
498 | |||
499 | if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) | ||
500 | clk_set_phase(tcon->dclk, 240); | ||
501 | |||
502 | if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) | ||
503 | clk_set_phase(tcon->dclk, 0); | ||
504 | } | ||
505 | |||
477 | regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, | 506 | regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, |
478 | SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, | 507 | SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, |
479 | val); | 508 | val); |
@@ -880,6 +909,36 @@ static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv, | |||
880 | return ERR_PTR(-EINVAL); | 909 | return ERR_PTR(-EINVAL); |
881 | } | 910 | } |
882 | 911 | ||
912 | static bool sun4i_tcon_connected_to_tcon_top(struct device_node *node) | ||
913 | { | ||
914 | struct device_node *remote; | ||
915 | bool ret = false; | ||
916 | |||
917 | remote = of_graph_get_remote_node(node, 0, -1); | ||
918 | if (remote) { | ||
919 | ret = !!of_match_node(sun8i_tcon_top_of_table, remote); | ||
920 | of_node_put(remote); | ||
921 | } | ||
922 | |||
923 | return ret; | ||
924 | } | ||
925 | |||
926 | static int sun4i_tcon_get_index(struct sun4i_drv *drv) | ||
927 | { | ||
928 | struct list_head *pos; | ||
929 | int size = 0; | ||
930 | |||
931 | /* | ||
932 | * Because TCON is added to the list at the end of the probe | ||
933 | * (after this function is called), index of the current TCON | ||
934 | * will be same as current TCON list size. | ||
935 | */ | ||
936 | list_for_each(pos, &drv->tcon_list) | ||
937 | ++size; | ||
938 | |||
939 | return size; | ||
940 | } | ||
941 | |||
883 | /* | 942 | /* |
884 | * On SoCs with the old display pipeline design (Display Engine 1.0), | 943 | * On SoCs with the old display pipeline design (Display Engine 1.0), |
885 | * we assumed the TCON was always tied to just one backend. However | 944 | * we assumed the TCON was always tied to just one backend. However |
@@ -928,8 +987,24 @@ static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, | |||
928 | * connections between the backend and TCON? | 987 | * connections between the backend and TCON? |
929 | */ | 988 | */ |
930 | if (of_get_child_count(port) > 1) { | 989 | if (of_get_child_count(port) > 1) { |
931 | /* Get our ID directly from an upstream endpoint */ | 990 | int id; |
932 | int id = sun4i_tcon_of_get_id_from_port(port); | 991 | |
992 | /* | ||
993 | * When pipeline has the same number of TCONs and engines which | ||
994 | * are represented by frontends/backends (DE1) or mixers (DE2), | ||
995 | * we match them by their respective IDs. However, if pipeline | ||
996 | * contains TCON TOP, chances are that there are either more | ||
997 | * TCONs than engines (R40) or TCONs with non-consecutive ids. | ||
998 | * (H6). In that case it's easier just use TCON index in list | ||
999 | * as an id. That means that on R40, any 2 TCONs can be enabled | ||
1000 | * in DT out of 4 (there are 2 mixers). Due to the design of | ||
1001 | * TCON TOP, remaining 2 TCONs can't be connected to anything | ||
1002 | * anyway. | ||
1003 | */ | ||
1004 | if (sun4i_tcon_connected_to_tcon_top(node)) | ||
1005 | id = sun4i_tcon_get_index(drv); | ||
1006 | else | ||
1007 | id = sun4i_tcon_of_get_id_from_port(port); | ||
933 | 1008 | ||
934 | /* Get our engine by matching our ID */ | 1009 | /* Get our engine by matching our ID */ |
935 | engine = sun4i_tcon_get_engine_by_id(drv, id); | 1010 | engine = sun4i_tcon_get_engine_by_id(drv, id); |
@@ -1244,6 +1319,40 @@ static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon, | |||
1244 | return 0; | 1319 | return 0; |
1245 | } | 1320 | } |
1246 | 1321 | ||
1322 | static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon, | ||
1323 | const struct drm_encoder *encoder) | ||
1324 | { | ||
1325 | struct device_node *port, *remote; | ||
1326 | struct platform_device *pdev; | ||
1327 | int id, ret; | ||
1328 | |||
1329 | /* find TCON TOP platform device and TCON id */ | ||
1330 | |||
1331 | port = of_graph_get_port_by_id(tcon->dev->of_node, 0); | ||
1332 | if (!port) | ||
1333 | return -EINVAL; | ||
1334 | |||
1335 | id = sun4i_tcon_of_get_id_from_port(port); | ||
1336 | of_node_put(port); | ||
1337 | |||
1338 | remote = of_graph_get_remote_node(tcon->dev->of_node, 0, -1); | ||
1339 | if (!remote) | ||
1340 | return -EINVAL; | ||
1341 | |||
1342 | pdev = of_find_device_by_node(remote); | ||
1343 | of_node_put(remote); | ||
1344 | if (!pdev) | ||
1345 | return -EINVAL; | ||
1346 | |||
1347 | if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) { | ||
1348 | ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id); | ||
1349 | if (ret) | ||
1350 | return ret; | ||
1351 | } | ||
1352 | |||
1353 | return sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id); | ||
1354 | } | ||
1355 | |||
1247 | static const struct sun4i_tcon_quirks sun4i_a10_quirks = { | 1356 | static const struct sun4i_tcon_quirks sun4i_a10_quirks = { |
1248 | .has_channel_0 = true, | 1357 | .has_channel_0 = true, |
1249 | .has_channel_1 = true, | 1358 | .has_channel_1 = true, |
@@ -1291,6 +1400,11 @@ static const struct sun4i_tcon_quirks sun8i_a83t_tv_quirks = { | |||
1291 | .has_channel_1 = true, | 1400 | .has_channel_1 = true, |
1292 | }; | 1401 | }; |
1293 | 1402 | ||
1403 | static const struct sun4i_tcon_quirks sun8i_r40_tv_quirks = { | ||
1404 | .has_channel_1 = true, | ||
1405 | .set_mux = sun8i_r40_tcon_tv_set_mux, | ||
1406 | }; | ||
1407 | |||
1294 | static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { | 1408 | static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { |
1295 | .has_channel_0 = true, | 1409 | .has_channel_0 = true, |
1296 | }; | 1410 | }; |
@@ -1315,6 +1429,7 @@ const struct of_device_id sun4i_tcon_of_table[] = { | |||
1315 | { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, | 1429 | { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, |
1316 | { .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks }, | 1430 | { .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks }, |
1317 | { .compatible = "allwinner,sun8i-a83t-tcon-tv", .data = &sun8i_a83t_tv_quirks }, | 1431 | { .compatible = "allwinner,sun8i-a83t-tcon-tv", .data = &sun8i_a83t_tv_quirks }, |
1432 | { .compatible = "allwinner,sun8i-r40-tcon-tv", .data = &sun8i_r40_tv_quirks }, | ||
1318 | { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, | 1433 | { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, |
1319 | { .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = &sun9i_a80_tcon_lcd_quirks }, | 1434 | { .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = &sun9i_a80_tcon_lcd_quirks }, |
1320 | { .compatible = "allwinner,sun9i-a80-tcon-tv", .data = &sun9i_a80_tcon_tv_quirks }, | 1435 | { .compatible = "allwinner,sun9i-a80-tcon-tv", .data = &sun9i_a80_tcon_tv_quirks }, |
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c index 31875b636434..ed2983770e9c 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | |||
@@ -125,10 +125,22 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, | |||
125 | return PTR_ERR(hdmi->clk_tmds); | 125 | return PTR_ERR(hdmi->clk_tmds); |
126 | } | 126 | } |
127 | 127 | ||
128 | hdmi->regulator = devm_regulator_get(dev, "hvcc"); | ||
129 | if (IS_ERR(hdmi->regulator)) { | ||
130 | dev_err(dev, "Couldn't get regulator\n"); | ||
131 | return PTR_ERR(hdmi->regulator); | ||
132 | } | ||
133 | |||
134 | ret = regulator_enable(hdmi->regulator); | ||
135 | if (ret) { | ||
136 | dev_err(dev, "Failed to enable regulator\n"); | ||
137 | return ret; | ||
138 | } | ||
139 | |||
128 | ret = reset_control_deassert(hdmi->rst_ctrl); | 140 | ret = reset_control_deassert(hdmi->rst_ctrl); |
129 | if (ret) { | 141 | if (ret) { |
130 | dev_err(dev, "Could not deassert ctrl reset control\n"); | 142 | dev_err(dev, "Could not deassert ctrl reset control\n"); |
131 | return ret; | 143 | goto err_disable_regulator; |
132 | } | 144 | } |
133 | 145 | ||
134 | ret = clk_prepare_enable(hdmi->clk_tmds); | 146 | ret = clk_prepare_enable(hdmi->clk_tmds); |
@@ -183,6 +195,8 @@ err_disable_clk_tmds: | |||
183 | clk_disable_unprepare(hdmi->clk_tmds); | 195 | clk_disable_unprepare(hdmi->clk_tmds); |
184 | err_assert_ctrl_reset: | 196 | err_assert_ctrl_reset: |
185 | reset_control_assert(hdmi->rst_ctrl); | 197 | reset_control_assert(hdmi->rst_ctrl); |
198 | err_disable_regulator: | ||
199 | regulator_disable(hdmi->regulator); | ||
186 | 200 | ||
187 | return ret; | 201 | return ret; |
188 | } | 202 | } |
@@ -196,6 +210,7 @@ static void sun8i_dw_hdmi_unbind(struct device *dev, struct device *master, | |||
196 | sun8i_hdmi_phy_remove(hdmi); | 210 | sun8i_hdmi_phy_remove(hdmi); |
197 | clk_disable_unprepare(hdmi->clk_tmds); | 211 | clk_disable_unprepare(hdmi->clk_tmds); |
198 | reset_control_assert(hdmi->rst_ctrl); | 212 | reset_control_assert(hdmi->rst_ctrl); |
213 | regulator_disable(hdmi->regulator); | ||
199 | } | 214 | } |
200 | 215 | ||
201 | static const struct component_ops sun8i_dw_hdmi_ops = { | 216 | static const struct component_ops sun8i_dw_hdmi_ops = { |
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index aadbe0a10b0c..7fdc1ecd2892 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <drm/drm_encoder.h> | 10 | #include <drm/drm_encoder.h> |
11 | #include <linux/clk.h> | 11 | #include <linux/clk.h> |
12 | #include <linux/regmap.h> | 12 | #include <linux/regmap.h> |
13 | #include <linux/regulator/consumer.h> | ||
13 | #include <linux/reset.h> | 14 | #include <linux/reset.h> |
14 | 15 | ||
15 | #define SUN8I_HDMI_PHY_DBG_CTRL_REG 0x0000 | 16 | #define SUN8I_HDMI_PHY_DBG_CTRL_REG 0x0000 |
@@ -176,6 +177,7 @@ struct sun8i_dw_hdmi { | |||
176 | struct drm_encoder encoder; | 177 | struct drm_encoder encoder; |
177 | struct sun8i_hdmi_phy *phy; | 178 | struct sun8i_hdmi_phy *phy; |
178 | struct dw_hdmi_plat_data plat_data; | 179 | struct dw_hdmi_plat_data plat_data; |
180 | struct regulator *regulator; | ||
179 | struct reset_control *rst_ctrl; | 181 | struct reset_control *rst_ctrl; |
180 | }; | 182 | }; |
181 | 183 | ||
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index fc3713608f78..8b3d02b146b7 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c | |||
@@ -569,6 +569,22 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { | |||
569 | .mod_rate = 150000000, | 569 | .mod_rate = 150000000, |
570 | }; | 570 | }; |
571 | 571 | ||
572 | static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { | ||
573 | .ccsc = 0, | ||
574 | .mod_rate = 297000000, | ||
575 | .scaler_mask = 0xf, | ||
576 | .ui_num = 3, | ||
577 | .vi_num = 1, | ||
578 | }; | ||
579 | |||
580 | static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { | ||
581 | .ccsc = 1, | ||
582 | .mod_rate = 297000000, | ||
583 | .scaler_mask = 0x3, | ||
584 | .ui_num = 1, | ||
585 | .vi_num = 1, | ||
586 | }; | ||
587 | |||
572 | static const struct of_device_id sun8i_mixer_of_table[] = { | 588 | static const struct of_device_id sun8i_mixer_of_table[] = { |
573 | { | 589 | { |
574 | .compatible = "allwinner,sun8i-a83t-de2-mixer-0", | 590 | .compatible = "allwinner,sun8i-a83t-de2-mixer-0", |
@@ -594,6 +610,14 @@ static const struct of_device_id sun8i_mixer_of_table[] = { | |||
594 | .compatible = "allwinner,sun8i-v3s-de2-mixer", | 610 | .compatible = "allwinner,sun8i-v3s-de2-mixer", |
595 | .data = &sun8i_v3s_mixer_cfg, | 611 | .data = &sun8i_v3s_mixer_cfg, |
596 | }, | 612 | }, |
613 | { | ||
614 | .compatible = "allwinner,sun50i-a64-de2-mixer-0", | ||
615 | .data = &sun50i_a64_mixer0_cfg, | ||
616 | }, | ||
617 | { | ||
618 | .compatible = "allwinner,sun50i-a64-de2-mixer-1", | ||
619 | .data = &sun50i_a64_mixer1_cfg, | ||
620 | }, | ||
597 | { } | 621 | { } |
598 | }; | 622 | }; |
599 | MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); | 623 | MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); |
diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c index 55fe398d8290..3040a79f298f 100644 --- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c +++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c | |||
@@ -129,8 +129,7 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, | |||
129 | if (!tcon_top) | 129 | if (!tcon_top) |
130 | return -ENOMEM; | 130 | return -ENOMEM; |
131 | 131 | ||
132 | clk_data = devm_kzalloc(dev, sizeof(*clk_data) + | 132 | clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, CLK_NUM), |
133 | sizeof(*clk_data->hws) * CLK_NUM, | ||
134 | GFP_KERNEL); | 133 | GFP_KERNEL); |
135 | if (!clk_data) | 134 | if (!clk_data) |
136 | return -ENOMEM; | 135 | return -ENOMEM; |
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index a2bd5876c633..b424bc911b95 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -1187,6 +1187,10 @@ static int host1x_drm_probe(struct host1x_device *dev) | |||
1187 | 1187 | ||
1188 | dev_set_drvdata(&dev->dev, drm); | 1188 | dev_set_drvdata(&dev->dev, drm); |
1189 | 1189 | ||
1190 | err = drm_fb_helper_remove_conflicting_framebuffers(NULL, "tegradrmfb", false); | ||
1191 | if (err < 0) | ||
1192 | goto unref; | ||
1193 | |||
1190 | err = drm_dev_register(drm, 0); | 1194 | err = drm_dev_register(drm, 0); |
1191 | if (err < 0) | 1195 | if (err < 0) |
1192 | goto unref; | 1196 | goto unref; |
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c index 19c7f70adfa5..255341ee4eb9 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c | |||
@@ -135,7 +135,7 @@ static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev, | |||
135 | /* | 135 | /* |
136 | * We don't embed drm_device, because that prevent us from using | 136 | * We don't embed drm_device, because that prevent us from using |
137 | * devm_kzalloc() to allocate tinydrm_device in the driver since | 137 | * devm_kzalloc() to allocate tinydrm_device in the driver since |
138 | * drm_dev_unref() frees the structure. The devm_ functions provide | 138 | * drm_dev_put() frees the structure. The devm_ functions provide |
139 | * for easy error handling. | 139 | * for easy error handling. |
140 | */ | 140 | */ |
141 | drm = drm_dev_alloc(driver, parent); | 141 | drm = drm_dev_alloc(driver, parent); |
@@ -155,7 +155,7 @@ static void tinydrm_fini(struct tinydrm_device *tdev) | |||
155 | drm_mode_config_cleanup(tdev->drm); | 155 | drm_mode_config_cleanup(tdev->drm); |
156 | mutex_destroy(&tdev->dirty_lock); | 156 | mutex_destroy(&tdev->dirty_lock); |
157 | tdev->drm->dev_private = NULL; | 157 | tdev->drm->dev_private = NULL; |
158 | drm_dev_unref(tdev->drm); | 158 | drm_dev_put(tdev->drm); |
159 | } | 159 | } |
160 | 160 | ||
161 | static void devm_tinydrm_release(void *data) | 161 | static void devm_tinydrm_release(void *data) |
@@ -172,7 +172,7 @@ static void devm_tinydrm_release(void *data) | |||
172 | * | 172 | * |
173 | * This function initializes @tdev, the underlying DRM device and it's | 173 | * This function initializes @tdev, the underlying DRM device and it's |
174 | * mode_config. Resources will be automatically freed on driver detach (devres) | 174 | * mode_config. Resources will be automatically freed on driver detach (devres) |
175 | * using drm_mode_config_cleanup() and drm_dev_unref(). | 175 | * using drm_mode_config_cleanup() and drm_dev_put(). |
176 | * | 176 | * |
177 | * Returns: | 177 | * Returns: |
178 | * Zero on success, negative error code on failure. | 178 | * Zero on success, negative error code on failure. |
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 04270a14fcaa..e2a15c63a81f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c | |||
@@ -248,24 +248,6 @@ static void vc4_match_add_drivers(struct device *dev, | |||
248 | } | 248 | } |
249 | } | 249 | } |
250 | 250 | ||
251 | static void vc4_kick_out_firmware_fb(void) | ||
252 | { | ||
253 | struct apertures_struct *ap; | ||
254 | |||
255 | ap = alloc_apertures(1); | ||
256 | if (!ap) | ||
257 | return; | ||
258 | |||
259 | /* Since VC4 is a UMA device, the simplefb node may have been | ||
260 | * located anywhere in memory. | ||
261 | */ | ||
262 | ap->ranges[0].base = 0; | ||
263 | ap->ranges[0].size = ~0; | ||
264 | |||
265 | drm_fb_helper_remove_conflicting_framebuffers(ap, "vc4drmfb", false); | ||
266 | kfree(ap); | ||
267 | } | ||
268 | |||
269 | static int vc4_drm_bind(struct device *dev) | 251 | static int vc4_drm_bind(struct device *dev) |
270 | { | 252 | { |
271 | struct platform_device *pdev = to_platform_device(dev); | 253 | struct platform_device *pdev = to_platform_device(dev); |
@@ -298,7 +280,7 @@ static int vc4_drm_bind(struct device *dev) | |||
298 | if (ret) | 280 | if (ret) |
299 | goto gem_destroy; | 281 | goto gem_destroy; |
300 | 282 | ||
301 | vc4_kick_out_firmware_fb(); | 283 | drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false); |
302 | 284 | ||
303 | ret = drm_dev_register(drm, 0); | 285 | ret = drm_dev_register(drm, 0); |
304 | if (ret < 0) | 286 | if (ret < 0) |
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index cfb50fedfa2b..cf78f74bb87f 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c | |||
@@ -200,9 +200,7 @@ static void vc4_plane_reset(struct drm_plane *plane) | |||
200 | if (!vc4_state) | 200 | if (!vc4_state) |
201 | return; | 201 | return; |
202 | 202 | ||
203 | plane->state = &vc4_state->base; | 203 | __drm_atomic_helper_plane_reset(plane, &vc4_state->base); |
204 | plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE; | ||
205 | vc4_state->base.plane = plane; | ||
206 | } | 204 | } |
207 | 205 | ||
208 | static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) | 206 | static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) |
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index 0e5620f76ee0..ec6af8b920da 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c | |||
@@ -504,7 +504,7 @@ out_free: | |||
504 | static void __exit vgem_exit(void) | 504 | static void __exit vgem_exit(void) |
505 | { | 505 | { |
506 | drm_dev_unregister(&vgem_device->drm); | 506 | drm_dev_unregister(&vgem_device->drm); |
507 | drm_dev_unref(&vgem_device->drm); | 507 | drm_dev_put(&vgem_device->drm); |
508 | } | 508 | } |
509 | 509 | ||
510 | module_init(vgem_init); | 510 | module_init(vgem_init); |
diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c index b28876c222b4..e6ee71323a66 100644 --- a/drivers/gpu/drm/vgem/vgem_fence.c +++ b/drivers/gpu/drm/vgem/vgem_fence.c | |||
@@ -43,16 +43,6 @@ static const char *vgem_fence_get_timeline_name(struct dma_fence *fence) | |||
43 | return "unbound"; | 43 | return "unbound"; |
44 | } | 44 | } |
45 | 45 | ||
46 | static bool vgem_fence_signaled(struct dma_fence *fence) | ||
47 | { | ||
48 | return false; | ||
49 | } | ||
50 | |||
51 | static bool vgem_fence_enable_signaling(struct dma_fence *fence) | ||
52 | { | ||
53 | return true; | ||
54 | } | ||
55 | |||
56 | static void vgem_fence_release(struct dma_fence *base) | 46 | static void vgem_fence_release(struct dma_fence *base) |
57 | { | 47 | { |
58 | struct vgem_fence *fence = container_of(base, typeof(*fence), base); | 48 | struct vgem_fence *fence = container_of(base, typeof(*fence), base); |
@@ -76,9 +66,6 @@ static void vgem_fence_timeline_value_str(struct dma_fence *fence, char *str, | |||
76 | static const struct dma_fence_ops vgem_fence_ops = { | 66 | static const struct dma_fence_ops vgem_fence_ops = { |
77 | .get_driver_name = vgem_fence_get_driver_name, | 67 | .get_driver_name = vgem_fence_get_driver_name, |
78 | .get_timeline_name = vgem_fence_get_timeline_name, | 68 | .get_timeline_name = vgem_fence_get_timeline_name, |
79 | .enable_signaling = vgem_fence_enable_signaling, | ||
80 | .signaled = vgem_fence_signaled, | ||
81 | .wait = dma_fence_default_wait, | ||
82 | .release = vgem_fence_release, | 69 | .release = vgem_fence_release, |
83 | 70 | ||
84 | .fence_value_str = vgem_fence_value_str, | 71 | .fence_value_str = vgem_fence_value_str, |
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 25503b933599..9f1e0a669d4c 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c | |||
@@ -109,6 +109,9 @@ static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc *crtc) | |||
109 | static void virtio_gpu_crtc_atomic_enable(struct drm_crtc *crtc, | 109 | static void virtio_gpu_crtc_atomic_enable(struct drm_crtc *crtc, |
110 | struct drm_crtc_state *old_state) | 110 | struct drm_crtc_state *old_state) |
111 | { | 111 | { |
112 | struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); | ||
113 | |||
114 | output->enabled = true; | ||
112 | } | 115 | } |
113 | 116 | ||
114 | static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc, | 117 | static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc, |
@@ -119,6 +122,7 @@ static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc, | |||
119 | struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); | 122 | struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); |
120 | 123 | ||
121 | virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0); | 124 | virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0); |
125 | output->enabled = false; | ||
122 | } | 126 | } |
123 | 127 | ||
124 | static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc, | 128 | static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc, |
diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c index 7df8d0c9026a..757ca28ab93e 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c | |||
@@ -28,26 +28,6 @@ | |||
28 | 28 | ||
29 | #include "virtgpu_drv.h" | 29 | #include "virtgpu_drv.h" |
30 | 30 | ||
31 | static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev) | ||
32 | { | ||
33 | struct apertures_struct *ap; | ||
34 | bool primary; | ||
35 | |||
36 | ap = alloc_apertures(1); | ||
37 | if (!ap) | ||
38 | return; | ||
39 | |||
40 | ap->ranges[0].base = pci_resource_start(pci_dev, 0); | ||
41 | ap->ranges[0].size = pci_resource_len(pci_dev, 0); | ||
42 | |||
43 | primary = pci_dev->resource[PCI_ROM_RESOURCE].flags | ||
44 | & IORESOURCE_ROM_SHADOW; | ||
45 | |||
46 | drm_fb_helper_remove_conflicting_framebuffers(ap, "virtiodrmfb", primary); | ||
47 | |||
48 | kfree(ap); | ||
49 | } | ||
50 | |||
51 | int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) | 31 | int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) |
52 | { | 32 | { |
53 | struct drm_device *dev; | 33 | struct drm_device *dev; |
@@ -69,7 +49,9 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) | |||
69 | pname); | 49 | pname); |
70 | dev->pdev = pdev; | 50 | dev->pdev = pdev; |
71 | if (vga) | 51 | if (vga) |
72 | virtio_pci_kick_out_firmware_fb(pdev); | 52 | drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, |
53 | 0, | ||
54 | "virtiodrmfb"); | ||
73 | 55 | ||
74 | snprintf(unique, sizeof(unique), "pci:%s", pname); | 56 | snprintf(unique, sizeof(unique), "pci:%s", pname); |
75 | ret = drm_dev_set_unique(dev, unique); | 57 | ret = drm_dev_set_unique(dev, unique); |
@@ -85,6 +67,6 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) | |||
85 | return 0; | 67 | return 0; |
86 | 68 | ||
87 | err_free: | 69 | err_free: |
88 | drm_dev_unref(dev); | 70 | drm_dev_put(dev); |
89 | return ret; | 71 | return ret; |
90 | } | 72 | } |
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 65605e207bbe..f8f4a40dd1b8 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h | |||
@@ -57,6 +57,7 @@ struct virtio_gpu_object { | |||
57 | uint32_t hw_res_handle; | 57 | uint32_t hw_res_handle; |
58 | 58 | ||
59 | struct sg_table *pages; | 59 | struct sg_table *pages; |
60 | uint32_t mapped; | ||
60 | void *vmap; | 61 | void *vmap; |
61 | bool dumb; | 62 | bool dumb; |
62 | struct ttm_place placement_code; | 63 | struct ttm_place placement_code; |
@@ -114,6 +115,7 @@ struct virtio_gpu_output { | |||
114 | struct virtio_gpu_update_cursor cursor; | 115 | struct virtio_gpu_update_cursor cursor; |
115 | int cur_x; | 116 | int cur_x; |
116 | int cur_y; | 117 | int cur_y; |
118 | bool enabled; | ||
117 | }; | 119 | }; |
118 | #define drm_crtc_to_virtio_gpu_output(x) \ | 120 | #define drm_crtc_to_virtio_gpu_output(x) \ |
119 | container_of(x, struct virtio_gpu_output, crtc) | 121 | container_of(x, struct virtio_gpu_output, crtc) |
@@ -276,13 +278,13 @@ int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, | |||
276 | struct virtio_gpu_object *obj, | 278 | struct virtio_gpu_object *obj, |
277 | uint32_t resource_id, | 279 | uint32_t resource_id, |
278 | struct virtio_gpu_fence **fence); | 280 | struct virtio_gpu_fence **fence); |
281 | void virtio_gpu_object_detach(struct virtio_gpu_device *vgdev, | ||
282 | struct virtio_gpu_object *obj); | ||
279 | int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev); | 283 | int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev); |
280 | int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev); | 284 | int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev); |
281 | void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, | 285 | void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, |
282 | struct virtio_gpu_output *output); | 286 | struct virtio_gpu_output *output); |
283 | int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev); | 287 | int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev); |
284 | void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, | ||
285 | uint32_t resource_id); | ||
286 | int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx); | 288 | int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx); |
287 | int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev, | 289 | int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev, |
288 | int idx, int version, | 290 | int idx, int version, |
@@ -372,7 +374,7 @@ int virtgpu_gem_prime_mmap(struct drm_gem_object *obj, | |||
372 | static inline struct virtio_gpu_object* | 374 | static inline struct virtio_gpu_object* |
373 | virtio_gpu_object_ref(struct virtio_gpu_object *bo) | 375 | virtio_gpu_object_ref(struct virtio_gpu_object *bo) |
374 | { | 376 | { |
375 | ttm_bo_reference(&bo->tbo); | 377 | ttm_bo_get(&bo->tbo); |
376 | return bo; | 378 | return bo; |
377 | } | 379 | } |
378 | 380 | ||
@@ -383,9 +385,8 @@ static inline void virtio_gpu_object_unref(struct virtio_gpu_object **bo) | |||
383 | if ((*bo) == NULL) | 385 | if ((*bo) == NULL) |
384 | return; | 386 | return; |
385 | tbo = &((*bo)->tbo); | 387 | tbo = &((*bo)->tbo); |
386 | ttm_bo_unref(&tbo); | 388 | ttm_bo_put(tbo); |
387 | if (tbo == NULL) | 389 | *bo = NULL; |
388 | *bo = NULL; | ||
389 | } | 390 | } |
390 | 391 | ||
391 | static inline u64 virtio_gpu_object_mmap_offset(struct virtio_gpu_object *bo) | 392 | static inline u64 virtio_gpu_object_mmap_offset(struct virtio_gpu_object *bo) |
diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c index a121b1c79522..b5cebc9a179a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fb.c +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c | |||
@@ -291,7 +291,7 @@ static int virtio_gpufb_create(struct drm_fb_helper *helper, | |||
291 | return 0; | 291 | return 0; |
292 | 292 | ||
293 | err_fb_alloc: | 293 | err_fb_alloc: |
294 | virtio_gpu_cmd_resource_inval_backing(vgdev, resid); | 294 | virtio_gpu_object_detach(vgdev, obj); |
295 | err_obj_attach: | 295 | err_obj_attach: |
296 | err_obj_vmap: | 296 | err_obj_vmap: |
297 | virtio_gpu_gem_free_object(&obj->gem_base); | 297 | virtio_gpu_gem_free_object(&obj->gem_base); |
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index dc5b5b2b7aab..88f2fb8c61c4 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c | |||
@@ -152,7 +152,7 @@ static void virtio_gpu_primary_plane_update(struct drm_plane *plane, | |||
152 | if (WARN_ON(!output)) | 152 | if (WARN_ON(!output)) |
153 | return; | 153 | return; |
154 | 154 | ||
155 | if (plane->state->fb) { | 155 | if (plane->state->fb && output->enabled) { |
156 | vgfb = to_virtio_gpu_framebuffer(plane->state->fb); | 156 | vgfb = to_virtio_gpu_framebuffer(plane->state->fb); |
157 | bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); | 157 | bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); |
158 | handle = bo->hw_res_handle; | 158 | handle = bo->hw_res_handle; |
diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c index 11f8ae5b5332..e3152d45c5f1 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ttm.c +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c | |||
@@ -106,29 +106,6 @@ static void virtio_gpu_ttm_global_fini(struct virtio_gpu_device *vgdev) | |||
106 | } | 106 | } |
107 | } | 107 | } |
108 | 108 | ||
109 | #if 0 | ||
110 | /* | ||
111 | * Hmm, seems to not do anything useful. Leftover debug hack? | ||
112 | * Something like printing pagefaults to kernel log? | ||
113 | */ | ||
114 | static struct vm_operations_struct virtio_gpu_ttm_vm_ops; | ||
115 | static const struct vm_operations_struct *ttm_vm_ops; | ||
116 | |||
117 | static int virtio_gpu_ttm_fault(struct vm_fault *vmf) | ||
118 | { | ||
119 | struct ttm_buffer_object *bo; | ||
120 | struct virtio_gpu_device *vgdev; | ||
121 | int r; | ||
122 | |||
123 | bo = (struct ttm_buffer_object *)vmf->vma->vm_private_data; | ||
124 | if (bo == NULL) | ||
125 | return VM_FAULT_NOPAGE; | ||
126 | vgdev = virtio_gpu_get_vgdev(bo->bdev); | ||
127 | r = ttm_vm_ops->fault(vmf); | ||
128 | return r; | ||
129 | } | ||
130 | #endif | ||
131 | |||
132 | int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma) | 109 | int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma) |
133 | { | 110 | { |
134 | struct drm_file *file_priv; | 111 | struct drm_file *file_priv; |
@@ -143,19 +120,8 @@ int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma) | |||
143 | return -EINVAL; | 120 | return -EINVAL; |
144 | } | 121 | } |
145 | r = ttm_bo_mmap(filp, vma, &vgdev->mman.bdev); | 122 | r = ttm_bo_mmap(filp, vma, &vgdev->mman.bdev); |
146 | #if 0 | 123 | |
147 | if (unlikely(r != 0)) | ||
148 | return r; | ||
149 | if (unlikely(ttm_vm_ops == NULL)) { | ||
150 | ttm_vm_ops = vma->vm_ops; | ||
151 | virtio_gpu_ttm_vm_ops = *ttm_vm_ops; | ||
152 | virtio_gpu_ttm_vm_ops.fault = &virtio_gpu_ttm_fault; | ||
153 | } | ||
154 | vma->vm_ops = &virtio_gpu_ttm_vm_ops; | ||
155 | return 0; | ||
156 | #else | ||
157 | return r; | 124 | return r; |
158 | #endif | ||
159 | } | 125 | } |
160 | 126 | ||
161 | static int virtio_gpu_invalidate_caches(struct ttm_bo_device *bdev, | 127 | static int virtio_gpu_invalidate_caches(struct ttm_bo_device *bdev, |
@@ -377,8 +343,7 @@ static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo, | |||
377 | 343 | ||
378 | if (!new_mem || (new_mem->placement & TTM_PL_FLAG_SYSTEM)) { | 344 | if (!new_mem || (new_mem->placement & TTM_PL_FLAG_SYSTEM)) { |
379 | if (bo->hw_res_handle) | 345 | if (bo->hw_res_handle) |
380 | virtio_gpu_cmd_resource_inval_backing(vgdev, | 346 | virtio_gpu_object_detach(vgdev, bo); |
381 | bo->hw_res_handle); | ||
382 | 347 | ||
383 | } else if (new_mem->placement & TTM_PL_FLAG_TT) { | 348 | } else if (new_mem->placement & TTM_PL_FLAG_TT) { |
384 | if (bo->hw_res_handle) { | 349 | if (bo->hw_res_handle) { |
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 020070d483d3..5784c3ea8767 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c | |||
@@ -423,8 +423,9 @@ void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, | |||
423 | virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); | 423 | virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); |
424 | } | 424 | } |
425 | 425 | ||
426 | void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, | 426 | static void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, |
427 | uint32_t resource_id) | 427 | uint32_t resource_id, |
428 | struct virtio_gpu_fence **fence) | ||
428 | { | 429 | { |
429 | struct virtio_gpu_resource_detach_backing *cmd_p; | 430 | struct virtio_gpu_resource_detach_backing *cmd_p; |
430 | struct virtio_gpu_vbuffer *vbuf; | 431 | struct virtio_gpu_vbuffer *vbuf; |
@@ -435,7 +436,7 @@ void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, | |||
435 | cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING); | 436 | cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING); |
436 | cmd_p->resource_id = cpu_to_le32(resource_id); | 437 | cmd_p->resource_id = cpu_to_le32(resource_id); |
437 | 438 | ||
438 | virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); | 439 | virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); |
439 | } | 440 | } |
440 | 441 | ||
441 | void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, | 442 | void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, |
@@ -648,11 +649,11 @@ int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev, | |||
648 | { | 649 | { |
649 | struct virtio_gpu_get_capset *cmd_p; | 650 | struct virtio_gpu_get_capset *cmd_p; |
650 | struct virtio_gpu_vbuffer *vbuf; | 651 | struct virtio_gpu_vbuffer *vbuf; |
651 | int max_size = vgdev->capsets[idx].max_size; | 652 | int max_size; |
652 | struct virtio_gpu_drv_cap_cache *cache_ent; | 653 | struct virtio_gpu_drv_cap_cache *cache_ent; |
653 | void *resp_buf; | 654 | void *resp_buf; |
654 | 655 | ||
655 | if (idx > vgdev->num_capsets) | 656 | if (idx >= vgdev->num_capsets) |
656 | return -EINVAL; | 657 | return -EINVAL; |
657 | 658 | ||
658 | if (version > vgdev->capsets[idx].max_version) | 659 | if (version > vgdev->capsets[idx].max_version) |
@@ -662,6 +663,7 @@ int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev, | |||
662 | if (!cache_ent) | 663 | if (!cache_ent) |
663 | return -ENOMEM; | 664 | return -ENOMEM; |
664 | 665 | ||
666 | max_size = vgdev->capsets[idx].max_size; | ||
665 | cache_ent->caps_cache = kmalloc(max_size, GFP_KERNEL); | 667 | cache_ent->caps_cache = kmalloc(max_size, GFP_KERNEL); |
666 | if (!cache_ent->caps_cache) { | 668 | if (!cache_ent->caps_cache) { |
667 | kfree(cache_ent); | 669 | kfree(cache_ent); |
@@ -848,9 +850,10 @@ int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, | |||
848 | uint32_t resource_id, | 850 | uint32_t resource_id, |
849 | struct virtio_gpu_fence **fence) | 851 | struct virtio_gpu_fence **fence) |
850 | { | 852 | { |
853 | bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev); | ||
851 | struct virtio_gpu_mem_entry *ents; | 854 | struct virtio_gpu_mem_entry *ents; |
852 | struct scatterlist *sg; | 855 | struct scatterlist *sg; |
853 | int si; | 856 | int si, nents; |
854 | 857 | ||
855 | if (!obj->pages) { | 858 | if (!obj->pages) { |
856 | int ret; | 859 | int ret; |
@@ -860,28 +863,60 @@ int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, | |||
860 | return ret; | 863 | return ret; |
861 | } | 864 | } |
862 | 865 | ||
866 | if (use_dma_api) { | ||
867 | obj->mapped = dma_map_sg(vgdev->vdev->dev.parent, | ||
868 | obj->pages->sgl, obj->pages->nents, | ||
869 | DMA_TO_DEVICE); | ||
870 | nents = obj->mapped; | ||
871 | } else { | ||
872 | nents = obj->pages->nents; | ||
873 | } | ||
874 | |||
863 | /* gets freed when the ring has consumed it */ | 875 | /* gets freed when the ring has consumed it */ |
864 | ents = kmalloc_array(obj->pages->nents, | 876 | ents = kmalloc_array(nents, sizeof(struct virtio_gpu_mem_entry), |
865 | sizeof(struct virtio_gpu_mem_entry), | ||
866 | GFP_KERNEL); | 877 | GFP_KERNEL); |
867 | if (!ents) { | 878 | if (!ents) { |
868 | DRM_ERROR("failed to allocate ent list\n"); | 879 | DRM_ERROR("failed to allocate ent list\n"); |
869 | return -ENOMEM; | 880 | return -ENOMEM; |
870 | } | 881 | } |
871 | 882 | ||
872 | for_each_sg(obj->pages->sgl, sg, obj->pages->nents, si) { | 883 | for_each_sg(obj->pages->sgl, sg, nents, si) { |
873 | ents[si].addr = cpu_to_le64(sg_phys(sg)); | 884 | ents[si].addr = cpu_to_le64(use_dma_api |
885 | ? sg_dma_address(sg) | ||
886 | : sg_phys(sg)); | ||
874 | ents[si].length = cpu_to_le32(sg->length); | 887 | ents[si].length = cpu_to_le32(sg->length); |
875 | ents[si].padding = 0; | 888 | ents[si].padding = 0; |
876 | } | 889 | } |
877 | 890 | ||
878 | virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id, | 891 | virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id, |
879 | ents, obj->pages->nents, | 892 | ents, nents, |
880 | fence); | 893 | fence); |
881 | obj->hw_res_handle = resource_id; | 894 | obj->hw_res_handle = resource_id; |
882 | return 0; | 895 | return 0; |
883 | } | 896 | } |
884 | 897 | ||
898 | void virtio_gpu_object_detach(struct virtio_gpu_device *vgdev, | ||
899 | struct virtio_gpu_object *obj) | ||
900 | { | ||
901 | bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev); | ||
902 | struct virtio_gpu_fence *fence; | ||
903 | |||
904 | if (use_dma_api && obj->mapped) { | ||
905 | /* detach backing and wait for the host process it ... */ | ||
906 | virtio_gpu_cmd_resource_inval_backing(vgdev, obj->hw_res_handle, &fence); | ||
907 | dma_fence_wait(&fence->f, true); | ||
908 | dma_fence_put(&fence->f); | ||
909 | |||
910 | /* ... then tear down iommu mappings */ | ||
911 | dma_unmap_sg(vgdev->vdev->dev.parent, | ||
912 | obj->pages->sgl, obj->mapped, | ||
913 | DMA_TO_DEVICE); | ||
914 | obj->mapped = 0; | ||
915 | } else { | ||
916 | virtio_gpu_cmd_resource_inval_backing(vgdev, obj->hw_res_handle, NULL); | ||
917 | } | ||
918 | } | ||
919 | |||
885 | void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, | 920 | void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, |
886 | struct virtio_gpu_output *output) | 921 | struct virtio_gpu_output *output) |
887 | { | 922 | { |
diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index 986297da51bf..37966914f70b 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile | |||
@@ -1,3 +1,3 @@ | |||
1 | vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o vkms_gem.o | 1 | vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o vkms_gem.o vkms_crc.o |
2 | 2 | ||
3 | obj-$(CONFIG_DRM_VKMS) += vkms.o | 3 | obj-$(CONFIG_DRM_VKMS) += vkms.o |
diff --git a/drivers/gpu/drm/vkms/vkms_crc.c b/drivers/gpu/drm/vkms/vkms_crc.c new file mode 100644 index 000000000000..68db42f15086 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_crc.c | |||
@@ -0,0 +1,153 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include "vkms_drv.h" | ||
3 | #include <linux/crc32.h> | ||
4 | #include <drm/drm_gem_framebuffer_helper.h> | ||
5 | |||
6 | static uint32_t _vkms_get_crc(struct vkms_crc_data *crc_data) | ||
7 | { | ||
8 | struct drm_framebuffer *fb = &crc_data->fb; | ||
9 | struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0); | ||
10 | struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(gem_obj); | ||
11 | u32 crc = 0; | ||
12 | int i = 0; | ||
13 | unsigned int x = crc_data->src.x1 >> 16; | ||
14 | unsigned int y = crc_data->src.y1 >> 16; | ||
15 | unsigned int height = drm_rect_height(&crc_data->src) >> 16; | ||
16 | unsigned int width = drm_rect_width(&crc_data->src) >> 16; | ||
17 | unsigned int cpp = fb->format->cpp[0]; | ||
18 | unsigned int src_offset; | ||
19 | unsigned int size_byte = width * cpp; | ||
20 | void *vaddr; | ||
21 | |||
22 | mutex_lock(&vkms_obj->pages_lock); | ||
23 | vaddr = vkms_obj->vaddr; | ||
24 | if (WARN_ON(!vaddr)) | ||
25 | goto out; | ||
26 | |||
27 | for (i = y; i < y + height; i++) { | ||
28 | src_offset = fb->offsets[0] + (i * fb->pitches[0]) + (x * cpp); | ||
29 | crc = crc32_le(crc, vaddr + src_offset, size_byte); | ||
30 | } | ||
31 | |||
32 | out: | ||
33 | mutex_unlock(&vkms_obj->pages_lock); | ||
34 | return crc; | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * vkms_crc_work_handle - ordered work_struct to compute CRC | ||
39 | * | ||
40 | * @work: work_struct | ||
41 | * | ||
42 | * Work handler for computing CRCs. work_struct scheduled in | ||
43 | * an ordered workqueue that's periodically scheduled to run by | ||
44 | * _vblank_handle() and flushed at vkms_atomic_crtc_destroy_state(). | ||
45 | */ | ||
46 | void vkms_crc_work_handle(struct work_struct *work) | ||
47 | { | ||
48 | struct vkms_crtc_state *crtc_state = container_of(work, | ||
49 | struct vkms_crtc_state, | ||
50 | crc_work); | ||
51 | struct drm_crtc *crtc = crtc_state->base.crtc; | ||
52 | struct vkms_output *out = drm_crtc_to_vkms_output(crtc); | ||
53 | struct vkms_device *vdev = container_of(out, struct vkms_device, | ||
54 | output); | ||
55 | struct vkms_crc_data *primary_crc = NULL; | ||
56 | struct drm_plane *plane; | ||
57 | u32 crc32 = 0; | ||
58 | u64 frame_start, frame_end; | ||
59 | unsigned long flags; | ||
60 | |||
61 | spin_lock_irqsave(&out->state_lock, flags); | ||
62 | frame_start = crtc_state->frame_start; | ||
63 | frame_end = crtc_state->frame_end; | ||
64 | spin_unlock_irqrestore(&out->state_lock, flags); | ||
65 | |||
66 | /* _vblank_handle() hasn't updated frame_start yet */ | ||
67 | if (!frame_start || frame_start == frame_end) | ||
68 | goto out; | ||
69 | |||
70 | drm_for_each_plane(plane, &vdev->drm) { | ||
71 | struct vkms_plane_state *vplane_state; | ||
72 | struct vkms_crc_data *crc_data; | ||
73 | |||
74 | vplane_state = to_vkms_plane_state(plane->state); | ||
75 | crc_data = vplane_state->crc_data; | ||
76 | |||
77 | if (drm_framebuffer_read_refcount(&crc_data->fb) == 0) | ||
78 | continue; | ||
79 | |||
80 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) { | ||
81 | primary_crc = crc_data; | ||
82 | break; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | if (primary_crc) | ||
87 | crc32 = _vkms_get_crc(primary_crc); | ||
88 | |||
89 | frame_end = drm_crtc_accurate_vblank_count(crtc); | ||
90 | |||
91 | /* queue_work can fail to schedule crc_work; add crc for | ||
92 | * missing frames | ||
93 | */ | ||
94 | while (frame_start <= frame_end) | ||
95 | drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32); | ||
96 | |||
97 | out: | ||
98 | /* to avoid using the same value for frame number again */ | ||
99 | spin_lock_irqsave(&out->state_lock, flags); | ||
100 | crtc_state->frame_end = frame_end; | ||
101 | crtc_state->frame_start = 0; | ||
102 | spin_unlock_irqrestore(&out->state_lock, flags); | ||
103 | } | ||
104 | |||
105 | static int vkms_crc_parse_source(const char *src_name, bool *enabled) | ||
106 | { | ||
107 | int ret = 0; | ||
108 | |||
109 | if (!src_name) { | ||
110 | *enabled = false; | ||
111 | } else if (strcmp(src_name, "auto") == 0) { | ||
112 | *enabled = true; | ||
113 | } else { | ||
114 | *enabled = false; | ||
115 | ret = -EINVAL; | ||
116 | } | ||
117 | |||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name, | ||
122 | size_t *values_cnt) | ||
123 | { | ||
124 | bool enabled; | ||
125 | |||
126 | if (vkms_crc_parse_source(src_name, &enabled) < 0) { | ||
127 | DRM_DEBUG_DRIVER("unknown source %s\n", src_name); | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | *values_cnt = 1; | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name) | ||
137 | { | ||
138 | struct vkms_output *out = drm_crtc_to_vkms_output(crtc); | ||
139 | bool enabled = false; | ||
140 | unsigned long flags; | ||
141 | int ret = 0; | ||
142 | |||
143 | ret = vkms_crc_parse_source(src_name, &enabled); | ||
144 | |||
145 | /* make sure nothing is scheduled on crtc workq */ | ||
146 | flush_workqueue(out->crc_workq); | ||
147 | |||
148 | spin_lock_irqsave(&out->lock, flags); | ||
149 | out->crc_enabled = enabled; | ||
150 | spin_unlock_irqrestore(&out->lock, flags); | ||
151 | |||
152 | return ret; | ||
153 | } | ||
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index 875fca662ac0..177bbcb38306 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c | |||
@@ -10,18 +10,44 @@ | |||
10 | #include <drm/drm_atomic_helper.h> | 10 | #include <drm/drm_atomic_helper.h> |
11 | #include <drm/drm_crtc_helper.h> | 11 | #include <drm/drm_crtc_helper.h> |
12 | 12 | ||
13 | static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) | 13 | static void _vblank_handle(struct vkms_output *output) |
14 | { | 14 | { |
15 | struct vkms_output *output = container_of(timer, struct vkms_output, | ||
16 | vblank_hrtimer); | ||
17 | struct drm_crtc *crtc = &output->crtc; | 15 | struct drm_crtc *crtc = &output->crtc; |
18 | int ret_overrun; | 16 | struct vkms_crtc_state *state = to_vkms_crtc_state(crtc->state); |
19 | bool ret; | 17 | bool ret; |
20 | 18 | ||
19 | spin_lock(&output->lock); | ||
21 | ret = drm_crtc_handle_vblank(crtc); | 20 | ret = drm_crtc_handle_vblank(crtc); |
22 | if (!ret) | 21 | if (!ret) |
23 | DRM_ERROR("vkms failure on handling vblank"); | 22 | DRM_ERROR("vkms failure on handling vblank"); |
24 | 23 | ||
24 | if (state && output->crc_enabled) { | ||
25 | u64 frame = drm_crtc_accurate_vblank_count(crtc); | ||
26 | |||
27 | /* update frame_start only if a queued vkms_crc_work_handle() | ||
28 | * has read the data | ||
29 | */ | ||
30 | spin_lock(&output->state_lock); | ||
31 | if (!state->frame_start) | ||
32 | state->frame_start = frame; | ||
33 | spin_unlock(&output->state_lock); | ||
34 | |||
35 | ret = queue_work(output->crc_workq, &state->crc_work); | ||
36 | if (!ret) | ||
37 | DRM_WARN("failed to queue vkms_crc_work_handle"); | ||
38 | } | ||
39 | |||
40 | spin_unlock(&output->lock); | ||
41 | } | ||
42 | |||
43 | static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) | ||
44 | { | ||
45 | struct vkms_output *output = container_of(timer, struct vkms_output, | ||
46 | vblank_hrtimer); | ||
47 | int ret_overrun; | ||
48 | |||
49 | _vblank_handle(output); | ||
50 | |||
25 | ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer, | 51 | ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer, |
26 | output->period_ns); | 52 | output->period_ns); |
27 | 53 | ||
@@ -64,15 +90,68 @@ bool vkms_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe, | |||
64 | return true; | 90 | return true; |
65 | } | 91 | } |
66 | 92 | ||
93 | static void vkms_atomic_crtc_reset(struct drm_crtc *crtc) | ||
94 | { | ||
95 | struct vkms_crtc_state *vkms_state = NULL; | ||
96 | |||
97 | if (crtc->state) { | ||
98 | vkms_state = to_vkms_crtc_state(crtc->state); | ||
99 | __drm_atomic_helper_crtc_destroy_state(crtc->state); | ||
100 | kfree(vkms_state); | ||
101 | crtc->state = NULL; | ||
102 | } | ||
103 | |||
104 | vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); | ||
105 | if (!vkms_state) | ||
106 | return; | ||
107 | |||
108 | crtc->state = &vkms_state->base; | ||
109 | crtc->state->crtc = crtc; | ||
110 | } | ||
111 | |||
112 | static struct drm_crtc_state * | ||
113 | vkms_atomic_crtc_duplicate_state(struct drm_crtc *crtc) | ||
114 | { | ||
115 | struct vkms_crtc_state *vkms_state; | ||
116 | |||
117 | if (WARN_ON(!crtc->state)) | ||
118 | return NULL; | ||
119 | |||
120 | vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); | ||
121 | if (!vkms_state) | ||
122 | return NULL; | ||
123 | |||
124 | __drm_atomic_helper_crtc_duplicate_state(crtc, &vkms_state->base); | ||
125 | |||
126 | INIT_WORK(&vkms_state->crc_work, vkms_crc_work_handle); | ||
127 | |||
128 | return &vkms_state->base; | ||
129 | } | ||
130 | |||
131 | static void vkms_atomic_crtc_destroy_state(struct drm_crtc *crtc, | ||
132 | struct drm_crtc_state *state) | ||
133 | { | ||
134 | struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(state); | ||
135 | |||
136 | __drm_atomic_helper_crtc_destroy_state(state); | ||
137 | |||
138 | if (vkms_state) { | ||
139 | flush_work(&vkms_state->crc_work); | ||
140 | kfree(vkms_state); | ||
141 | } | ||
142 | } | ||
143 | |||
67 | static const struct drm_crtc_funcs vkms_crtc_funcs = { | 144 | static const struct drm_crtc_funcs vkms_crtc_funcs = { |
68 | .set_config = drm_atomic_helper_set_config, | 145 | .set_config = drm_atomic_helper_set_config, |
69 | .destroy = drm_crtc_cleanup, | 146 | .destroy = drm_crtc_cleanup, |
70 | .page_flip = drm_atomic_helper_page_flip, | 147 | .page_flip = drm_atomic_helper_page_flip, |
71 | .reset = drm_atomic_helper_crtc_reset, | 148 | .reset = vkms_atomic_crtc_reset, |
72 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | 149 | .atomic_duplicate_state = vkms_atomic_crtc_duplicate_state, |
73 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | 150 | .atomic_destroy_state = vkms_atomic_crtc_destroy_state, |
74 | .enable_vblank = vkms_enable_vblank, | 151 | .enable_vblank = vkms_enable_vblank, |
75 | .disable_vblank = vkms_disable_vblank, | 152 | .disable_vblank = vkms_disable_vblank, |
153 | .set_crc_source = vkms_set_crc_source, | ||
154 | .verify_crc_source = vkms_verify_crc_source, | ||
76 | }; | 155 | }; |
77 | 156 | ||
78 | static void vkms_crtc_atomic_enable(struct drm_crtc *crtc, | 157 | static void vkms_crtc_atomic_enable(struct drm_crtc *crtc, |
@@ -87,9 +166,21 @@ static void vkms_crtc_atomic_disable(struct drm_crtc *crtc, | |||
87 | drm_crtc_vblank_off(crtc); | 166 | drm_crtc_vblank_off(crtc); |
88 | } | 167 | } |
89 | 168 | ||
169 | static void vkms_crtc_atomic_begin(struct drm_crtc *crtc, | ||
170 | struct drm_crtc_state *old_crtc_state) | ||
171 | { | ||
172 | struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc); | ||
173 | |||
174 | /* This lock is held across the atomic commit to block vblank timer | ||
175 | * from scheduling vkms_crc_work_handle until the crc_data is updated | ||
176 | */ | ||
177 | spin_lock_irq(&vkms_output->lock); | ||
178 | } | ||
179 | |||
90 | static void vkms_crtc_atomic_flush(struct drm_crtc *crtc, | 180 | static void vkms_crtc_atomic_flush(struct drm_crtc *crtc, |
91 | struct drm_crtc_state *old_crtc_state) | 181 | struct drm_crtc_state *old_crtc_state) |
92 | { | 182 | { |
183 | struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc); | ||
93 | unsigned long flags; | 184 | unsigned long flags; |
94 | 185 | ||
95 | if (crtc->state->event) { | 186 | if (crtc->state->event) { |
@@ -104,9 +195,12 @@ static void vkms_crtc_atomic_flush(struct drm_crtc *crtc, | |||
104 | 195 | ||
105 | crtc->state->event = NULL; | 196 | crtc->state->event = NULL; |
106 | } | 197 | } |
198 | |||
199 | spin_unlock_irq(&vkms_output->lock); | ||
107 | } | 200 | } |
108 | 201 | ||
109 | static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { | 202 | static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { |
203 | .atomic_begin = vkms_crtc_atomic_begin, | ||
110 | .atomic_flush = vkms_crtc_atomic_flush, | 204 | .atomic_flush = vkms_crtc_atomic_flush, |
111 | .atomic_enable = vkms_crtc_atomic_enable, | 205 | .atomic_enable = vkms_crtc_atomic_enable, |
112 | .atomic_disable = vkms_crtc_atomic_disable, | 206 | .atomic_disable = vkms_crtc_atomic_disable, |
@@ -115,6 +209,7 @@ static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { | |||
115 | int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, | 209 | int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, |
116 | struct drm_plane *primary, struct drm_plane *cursor) | 210 | struct drm_plane *primary, struct drm_plane *cursor) |
117 | { | 211 | { |
212 | struct vkms_output *vkms_out = drm_crtc_to_vkms_output(crtc); | ||
118 | int ret; | 213 | int ret; |
119 | 214 | ||
120 | ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor, | 215 | ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor, |
@@ -126,5 +221,10 @@ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, | |||
126 | 221 | ||
127 | drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs); | 222 | drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs); |
128 | 223 | ||
224 | spin_lock_init(&vkms_out->lock); | ||
225 | spin_lock_init(&vkms_out->state_lock); | ||
226 | |||
227 | vkms_out->crc_workq = alloc_ordered_workqueue("vkms_crc_workq", 0); | ||
228 | |||
129 | return ret; | 229 | return ret; |
130 | } | 230 | } |
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 6e728b825259..bd9d4b2389bd 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c | |||
@@ -47,6 +47,7 @@ static void vkms_release(struct drm_device *dev) | |||
47 | drm_atomic_helper_shutdown(&vkms->drm); | 47 | drm_atomic_helper_shutdown(&vkms->drm); |
48 | drm_mode_config_cleanup(&vkms->drm); | 48 | drm_mode_config_cleanup(&vkms->drm); |
49 | drm_dev_fini(&vkms->drm); | 49 | drm_dev_fini(&vkms->drm); |
50 | destroy_workqueue(vkms->output.crc_workq); | ||
50 | } | 51 | } |
51 | 52 | ||
52 | static struct drm_driver vkms_driver = { | 53 | static struct drm_driver vkms_driver = { |
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 07be29f2dc44..80af6d3a65e7 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h | |||
@@ -20,6 +20,35 @@ static const u32 vkms_formats[] = { | |||
20 | DRM_FORMAT_XRGB8888, | 20 | DRM_FORMAT_XRGB8888, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | struct vkms_crc_data { | ||
24 | struct drm_rect src; | ||
25 | struct drm_framebuffer fb; | ||
26 | }; | ||
27 | |||
28 | /** | ||
29 | * vkms_plane_state - Driver specific plane state | ||
30 | * @base: base plane state | ||
31 | * @crc_data: data required for CRC computation | ||
32 | */ | ||
33 | struct vkms_plane_state { | ||
34 | struct drm_plane_state base; | ||
35 | struct vkms_crc_data *crc_data; | ||
36 | }; | ||
37 | |||
38 | /** | ||
39 | * vkms_crtc_state - Driver specific CRTC state | ||
40 | * @base: base CRTC state | ||
41 | * @crc_work: work struct to compute and add CRC entries | ||
42 | * @n_frame_start: start frame number for computed CRC | ||
43 | * @n_frame_end: end frame number for computed CRC | ||
44 | */ | ||
45 | struct vkms_crtc_state { | ||
46 | struct drm_crtc_state base; | ||
47 | struct work_struct crc_work; | ||
48 | u64 frame_start; | ||
49 | u64 frame_end; | ||
50 | }; | ||
51 | |||
23 | struct vkms_output { | 52 | struct vkms_output { |
24 | struct drm_crtc crtc; | 53 | struct drm_crtc crtc; |
25 | struct drm_encoder encoder; | 54 | struct drm_encoder encoder; |
@@ -27,6 +56,13 @@ struct vkms_output { | |||
27 | struct hrtimer vblank_hrtimer; | 56 | struct hrtimer vblank_hrtimer; |
28 | ktime_t period_ns; | 57 | ktime_t period_ns; |
29 | struct drm_pending_vblank_event *event; | 58 | struct drm_pending_vblank_event *event; |
59 | bool crc_enabled; | ||
60 | /* ordered wq for crc_work */ | ||
61 | struct workqueue_struct *crc_workq; | ||
62 | /* protects concurrent access to crc_data */ | ||
63 | spinlock_t lock; | ||
64 | /* protects concurrent access to crtc_state */ | ||
65 | spinlock_t state_lock; | ||
30 | }; | 66 | }; |
31 | 67 | ||
32 | struct vkms_device { | 68 | struct vkms_device { |
@@ -39,6 +75,8 @@ struct vkms_gem_object { | |||
39 | struct drm_gem_object gem; | 75 | struct drm_gem_object gem; |
40 | struct mutex pages_lock; /* Page lock used in page fault handler */ | 76 | struct mutex pages_lock; /* Page lock used in page fault handler */ |
41 | struct page **pages; | 77 | struct page **pages; |
78 | unsigned int vmap_count; | ||
79 | void *vaddr; | ||
42 | }; | 80 | }; |
43 | 81 | ||
44 | #define drm_crtc_to_vkms_output(target) \ | 82 | #define drm_crtc_to_vkms_output(target) \ |
@@ -47,6 +85,15 @@ struct vkms_gem_object { | |||
47 | #define drm_device_to_vkms_device(target) \ | 85 | #define drm_device_to_vkms_device(target) \ |
48 | container_of(target, struct vkms_device, drm) | 86 | container_of(target, struct vkms_device, drm) |
49 | 87 | ||
88 | #define drm_gem_to_vkms_gem(target)\ | ||
89 | container_of(target, struct vkms_gem_object, gem) | ||
90 | |||
91 | #define to_vkms_crtc_state(target)\ | ||
92 | container_of(target, struct vkms_crtc_state, base) | ||
93 | |||
94 | #define to_vkms_plane_state(target)\ | ||
95 | container_of(target, struct vkms_plane_state, base) | ||
96 | |||
50 | /* CRTC */ | 97 | /* CRTC */ |
51 | int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, | 98 | int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, |
52 | struct drm_plane *primary, struct drm_plane *cursor); | 99 | struct drm_plane *primary, struct drm_plane *cursor); |
@@ -65,7 +112,7 @@ struct drm_gem_object *vkms_gem_create(struct drm_device *dev, | |||
65 | u32 *handle, | 112 | u32 *handle, |
66 | u64 size); | 113 | u64 size); |
67 | 114 | ||
68 | int vkms_gem_fault(struct vm_fault *vmf); | 115 | vm_fault_t vkms_gem_fault(struct vm_fault *vmf); |
69 | 116 | ||
70 | int vkms_dumb_create(struct drm_file *file, struct drm_device *dev, | 117 | int vkms_dumb_create(struct drm_file *file, struct drm_device *dev, |
71 | struct drm_mode_create_dumb *args); | 118 | struct drm_mode_create_dumb *args); |
@@ -75,4 +122,14 @@ int vkms_dumb_map(struct drm_file *file, struct drm_device *dev, | |||
75 | 122 | ||
76 | void vkms_gem_free_object(struct drm_gem_object *obj); | 123 | void vkms_gem_free_object(struct drm_gem_object *obj); |
77 | 124 | ||
125 | int vkms_gem_vmap(struct drm_gem_object *obj); | ||
126 | |||
127 | void vkms_gem_vunmap(struct drm_gem_object *obj); | ||
128 | |||
129 | /* CRC Support */ | ||
130 | int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name); | ||
131 | int vkms_verify_crc_source(struct drm_crtc *crtc, const char *source_name, | ||
132 | size_t *values_cnt); | ||
133 | void vkms_crc_work_handle(struct work_struct *work); | ||
134 | |||
78 | #endif /* _VKMS_DRV_H_ */ | 135 | #endif /* _VKMS_DRV_H_ */ |
diff --git a/drivers/gpu/drm/vkms/vkms_gem.c b/drivers/gpu/drm/vkms/vkms_gem.c index c7e38368602b..d04e988b4cbe 100644 --- a/drivers/gpu/drm/vkms/vkms_gem.c +++ b/drivers/gpu/drm/vkms/vkms_gem.c | |||
@@ -37,20 +37,22 @@ void vkms_gem_free_object(struct drm_gem_object *obj) | |||
37 | struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object, | 37 | struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object, |
38 | gem); | 38 | gem); |
39 | 39 | ||
40 | kvfree(gem->pages); | 40 | WARN_ON(gem->pages); |
41 | WARN_ON(gem->vaddr); | ||
42 | |||
41 | mutex_destroy(&gem->pages_lock); | 43 | mutex_destroy(&gem->pages_lock); |
42 | drm_gem_object_release(obj); | 44 | drm_gem_object_release(obj); |
43 | kfree(gem); | 45 | kfree(gem); |
44 | } | 46 | } |
45 | 47 | ||
46 | int vkms_gem_fault(struct vm_fault *vmf) | 48 | vm_fault_t vkms_gem_fault(struct vm_fault *vmf) |
47 | { | 49 | { |
48 | struct vm_area_struct *vma = vmf->vma; | 50 | struct vm_area_struct *vma = vmf->vma; |
49 | struct vkms_gem_object *obj = vma->vm_private_data; | 51 | struct vkms_gem_object *obj = vma->vm_private_data; |
50 | unsigned long vaddr = vmf->address; | 52 | unsigned long vaddr = vmf->address; |
51 | pgoff_t page_offset; | 53 | pgoff_t page_offset; |
52 | loff_t num_pages; | 54 | loff_t num_pages; |
53 | int ret; | 55 | vm_fault_t ret = VM_FAULT_SIGBUS; |
54 | 56 | ||
55 | page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT; | 57 | page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT; |
56 | num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE); | 58 | num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE); |
@@ -58,7 +60,6 @@ int vkms_gem_fault(struct vm_fault *vmf) | |||
58 | if (page_offset > num_pages) | 60 | if (page_offset > num_pages) |
59 | return VM_FAULT_SIGBUS; | 61 | return VM_FAULT_SIGBUS; |
60 | 62 | ||
61 | ret = -ENOENT; | ||
62 | mutex_lock(&obj->pages_lock); | 63 | mutex_lock(&obj->pages_lock); |
63 | if (obj->pages) { | 64 | if (obj->pages) { |
64 | get_page(obj->pages[page_offset]); | 65 | get_page(obj->pages[page_offset]); |
@@ -177,3 +178,77 @@ unref: | |||
177 | 178 | ||
178 | return ret; | 179 | return ret; |
179 | } | 180 | } |
181 | |||
182 | static struct page **_get_pages(struct vkms_gem_object *vkms_obj) | ||
183 | { | ||
184 | struct drm_gem_object *gem_obj = &vkms_obj->gem; | ||
185 | |||
186 | if (!vkms_obj->pages) { | ||
187 | struct page **pages = drm_gem_get_pages(gem_obj); | ||
188 | |||
189 | if (IS_ERR(pages)) | ||
190 | return pages; | ||
191 | |||
192 | if (cmpxchg(&vkms_obj->pages, NULL, pages)) | ||
193 | drm_gem_put_pages(gem_obj, pages, false, true); | ||
194 | } | ||
195 | |||
196 | return vkms_obj->pages; | ||
197 | } | ||
198 | |||
199 | void vkms_gem_vunmap(struct drm_gem_object *obj) | ||
200 | { | ||
201 | struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(obj); | ||
202 | |||
203 | mutex_lock(&vkms_obj->pages_lock); | ||
204 | if (vkms_obj->vmap_count < 1) { | ||
205 | WARN_ON(vkms_obj->vaddr); | ||
206 | WARN_ON(vkms_obj->pages); | ||
207 | mutex_unlock(&vkms_obj->pages_lock); | ||
208 | return; | ||
209 | } | ||
210 | |||
211 | vkms_obj->vmap_count--; | ||
212 | |||
213 | if (vkms_obj->vmap_count == 0) { | ||
214 | vunmap(vkms_obj->vaddr); | ||
215 | vkms_obj->vaddr = NULL; | ||
216 | drm_gem_put_pages(obj, vkms_obj->pages, false, true); | ||
217 | vkms_obj->pages = NULL; | ||
218 | } | ||
219 | |||
220 | mutex_unlock(&vkms_obj->pages_lock); | ||
221 | } | ||
222 | |||
223 | int vkms_gem_vmap(struct drm_gem_object *obj) | ||
224 | { | ||
225 | struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(obj); | ||
226 | int ret = 0; | ||
227 | |||
228 | mutex_lock(&vkms_obj->pages_lock); | ||
229 | |||
230 | if (!vkms_obj->vaddr) { | ||
231 | unsigned int n_pages = obj->size >> PAGE_SHIFT; | ||
232 | struct page **pages = _get_pages(vkms_obj); | ||
233 | |||
234 | if (IS_ERR(pages)) { | ||
235 | ret = PTR_ERR(pages); | ||
236 | goto out; | ||
237 | } | ||
238 | |||
239 | vkms_obj->vaddr = vmap(pages, n_pages, VM_MAP, PAGE_KERNEL); | ||
240 | if (!vkms_obj->vaddr) | ||
241 | goto err_vmap; | ||
242 | } | ||
243 | |||
244 | vkms_obj->vmap_count++; | ||
245 | goto out; | ||
246 | |||
247 | err_vmap: | ||
248 | ret = -ENOMEM; | ||
249 | drm_gem_put_pages(obj, vkms_obj->pages, false, true); | ||
250 | vkms_obj->pages = NULL; | ||
251 | out: | ||
252 | mutex_unlock(&vkms_obj->pages_lock); | ||
253 | return ret; | ||
254 | } | ||
diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index 9f75b1e2c1c4..c91661631c76 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c | |||
@@ -8,24 +8,158 @@ | |||
8 | 8 | ||
9 | #include "vkms_drv.h" | 9 | #include "vkms_drv.h" |
10 | #include <drm/drm_plane_helper.h> | 10 | #include <drm/drm_plane_helper.h> |
11 | #include <drm/drm_atomic.h> | ||
11 | #include <drm/drm_atomic_helper.h> | 12 | #include <drm/drm_atomic_helper.h> |
13 | #include <drm/drm_gem_framebuffer_helper.h> | ||
14 | |||
15 | static struct drm_plane_state * | ||
16 | vkms_plane_duplicate_state(struct drm_plane *plane) | ||
17 | { | ||
18 | struct vkms_plane_state *vkms_state; | ||
19 | struct vkms_crc_data *crc_data; | ||
20 | |||
21 | vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); | ||
22 | if (!vkms_state) | ||
23 | return NULL; | ||
24 | |||
25 | crc_data = kzalloc(sizeof(*crc_data), GFP_KERNEL); | ||
26 | if (WARN_ON(!crc_data)) | ||
27 | DRM_INFO("Couldn't allocate crc_data"); | ||
28 | |||
29 | vkms_state->crc_data = crc_data; | ||
30 | |||
31 | __drm_atomic_helper_plane_duplicate_state(plane, | ||
32 | &vkms_state->base); | ||
33 | |||
34 | return &vkms_state->base; | ||
35 | } | ||
36 | |||
37 | static void vkms_plane_destroy_state(struct drm_plane *plane, | ||
38 | struct drm_plane_state *old_state) | ||
39 | { | ||
40 | struct vkms_plane_state *vkms_state = to_vkms_plane_state(old_state); | ||
41 | struct drm_crtc *crtc = vkms_state->base.crtc; | ||
42 | |||
43 | if (crtc) { | ||
44 | /* dropping the reference we acquired in | ||
45 | * vkms_primary_plane_update() | ||
46 | */ | ||
47 | if (drm_framebuffer_read_refcount(&vkms_state->crc_data->fb)) | ||
48 | drm_framebuffer_put(&vkms_state->crc_data->fb); | ||
49 | } | ||
50 | |||
51 | kfree(vkms_state->crc_data); | ||
52 | vkms_state->crc_data = NULL; | ||
53 | |||
54 | __drm_atomic_helper_plane_destroy_state(old_state); | ||
55 | kfree(vkms_state); | ||
56 | } | ||
57 | |||
58 | static void vkms_plane_reset(struct drm_plane *plane) | ||
59 | { | ||
60 | struct vkms_plane_state *vkms_state; | ||
61 | |||
62 | if (plane->state) | ||
63 | vkms_plane_destroy_state(plane, plane->state); | ||
64 | |||
65 | vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); | ||
66 | if (!vkms_state) { | ||
67 | DRM_ERROR("Cannot allocate vkms_plane_state\n"); | ||
68 | return; | ||
69 | } | ||
70 | |||
71 | plane->state = &vkms_state->base; | ||
72 | plane->state->plane = plane; | ||
73 | } | ||
12 | 74 | ||
13 | static const struct drm_plane_funcs vkms_plane_funcs = { | 75 | static const struct drm_plane_funcs vkms_plane_funcs = { |
14 | .update_plane = drm_atomic_helper_update_plane, | 76 | .update_plane = drm_atomic_helper_update_plane, |
15 | .disable_plane = drm_atomic_helper_disable_plane, | 77 | .disable_plane = drm_atomic_helper_disable_plane, |
16 | .destroy = drm_plane_cleanup, | 78 | .destroy = drm_plane_cleanup, |
17 | .reset = drm_atomic_helper_plane_reset, | 79 | .reset = vkms_plane_reset, |
18 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | 80 | .atomic_duplicate_state = vkms_plane_duplicate_state, |
19 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | 81 | .atomic_destroy_state = vkms_plane_destroy_state, |
20 | }; | 82 | }; |
21 | 83 | ||
22 | static void vkms_primary_plane_update(struct drm_plane *plane, | 84 | static void vkms_primary_plane_update(struct drm_plane *plane, |
23 | struct drm_plane_state *old_state) | 85 | struct drm_plane_state *old_state) |
24 | { | 86 | { |
87 | struct vkms_plane_state *vkms_plane_state; | ||
88 | struct vkms_crc_data *crc_data; | ||
89 | |||
90 | if (!plane->state->crtc || !plane->state->fb) | ||
91 | return; | ||
92 | |||
93 | vkms_plane_state = to_vkms_plane_state(plane->state); | ||
94 | crc_data = vkms_plane_state->crc_data; | ||
95 | memcpy(&crc_data->src, &plane->state->src, sizeof(struct drm_rect)); | ||
96 | memcpy(&crc_data->fb, plane->state->fb, sizeof(struct drm_framebuffer)); | ||
97 | drm_framebuffer_get(&crc_data->fb); | ||
98 | } | ||
99 | |||
100 | static int vkms_plane_atomic_check(struct drm_plane *plane, | ||
101 | struct drm_plane_state *state) | ||
102 | { | ||
103 | struct drm_crtc_state *crtc_state; | ||
104 | int ret; | ||
105 | |||
106 | if (!state->fb | !state->crtc) | ||
107 | return 0; | ||
108 | |||
109 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); | ||
110 | if (IS_ERR(crtc_state)) | ||
111 | return PTR_ERR(crtc_state); | ||
112 | |||
113 | ret = drm_atomic_helper_check_plane_state(state, crtc_state, | ||
114 | DRM_PLANE_HELPER_NO_SCALING, | ||
115 | DRM_PLANE_HELPER_NO_SCALING, | ||
116 | false, true); | ||
117 | if (ret != 0) | ||
118 | return ret; | ||
119 | |||
120 | /* for now primary plane must be visible and full screen */ | ||
121 | if (!state->visible) | ||
122 | return -EINVAL; | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int vkms_prepare_fb(struct drm_plane *plane, | ||
128 | struct drm_plane_state *state) | ||
129 | { | ||
130 | struct drm_gem_object *gem_obj; | ||
131 | struct vkms_gem_object *vkms_obj; | ||
132 | int ret; | ||
133 | |||
134 | if (!state->fb) | ||
135 | return 0; | ||
136 | |||
137 | gem_obj = drm_gem_fb_get_obj(state->fb, 0); | ||
138 | vkms_obj = drm_gem_to_vkms_gem(gem_obj); | ||
139 | ret = vkms_gem_vmap(gem_obj); | ||
140 | if (ret) | ||
141 | DRM_ERROR("vmap failed: %d\n", ret); | ||
142 | |||
143 | return drm_gem_fb_prepare_fb(plane, state); | ||
144 | } | ||
145 | |||
146 | static void vkms_cleanup_fb(struct drm_plane *plane, | ||
147 | struct drm_plane_state *old_state) | ||
148 | { | ||
149 | struct drm_gem_object *gem_obj; | ||
150 | |||
151 | if (!old_state->fb) | ||
152 | return; | ||
153 | |||
154 | gem_obj = drm_gem_fb_get_obj(old_state->fb, 0); | ||
155 | vkms_gem_vunmap(gem_obj); | ||
25 | } | 156 | } |
26 | 157 | ||
27 | static const struct drm_plane_helper_funcs vkms_primary_helper_funcs = { | 158 | static const struct drm_plane_helper_funcs vkms_primary_helper_funcs = { |
28 | .atomic_update = vkms_primary_plane_update, | 159 | .atomic_update = vkms_primary_plane_update, |
160 | .atomic_check = vkms_plane_atomic_check, | ||
161 | .prepare_fb = vkms_prepare_fb, | ||
162 | .cleanup_fb = vkms_cleanup_fb, | ||
29 | }; | 163 | }; |
30 | 164 | ||
31 | struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev) | 165 | struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev) |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 23beff5d8e3c..0c25bb8faf80 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | |||
@@ -720,9 +720,7 @@ void vmw_du_plane_reset(struct drm_plane *plane) | |||
720 | return; | 720 | return; |
721 | } | 721 | } |
722 | 722 | ||
723 | plane->state = &vps->base; | 723 | __drm_atomic_helper_plane_reset(plane, &vps->base); |
724 | plane->state->plane = plane; | ||
725 | plane->state->rotation = DRM_MODE_ROTATE_0; | ||
726 | } | 724 | } |
727 | 725 | ||
728 | 726 | ||
diff --git a/drivers/gpu/drm/xen/xen_drm_front_gem.c b/drivers/gpu/drm/xen/xen_drm_front_gem.c index c85bfe7571cb..47ff019d3aef 100644 --- a/drivers/gpu/drm/xen/xen_drm_front_gem.c +++ b/drivers/gpu/drm/xen/xen_drm_front_gem.c | |||
@@ -179,7 +179,7 @@ struct sg_table *xen_drm_front_gem_get_sg_table(struct drm_gem_object *gem_obj) | |||
179 | struct xen_gem_object *xen_obj = to_xen_gem_obj(gem_obj); | 179 | struct xen_gem_object *xen_obj = to_xen_gem_obj(gem_obj); |
180 | 180 | ||
181 | if (!xen_obj->pages) | 181 | if (!xen_obj->pages) |
182 | return NULL; | 182 | return ERR_PTR(-ENOMEM); |
183 | 183 | ||
184 | return drm_prime_pages_to_sg(xen_obj->pages, xen_obj->num_pages); | 184 | return drm_prime_pages_to_sg(xen_obj->pages, xen_obj->num_pages); |
185 | } | 185 | } |
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 20405421a5ed..5ffadc8e681d 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/fb.h> | 34 | #include <linux/fb.h> |
35 | #include <linux/fbcon.h> | 35 | #include <linux/fbcon.h> |
36 | #include <linux/mem_encrypt.h> | 36 | #include <linux/mem_encrypt.h> |
37 | #include <linux/pci.h> | ||
37 | 38 | ||
38 | #include <asm/fb.h> | 39 | #include <asm/fb.h> |
39 | 40 | ||
@@ -1605,8 +1606,8 @@ static int do_remove_conflicting_framebuffers(struct apertures_struct *a, | |||
1605 | (primary && gen_aper && gen_aper->count && | 1606 | (primary && gen_aper && gen_aper->count && |
1606 | gen_aper->ranges[0].base == VGA_FB_PHYS)) { | 1607 | gen_aper->ranges[0].base == VGA_FB_PHYS)) { |
1607 | 1608 | ||
1608 | printk(KERN_INFO "fb: switching to %s from %s\n", | 1609 | printk(KERN_INFO "fb%d: switching to %s from %s\n", |
1609 | name, registered_fb[i]->fix.id); | 1610 | i, name, registered_fb[i]->fix.id); |
1610 | ret = do_unregister_framebuffer(registered_fb[i]); | 1611 | ret = do_unregister_framebuffer(registered_fb[i]); |
1611 | if (ret) | 1612 | if (ret) |
1612 | return ret; | 1613 | return ret; |
@@ -1793,20 +1794,78 @@ int unlink_framebuffer(struct fb_info *fb_info) | |||
1793 | } | 1794 | } |
1794 | EXPORT_SYMBOL(unlink_framebuffer); | 1795 | EXPORT_SYMBOL(unlink_framebuffer); |
1795 | 1796 | ||
1797 | /** | ||
1798 | * remove_conflicting_framebuffers - remove firmware-configured framebuffers | ||
1799 | * @a: memory range, users of which are to be removed | ||
1800 | * @name: requesting driver name | ||
1801 | * @primary: also kick vga16fb if present | ||
1802 | * | ||
1803 | * This function removes framebuffer devices (initialized by firmware/bootloader) | ||
1804 | * which use memory range described by @a. If @a is NULL all such devices are | ||
1805 | * removed. | ||
1806 | */ | ||
1796 | int remove_conflicting_framebuffers(struct apertures_struct *a, | 1807 | int remove_conflicting_framebuffers(struct apertures_struct *a, |
1797 | const char *name, bool primary) | 1808 | const char *name, bool primary) |
1798 | { | 1809 | { |
1799 | int ret; | 1810 | int ret; |
1811 | bool do_free = false; | ||
1812 | |||
1813 | if (!a) { | ||
1814 | a = alloc_apertures(1); | ||
1815 | if (!a) | ||
1816 | return -ENOMEM; | ||
1817 | |||
1818 | a->ranges[0].base = 0; | ||
1819 | a->ranges[0].size = ~0; | ||
1820 | do_free = true; | ||
1821 | } | ||
1800 | 1822 | ||
1801 | mutex_lock(®istration_lock); | 1823 | mutex_lock(®istration_lock); |
1802 | ret = do_remove_conflicting_framebuffers(a, name, primary); | 1824 | ret = do_remove_conflicting_framebuffers(a, name, primary); |
1803 | mutex_unlock(®istration_lock); | 1825 | mutex_unlock(®istration_lock); |
1804 | 1826 | ||
1827 | if (do_free) | ||
1828 | kfree(a); | ||
1829 | |||
1805 | return ret; | 1830 | return ret; |
1806 | } | 1831 | } |
1807 | EXPORT_SYMBOL(remove_conflicting_framebuffers); | 1832 | EXPORT_SYMBOL(remove_conflicting_framebuffers); |
1808 | 1833 | ||
1809 | /** | 1834 | /** |
1835 | * remove_conflicting_pci_framebuffers - remove firmware-configured framebuffers for PCI devices | ||
1836 | * @pdev: PCI device | ||
1837 | * @resource_id: index of PCI BAR configuring framebuffer memory | ||
1838 | * @name: requesting driver name | ||
1839 | * | ||
1840 | * This function removes framebuffer devices (eg. initialized by firmware) | ||
1841 | * using memory range configured for @pdev's BAR @resource_id. | ||
1842 | * | ||
1843 | * The function assumes that PCI device with shadowed ROM drives a primary | ||
1844 | * display and so kicks out vga16fb. | ||
1845 | */ | ||
1846 | int remove_conflicting_pci_framebuffers(struct pci_dev *pdev, int res_id, const char *name) | ||
1847 | { | ||
1848 | struct apertures_struct *ap; | ||
1849 | bool primary = false; | ||
1850 | int err; | ||
1851 | |||
1852 | ap = alloc_apertures(1); | ||
1853 | if (!ap) | ||
1854 | return -ENOMEM; | ||
1855 | |||
1856 | ap->ranges[0].base = pci_resource_start(pdev, res_id); | ||
1857 | ap->ranges[0].size = pci_resource_len(pdev, res_id); | ||
1858 | #ifdef CONFIG_X86 | ||
1859 | primary = pdev->resource[PCI_ROM_RESOURCE].flags & | ||
1860 | IORESOURCE_ROM_SHADOW; | ||
1861 | #endif | ||
1862 | err = remove_conflicting_framebuffers(ap, name, primary); | ||
1863 | kfree(ap); | ||
1864 | return err; | ||
1865 | } | ||
1866 | EXPORT_SYMBOL(remove_conflicting_pci_framebuffers); | ||
1867 | |||
1868 | /** | ||
1810 | * register_framebuffer - registers a frame buffer device | 1869 | * register_framebuffer - registers a frame buffer device |
1811 | * @fb_info: frame buffer info structure | 1870 | * @fb_info: frame buffer info structure |
1812 | * | 1871 | * |
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 99e2a5297c69..f4c7ed876c97 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h | |||
@@ -156,6 +156,8 @@ void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state); | |||
156 | void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, | 156 | void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, |
157 | struct drm_crtc_state *state); | 157 | struct drm_crtc_state *state); |
158 | 158 | ||
159 | void __drm_atomic_helper_plane_reset(struct drm_plane *plane, | ||
160 | struct drm_plane_state *state); | ||
159 | void drm_atomic_helper_plane_reset(struct drm_plane *plane); | 161 | void drm_atomic_helper_plane_reset(struct drm_plane *plane); |
160 | void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, | 162 | void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, |
161 | struct drm_plane_state *state); | 163 | struct drm_plane_state *state); |
diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h index 330c561c4c11..88bdfec3bd88 100644 --- a/include/drm/drm_blend.h +++ b/include/drm/drm_blend.h | |||
@@ -27,6 +27,10 @@ | |||
27 | #include <linux/ctype.h> | 27 | #include <linux/ctype.h> |
28 | #include <drm/drm_mode.h> | 28 | #include <drm/drm_mode.h> |
29 | 29 | ||
30 | #define DRM_MODE_BLEND_PREMULTI 0 | ||
31 | #define DRM_MODE_BLEND_COVERAGE 1 | ||
32 | #define DRM_MODE_BLEND_PIXEL_NONE 2 | ||
33 | |||
30 | struct drm_device; | 34 | struct drm_device; |
31 | struct drm_atomic_state; | 35 | struct drm_atomic_state; |
32 | struct drm_plane; | 36 | struct drm_plane; |
@@ -52,4 +56,6 @@ int drm_plane_create_zpos_immutable_property(struct drm_plane *plane, | |||
52 | unsigned int zpos); | 56 | unsigned int zpos); |
53 | int drm_atomic_normalize_zpos(struct drm_device *dev, | 57 | int drm_atomic_normalize_zpos(struct drm_device *dev, |
54 | struct drm_atomic_state *state); | 58 | struct drm_atomic_state *state); |
59 | int drm_plane_create_blend_mode_property(struct drm_plane *plane, | ||
60 | unsigned int supported_modes); | ||
55 | #endif | 61 | #endif |
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 92e7fc7f05a4..b21437bc95bf 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h | |||
@@ -744,8 +744,45 @@ struct drm_crtc_funcs { | |||
744 | * | 744 | * |
745 | * 0 on success or a negative error code on failure. | 745 | * 0 on success or a negative error code on failure. |
746 | */ | 746 | */ |
747 | int (*set_crc_source)(struct drm_crtc *crtc, const char *source, | 747 | int (*set_crc_source)(struct drm_crtc *crtc, const char *source); |
748 | size_t *values_cnt); | 748 | /** |
749 | * @verify_crc_source: | ||
750 | * | ||
751 | * verifies the source of CRC checksums of frames before setting the | ||
752 | * source for CRC and during crc open. Source parameter can be NULL | ||
753 | * while disabling crc source. | ||
754 | * | ||
755 | * This callback is optional if the driver does not support any CRC | ||
756 | * generation functionality. | ||
757 | * | ||
758 | * RETURNS: | ||
759 | * | ||
760 | * 0 on success or a negative error code on failure. | ||
761 | */ | ||
762 | int (*verify_crc_source)(struct drm_crtc *crtc, const char *source, | ||
763 | size_t *values_cnt); | ||
764 | /** | ||
765 | * @get_crc_sources: | ||
766 | * | ||
767 | * Driver callback for getting a list of all the available sources for | ||
768 | * CRC generation. This callback depends upon verify_crc_source, So | ||
769 | * verify_crc_source callback should be implemented before implementing | ||
770 | * this. Driver can pass full list of available crc sources, this | ||
771 | * callback does the verification on each crc-source before passing it | ||
772 | * to userspace. | ||
773 | * | ||
774 | * This callback is optional if the driver does not support exporting of | ||
775 | * possible CRC sources list. | ||
776 | * | ||
777 | * RETURNS: | ||
778 | * | ||
779 | * a constant character pointer to the list of all the available CRC | ||
780 | * sources. On failure driver should return NULL. count should be | ||
781 | * updated with number of sources in list. if zero we don't process any | ||
782 | * source from the list. | ||
783 | */ | ||
784 | const char *const *(*get_crc_sources)(struct drm_crtc *crtc, | ||
785 | size_t *count); | ||
749 | 786 | ||
750 | /** | 787 | /** |
751 | * @atomic_print_state: | 788 | * @atomic_print_state: |
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 05cc31b5db16..698082a02b97 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h | |||
@@ -123,8 +123,9 @@ | |||
123 | # define DP_FRAMING_CHANGE_CAP (1 << 1) | 123 | # define DP_FRAMING_CHANGE_CAP (1 << 1) |
124 | # define DP_DPCD_DISPLAY_CONTROL_CAPABLE (1 << 3) /* edp v1.2 or higher */ | 124 | # define DP_DPCD_DISPLAY_CONTROL_CAPABLE (1 << 3) /* edp v1.2 or higher */ |
125 | 125 | ||
126 | #define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ | 126 | #define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ |
127 | # define DP_TRAINING_AUX_RD_MASK 0x7F /* XXX 1.2? */ | 127 | # define DP_TRAINING_AUX_RD_MASK 0x7F /* DP 1.3 */ |
128 | # define DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT (1 << 7) /* DP 1.3 */ | ||
128 | 129 | ||
129 | #define DP_ADAPTER_CAP 0x00f /* 1.2 */ | 130 | #define DP_ADAPTER_CAP 0x00f /* 1.2 */ |
130 | # define DP_FORCE_LOAD_SENSE_CAP (1 << 0) | 131 | # define DP_FORCE_LOAD_SENSE_CAP (1 << 0) |
diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index 96e26e3b9a0c..4a65f0d155b0 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h | |||
@@ -26,7 +26,6 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma); | |||
26 | 26 | ||
27 | void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma); | 27 | void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma); |
28 | void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma); | 28 | void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma); |
29 | void drm_fbdev_cma_set_suspend(struct drm_fbdev_cma *fbdev_cma, bool state); | ||
30 | void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma *fbdev_cma, | 29 | void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma *fbdev_cma, |
31 | bool state); | 30 | bool state); |
32 | 31 | ||
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 5db08c8f1d25..8b6ab3200a2c 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h | |||
@@ -615,4 +615,16 @@ drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, | |||
615 | #endif | 615 | #endif |
616 | } | 616 | } |
617 | 617 | ||
618 | static inline int | ||
619 | drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, | ||
620 | int resource_id, | ||
621 | const char *name) | ||
622 | { | ||
623 | #if IS_REACHABLE(CONFIG_FB) | ||
624 | return remove_conflicting_pci_framebuffers(pdev, resource_id, name); | ||
625 | #else | ||
626 | return 0; | ||
627 | #endif | ||
628 | } | ||
629 | |||
618 | #endif | 630 | #endif |
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 582a0ec0aa70..a82c292af6c5 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h | |||
@@ -82,6 +82,7 @@ struct drm_panel_funcs { | |||
82 | * @drm: DRM device owning the panel | 82 | * @drm: DRM device owning the panel |
83 | * @connector: DRM connector that the panel is attached to | 83 | * @connector: DRM connector that the panel is attached to |
84 | * @dev: parent device of the panel | 84 | * @dev: parent device of the panel |
85 | * @link: link from panel device (supplier) to DRM device (consumer) | ||
85 | * @funcs: operations that can be performed on the panel | 86 | * @funcs: operations that can be performed on the panel |
86 | * @list: panel entry in registry | 87 | * @list: panel entry in registry |
87 | */ | 88 | */ |
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 8a152dc16ea5..16f5b66684ca 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h | |||
@@ -119,6 +119,14 @@ struct drm_plane_state { | |||
119 | u16 alpha; | 119 | u16 alpha; |
120 | 120 | ||
121 | /** | 121 | /** |
122 | * @pixel_blend_mode: | ||
123 | * The alpha blending equation selection, describing how the pixels from | ||
124 | * the current plane are composited with the background. Value can be | ||
125 | * one of DRM_MODE_BLEND_* | ||
126 | */ | ||
127 | uint16_t pixel_blend_mode; | ||
128 | |||
129 | /** | ||
122 | * @rotation: | 130 | * @rotation: |
123 | * Rotation of the plane. See drm_plane_create_rotation_property() for | 131 | * Rotation of the plane. See drm_plane_create_rotation_property() for |
124 | * more details. | 132 | * more details. |
@@ -659,6 +667,14 @@ struct drm_plane { | |||
659 | * drm_plane_create_rotation_property(). | 667 | * drm_plane_create_rotation_property(). |
660 | */ | 668 | */ |
661 | struct drm_property *rotation_property; | 669 | struct drm_property *rotation_property; |
670 | /** | ||
671 | * @blend_mode_property: | ||
672 | * Optional "pixel blend mode" enum property for this plane. | ||
673 | * Blend mode property represents the alpha blending equation selection, | ||
674 | * describing how the pixels from the current plane are composited with | ||
675 | * the background. | ||
676 | */ | ||
677 | struct drm_property *blend_mode_property; | ||
662 | 678 | ||
663 | /** | 679 | /** |
664 | * @color_encoding_property: | 680 | * @color_encoding_property: |
diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index f3e6eed3e79c..afbc3beef089 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h | |||
@@ -381,7 +381,7 @@ void drm_err(const char *format, ...); | |||
381 | 381 | ||
382 | #define DRM_DEV_DEBUG_DP(dev, fmt, ...) \ | 382 | #define DRM_DEV_DEBUG_DP(dev, fmt, ...) \ |
383 | drm_dev_dbg(dev, DRM_UT_DP, fmt, ## __VA_ARGS__) | 383 | drm_dev_dbg(dev, DRM_UT_DP, fmt, ## __VA_ARGS__) |
384 | #define DRM_DEBUG_DP(dev, fmt, ...) \ | 384 | #define DRM_DEBUG_DP(fmt, ...) \ |
385 | drm_dbg(DRM_UT_DP, fmt, ## __VA_ARGS__) | 385 | drm_dbg(DRM_UT_DP, fmt, ## __VA_ARGS__) |
386 | 386 | ||
387 | #define _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, category, fmt, ...) \ | 387 | #define _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, category, fmt, ...) \ |
diff --git a/include/drm/drm_syncobj.h b/include/drm/drm_syncobj.h index 3980602472c0..e419c79ba94d 100644 --- a/include/drm/drm_syncobj.h +++ b/include/drm/drm_syncobj.h | |||
@@ -131,11 +131,6 @@ drm_syncobj_fence_get(struct drm_syncobj *syncobj) | |||
131 | 131 | ||
132 | struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, | 132 | struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, |
133 | u32 handle); | 133 | u32 handle); |
134 | void drm_syncobj_add_callback(struct drm_syncobj *syncobj, | ||
135 | struct drm_syncobj_cb *cb, | ||
136 | drm_syncobj_func_t func); | ||
137 | void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, | ||
138 | struct drm_syncobj_cb *cb); | ||
139 | void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, | 134 | void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, |
140 | struct dma_fence *fence); | 135 | struct dma_fence *fence); |
141 | int drm_syncobj_find_fence(struct drm_file *file_private, | 136 | int drm_syncobj_find_fence(struct drm_file *file_private, |
diff --git a/include/linux/fb.h b/include/linux/fb.h index 3e7e75383d32..3cd375dafd0e 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h | |||
@@ -632,6 +632,8 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, | |||
632 | extern int register_framebuffer(struct fb_info *fb_info); | 632 | extern int register_framebuffer(struct fb_info *fb_info); |
633 | extern int unregister_framebuffer(struct fb_info *fb_info); | 633 | extern int unregister_framebuffer(struct fb_info *fb_info); |
634 | extern int unlink_framebuffer(struct fb_info *fb_info); | 634 | extern int unlink_framebuffer(struct fb_info *fb_info); |
635 | extern int remove_conflicting_pci_framebuffers(struct pci_dev *pdev, int res_id, | ||
636 | const char *name); | ||
635 | extern int remove_conflicting_framebuffers(struct apertures_struct *a, | 637 | extern int remove_conflicting_framebuffers(struct apertures_struct *a, |
636 | const char *name, bool primary); | 638 | const char *name, bool primary); |
637 | extern int fb_prepare_logo(struct fb_info *fb_info, int rotate); | 639 | extern int fb_prepare_logo(struct fb_info *fb_info, int rotate); |
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 721ab7e54d96..2ed46e9ae16a 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h | |||
@@ -30,6 +30,42 @@ | |||
30 | extern "C" { | 30 | extern "C" { |
31 | #endif | 31 | #endif |
32 | 32 | ||
33 | /** | ||
34 | * DOC: overview | ||
35 | * | ||
36 | * In the DRM subsystem, framebuffer pixel formats are described using the | ||
37 | * fourcc codes defined in `include/uapi/drm/drm_fourcc.h`. In addition to the | ||
38 | * fourcc code, a Format Modifier may optionally be provided, in order to | ||
39 | * further describe the buffer's format - for example tiling or compression. | ||
40 | * | ||
41 | * Format Modifiers | ||
42 | * ---------------- | ||
43 | * | ||
44 | * Format modifiers are used in conjunction with a fourcc code, forming a | ||
45 | * unique fourcc:modifier pair. This format:modifier pair must fully define the | ||
46 | * format and data layout of the buffer, and should be the only way to describe | ||
47 | * that particular buffer. | ||
48 | * | ||
49 | * Having multiple fourcc:modifier pairs which describe the same layout should | ||
50 | * be avoided, as such aliases run the risk of different drivers exposing | ||
51 | * different names for the same data format, forcing userspace to understand | ||
52 | * that they are aliases. | ||
53 | * | ||
54 | * Format modifiers may change any property of the buffer, including the number | ||
55 | * of planes and/or the required allocation size. Format modifiers are | ||
56 | * vendor-namespaced, and as such the relationship between a fourcc code and a | ||
57 | * modifier is specific to the modifer being used. For example, some modifiers | ||
58 | * may preserve meaning - such as number of planes - from the fourcc code, | ||
59 | * whereas others may not. | ||
60 | * | ||
61 | * Vendors should document their modifier usage in as much detail as | ||
62 | * possible, to ensure maximum compatibility across devices, drivers and | ||
63 | * applications. | ||
64 | * | ||
65 | * The authoritative list of format modifier codes is found in | ||
66 | * `include/uapi/drm/drm_fourcc.h` | ||
67 | */ | ||
68 | |||
33 | #define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ | 69 | #define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ |
34 | ((__u32)(c) << 16) | ((__u32)(d) << 24)) | 70 | ((__u32)(c) << 16) | ((__u32)(d) << 24)) |
35 | 71 | ||
diff --git a/include/uapi/linux/udmabuf.h b/include/uapi/linux/udmabuf.h new file mode 100644 index 000000000000..46b6532ed855 --- /dev/null +++ b/include/uapi/linux/udmabuf.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
2 | #ifndef _UAPI_LINUX_UDMABUF_H | ||
3 | #define _UAPI_LINUX_UDMABUF_H | ||
4 | |||
5 | #include <linux/types.h> | ||
6 | #include <linux/ioctl.h> | ||
7 | |||
8 | #define UDMABUF_FLAGS_CLOEXEC 0x01 | ||
9 | |||
10 | struct udmabuf_create { | ||
11 | __u32 memfd; | ||
12 | __u32 flags; | ||
13 | __u64 offset; | ||
14 | __u64 size; | ||
15 | }; | ||
16 | |||
17 | struct udmabuf_create_item { | ||
18 | __u32 memfd; | ||
19 | __u32 __pad; | ||
20 | __u64 offset; | ||
21 | __u64 size; | ||
22 | }; | ||
23 | |||
24 | struct udmabuf_create_list { | ||
25 | __u32 flags; | ||
26 | __u32 count; | ||
27 | struct udmabuf_create_item list[]; | ||
28 | }; | ||
29 | |||
30 | #define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create) | ||
31 | #define UDMABUF_CREATE_LIST _IOW('u', 0x43, struct udmabuf_create_list) | ||
32 | |||
33 | #endif /* _UAPI_LINUX_UDMABUF_H */ | ||
diff --git a/tools/testing/selftests/drivers/dma-buf/Makefile b/tools/testing/selftests/drivers/dma-buf/Makefile new file mode 100644 index 000000000000..4154c3d7aa58 --- /dev/null +++ b/tools/testing/selftests/drivers/dma-buf/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | CFLAGS += -I../../../../../usr/include/ | ||
2 | |||
3 | TEST_GEN_PROGS := udmabuf | ||
4 | |||
5 | include ../../lib.mk | ||
diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf.c b/tools/testing/selftests/drivers/dma-buf/udmabuf.c new file mode 100644 index 000000000000..376b1d6730bd --- /dev/null +++ b/tools/testing/selftests/drivers/dma-buf/udmabuf.c | |||
@@ -0,0 +1,96 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <stdio.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <unistd.h> | ||
5 | #include <string.h> | ||
6 | #include <errno.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <malloc.h> | ||
9 | |||
10 | #include <sys/ioctl.h> | ||
11 | #include <sys/syscall.h> | ||
12 | #include <linux/memfd.h> | ||
13 | #include <linux/udmabuf.h> | ||
14 | |||
15 | #define TEST_PREFIX "drivers/dma-buf/udmabuf" | ||
16 | #define NUM_PAGES 4 | ||
17 | |||
18 | static int memfd_create(const char *name, unsigned int flags) | ||
19 | { | ||
20 | return syscall(__NR_memfd_create, name, flags); | ||
21 | } | ||
22 | |||
23 | int main(int argc, char *argv[]) | ||
24 | { | ||
25 | struct udmabuf_create create; | ||
26 | int devfd, memfd, buf, ret; | ||
27 | off_t size; | ||
28 | void *mem; | ||
29 | |||
30 | devfd = open("/dev/udmabuf", O_RDWR); | ||
31 | if (devfd < 0) { | ||
32 | printf("%s: [skip,no-udmabuf]\n", TEST_PREFIX); | ||
33 | exit(77); | ||
34 | } | ||
35 | |||
36 | memfd = memfd_create("udmabuf-test", MFD_CLOEXEC); | ||
37 | if (memfd < 0) { | ||
38 | printf("%s: [skip,no-memfd]\n", TEST_PREFIX); | ||
39 | exit(77); | ||
40 | } | ||
41 | |||
42 | size = getpagesize() * NUM_PAGES; | ||
43 | ret = ftruncate(memfd, size); | ||
44 | if (ret == -1) { | ||
45 | printf("%s: [FAIL,memfd-truncate]\n", TEST_PREFIX); | ||
46 | exit(1); | ||
47 | } | ||
48 | |||
49 | memset(&create, 0, sizeof(create)); | ||
50 | |||
51 | /* should fail (offset not page aligned) */ | ||
52 | create.memfd = memfd; | ||
53 | create.offset = getpagesize()/2; | ||
54 | create.size = getpagesize(); | ||
55 | buf = ioctl(devfd, UDMABUF_CREATE, &create); | ||
56 | if (buf >= 0) { | ||
57 | printf("%s: [FAIL,test-1]\n", TEST_PREFIX); | ||
58 | exit(1); | ||
59 | } | ||
60 | |||
61 | /* should fail (size not multiple of page) */ | ||
62 | create.memfd = memfd; | ||
63 | create.offset = 0; | ||
64 | create.size = getpagesize()/2; | ||
65 | buf = ioctl(devfd, UDMABUF_CREATE, &create); | ||
66 | if (buf >= 0) { | ||
67 | printf("%s: [FAIL,test-2]\n", TEST_PREFIX); | ||
68 | exit(1); | ||
69 | } | ||
70 | |||
71 | /* should fail (not memfd) */ | ||
72 | create.memfd = 0; /* stdin */ | ||
73 | create.offset = 0; | ||
74 | create.size = size; | ||
75 | buf = ioctl(devfd, UDMABUF_CREATE, &create); | ||
76 | if (buf >= 0) { | ||
77 | printf("%s: [FAIL,test-3]\n", TEST_PREFIX); | ||
78 | exit(1); | ||
79 | } | ||
80 | |||
81 | /* should work */ | ||
82 | create.memfd = memfd; | ||
83 | create.offset = 0; | ||
84 | create.size = size; | ||
85 | buf = ioctl(devfd, UDMABUF_CREATE, &create); | ||
86 | if (buf < 0) { | ||
87 | printf("%s: [FAIL,test-4]\n", TEST_PREFIX); | ||
88 | exit(1); | ||
89 | } | ||
90 | |||
91 | fprintf(stderr, "%s: ok\n", TEST_PREFIX); | ||
92 | close(buf); | ||
93 | close(memfd); | ||
94 | close(devfd); | ||
95 | return 0; | ||
96 | } | ||