diff options
author | Dave Airlie <airlied@redhat.com> | 2020-02-20 08:21:02 +0300 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2020-02-20 22:44:40 +0300 |
commit | 1b245ec5b685ebf8e6e5d1e6b5bcc03b6608e8b0 (patch) | |
tree | 07cfbfb9b12a0d5a3f5c892115ac9a06104559af | |
parent | 11a48a5a18c63fd7621bb050228cebf13566e4d8 (diff) | |
parent | 06f749af622ca28c4e1f60c43fabd3917114f95a (diff) | |
download | linux-1b245ec5b685ebf8e6e5d1e6b5bcc03b6608e8b0.tar.xz |
Merge tag 'drm-misc-next-2020-02-10' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.7:
UAPI Changes:
- lima: Add support for heap buffers
Cross-subsystem Changes:
Core Changes:
- Implement mode_config mode_valid for memory constrained drivers
- Bus format negociation between bridges
- Consolidate fake vblank events for drivers without vblank interrupts
- drm/bufs: dma_alloc related cleanups
- drm/dp_mst: Various fixes
- drm/print: New drm_device based print helpers
- Thomas is a drm-misc maintainer now!
Driver Changes:
- DPMS cleanups for atomic drivers
- Removal of owner field in SPI tinydrm drivers
- Removal of explicit dependency on DT for tinydrm drivers
- Conversion to YAML schemas for DT bindings
- tidss: New driver
- virtio: various reworks and fixes
- Our usual dozen or so new panels or bridges
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20200210093421.xu4sofldm6wm6xq6@gilmour.lan
261 files changed, 11248 insertions, 1833 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/ps8640.yaml b/Documentation/devicetree/bindings/display/bridge/ps8640.yaml new file mode 100644 index 000000000000..5dff93641bea --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/ps8640.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/ps8640.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MIPI DSI to eDP Video Format Converter Device Tree Bindings + +maintainers: + - Nicolas Boichat <drinkcat@chromium.org> + - Enric Balletbo i Serra <enric.balletbo@collabora.com> + +description: | + The PS8640 is a low power MIPI-to-eDP video format converter supporting + mobile devices with embedded panel resolutions up to 2048 x 1536. The + device accepts a single channel of MIPI DSI v1.1, with up to four lanes + plus clock, at a transmission rate up to 1.5Gbit/sec per lane. The + device outputs eDP v1.4, one or two lanes, at a link rate of up to + 3.24Gbit/sec per lane. + +properties: + compatible: + const: parade,ps8640 + + reg: + maxItems: 1 + description: Base I2C address of the device. + + powerdown-gpios: + maxItems: 1 + description: GPIO connected to active low powerdown. + + reset-gpios: + maxItems: 1 + description: GPIO connected to active low reset. + + vdd12-supply: + maxItems: 1 + description: Regulator for 1.2V digital core power. + + vdd33-supply: + maxItems: 1 + description: Regulator for 3.3V digital core power. + + ports: + type: object + description: + A node containing DSI input & output port nodes with endpoint + definitions as documented in + Documentation/devicetree/bindings/media/video-interfaces.txt + Documentation/devicetree/bindings/graph.txt + properties: + port@0: + type: object + description: | + Video port for DSI input + + port@1: + type: object + description: | + Video port for eDP output (panel or connector). + + required: + - port@0 + +required: + - compatible + - reg + - powerdown-gpios + - reset-gpios + - vdd12-supply + - vdd33-supply + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + ps8640: edp-bridge@18 { + compatible = "parade,ps8640"; + reg = <0x18>; + powerdown-gpios = <&pio 116 GPIO_ACTIVE_LOW>; + reset-gpios = <&pio 115 GPIO_ACTIVE_LOW>; + vdd12-supply = <&ps8640_fixed_1v2>; + vdd33-supply = <&mt6397_vgp2_reg>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ps8640_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + ps8640_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; + }; + diff --git a/Documentation/devicetree/bindings/display/ilitek,ili9486.yaml b/Documentation/devicetree/bindings/display/ilitek,ili9486.yaml new file mode 100644 index 000000000000..66e93e563653 --- /dev/null +++ b/Documentation/devicetree/bindings/display/ilitek,ili9486.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/ilitek,ili9486.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ILI9486 display panels device tree bindings + +maintainers: + - Kamlesh Gurudasani <kamlesh.gurudasani@gmail.com> + +description: + This binding is for display panels using an Ilitek ILI9486 controller in SPI + mode. + +allOf: + - $ref: panel/panel-common.yaml# + +properties: + compatible: + items: + - enum: + # Waveshare 3.5" 320x480 Color TFT LCD + - waveshare,rpi-lcd-35 + # Ozzmaker 3.5" 320x480 Color TFT LCD + - ozzmaker,piscreen + - const: ilitek,ili9486 + + spi-max-frequency: + maximum: 32000000 + + dc-gpios: + maxItems: 1 + description: Display data/command selection (D/CX) + + backlight: true + reg: true + reset-gpios: true + rotation: true + +required: + - compatible + - reg + - dc-gpios + - reset-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&gpio 22 GPIO_ACTIVE_HIGH>; + }; + spi { + #address-cells = <1>; + #size-cells = <0>; + + + display@0{ + compatible = "waveshare,rpi-lcd-35", "ilitek,ili9486"; + reg = <0>; + spi-max-frequency = <32000000>; + dc-gpios = <&gpio0 24 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>; + rotation = <180>; + backlight = <&backlight>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/auo,b080uan01.txt b/Documentation/devicetree/bindings/display/panel/auo,b080uan01.txt deleted file mode 100644 index bae0e2b51467..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,b080uan01.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 8.0" WUXGA TFT LCD panel - -Required properties: -- compatible: should be "auo,b101ean01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,b101aw03.txt b/Documentation/devicetree/bindings/display/panel/auo,b101aw03.txt deleted file mode 100644 index 72e088a4fb3a..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,b101aw03.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 10.1" WSVGA TFT LCD panel - -Required properties: -- compatible: should be "auo,b101aw03" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,b101ean01.txt b/Documentation/devicetree/bindings/display/panel/auo,b101ean01.txt deleted file mode 100644 index 3590b0741619..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,b101ean01.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 10.1" WSVGA TFT LCD panel - -Required properties: -- compatible: should be "auo,b101ean01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,b101xtn01.txt b/Documentation/devicetree/bindings/display/panel/auo,b101xtn01.txt deleted file mode 100644 index 889d511d66c9..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,b101xtn01.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 10.1" WXGA TFT LCD panel - -Required properties: -- compatible: should be "auo,b101xtn01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,b116xw03.txt b/Documentation/devicetree/bindings/display/panel/auo,b116xw03.txt deleted file mode 100644 index 690d0a568ef3..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,b116xw03.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 11.6" HD (1366x768) color TFT-LCD panel - -Required properties: -- compatible: should be "auo,b116xw03" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,b133htn01.txt b/Documentation/devicetree/bindings/display/panel/auo,b133htn01.txt deleted file mode 100644 index 302226b5bb55..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,b133htn01.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 13.3" FHD (1920x1080) color TFT-LCD panel - -Required properties: -- compatible: should be "auo,b133htn01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,b133xtn01.txt b/Documentation/devicetree/bindings/display/panel/auo,b133xtn01.txt deleted file mode 100644 index 7443b7c76769..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,b133xtn01.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 13.3" WXGA (1366x768) TFT LCD panel - -Required properties: -- compatible: should be "auo,b133xtn01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,g070vvn01.txt b/Documentation/devicetree/bindings/display/panel/auo,g070vvn01.txt deleted file mode 100644 index 49e4105378f6..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,g070vvn01.txt +++ /dev/null @@ -1,29 +0,0 @@ -AU Optronics Corporation 7.0" FHD (800 x 480) TFT LCD panel - -Required properties: -- compatible: should be "auo,g070vvn01" -- backlight: phandle of the backlight device attached to the panel -- power-supply: single regulator to provide the supply voltage - -Required nodes: -- port: Parallel port mapping to connect this display - -This panel needs single power supply voltage. Its backlight is conntrolled -via PWM signal. - -Example: --------- - -Example device-tree definition when connected to iMX6Q based board - - lcd_panel: lcd-panel { - compatible = "auo,g070vvn01"; - backlight = <&backlight_lcd>; - power-supply = <®_display>; - - port { - lcd_panel_in: endpoint { - remote-endpoint = <&lcd_display_out>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/panel/auo,g101evn010.txt b/Documentation/devicetree/bindings/display/panel/auo,g101evn010.txt deleted file mode 100644 index bc6a0c858e23..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,g101evn010.txt +++ /dev/null @@ -1,12 +0,0 @@ -AU Optronics Corporation 10.1" (1280x800) color TFT LCD panel - -Required properties: -- compatible: should be "auo,g101evn010" -- power-supply: as specified in the base binding - -Optional properties: -- backlight: as specified in the base binding -- enable-gpios: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,g104sn02.txt b/Documentation/devicetree/bindings/display/panel/auo,g104sn02.txt deleted file mode 100644 index 85626edf63e5..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,g104sn02.txt +++ /dev/null @@ -1,12 +0,0 @@ -AU Optronics Corporation 10.4" (800x600) color TFT LCD panel - -Required properties: -- compatible: should be "auo,g104sn02" -- power-supply: as specified in the base binding - -Optional properties: -- backlight: as specified in the base binding -- enable-gpios: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,g133han01.txt b/Documentation/devicetree/bindings/display/panel/auo,g133han01.txt deleted file mode 100644 index 3afc76747824..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,g133han01.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 13.3" FHD (1920x1080) TFT LCD panel - -Required properties: -- compatible: should be "auo,g133han01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,g185han01.txt b/Documentation/devicetree/bindings/display/panel/auo,g185han01.txt deleted file mode 100644 index ed657c2141d4..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,g185han01.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 18.5" FHD (1920x1080) TFT LCD panel - -Required properties: -- compatible: should be "auo,g185han01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,p320hvn03.txt b/Documentation/devicetree/bindings/display/panel/auo,p320hvn03.txt deleted file mode 100644 index 59bb6cd8aa75..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,p320hvn03.txt +++ /dev/null @@ -1,8 +0,0 @@ -AU Optronics Corporation 31.5" FHD (1920x1080) TFT LCD panel - -Required properties: -- compatible: should be "auo,p320hvn03" -- power-supply: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/auo,t215hvn01.txt b/Documentation/devicetree/bindings/display/panel/auo,t215hvn01.txt deleted file mode 100644 index cbd9da3f03b1..000000000000 --- a/Documentation/devicetree/bindings/display/panel/auo,t215hvn01.txt +++ /dev/null @@ -1,7 +0,0 @@ -AU Optronics Corporation 21.5" FHD (1920x1080) color TFT LCD panel - -Required properties: -- compatible: should be "auo,t215hvn01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/avic,tm070ddh03.txt b/Documentation/devicetree/bindings/display/panel/avic,tm070ddh03.txt deleted file mode 100644 index b6f2f3e8f44e..000000000000 --- a/Documentation/devicetree/bindings/display/panel/avic,tm070ddh03.txt +++ /dev/null @@ -1,7 +0,0 @@ -Shanghai AVIC Optoelectronics 7" 1024x600 color TFT-LCD panel - -Required properties: -- compatible: should be "avic,tm070ddh03" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/boe,hv070wsa-100.txt b/Documentation/devicetree/bindings/display/panel/boe,hv070wsa-100.txt deleted file mode 100644 index 55183d360032..000000000000 --- a/Documentation/devicetree/bindings/display/panel/boe,hv070wsa-100.txt +++ /dev/null @@ -1,28 +0,0 @@ -BOE HV070WSA-100 7.01" WSVGA TFT LCD panel - -Required properties: -- compatible: should be "boe,hv070wsa-100" -- power-supply: regulator to provide the VCC supply voltage (3.3 volts) -- enable-gpios: GPIO pin to enable and disable panel (active high) - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. - -The device node can contain one 'port' child node with one child -'endpoint' node, according to the bindings defined in [1]. This -node should describe panel's video bus. - -[1]: Documentation/devicetree/bindings/media/video-interfaces.txt - -Example: - - panel: panel { - compatible = "boe,hv070wsa-100"; - power-supply = <&vcc_3v3_reg>; - enable-gpios = <&gpd1 3 GPIO_ACTIVE_HIGH>; - port { - panel_ep: endpoint { - remote-endpoint = <&bridge_out_ep>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/panel/boe,nv101wxmn51.txt b/Documentation/devicetree/bindings/display/panel/boe,nv101wxmn51.txt deleted file mode 100644 index b258d6a91ec6..000000000000 --- a/Documentation/devicetree/bindings/display/panel/boe,nv101wxmn51.txt +++ /dev/null @@ -1,7 +0,0 @@ -BOE OPTOELECTRONICS TECHNOLOGY 10.1" WXGA TFT LCD panel - -Required properties: -- compatible: should be "boe,nv101wxmn51" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/boe,tv080wum-nl0.txt b/Documentation/devicetree/bindings/display/panel/boe,tv080wum-nl0.txt deleted file mode 100644 index 50be5e2438b2..000000000000 --- a/Documentation/devicetree/bindings/display/panel/boe,tv080wum-nl0.txt +++ /dev/null @@ -1,7 +0,0 @@ -Boe Corporation 8.0" WUXGA TFT LCD panel - -Required properties: -- compatible: should be "boe,tv080wum-nl0" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/boe,tv101wum-nl6.yaml b/Documentation/devicetree/bindings/display/panel/boe,tv101wum-nl6.yaml new file mode 100644 index 000000000000..740213459134 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/boe,tv101wum-nl6.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/boe,tv101wum-nl6.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: BOE TV101WUM-NL6 DSI Display Panel + +maintainers: + - Thierry Reding <thierry.reding@gmail.com> + - Sam Ravnborg <sam@ravnborg.org> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + enum: + # BOE TV101WUM-NL6 10.1" WUXGA TFT LCD panel + - boe,tv101wum-nl6 + # AUO KD101N80-45NA 10.1" WUXGA TFT LCD panel + - auo,kd101n80-45na + # BOE TV101WUM-N53 10.1" WUXGA TFT LCD panel + - boe,tv101wum-n53 + # AUO B101UAN08.3 10.1" WUXGA TFT LCD panel + - auo,b101uan08.3 + + reg: + description: the virtual channel number of a DSI peripheral + + enable-gpios: + description: a GPIO spec for the enable pin + + pp1800-supply: + description: core voltage supply + + avdd-supply: + description: phandle of the regulator that provides positive voltage + + avee-supply: + description: phandle of the regulator that provides negative voltage + + backlight: + description: phandle of the backlight device attached to the panel + + port: true + +required: + - compatible + - reg + - enable-gpios + - pp1800-supply + - avdd-supply + - avee-supply + +additionalProperties: false + +examples: + - | + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "boe,tv101wum-nl6"; + reg = <0>; + enable-gpios = <&pio 45 0>; + avdd-supply = <&ppvarn_lcd>; + avee-supply = <&ppvarp_lcd>; + pp1800-supply = <&pp1800_lcd>; + backlight = <&backlight_lcd0>; + status = "okay"; + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/cdtech,s043wq26h-ct7.txt b/Documentation/devicetree/bindings/display/panel/cdtech,s043wq26h-ct7.txt deleted file mode 100644 index 057f7f3f6dbe..000000000000 --- a/Documentation/devicetree/bindings/display/panel/cdtech,s043wq26h-ct7.txt +++ /dev/null @@ -1,12 +0,0 @@ -CDTech(H.K.) Electronics Limited 4.3" 480x272 color TFT-LCD panel - -Required properties: -- compatible: should be "cdtech,s043wq26h-ct7" -- power-supply: as specified in the base binding - -Optional properties: -- backlight: as specified in the base binding -- enable-gpios: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/cdtech,s070wv95-ct16.txt b/Documentation/devicetree/bindings/display/panel/cdtech,s070wv95-ct16.txt deleted file mode 100644 index 505615dfa0df..000000000000 --- a/Documentation/devicetree/bindings/display/panel/cdtech,s070wv95-ct16.txt +++ /dev/null @@ -1,12 +0,0 @@ -CDTech(H.K.) Electronics Limited 7" 800x480 color TFT-LCD panel - -Required properties: -- compatible: should be "cdtech,s070wv95-ct16" -- power-supply: as specified in the base binding - -Optional properties: -- backlight: as specified in the base binding -- enable-gpios: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/chunghwa,claa070wp03xg.txt b/Documentation/devicetree/bindings/display/panel/chunghwa,claa070wp03xg.txt deleted file mode 100644 index dd22685d2adc..000000000000 --- a/Documentation/devicetree/bindings/display/panel/chunghwa,claa070wp03xg.txt +++ /dev/null @@ -1,7 +0,0 @@ -Chunghwa Picture Tubes Ltd. 7" WXGA TFT LCD panel - -Required properties: -- compatible: should be "chunghwa,claa070wp03xg" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/chunghwa,claa101wa01a.txt b/Documentation/devicetree/bindings/display/panel/chunghwa,claa101wa01a.txt deleted file mode 100644 index f24614e4d5ec..000000000000 --- a/Documentation/devicetree/bindings/display/panel/chunghwa,claa101wa01a.txt +++ /dev/null @@ -1,7 +0,0 @@ -Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel - -Required properties: -- compatible: should be "chunghwa,claa101wa01a" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/chunghwa,claa101wb03.txt b/Documentation/devicetree/bindings/display/panel/chunghwa,claa101wb03.txt deleted file mode 100644 index 0ab2c05a4c22..000000000000 --- a/Documentation/devicetree/bindings/display/panel/chunghwa,claa101wb03.txt +++ /dev/null @@ -1,7 +0,0 @@ -Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel - -Required properties: -- compatible: should be "chunghwa,claa101wb03" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/dataimage,scf0700c48ggu18.txt b/Documentation/devicetree/bindings/display/panel/dataimage,scf0700c48ggu18.txt deleted file mode 100644 index 897085ee3cd4..000000000000 --- a/Documentation/devicetree/bindings/display/panel/dataimage,scf0700c48ggu18.txt +++ /dev/null @@ -1,8 +0,0 @@ -DataImage, Inc. 7" WVGA (800x480) TFT LCD panel with 24-bit parallel interface. - -Required properties: -- compatible: should be "dataimage,scf0700c48ggu18" -- power-supply: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/dlc,dlc1010gig.txt b/Documentation/devicetree/bindings/display/panel/dlc,dlc1010gig.txt deleted file mode 100644 index fbf5dcd15661..000000000000 --- a/Documentation/devicetree/bindings/display/panel/dlc,dlc1010gig.txt +++ /dev/null @@ -1,12 +0,0 @@ -DLC Display Co. DLC1010GIG 10.1" WXGA TFT LCD Panel - -Required properties: -- compatible: should be "dlc,dlc1010gig" -- power-supply: See simple-panel.txt - -Optional properties: -- enable-gpios: See simple-panel.txt -- backlight: See simple-panel.txt - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/edt,et-series.txt b/Documentation/devicetree/bindings/display/panel/edt,et-series.txt deleted file mode 100644 index b7ac1c725f97..000000000000 --- a/Documentation/devicetree/bindings/display/panel/edt,et-series.txt +++ /dev/null @@ -1,55 +0,0 @@ -Emerging Display Technology Corp. Displays -========================================== - - -Display bindings for EDT Display Technology Corp. Displays which are -compatible with the simple-panel binding, which is specified in -simple-panel.txt - -3,5" QVGA TFT Panels --------------------- -+-----------------+---------------------+-------------------------------------+ -| Identifier | compatbile | description | -+=================+=====================+=====================================+ -| ET035012DM6 | edt,et035012dm6 | 3.5" QVGA TFT LCD panel | -+-----------------+---------------------+-------------------------------------+ - -4,3" WVGA TFT Panels --------------------- - -+-----------------+---------------------+-------------------------------------+ -| Identifier | compatbile | description | -+=================+=====================+=====================================+ -| ETM0430G0DH6 | edt,etm0430g0dh6 | 480x272 TFT Display | -+-----------------+---------------------+-------------------------------------+ - -5,7" WVGA TFT Panels --------------------- - -+-----------------+---------------------+-------------------------------------+ -| Identifier | compatbile | description | -+=================+=====================+=====================================+ -| ET057090DHU | edt,et057090dhu | 5.7" VGA TFT LCD panel | -+-----------------+---------------------+-------------------------------------+ - - -7,0" WVGA TFT Panels --------------------- - -+-----------------+---------------------+-------------------------------------+ -| Identifier | compatbile | description | -+=================+=====================+=====================================+ -| ETM0700G0DH6 | edt,etm070080dh6 | WVGA TFT Display with capacitive | -| | edt,etm0700g0dh6 | Touchscreen | -+-----------------+---------------------+-------------------------------------+ -| ETM0700G0BDH6 | edt,etm070080bdh6 | Same as ETM0700G0DH6 but with | -| | | inverted pixel clock. | -+-----------------+---------------------+-------------------------------------+ -| ETM0700G0EDH6 | edt,etm070080edh6 | Same display as the ETM0700G0BDH6, | -| | | but with changed Hardware for the | -| | | backlight and the touch interface | -+-----------------+---------------------+-------------------------------------+ -| ET070080DH6 | edt,etm070080dh6 | Same timings as the ETM0700G0DH6, | -| | | but with resistive touch. | -+-----------------+---------------------+-------------------------------------+ - diff --git a/Documentation/devicetree/bindings/display/panel/evervision,vgg804821.txt b/Documentation/devicetree/bindings/display/panel/evervision,vgg804821.txt deleted file mode 100644 index 82d22e191ac3..000000000000 --- a/Documentation/devicetree/bindings/display/panel/evervision,vgg804821.txt +++ /dev/null @@ -1,12 +0,0 @@ -Evervision Electronics Co. Ltd. VGG804821 5.0" WVGA TFT LCD Panel - -Required properties: -- compatible: should be "evervision,vgg804821" -- power-supply: See simple-panel.txt - -Optional properties: -- backlight: See simple-panel.txt -- enable-gpios: See simple-panel.txt - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/feixin,k101-im2ba02.yaml b/Documentation/devicetree/bindings/display/panel/feixin,k101-im2ba02.yaml new file mode 100644 index 000000000000..927f1eea18d2 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/feixin,k101-im2ba02.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/feixin,k101-im2ba02.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Feixin K101 IM2BA02 10.1" MIPI-DSI LCD panel + +maintainers: + - Icenowy Zheng <icenowy@aosc.io> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: feixin,k101-im2ba02 + reg: true + backlight: true + reset-gpios: true + avdd-supply: + description: regulator that supplies the AVDD voltage + dvdd-supply: + description: regulator that supplies the DVDD voltage + cvdd-supply: + description: regulator that supplies the CVDD voltage + +required: + - compatible + - reg + - avdd-supply + - dvdd-supply + - cvdd-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "feixin,k101-im2ba02"; + reg = <0>; + avdd-supply = <®_dc1sw>; + dvdd-supply = <®_dc1sw>; + cvdd-supply = <®_ldo_io1>; + reset-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; + backlight = <&backlight>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/foxlink,fl500wvr00-a0t.txt b/Documentation/devicetree/bindings/display/panel/foxlink,fl500wvr00-a0t.txt deleted file mode 100644 index b47f9d87bc19..000000000000 --- a/Documentation/devicetree/bindings/display/panel/foxlink,fl500wvr00-a0t.txt +++ /dev/null @@ -1,7 +0,0 @@ -Foxlink Group 5" WVGA TFT LCD panel - -Required properties: -- compatible: should be "foxlink,fl500wvr00-a0t" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/friendlyarm,hd702e.txt b/Documentation/devicetree/bindings/display/panel/friendlyarm,hd702e.txt deleted file mode 100644 index 6c9156fc3478..000000000000 --- a/Documentation/devicetree/bindings/display/panel/friendlyarm,hd702e.txt +++ /dev/null @@ -1,32 +0,0 @@ -FriendlyELEC HD702E 800x1280 LCD panel - -HD702E lcd is FriendlyELEC developed eDP LCD panel with 800x1280 -resolution. It has built in Goodix, GT9271 captive touchscreen -with backlight adjustable via PWM. - -Required properties: -- compatible: should be "friendlyarm,hd702e" -- power-supply: regulator to provide the supply voltage - -Optional properties: -- backlight: phandle of the backlight device attached to the panel - -Optional nodes: -- Video port for LCD panel input. - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. - -Example: - - panel { - compatible ="friendlyarm,hd702e", "simple-panel"; - backlight = <&backlight>; - power-supply = <&vcc3v3_sys>; - - port { - panel_in_edp: endpoint { - remote-endpoint = <&edp_out_panel>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/panel/giantplus,gpg482739qs5.txt b/Documentation/devicetree/bindings/display/panel/giantplus,gpg482739qs5.txt deleted file mode 100644 index 24b0b624434b..000000000000 --- a/Documentation/devicetree/bindings/display/panel/giantplus,gpg482739qs5.txt +++ /dev/null @@ -1,7 +0,0 @@ -GiantPlus GPG48273QS5 4.3" (480x272) WQVGA TFT LCD panel - -Required properties: -- compatible: should be "giantplus,gpg48273qs5" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/hannstar,hsd070pww1.txt b/Documentation/devicetree/bindings/display/panel/hannstar,hsd070pww1.txt deleted file mode 100644 index 7da1d5c038ff..000000000000 --- a/Documentation/devicetree/bindings/display/panel/hannstar,hsd070pww1.txt +++ /dev/null @@ -1,7 +0,0 @@ -HannStar Display Corp. HSD070PWW1 7.0" WXGA TFT LCD panel - -Required properties: -- compatible: should be "hannstar,hsd070pww1" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/hannstar,hsd100pxn1.txt b/Documentation/devicetree/bindings/display/panel/hannstar,hsd100pxn1.txt deleted file mode 100644 index 8270319a99de..000000000000 --- a/Documentation/devicetree/bindings/display/panel/hannstar,hsd100pxn1.txt +++ /dev/null @@ -1,7 +0,0 @@ -HannStar Display Corp. HSD100PXN1 10.1" XGA LVDS panel - -Required properties: -- compatible: should be "hannstar,hsd100pxn1" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/hit,tx23d38vm0caa.txt b/Documentation/devicetree/bindings/display/panel/hit,tx23d38vm0caa.txt deleted file mode 100644 index 04caaae19af6..000000000000 --- a/Documentation/devicetree/bindings/display/panel/hit,tx23d38vm0caa.txt +++ /dev/null @@ -1,7 +0,0 @@ -Hitachi Ltd. Corporation 9" WVGA (800x480) TFT LCD panel - -Required properties: -- compatible: should be "hit,tx23d38vm0caa" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,at043tn24.txt b/Documentation/devicetree/bindings/display/panel/innolux,at043tn24.txt deleted file mode 100644 index 4104226b61bc..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,at043tn24.txt +++ /dev/null @@ -1,7 +0,0 @@ -Innolux AT043TN24 4.3" WQVGA TFT LCD panel - -Required properties: -- compatible: should be "innolux,at043tn24" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,at070tn92.txt b/Documentation/devicetree/bindings/display/panel/innolux,at070tn92.txt deleted file mode 100644 index 3e10cd782491..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,at070tn92.txt +++ /dev/null @@ -1,7 +0,0 @@ -Innolux AT070TN92 7.0" WQVGA TFT LCD panel - -Required properties: -- compatible: should be "innolux,at070tn92" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,g070y2-l01.txt b/Documentation/devicetree/bindings/display/panel/innolux,g070y2-l01.txt deleted file mode 100644 index 7c234cf68e11..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,g070y2-l01.txt +++ /dev/null @@ -1,12 +0,0 @@ -Innolux G070Y2-L01 7" WVGA (800x480) TFT LCD panel - -Required properties: -- compatible: should be "innolux,g070y2-l01" -- power-supply: as specified in the base binding - -Optional properties: -- backlight: as specified in the base binding -- enable-gpios: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,g101ice-l01.txt b/Documentation/devicetree/bindings/display/panel/innolux,g101ice-l01.txt deleted file mode 100644 index 9e7590465227..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,g101ice-l01.txt +++ /dev/null @@ -1,7 +0,0 @@ -Innolux Corporation 10.1" G101ICE-L01 WXGA (1280x800) LVDS panel - -Required properties: -- compatible: should be "innolux,g101ice-l01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,g121i1-l01.txt b/Documentation/devicetree/bindings/display/panel/innolux,g121i1-l01.txt deleted file mode 100644 index 2743b07cd2f2..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,g121i1-l01.txt +++ /dev/null @@ -1,7 +0,0 @@ -Innolux Corporation 12.1" WXGA (1280x800) TFT LCD panel - -Required properties: -- compatible: should be "innolux,g121i1-l01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,g121x1-l03.txt b/Documentation/devicetree/bindings/display/panel/innolux,g121x1-l03.txt deleted file mode 100644 index 649744620ae1..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,g121x1-l03.txt +++ /dev/null @@ -1,7 +0,0 @@ -Innolux Corporation 12.1" G121X1-L03 XGA (1024x768) TFT LCD panel - -Required properties: -- compatible: should be "innolux,g121x1-l03" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,n116bge.txt b/Documentation/devicetree/bindings/display/panel/innolux,n116bge.txt deleted file mode 100644 index 081bb939ed31..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,n116bge.txt +++ /dev/null @@ -1,7 +0,0 @@ -Innolux Corporation 11.6" WXGA (1366x768) TFT LCD panel - -Required properties: -- compatible: should be "innolux,n116bge" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,n156bge-l21.txt b/Documentation/devicetree/bindings/display/panel/innolux,n156bge-l21.txt deleted file mode 100644 index 7825844aafdf..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,n156bge-l21.txt +++ /dev/null @@ -1,7 +0,0 @@ -InnoLux 15.6" WXGA TFT LCD panel - -Required properties: -- compatible: should be "innolux,n156bge-l21" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/innolux,zj070na-01p.txt b/Documentation/devicetree/bindings/display/panel/innolux,zj070na-01p.txt deleted file mode 100644 index 824f87f1526d..000000000000 --- a/Documentation/devicetree/bindings/display/panel/innolux,zj070na-01p.txt +++ /dev/null @@ -1,7 +0,0 @@ -Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel - -Required properties: -- compatible: should be "innolux,zj070na-01p" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/koe,tx14d24vm1bpa.txt b/Documentation/devicetree/bindings/display/panel/koe,tx14d24vm1bpa.txt deleted file mode 100644 index be7ac666807b..000000000000 --- a/Documentation/devicetree/bindings/display/panel/koe,tx14d24vm1bpa.txt +++ /dev/null @@ -1,42 +0,0 @@ -Kaohsiung Opto-Electronics Inc. 5.7" QVGA (320 x 240) TFT LCD panel - -Required properties: -- compatible: should be "koe,tx14d24vm1bpa" -- backlight: phandle of the backlight device attached to the panel -- power-supply: single regulator to provide the supply voltage - -Required nodes: -- port: Parallel port mapping to connect this display - -This panel needs single power supply voltage. Its backlight is conntrolled -via PWM signal. - -Example: --------- - -Example device-tree definition when connected to iMX53 based board - - lcd_panel: lcd-panel { - compatible = "koe,tx14d24vm1bpa"; - backlight = <&backlight_lcd>; - power-supply = <®_3v3>; - - port { - lcd_panel_in: endpoint { - remote-endpoint = <&lcd_display_out>; - }; - }; - }; - -Then one needs to extend the dispX node: - - lcd_display: disp1 { - - port@1 { - reg = <1>; - - lcd_display_out: endpoint { - remote-endpoint = <&lcd_panel_in>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/panel/koe,tx31d200vm0baa.txt b/Documentation/devicetree/bindings/display/panel/koe,tx31d200vm0baa.txt deleted file mode 100644 index 6a036ede3e28..000000000000 --- a/Documentation/devicetree/bindings/display/panel/koe,tx31d200vm0baa.txt +++ /dev/null @@ -1,25 +0,0 @@ -Kaohsiung Opto-Electronics. TX31D200VM0BAA 12.3" HSXGA LVDS panel - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. - -Required properties: -- compatible: should be "koe,tx31d200vm0baa" - -Optional properties: -- backlight: phandle of the backlight device attached to the panel - -Optional nodes: -- Video port for LVDS panel input. - -Example: - panel { - compatible = "koe,tx31d200vm0baa"; - backlight = <&backlight_lvds>; - - port { - panel_in: endpoint { - remote-endpoint = <&lvds0_out>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/panel/kyo,tcg121xglp.txt b/Documentation/devicetree/bindings/display/panel/kyo,tcg121xglp.txt deleted file mode 100644 index a8e940fe731e..000000000000 --- a/Documentation/devicetree/bindings/display/panel/kyo,tcg121xglp.txt +++ /dev/null @@ -1,7 +0,0 @@ -Kyocera Corporation 12.1" XGA (1024x768) TFT LCD panel - -Required properties: -- compatible: should be "kyo,tcg121xglp" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/lemaker,bl035-rgb-002.txt b/Documentation/devicetree/bindings/display/panel/lemaker,bl035-rgb-002.txt deleted file mode 100644 index 74ee7ea6b493..000000000000 --- a/Documentation/devicetree/bindings/display/panel/lemaker,bl035-rgb-002.txt +++ /dev/null @@ -1,12 +0,0 @@ -LeMaker BL035-RGB-002 3.5" QVGA TFT LCD panel - -Required properties: -- compatible: should be "lemaker,bl035-rgb-002" -- power-supply: as specified in the base binding - -Optional properties: -- backlight: as specified in the base binding -- enable-gpios: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/lg,lb070wv8.txt b/Documentation/devicetree/bindings/display/panel/lg,lb070wv8.txt deleted file mode 100644 index a7588e5259cf..000000000000 --- a/Documentation/devicetree/bindings/display/panel/lg,lb070wv8.txt +++ /dev/null @@ -1,7 +0,0 @@ -LG 7" (800x480 pixels) TFT LCD panel - -Required properties: -- compatible: should be "lg,lb070wv8" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/lg,lp079qx1-sp0v.txt b/Documentation/devicetree/bindings/display/panel/lg,lp079qx1-sp0v.txt deleted file mode 100644 index b9877acad012..000000000000 --- a/Documentation/devicetree/bindings/display/panel/lg,lp079qx1-sp0v.txt +++ /dev/null @@ -1,7 +0,0 @@ -LG LP079QX1-SP0V 7.9" (1536x2048 pixels) TFT LCD panel - -Required properties: -- compatible: should be "lg,lp079qx1-sp0v" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/lg,lp097qx1-spa1.txt b/Documentation/devicetree/bindings/display/panel/lg,lp097qx1-spa1.txt deleted file mode 100644 index 42141516f078..000000000000 --- a/Documentation/devicetree/bindings/display/panel/lg,lp097qx1-spa1.txt +++ /dev/null @@ -1,7 +0,0 @@ -LG 9.7" (2048x1536 pixels) TFT LCD panel - -Required properties: -- compatible: should be "lg,lp097qx1-spa1" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/lg,lp120up1.txt b/Documentation/devicetree/bindings/display/panel/lg,lp120up1.txt deleted file mode 100644 index 8c5de692c55c..000000000000 --- a/Documentation/devicetree/bindings/display/panel/lg,lp120up1.txt +++ /dev/null @@ -1,7 +0,0 @@ -LG 12.0" (1920x1280 pixels) TFT LCD panel - -Required properties: -- compatible: should be "lg,lp120up1" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/lg,lp129qe.txt b/Documentation/devicetree/bindings/display/panel/lg,lp129qe.txt deleted file mode 100644 index 9f262e0c5a2e..000000000000 --- a/Documentation/devicetree/bindings/display/panel/lg,lp129qe.txt +++ /dev/null @@ -1,7 +0,0 @@ -LG 12.9" (2560x1700 pixels) TFT LCD panel - -Required properties: -- compatible: should be "lg,lp129qe" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/mitsubishi,aa070mc01.txt b/Documentation/devicetree/bindings/display/panel/mitsubishi,aa070mc01.txt deleted file mode 100644 index 7d8f6eeef6d9..000000000000 --- a/Documentation/devicetree/bindings/display/panel/mitsubishi,aa070mc01.txt +++ /dev/null @@ -1,7 +0,0 @@ -Mitsubishi "AA070MC01 7.0" WVGA TFT LCD panel - -Required properties: -- compatible: should be "mitsubishi,aa070mc01-ca1" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/nec,nl12880b20-05.txt b/Documentation/devicetree/bindings/display/panel/nec,nl12880b20-05.txt deleted file mode 100644 index 71cbc49ecfab..000000000000 --- a/Documentation/devicetree/bindings/display/panel/nec,nl12880b20-05.txt +++ /dev/null @@ -1,8 +0,0 @@ -NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel - -Required properties: -- compatible: should be "nec,nl12880bc20-05" -- power-supply: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/nec,nl4827hc19-05b.txt b/Documentation/devicetree/bindings/display/panel/nec,nl4827hc19-05b.txt deleted file mode 100644 index 8e1914d1edb8..000000000000 --- a/Documentation/devicetree/bindings/display/panel/nec,nl4827hc19-05b.txt +++ /dev/null @@ -1,7 +0,0 @@ -NEC LCD Technologies,Ltd. WQVGA TFT LCD panel - -Required properties: -- compatible: should be "nec,nl4827hc19-05b" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/netron-dy,e231732.txt b/Documentation/devicetree/bindings/display/panel/netron-dy,e231732.txt deleted file mode 100644 index c6d06b5eab51..000000000000 --- a/Documentation/devicetree/bindings/display/panel/netron-dy,e231732.txt +++ /dev/null @@ -1,7 +0,0 @@ -Netron-DY E231732 7.0" WSVGA TFT LCD panel - -Required properties: -- compatible: should be "netron-dy,e231732" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt b/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt deleted file mode 100644 index e78292b1a131..000000000000 --- a/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt +++ /dev/null @@ -1,7 +0,0 @@ -Newhaven Display International 480 x 272 TFT LCD panel - -Required properties: -- compatible: should be "newhaven,nhd-4.3-480272ef-atxl" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/nlt,nl192108ac18-02d.txt b/Documentation/devicetree/bindings/display/panel/nlt,nl192108ac18-02d.txt deleted file mode 100644 index 1a639fd8778d..000000000000 --- a/Documentation/devicetree/bindings/display/panel/nlt,nl192108ac18-02d.txt +++ /dev/null @@ -1,8 +0,0 @@ -NLT Technologies, Ltd. 15.6" FHD (1920x1080) LVDS TFT LCD panel - -Required properties: -- compatible: should be "nlt,nl192108ac18-02d" -- power-supply: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/nvd,9128.txt b/Documentation/devicetree/bindings/display/panel/nvd,9128.txt deleted file mode 100644 index 17bcd017c678..000000000000 --- a/Documentation/devicetree/bindings/display/panel/nvd,9128.txt +++ /dev/null @@ -1,7 +0,0 @@ -New Vision Display 7.0" 800 RGB x 480 TFT LCD panel - -Required properties: -- compatible: should be "nvd,9128" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/okaya,rs800480t-7x0gp.txt b/Documentation/devicetree/bindings/display/panel/okaya,rs800480t-7x0gp.txt deleted file mode 100644 index ddf8e211d382..000000000000 --- a/Documentation/devicetree/bindings/display/panel/okaya,rs800480t-7x0gp.txt +++ /dev/null @@ -1,7 +0,0 @@ -OKAYA Electric America, Inc. RS800480T-7X0GP 7" WVGA LCD panel - -Required properties: -- compatible: should be "okaya,rs800480t-7x0gp" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino-43-ts.txt b/Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino-43-ts.txt deleted file mode 100644 index 74540a090669..000000000000 --- a/Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino-43-ts.txt +++ /dev/null @@ -1,7 +0,0 @@ -Olimex 4.3" TFT LCD panel - -Required properties: -- compatible: should be "olimex,lcd-olinuxino-43-ts" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/ontat,yx700wv03.txt b/Documentation/devicetree/bindings/display/panel/ontat,yx700wv03.txt deleted file mode 100644 index 3d8a5e029242..000000000000 --- a/Documentation/devicetree/bindings/display/panel/ontat,yx700wv03.txt +++ /dev/null @@ -1,7 +0,0 @@ -On Tat Industrial Company 7" DPI TFT panel. - -Required properties: -- compatible: should be "ontat,yx700wv03" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt deleted file mode 100644 index 203b03eefb68..000000000000 --- a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.txt +++ /dev/null @@ -1,23 +0,0 @@ -Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode) - -The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using -a MIPI-DSI video interface. Its backlight is managed through the DSI link. - -Required properties: - - compatible: "orisetech,otm8009a" - - reg: the virtual channel number of a DSI peripheral - -Optional properties: - - reset-gpios: a GPIO spec for the reset pin (active low). - - power-supply: phandle of the regulator that provides the supply voltage. - -Example: -&dsi { - ... - panel@0 { - compatible = "orisetech,otm8009a"; - reg = <0>; - reset-gpios = <&gpioh 7 GPIO_ACTIVE_LOW>; - power-supply = <&v1v8>; - }; -}; diff --git a/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.yaml b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.yaml new file mode 100644 index 000000000000..6e6ac995c27b --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/orisetech,otm8009a.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/orisetech,otm8009a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Orise Tech OTM8009A 3.97" 480x800 TFT LCD panel (MIPI-DSI video mode) + +maintainers: + - Philippe CORNU <philippe.cornu@st.com> + +description: | + The Orise Tech OTM8009A is a 3.97" 480x800 TFT LCD panel connected using + a MIPI-DSI video interface. Its backlight is managed through the DSI link. +allOf: + - $ref: panel-common.yaml# + +properties: + + compatible: + const: orisetech,otm8009a + + reg: + maxItems: 1 + description: DSI virtual channel + + enable-gpios: true + port: true + power-supply: true + + reset-gpios: + maxItems: 1 + +additionalProperties: false + +required: + - compatible + - reg + +examples: + - | + dsi@0 { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "orisetech,otm8009a"; + reg = <0>; + reset-gpios = <&gpiof 15 0>; + power-supply = <&v1v8>; + }; + }; +... + diff --git a/Documentation/devicetree/bindings/display/panel/ortustech,com37h3m05dtc.txt b/Documentation/devicetree/bindings/display/panel/ortustech,com37h3m05dtc.txt deleted file mode 100644 index c16907c02f80..000000000000 --- a/Documentation/devicetree/bindings/display/panel/ortustech,com37h3m05dtc.txt +++ /dev/null @@ -1,12 +0,0 @@ -OrtusTech COM37H3M05DTC Blanview 3.7" VGA portrait TFT-LCD panel - -Required properties: -- compatible: should be "ortustech,com37h3m05dtc" - -Optional properties: -- enable-gpios: GPIO pin to enable or disable the panel -- backlight: phandle of the backlight device attached to the panel -- power-supply: phandle of the regulator that provides the supply voltage - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/ortustech,com37h3m99dtc.txt b/Documentation/devicetree/bindings/display/panel/ortustech,com37h3m99dtc.txt deleted file mode 100644 index 06a73c3f46b5..000000000000 --- a/Documentation/devicetree/bindings/display/panel/ortustech,com37h3m99dtc.txt +++ /dev/null @@ -1,12 +0,0 @@ -OrtusTech COM37H3M99DTC Blanview 3.7" VGA portrait TFT-LCD panel - -Required properties: -- compatible: should be "ortustech,com37h3m99dtc" - -Optional properties: -- enable-gpios: GPIO pin to enable or disable the panel -- backlight: phandle of the backlight device attached to the panel -- power-supply: phandle of the regulator that provides the supply voltage - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/ortustech,com43h4m85ulc.txt b/Documentation/devicetree/bindings/display/panel/ortustech,com43h4m85ulc.txt deleted file mode 100644 index de19e9398618..000000000000 --- a/Documentation/devicetree/bindings/display/panel/ortustech,com43h4m85ulc.txt +++ /dev/null @@ -1,7 +0,0 @@ -OrtusTech COM43H4M85ULC Blanview 3.7" TFT-LCD panel - -Required properties: -- compatible: should be "ortustech,com43h4m85ulc" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/osddisplays,osd070t1718-19ts.txt b/Documentation/devicetree/bindings/display/panel/osddisplays,osd070t1718-19ts.txt deleted file mode 100644 index e57883ccdf2f..000000000000 --- a/Documentation/devicetree/bindings/display/panel/osddisplays,osd070t1718-19ts.txt +++ /dev/null @@ -1,12 +0,0 @@ -OSD Displays OSD070T1718-19TS 7" WVGA TFT LCD panel - -Required properties: -- compatible: shall be "osddisplays,osd070t1718-19ts" -- power-supply: see simple-panel.txt - -Optional properties: -- backlight: see simple-panel.txt - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. No other simple-panel properties than -the ones specified herein are valid. diff --git a/Documentation/devicetree/bindings/display/panel/osddisplays,osd101t2045-53ts.txt b/Documentation/devicetree/bindings/display/panel/osddisplays,osd101t2045-53ts.txt deleted file mode 100644 index 85c0b2cacfda..000000000000 --- a/Documentation/devicetree/bindings/display/panel/osddisplays,osd101t2045-53ts.txt +++ /dev/null @@ -1,11 +0,0 @@ -One Stop Displays OSD101T2045-53TS 10.1" 1920x1200 panel - -Required properties: -- compatible: should be "osddisplays,osd101t2045-53ts" -- power-supply: as specified in the base binding - -Optional properties: -- backlight: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/panasonic,vvx10f004b00.txt b/Documentation/devicetree/bindings/display/panel/panasonic,vvx10f004b00.txt deleted file mode 100644 index d328b0341bf4..000000000000 --- a/Documentation/devicetree/bindings/display/panel/panasonic,vvx10f004b00.txt +++ /dev/null @@ -1,7 +0,0 @@ -Panasonic Corporation 10.1" WUXGA TFT LCD panel - -Required properties: -- compatible: should be "panasonic,vvx10f004b00" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/panasonic,vvx10f034n00.txt b/Documentation/devicetree/bindings/display/panel/panasonic,vvx10f034n00.txt deleted file mode 100644 index 37dedf6a6702..000000000000 --- a/Documentation/devicetree/bindings/display/panel/panasonic,vvx10f034n00.txt +++ /dev/null @@ -1,20 +0,0 @@ -Panasonic 10" WUXGA TFT LCD panel - -Required properties: -- compatible: should be "panasonic,vvx10f034n00" -- reg: DSI virtual channel of the peripheral -- power-supply: phandle of the regulator that provides the supply voltage - -Optional properties: -- backlight: phandle of the backlight device attached to the panel - -Example: - - mdss_dsi@fd922800 { - panel@0 { - compatible = "panasonic,vvx10f034n00"; - reg = <0>; - power-supply = <&vreg_vsp>; - backlight = <&lp8566_wled>; - }; - }; diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml new file mode 100644 index 000000000000..8b60368a2425 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/panel-simple-dsi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Simple DSI panels with a single power-supply + +maintainers: + - Thierry Reding <thierry.reding@gmail.com> + - Sam Ravnborg <sam@ravnborg.org> + +description: | + This binding file is a collection of the DSI panels that + requires only a single power-supply. + There are optionally a backlight and an enable GPIO. + The panel may use an OF graph binding for the association to the display, + or it may be a direct child node of the display. + + If the panel is more advanced a dedicated binding file is required. + +allOf: + - $ref: panel-common.yaml# + +properties: + + compatible: + enum: + # compatible must be listed in alphabetical order, ordered by compatible. + # The description in the comment is mandatory for each compatible. + + # Panasonic 10" WUXGA TFT LCD panel + - panasonic,vvx10f034n00 + + reg: + maxItems: 1 + description: DSI virtual channel + + backlight: true + enable-gpios: true + port: true + power-supply: true + +additionalProperties: false + +required: + - compatible + - power-supply + - reg + +examples: + - | + mdss_dsi@fd922800 { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "panasonic,vvx10f034n00"; + reg = <0>; + power-supply = <&vcc_lcd_reg>; + + port { + panel: endpoint { + remote-endpoint = <<dc_out>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 8fe60ee2531c..cf23b0ad077a 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -33,16 +33,221 @@ properties: - ampire,am-480272h3tmqw-t01h # Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel - ampire,am800480r3tmqwa1h + # AU Optronics Corporation 8.0" WUXGA TFT LCD panel + - auo,b080uan01 + # AU Optronics Corporation 10.1" WSVGA TFT LCD panel + - auo,b101aw03 + # AU Optronics Corporation 10.1" WSVGA TFT LCD panel + - auo,b101ean01 + # AU Optronics Corporation 10.1" WXGA TFT LCD panel + - auo,b101xtn01 # AUO B116XAK01 eDP TFT LCD panel - auo,b116xa01 + # AU Optronics Corporation 11.6" HD (1366x768) color TFT-LCD panel + - auo,b116xw03 + # AU Optronics Corporation 13.3" FHD (1920x1080) color TFT-LCD panel + - auo,b133htn01 + # AU Optronics Corporation 13.3" WXGA (1366x768) TFT LCD panel + - auo,b133xtn01 + # AU Optronics Corporation 7.0" FHD (800 x 480) TFT LCD panel + - auo,g070vvn01 + # AU Optronics Corporation 10.1" (1280x800) color TFT LCD panel + - auo,g101evn010 + # AU Optronics Corporation 10.4" (800x600) color TFT LCD panel + - auo,g104sn02 + # AU Optronics Corporation 13.3" FHD (1920x1080) TFT LCD panel + - auo,g133han01 + # AU Optronics Corporation 18.5" FHD (1920x1080) TFT LCD panel + - auo,g185han01 + # AU Optronics Corporation 31.5" FHD (1920x1080) TFT LCD panel + - auo,p320hvn03 + # AU Optronics Corporation 21.5" FHD (1920x1080) color TFT LCD panel + - auo,t215hvn01 + # Shanghai AVIC Optoelectronics 7" 1024x600 color TFT-LCD panel + - avic,tm070ddh03 + # BOE HV070WSA-100 7.01" WSVGA TFT LCD panel + - boe,hv070wsa-100 + # BOE OPTOELECTRONICS TECHNOLOGY 10.1" WXGA TFT LCD panel + - boe,nv101wxmn51 # BOE NV140FHM-N49 14.0" FHD a-Si FT panel - boe,nv140fhmn49 + # Boe Corporation 8.0" WUXGA TFT LCD panel + - boe,tv080wum-nl0 + # CDTech(H.K.) Electronics Limited 4.3" 480x272 color TFT-LCD panel + - cdtech,s043wq26h-ct7 + # CDTech(H.K.) Electronics Limited 7" 800x480 color TFT-LCD panel + - cdtech,s070wv95-ct16 + # Chunghwa Picture Tubes Ltd. 7" WXGA TFT LCD panel + - chunghwa,claa070wp03xg + # Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel + - chunghwa,claa101wa01a + # Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel + - chunghwa,claa101wb03 + # DataImage, Inc. 7" WVGA (800x480) TFT LCD panel with 24-bit parallel interface. + - dataimage,scf0700c48ggu18 + # DLC Display Co. DLC1010GIG 10.1" WXGA TFT LCD Panel + - dlc,dlc1010gig + # Emerging Display Technology Corp. 3.5" QVGA TFT LCD panel + - edt,et035012dm6 + # Emerging Display Technology Corp. 480x272 TFT Display with capacitive touch + - edt,etm043080dh6gp + # Emerging Display Technology Corp. 480x272 TFT Display + - edt,etm0430g0dh6 + # Emerging Display Technology Corp. 5.7" VGA TFT LCD panel + - edt,et057090dhu + # Emerging Display Technology Corp. WVGA TFT Display with capacitive touch + - edt,etm070080dh6 + # Emerging Display Technology Corp. WVGA TFT Display with capacitive touch + - edt,etm0700g0dh6 + # Emerging Display Technology Corp. WVGA TFT Display with capacitive touch + # Same as ETM0700G0DH6 but with inverted pixel clock. + - edt,etm070080bdh6 + # Emerging Display Technology Corp. WVGA TFT Display with capacitive touch + # Same display as the ETM0700G0BDH6, but with changed hardware for the + # backlight and the touch interface. + - edt,etm070080edh6 + # Emerging Display Technology Corp. WVGA TFT Display with capacitive touch + # Same timings as the ETM0700G0DH6, but with resistive touch. + - edt,etm070080dh6 + # Evervision Electronics Co. Ltd. VGG804821 5.0" WVGA TFT LCD Panel + - evervision,vgg804821 + # Foxlink Group 5" WVGA TFT LCD panel + - foxlink,fl500wvr00-a0t + # Frida FRD350H54004 3.5" QVGA TFT LCD panel + - frida,frd350h54004 + # FriendlyELEC HD702E 800x1280 LCD panel + - friendlyarm,hd702e + # GiantPlus GPG48273QS5 4.3" (480x272) WQVGA TFT LCD panel + - giantplus,gpg48273qs5 # GiantPlus GPM940B0 3.0" QVGA TFT LCD panel - giantplus,gpm940b0 + # HannStar Display Corp. HSD070PWW1 7.0" WXGA TFT LCD panel + - hannstar,hsd070pww1 + # HannStar Display Corp. HSD100PXN1 10.1" XGA LVDS panel + - hannstar,hsd100pxn1 + # Hitachi Ltd. Corporation 9" WVGA (800x480) TFT LCD panel + - hit,tx23d38vm0caa + # Innolux AT043TN24 4.3" WQVGA TFT LCD panel + - innolux,at043tn24 + # Innolux AT070TN92 7.0" WQVGA TFT LCD panel + - innolux,at070tn92 + # Innolux G070Y2-L01 7" WVGA (800x480) TFT LCD panel + - innolux,g070y2-l01 + # Innolux Corporation 10.1" G101ICE-L01 WXGA (1280x800) LVDS panel + - innolux,g101ice-l01 + # Innolux Corporation 12.1" WXGA (1280x800) TFT LCD panel + - innolux,g121i1-l01 + # Innolux Corporation 12.1" G121X1-L03 XGA (1024x768) TFT LCD panel + - innolux,g121x1-l03 + # Innolux Corporation 11.6" WXGA (1366x768) TFT LCD panel + - innolux,n116bge + # InnoLux 15.6" WXGA TFT LCD panel + - innolux,n156bge-l21 + # Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel + - innolux,zj070na-01p + # Kaohsiung Opto-Electronics Inc. 5.7" QVGA (320 x 240) TFT LCD panel + - koe,tx14d24vm1bpa + # Kaohsiung Opto-Electronics. TX31D200VM0BAA 12.3" HSXGA LVDS panel + - koe,tx31d200vm0baa + # Kyocera Corporation 12.1" XGA (1024x768) TFT LCD panel + - kyo,tcg121xglp + # LeMaker BL035-RGB-002 3.5" QVGA TFT LCD panel + - lemaker,bl035-rgb-002 + # LG 7" (800x480 pixels) TFT LCD panel + - lg,lb070wv8 + # LG LP079QX1-SP0V 7.9" (1536x2048 pixels) TFT LCD panel + - lg,lp079qx1-sp0v + # LG 9.7" (2048x1536 pixels) TFT LCD panel + - lg,lp097qx1-spa1 + # LG 12.0" (1920x1280 pixels) TFT LCD panel + - lg,lp120up1 + # LG 12.9" (2560x1700 pixels) TFT LCD panel + - lg,lp129qe + # Logic Technologies LT161010-2NHC 7" WVGA TFT Cap Touch Module + - logictechno,lt161010-2nhc + # Logic Technologies LT161010-2NHR 7" WVGA TFT Resistive Touch Module + - logictechno,lt161010-2nhr + # Logic Technologies LT170410-2WHC 10.1" 1280x800 IPS TFT Cap Touch Mod. + - logictechno,lt170410-2whc + # Mitsubishi "AA070MC01 7.0" WVGA TFT LCD panel + - mitsubishi,aa070mc01-ca1 + # NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel + - nec,nl12880bc20-05 + # NEC LCD Technologies,Ltd. WQVGA TFT LCD panel + - nec,nl4827hc19-05b + # Netron-DY E231732 7.0" WSVGA TFT LCD panel + - netron-dy,e231732 + # Newhaven Display International 480 x 272 TFT LCD panel + - newhaven,nhd-4.3-480272ef-atxl + # NLT Technologies, Ltd. 15.6" FHD (1920x1080) LVDS TFT LCD panel + - nlt,nl192108ac18-02d + # New Vision Display 7.0" 800 RGB x 480 TFT LCD panel + - nvd,9128 + # OKAYA Electric America, Inc. RS800480T-7X0GP 7" WVGA LCD panel + - okaya,rs800480t-7x0gp + # Olimex 4.3" TFT LCD panel + - olimex,lcd-olinuxino-43-ts + # On Tat Industrial Company 7" DPI TFT panel. + - ontat,yx700wv03 + # OrtusTech COM37H3M05DTC Blanview 3.7" VGA portrait TFT-LCD panel + - ortustech,com37h3m05dtc + # OrtusTech COM37H3M99DTC Blanview 3.7" VGA portrait TFT-LCD panel + - ortustech,com37h3m99dtc + # OrtusTech COM43H4M85ULC Blanview 3.7" TFT-LCD panel + - ortustech,com43h4m85ulc + # OSD Displays OSD070T1718-19TS 7" WVGA TFT LCD panel + - osddisplays,osd070t1718-19ts + # One Stop Displays OSD101T2045-53TS 10.1" 1920x1200 panel + - osddisplays,osd101t2045-53ts + # QiaoDian XianShi Corporation 4"3 TFT LCD panel + - qiaodian,qd43003c0-40 + # Rocktech Display Ltd. RK070ER9427 800(RGB)x480 TFT LCD panel + - rocktech,rk070er9427 + # Samsung 12.2" (2560x1600 pixels) TFT LCD panel + - samsung,lsn122dl01-c01 + # Samsung Electronics 10.1" WSVGA TFT LCD panel + - samsung,ltn101nt05 + # Samsung Electronics 14" WXGA (1366x768) TFT LCD panel + - samsung,ltn140at29-301 # Satoz SAT050AT40H12R2 5.0" WVGA TFT LCD panel - satoz,sat050at40h12r2 + # Sharp LQ035Q7DB03 3.5" QVGA TFT LCD panel + - sharp,lq035q7db03 + # Sharp LQ070Y3DG3B 7.0" WVGA landscape TFT LCD panel + - sharp,lq070y3dg3b + # Sharp Display Corp. LQ101K1LY04 10.07" WXGA TFT LCD panel + - sharp,lq101k1ly04 + # Sharp 12.3" (2400x1600 pixels) TFT LCD panel + - sharp,lq123p1jx31 # Sharp LS020B1DD01D 2.0" HQVGA TFT LCD panel - sharp,ls020b1dd01d + # Shelly SCA07010-BFN-LNN 7.0" WVGA TFT LCD panel + - shelly,sca07010-bfn-lnn + # Starry 12.2" (1920x1200 pixels) TFT LCD panel + - starry,kr122ea0sra + # Tianma Micro-electronics TM070JDHG30 7.0" WXGA TFT LCD panel + - tianma,tm070jdhg30 + # Tianma Micro-electronics TM070RVHG71 7.0" WXGA TFT LCD panel + - tianma,tm070rvhg71 + # Toshiba 8.9" WXGA (1280x768) TFT LCD panel + - toshiba,lt089ac29000 + # TPK U.S.A. LLC Fusion 7" 800 x 480 (WVGA) LCD panel with capacitive touch + - tpk,f07a-0102 + # TPK U.S.A. LLC Fusion 10.1" 1024 x 600 (WSVGA) LCD panel with capacitive touch + - tpk,f10a-0102 + # United Radiant Technology UMSH-8596MD-xT 7.0" WVGA TFT LCD panel + # Supported are LVDS versions (-11T, -19T) and parallel ones + # (-T, -1T, -7T, -20T). + - urt,umsh-8596md-t + - urt,umsh-8596md-1t + - urt,umsh-8596md-7t + - urt,umsh-8596md-11t + - urt,umsh-8596md-19t + - urt,umsh-8596md-20t + # VXT 800x480 color TFT LCD panel + - vxt,vl050-8048nt-c01 + # Winstar Display Corporation 3.5" QVGA (320x240) TFT LCD panel + - winstar,wf35ltiacd backlight: true enable-gpios: true diff --git a/Documentation/devicetree/bindings/display/panel/qiaodian,qd43003c0-40.txt b/Documentation/devicetree/bindings/display/panel/qiaodian,qd43003c0-40.txt deleted file mode 100644 index 0fbdab89ac3d..000000000000 --- a/Documentation/devicetree/bindings/display/panel/qiaodian,qd43003c0-40.txt +++ /dev/null @@ -1,7 +0,0 @@ -QiaoDian XianShi Corporation 4"3 TFT LCD panel - -Required properties: -- compatible: should be "qiaodian,qd43003c0-40" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm68200.txt b/Documentation/devicetree/bindings/display/panel/raydium,rm68200.txt deleted file mode 100644 index cbb79ef3bfc9..000000000000 --- a/Documentation/devicetree/bindings/display/panel/raydium,rm68200.txt +++ /dev/null @@ -1,25 +0,0 @@ -Raydium Semiconductor Corporation RM68200 5.5" 720p MIPI-DSI TFT LCD panel - -The Raydium Semiconductor Corporation RM68200 is a 5.5" 720x1280 TFT LCD -panel connected using a MIPI-DSI video interface. - -Required properties: - - compatible: "raydium,rm68200" - - reg: the virtual channel number of a DSI peripheral - -Optional properties: - - reset-gpios: a GPIO spec for the reset pin (active low). - - power-supply: phandle of the regulator that provides the supply voltage. - - backlight: phandle of the backlight device attached to the panel. - -Example: -&dsi { - ... - panel@0 { - compatible = "raydium,rm68200"; - reg = <0>; - reset-gpios = <&gpiof 15 GPIO_ACTIVE_LOW>; - power-supply = <&v1v8>; - backlight = <&pwm_backlight>; - }; -}; diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm68200.yaml b/Documentation/devicetree/bindings/display/panel/raydium,rm68200.yaml new file mode 100644 index 000000000000..09149f140d5f --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/raydium,rm68200.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/raydium,rm68200.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raydium Semiconductor Corporation RM68200 5.5" 720p MIPI-DSI TFT LCD panel + +maintainers: + - Philippe CORNU <philippe.cornu@st.com> + +description: | + The Raydium Semiconductor Corporation RM68200 is a 5.5" 720x1280 TFT LCD + panel connected using a MIPI-DSI video interface. + +allOf: + - $ref: panel-common.yaml# + +properties: + + compatible: + const: raydium,rm68200 + + reg: + maxItems: 1 + description: DSI virtual channel + + backlight: true + enable-gpios: true + port: true + power-supply: true + + reset-gpios: + maxItems: 1 + +additionalProperties: false + +required: + - compatible + - power-supply + - reg + +examples: + - | + dsi@0 { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "raydium,rm68200"; + reg = <0>; + reset-gpios = <&gpiof 15 0>; + power-supply = <&v1v8>; + backlight = <&pwm_backlight>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/panel/rocktech,rk070er9427.txt b/Documentation/devicetree/bindings/display/panel/rocktech,rk070er9427.txt deleted file mode 100644 index eb1fb9f8d1f4..000000000000 --- a/Documentation/devicetree/bindings/display/panel/rocktech,rk070er9427.txt +++ /dev/null @@ -1,25 +0,0 @@ -Rocktech Display Ltd. RK070ER9427 800(RGB)x480 TFT LCD panel - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. - -Required properties: -- compatible: should be "rocktech,rk070er9427" - -Optional properties: -- backlight: phandle of the backlight device attached to the panel - -Optional nodes: -- Video port for LCD panel input. - -Example: - panel { - compatible = "rocktech,rk070er9427"; - backlight = <&backlight_lcd>; - - port { - lcd_panel_in: endpoint { - remote-endpoint = <&lcd_display_out>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/panel/samsung,lsn122dl01-c01.txt b/Documentation/devicetree/bindings/display/panel/samsung,lsn122dl01-c01.txt deleted file mode 100644 index dba298b43b24..000000000000 --- a/Documentation/devicetree/bindings/display/panel/samsung,lsn122dl01-c01.txt +++ /dev/null @@ -1,7 +0,0 @@ -Samsung 12.2" (2560x1600 pixels) TFT LCD panel - -Required properties: -- compatible: should be "samsung,lsn122dl01-c01" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/samsung,ltn101nt05.txt b/Documentation/devicetree/bindings/display/panel/samsung,ltn101nt05.txt deleted file mode 100644 index ef522c6bb85f..000000000000 --- a/Documentation/devicetree/bindings/display/panel/samsung,ltn101nt05.txt +++ /dev/null @@ -1,7 +0,0 @@ -Samsung Electronics 10.1" WSVGA TFT LCD panel - -Required properties: -- compatible: should be "samsung,ltn101nt05" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/samsung,ltn140at29-301.txt b/Documentation/devicetree/bindings/display/panel/samsung,ltn140at29-301.txt deleted file mode 100644 index e7f969d891cc..000000000000 --- a/Documentation/devicetree/bindings/display/panel/samsung,ltn140at29-301.txt +++ /dev/null @@ -1,7 +0,0 @@ -Samsung Electronics 14" WXGA (1366x768) TFT LCD panel - -Required properties: -- compatible: should be "samsung,ltn140at29-301" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e88a0-ams452ef01.yaml b/Documentation/devicetree/bindings/display/panel/samsung,s6e88a0-ams452ef01.yaml new file mode 100644 index 000000000000..7a685d0428b3 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e88a0-ams452ef01.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/samsung,s6e88a0-ams452ef01.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung AMS452EF01 AMOLED panel with S6E88A0 video mode DSI controller + +maintainers: + - Michael Srba <Michael.Srba@seznam.cz> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: samsung,s6e88a0-ams452ef01 + reg: true + reset-gpios: true + vdd3-supply: + description: core voltage supply + vci-supply: + description: voltage supply for analog circuits + +required: + - compatible + - reg + - vdd3-supply + - vci-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + reg = <0>; + + compatible = "samsung,s6e88a0-ams452ef01"; + + vdd3-supply = <&pm8916_l17>; + vci-supply = <®_vlcd_vci>; + reset-gpios = <&msmgpio 25 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/display/panel/sharp,lq035q7db03.txt b/Documentation/devicetree/bindings/display/panel/sharp,lq035q7db03.txt deleted file mode 100644 index 0753f6967279..000000000000 --- a/Documentation/devicetree/bindings/display/panel/sharp,lq035q7db03.txt +++ /dev/null @@ -1,12 +0,0 @@ -Sharp LQ035Q7DB03 3.5" QVGA TFT LCD panel - -Required properties: -- compatible: should be "sharp,lq035q7db03" -- power-supply: phandle of the regulator that provides the supply voltage - -Optional properties: -- enable-gpios: GPIO pin to enable or disable the panel -- backlight: phandle of the backlight device attached to the panel - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/sharp,lq070y3dg3b.txt b/Documentation/devicetree/bindings/display/panel/sharp,lq070y3dg3b.txt deleted file mode 100644 index 95534b55ee5f..000000000000 --- a/Documentation/devicetree/bindings/display/panel/sharp,lq070y3dg3b.txt +++ /dev/null @@ -1,12 +0,0 @@ -Sharp LQ070Y3DG3B 7.0" WVGA landscape TFT LCD panel - -Required properties: -- compatible: should be "sharp,lq070y3dg3b" - -Optional properties: -- enable-gpios: GPIO pin to enable or disable the panel -- backlight: phandle of the backlight device attached to the panel -- power-supply: phandle of the regulator that provides the supply voltage - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/sharp,lq101k1ly04.txt b/Documentation/devicetree/bindings/display/panel/sharp,lq101k1ly04.txt deleted file mode 100644 index 4aff25b8dfe6..000000000000 --- a/Documentation/devicetree/bindings/display/panel/sharp,lq101k1ly04.txt +++ /dev/null @@ -1,7 +0,0 @@ -Sharp Display Corp. LQ101K1LY04 10.07" WXGA TFT LCD panel - -Required properties: -- compatible: should be "sharp,lq101k1ly04" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/sharp,lq123p1jx31.txt b/Documentation/devicetree/bindings/display/panel/sharp,lq123p1jx31.txt deleted file mode 100644 index bcb0e8a29f71..000000000000 --- a/Documentation/devicetree/bindings/display/panel/sharp,lq123p1jx31.txt +++ /dev/null @@ -1,7 +0,0 @@ -Sharp 12.3" (2400x1600 pixels) TFT LCD panel - -Required properties: -- compatible: should be "sharp,lq123p1jx31" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/shelly,sca07010-bfn-lnn.txt b/Documentation/devicetree/bindings/display/panel/shelly,sca07010-bfn-lnn.txt deleted file mode 100644 index fc1ea9e26c94..000000000000 --- a/Documentation/devicetree/bindings/display/panel/shelly,sca07010-bfn-lnn.txt +++ /dev/null @@ -1,7 +0,0 @@ -Shelly SCA07010-BFN-LNN 7.0" WVGA TFT LCD panel - -Required properties: -- compatible: should be "shelly,sca07010-bfn-lnn" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/starry,kr122ea0sra.txt b/Documentation/devicetree/bindings/display/panel/starry,kr122ea0sra.txt deleted file mode 100644 index 1e87fe6078af..000000000000 --- a/Documentation/devicetree/bindings/display/panel/starry,kr122ea0sra.txt +++ /dev/null @@ -1,7 +0,0 @@ -Starry 12.2" (1920x1200 pixels) TFT LCD panel - -Required properties: -- compatible: should be "starry,kr122ea0sra" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/tianma,tm070jdhg30.txt b/Documentation/devicetree/bindings/display/panel/tianma,tm070jdhg30.txt deleted file mode 100644 index eb9501a82e25..000000000000 --- a/Documentation/devicetree/bindings/display/panel/tianma,tm070jdhg30.txt +++ /dev/null @@ -1,7 +0,0 @@ -Tianma Micro-electronics TM070JDHG30 7.0" WXGA TFT LCD panel - -Required properties: -- compatible: should be "tianma,tm070jdhg30" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/tianma,tm070rvhg71.txt b/Documentation/devicetree/bindings/display/panel/tianma,tm070rvhg71.txt deleted file mode 100644 index b25261e63a6d..000000000000 --- a/Documentation/devicetree/bindings/display/panel/tianma,tm070rvhg71.txt +++ /dev/null @@ -1,29 +0,0 @@ -Tianma Micro-electronics TM070RVHG71 7.0" WXGA TFT LCD panel - -Required properties: -- compatible: should be "tianma,tm070rvhg71" -- power-supply: single regulator to provide the supply voltage -- backlight: phandle of the backlight device attached to the panel - -Required nodes: -- port: LVDS port mapping to connect this display - -This panel needs single power supply voltage. Its backlight is conntrolled -via PWM signal. - -Example: --------- - -Example device-tree definition when connected to iMX6Q based board - - panel: panel-lvds0 { - compatible = "tianma,tm070rvhg71"; - backlight = <&backlight_lvds>; - power-supply = <®_lvds>; - - port { - panel_in_lvds0: endpoint { - remote-endpoint = <&lvds0_out>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt b/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt deleted file mode 100644 index 89826116628c..000000000000 --- a/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt +++ /dev/null @@ -1,8 +0,0 @@ -Toshiba 8.9" WXGA (1280x768) TFT LCD panel - -Required properties: -- compatible: should be "toshiba,lt089ac29000" -- power-supply: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/tpk,f07a-0102.txt b/Documentation/devicetree/bindings/display/panel/tpk,f07a-0102.txt deleted file mode 100644 index a2613b9675df..000000000000 --- a/Documentation/devicetree/bindings/display/panel/tpk,f07a-0102.txt +++ /dev/null @@ -1,8 +0,0 @@ -TPK U.S.A. LLC Fusion 7" integrated projected capacitive touch display with, -800 x 480 (WVGA) LCD panel. - -Required properties: -- compatible: should be "tpk,f07a-0102" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/tpk,f10a-0102.txt b/Documentation/devicetree/bindings/display/panel/tpk,f10a-0102.txt deleted file mode 100644 index b9d051196ba9..000000000000 --- a/Documentation/devicetree/bindings/display/panel/tpk,f10a-0102.txt +++ /dev/null @@ -1,8 +0,0 @@ -TPK U.S.A. LLC Fusion 10.1" integrated projected capacitive touch display with, -1024 x 600 (WSVGA) LCD panel. - -Required properties: -- compatible: should be "tpk,f10a-0102" - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/urt,umsh-8596md.txt b/Documentation/devicetree/bindings/display/panel/urt,umsh-8596md.txt deleted file mode 100644 index 088a6cea5015..000000000000 --- a/Documentation/devicetree/bindings/display/panel/urt,umsh-8596md.txt +++ /dev/null @@ -1,16 +0,0 @@ -United Radiant Technology UMSH-8596MD-xT 7.0" WVGA TFT LCD panel - -Supported are LVDS versions (-11T, -19T) and parallel ones -(-T, -1T, -7T, -20T). - -Required properties: -- compatible: should be one of: - "urt,umsh-8596md-t", - "urt,umsh-8596md-1t", - "urt,umsh-8596md-7t", - "urt,umsh-8596md-11t", - "urt,umsh-8596md-19t", - "urt,umsh-8596md-20t". - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/vl050_8048nt_c01.txt b/Documentation/devicetree/bindings/display/panel/vl050_8048nt_c01.txt deleted file mode 100644 index b42bf06bbd99..000000000000 --- a/Documentation/devicetree/bindings/display/panel/vl050_8048nt_c01.txt +++ /dev/null @@ -1,12 +0,0 @@ -VXT 800x480 color TFT LCD panel - -Required properties: -- compatible: should be "vxt,vl050-8048nt-c01" -- power-supply: as specified in the base binding - -Optional properties: -- backlight: as specified in the base binding -- enable-gpios: as specified in the base binding - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/display/panel/winstar,wf35ltiacd.txt b/Documentation/devicetree/bindings/display/panel/winstar,wf35ltiacd.txt deleted file mode 100644 index 2a7e6e3ba64c..000000000000 --- a/Documentation/devicetree/bindings/display/panel/winstar,wf35ltiacd.txt +++ /dev/null @@ -1,48 +0,0 @@ -Winstar Display Corporation 3.5" QVGA (320x240) TFT LCD panel - -Required properties: -- compatible: should be "winstar,wf35ltiacd" -- power-supply: regulator to provide the VCC supply voltage (3.3 volts) - -This binding is compatible with the simple-panel binding, which is specified -in simple-panel.txt in this directory. - -Example: - backlight: backlight { - compatible = "pwm-backlight"; - pwms = <&hlcdc_pwm 0 50000 PWM_POLARITY_INVERTED>; - brightness-levels = <0 31 63 95 127 159 191 223 255>; - default-brightness-level = <191>; - power-supply = <&bl_reg>; - }; - - bl_reg: backlight_regulator { - compatible = "regulator-fixed"; - regulator-name = "backlight-power-supply"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - }; - - panel: panel { - compatible = "winstar,wf35ltiacd", "simple-panel"; - backlight = <&backlight>; - power-supply = <&panel_reg>; - #address-cells = <1>; - #size-cells = <0>; - - port { - #address-cells = <1>; - #size-cells = <0>; - - panel_input: endpoint { - remote-endpoint = <&hlcdc_panel_output>; - }; - }; - }; - - panel_reg: panel_regulator { - compatible = "regulator-fixed"; - regulator-name = "panel-power-supply"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; diff --git a/Documentation/devicetree/bindings/display/sitronix,st7735r.txt b/Documentation/devicetree/bindings/display/sitronix,st7735r.txt deleted file mode 100644 index cd5c7186890a..000000000000 --- a/Documentation/devicetree/bindings/display/sitronix,st7735r.txt +++ /dev/null @@ -1,35 +0,0 @@ -Sitronix ST7735R display panels - -This binding is for display panels using a Sitronix ST7735R controller in SPI -mode. - -Required properties: -- compatible: "jianda,jd-t18003-t01", "sitronix,st7735r" -- dc-gpios: Display data/command selection (D/CX) -- reset-gpios: Reset signal (RSTX) - -The node for this driver must be a child node of a SPI controller, hence -all mandatory properties described in ../spi/spi-bus.txt must be specified. - -Optional properties: -- rotation: panel rotation in degrees counter clockwise (0,90,180,270) -- backlight: phandle of the backlight device attached to the panel - -Example: - - backlight: backlight { - compatible = "gpio-backlight"; - gpios = <&gpio 44 GPIO_ACTIVE_HIGH>; - }; - - ... - - display@0{ - compatible = "jianda,jd-t18003-t01", "sitronix,st7735r"; - reg = <0>; - spi-max-frequency = <32000000>; - dc-gpios = <&gpio 43 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio 80 GPIO_ACTIVE_HIGH>; - rotation = <270>; - backlight = &backlight; - }; diff --git a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml new file mode 100644 index 000000000000..0cebaaefda03 --- /dev/null +++ b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/sitronix,st7735r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sitronix ST7735R Display Panels Device Tree Bindings + +maintainers: + - David Lechner <david@lechnology.com> + +description: + This binding is for display panels using a Sitronix ST7715R or ST7735R + controller in SPI mode. + +allOf: + - $ref: panel/panel-common.yaml# + +properties: + compatible: + oneOf: + - description: + Adafruit 1.8" 160x128 Color TFT LCD (Product ID 358 or 618) + items: + - enum: + - jianda,jd-t18003-t01 + - const: sitronix,st7735r + - description: + Okaya 1.44" 128x128 Color TFT LCD (E.g. Renesas YRSK-LCD-PMOD) + items: + - enum: + - okaya,rh128128t + - const: sitronix,st7715r + + spi-max-frequency: + maximum: 32000000 + + dc-gpios: + maxItems: 1 + description: Display data/command selection (D/CX) + + backlight: true + reg: true + reset-gpios: true + rotation: true + +required: + - compatible + - reg + - dc-gpios + - reset-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&gpio 44 GPIO_ACTIVE_HIGH>; + }; + + spi { + #address-cells = <1>; + #size-cells = <0>; + + display@0{ + compatible = "jianda,jd-t18003-t01", "sitronix,st7735r"; + reg = <0>; + spi-max-frequency = <32000000>; + dc-gpios = <&gpio 43 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 80 GPIO_ACTIVE_HIGH>; + rotation = <270>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml new file mode 100644 index 000000000000..cac61a998203 --- /dev/null +++ b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/display/ti/ti,am65x-dss.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Texas Instruments AM65x Display Subsystem + +maintainers: + - Jyri Sarha <jsarha@ti.com> + - Tomi Valkeinen <tomi.valkeinen@ti.com> + +description: | + The AM65x TI Keystone Display SubSystem with two output ports and + two video planes. The first video port supports OLDI and the second + supports DPI format. The fist plane is full video plane with all + features and the second is a "lite plane" without scaling support. + +properties: + compatible: + const: ti,am65x-dss + + reg: + description: + Addresses to each DSS memory region described in the SoC's TRM. + items: + - description: common DSS register area + - description: VIDL1 light video plane + - description: VID video plane + - description: OVR1 overlay manager for vp1 + - description: OVR2 overlay manager for vp2 + - description: VP1 video port 1 + - description: VP2 video port 2 + + reg-names: + items: + - const: common + - const: vidl1 + - const: vid + - const: ovr1 + - const: ovr2 + - const: vp1 + - const: vp2 + + clocks: + items: + - description: fck DSS functional clock + - description: vp1 Video Port 1 pixel clock + - description: vp2 Video Port 2 pixel clock + + clock-names: + items: + - const: fck + - const: vp1 + - const: vp2 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + description: phandle to the associated power domain + + ports: + type: object + description: + Ports as described in Documentation/devictree/bindings/graph.txt + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + port@0: + type: object + description: + The DSS OLDI output port node form video port 1 + + port@1: + type: object + description: + The DSS DPI output port node from video port 2 + + required: + - "#address-cells" + - "#size-cells" + + ti,am65x-oldi-io-ctrl: + allOf: + - $ref: "/schemas/types.yaml#/definitions/phandle-array" + - maxItems: 1 + description: + phandle to syscon device node mapping OLDI IO_CTRL registers. + The mapped range should point to OLDI_DAT0_IO_CTRL, map it and + following OLDI_DAT1_IO_CTRL, OLDI_DAT2_IO_CTRL, OLDI_DAT3_IO_CTRL, + and OLDI_CLK_IO_CTRL registers. This property is needed for OLDI + interface to work. + + max-memory-bandwidth: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Input memory (from main memory to dispc) bandwidth limit in + bytes per second + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/soc/ti,sci_pm_domain.h> + + dss: dss@04a00000 { + compatible = "ti,am65x-dss"; + reg = <0x0 0x04a00000 0x0 0x1000>, /* common */ + <0x0 0x04a02000 0x0 0x1000>, /* vidl1 */ + <0x0 0x04a06000 0x0 0x1000>, /* vid */ + <0x0 0x04a07000 0x0 0x1000>, /* ovr1 */ + <0x0 0x04a08000 0x0 0x1000>, /* ovr2 */ + <0x0 0x04a0a000 0x0 0x1000>, /* vp1 */ + <0x0 0x04a0b000 0x0 0x1000>; /* vp2 */ + reg-names = "common", "vidl1", "vid", + "ovr1", "ovr2", "vp1", "vp2"; + ti,am65x-oldi-io-ctrl = <&dss_oldi_io_ctrl>; + power-domains = <&k3_pds 67 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 67 1>, + <&k3_clks 216 1>, + <&k3_clks 67 2>; + clock-names = "fck", "vp1", "vp2"; + interrupts = <GIC_SPI 166 IRQ_TYPE_EDGE_RISING>; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + oldi_out0: endpoint { + remote-endpoint = <&lcd_in0>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml new file mode 100644 index 000000000000..ade9b2f513f5 --- /dev/null +++ b/Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml @@ -0,0 +1,208 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/display/ti/ti,j721e-dss.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Texas Instruments J721E Display Subsystem + +maintainers: + - Jyri Sarha <jsarha@ti.com> + - Tomi Valkeinen <tomi.valkeinen@ti.com> + +description: | + The J721E TI Keystone Display SubSystem with four output ports and + four video planes. There is two full video planes and two "lite + planes" without scaling support. The video ports can be connected to + the SoC's DPI pins or to integrated display bridges on the SoC. + +properties: + compatible: + const: ti,j721e-dss + + reg: + items: + - description: common_m DSS Master common + - description: common_s0 DSS Shared common 0 + - description: common_s1 DSS Shared common 1 + - description: common_s2 DSS Shared common 2 + - description: VIDL1 light video plane 1 + - description: VIDL2 light video plane 2 + - description: VID1 video plane 1 + - description: VID1 video plane 2 + - description: OVR1 overlay manager for vp1 + - description: OVR2 overlay manager for vp2 + - description: OVR3 overlay manager for vp3 + - description: OVR4 overlay manager for vp4 + - description: VP1 video port 1 + - description: VP2 video port 2 + - description: VP3 video port 3 + - description: VP4 video port 4 + - description: WB Write Back + + reg-names: + items: + - const: common_m + - const: common_s0 + - const: common_s1 + - const: common_s2 + - const: vidl1 + - const: vidl2 + - const: vid1 + - const: vid2 + - const: ovr1 + - const: ovr2 + - const: ovr3 + - const: ovr4 + - const: vp1 + - const: vp2 + - const: vp3 + - const: vp4 + - const: wb + + clocks: + items: + - description: fck DSS functional clock + - description: vp1 Video Port 1 pixel clock + - description: vp2 Video Port 2 pixel clock + - description: vp3 Video Port 3 pixel clock + - description: vp4 Video Port 4 pixel clock + + clock-names: + items: + - const: fck + - const: vp1 + - const: vp2 + - const: vp3 + - const: vp4 + + interrupts: + items: + - description: common_m DSS Master common + - description: common_s0 DSS Shared common 0 + - description: common_s1 DSS Shared common 1 + - description: common_s2 DSS Shared common 2 + + interrupt-names: + items: + - const: common_m + - const: common_s0 + - const: common_s1 + - const: common_s2 + + power-domains: + maxItems: 1 + description: phandle to the associated power domain + + ports: + type: object + description: + Ports as described in Documentation/devictree/bindings/graph.txt + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + port@0: + type: object + description: + The output port node form video port 1 + + port@1: + type: object + description: + The output port node from video port 2 + + port@2: + type: object + description: + The output port node from video port 3 + + port@3: + type: object + description: + The output port node from video port 4 + + required: + - "#address-cells" + - "#size-cells" + + max-memory-bandwidth: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Input memory (from main memory to dispc) bandwidth limit in + bytes per second + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - interrupt-names + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/soc/ti,sci_pm_domain.h> + + dss: dss@04a00000 { + compatible = "ti,j721e-dss"; + reg = <0x00 0x04a00000 0x00 0x10000>, /* common_m */ + <0x00 0x04a10000 0x00 0x10000>, /* common_s0*/ + <0x00 0x04b00000 0x00 0x10000>, /* common_s1*/ + <0x00 0x04b10000 0x00 0x10000>, /* common_s2*/ + <0x00 0x04a20000 0x00 0x10000>, /* vidl1 */ + <0x00 0x04a30000 0x00 0x10000>, /* vidl2 */ + <0x00 0x04a50000 0x00 0x10000>, /* vid1 */ + <0x00 0x04a60000 0x00 0x10000>, /* vid2 */ + <0x00 0x04a70000 0x00 0x10000>, /* ovr1 */ + <0x00 0x04a90000 0x00 0x10000>, /* ovr2 */ + <0x00 0x04ab0000 0x00 0x10000>, /* ovr3 */ + <0x00 0x04ad0000 0x00 0x10000>, /* ovr4 */ + <0x00 0x04a80000 0x00 0x10000>, /* vp1 */ + <0x00 0x04aa0000 0x00 0x10000>, /* vp2 */ + <0x00 0x04ac0000 0x00 0x10000>, /* vp3 */ + <0x00 0x04ae0000 0x00 0x10000>, /* vp4 */ + <0x00 0x04af0000 0x00 0x10000>; /* wb */ + reg-names = "common_m", "common_s0", + "common_s1", "common_s2", + "vidl1", "vidl2","vid1","vid2", + "ovr1", "ovr2", "ovr3", "ovr4", + "vp1", "vp2", "vp3", "vp4", + "wb"; + clocks = <&k3_clks 152 0>, + <&k3_clks 152 1>, + <&k3_clks 152 4>, + <&k3_clks 152 9>, + <&k3_clks 152 13>; + clock-names = "fck", "vp1", "vp2", "vp3", "vp4"; + power-domains = <&k3_pds 152 TI_SCI_PD_EXCLUSIVE>; + interrupts = <GIC_SPI 602 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 603 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "common_m", + "common_s0", + "common_s1", + "common_s2"; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + + dpi_out_0: endpoint { + remote-endpoint = <&dp_bridge_input>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/ti/ti,k2g-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,k2g-dss.yaml new file mode 100644 index 000000000000..385bd060ccf9 --- /dev/null +++ b/Documentation/devicetree/bindings/display/ti/ti,k2g-dss.yaml @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/display/ti/ti,k2g-dss.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Texas Instruments K2G Display Subsystem + +maintainers: + - Jyri Sarha <jsarha@ti.com> + - Tomi Valkeinen <tomi.valkeinen@ti.com> + +description: | + The K2G DSS is an ultra-light version of TI Keystone Display + SubSystem. It has only one output port and video plane. The + output is DPI. + +properties: + compatible: + const: ti,k2g-dss + + reg: + items: + - description: cfg DSS top level + - description: common DISPC common + - description: VID1 video plane 1 + - description: OVR1 overlay manager for vp1 + - description: VP1 video port 1 + + reg-names: + items: + - const: cfg + - const: common + - const: vid1 + - const: ovr1 + - const: vp1 + + clocks: + items: + - description: fck DSS functional clock + - description: vp1 Video Port 1 pixel clock + + clock-names: + items: + - const: fck + - const: vp1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + description: phandle to the associated power domain + + port: + type: object + description: + Port as described in Documentation/devictree/bindings/graph.txt. + The DSS DPI output port node + + max-memory-bandwidth: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Input memory (from main memory to dispc) bandwidth limit in + bytes per second + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + dss: dss@02540000 { + compatible = "ti,k2g-dss"; + reg = <0x02540000 0x400>, + <0x02550000 0x1000>, + <0x02557000 0x1000>, + <0x0255a800 0x100>, + <0x0255ac00 0x100>; + reg-names = "cfg", "common", "vid1", "ovr1", "vp1"; + clocks = <&k2g_clks 0x2 0>, + <&k2g_clks 0x2 1>; + clock-names = "fck", "vp1"; + interrupts = <GIC_SPI 247 IRQ_TYPE_EDGE_RISING>; + + power-domains = <&k2g_pds 0x2>; + + max-memory-bandwidth = <230000000>; + + port { + dpi_out: endpoint { + remote-endpoint = <&sii9022_in>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 9e67944bec9c..9a0cca2fd166 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -337,12 +337,16 @@ patternProperties: description: Fastrax Oy "^fcs,.*": description: Fairchild Semiconductor + "^feixin,.*": + description: Shenzhen Feixin Photoelectic Co., Ltd "^feiyang,.*": description: Shenzhen Fly Young Technology Co.,LTD. "^firefly,.*": description: Firefly "^focaltech,.*": description: FocalTech Systems Co.,Ltd + "^frida,.*": + description: Shenzhen Frida LCD Co., Ltd. "^friendlyarm,.*": description: Guangzhou FriendlyARM Computer Tech Co., Ltd "^fsl,.*": @@ -553,6 +557,8 @@ patternProperties: description: Linear Technology Corporation "^logicpd,.*": description: Logic PD, Inc. + "^logictechno,.*": + description: Logic Technologies Limited "^longcheer,.*": description: Longcheer Technology (Shanghai) Co., Ltd. "^loongson,.*": diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index bc869b23fc39..370ac678106e 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -72,6 +72,28 @@ Contact: Ville Syrjälä, Daniel Vetter, driver maintainers Level: Advanced +Improve plane atomic_check helpers +---------------------------------- + +Aside from the clipped coordinates right above there's a few suboptimal things +with the current helpers: + +- drm_plane_helper_funcs->atomic_check gets called for enabled or disabled + planes. At best this seems to confuse drivers, worst it means they blow up + when the plane is disabled without the CRTC. The only special handling is + resetting values in the plane state structures, which instead should be moved + into the drm_plane_funcs->atomic_duplicate_state functions. + +- Once that's done, helpers could stop calling ->atomic_check for disabled + planes. + +- Then we could go through all the drivers and remove the more-or-less confused + checks for plane_state->fb and plane_state->crtc. + +Contact: Daniel Vetter + +Level: Advanced + Convert early atomic drivers to async commit helpers ---------------------------------------------------- diff --git a/MAINTAINERS b/MAINTAINERS index a0d86490c2c6..978766c6715e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5262,6 +5262,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc S: Maintained F: drivers/gpu/drm/tve200/ +DRM DRIVER FOR FEIXIN K101 IM2BA02 MIPI-DSI LCD PANELS +M: Icenowy Zheng <icenowy@aosc.io> +S: Maintained +F: drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c +F: Documentation/devicetree/bindings/display/panel/feixin,k101-im2ba02.yaml + DRM DRIVER FOR FEIYANG FY07024DI26A30-D MIPI-DSI LCD PANELS M: Jagan Teki <jagan@amarulasolutions.com> S: Maintained @@ -5281,6 +5287,13 @@ S: Maintained F: drivers/gpu/drm/tiny/ili9225.c F: Documentation/devicetree/bindings/display/ilitek,ili9225.txt +DRM DRIVER FOR ILITEK ILI9486 PANELS +M: Kamlesh Gurudasani <kamlesh.gurudasani@gmail.com> +T: git git://anongit.freedesktop.org/drm/drm-misc +S: Maintained +F: drivers/gpu/drm/tiny/ili9486.c +F: Documentation/devicetree/bindings/display/ilitek,ili9486.yaml + DRM DRIVER FOR HX8357D PANELS M: Eric Anholt <eric@anholt.net> T: git git://anongit.freedesktop.org/drm/drm-misc @@ -5409,7 +5422,7 @@ M: David Lechner <david@lechnology.com> T: git git://anongit.freedesktop.org/drm/drm-misc S: Maintained F: drivers/gpu/drm/tiny/st7735r.c -F: Documentation/devicetree/bindings/display/sitronix,st7735r.txt +F: Documentation/devicetree/bindings/display/sitronix,st7735r.yaml DRM DRIVER FOR SONY ACX424AKP PANELS M: Linus Walleij <linus.walleij@linaro.org> @@ -5489,6 +5502,7 @@ F: include/linux/vga* DRM DRIVERS AND MISC GPU PATCHES M: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> M: Maxime Ripard <mripard@kernel.org> +M: Thomas Zimmermann <tzimmermann@suse.de> W: https://01.org/linuxgraphics/gfx-docs/maintainer-tools/drm-misc.html S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc @@ -5568,7 +5582,6 @@ S: Supported F: drivers/gpu/drm/fsl-dcu/ F: Documentation/devicetree/bindings/display/fsl,dcu.txt F: Documentation/devicetree/bindings/display/fsl,tcon.txt -F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19-05b.txt T: git git://anongit.freedesktop.org/drm/drm-misc DRM DRIVERS FOR FREESCALE IMX @@ -5684,6 +5697,17 @@ S: Maintained F: drivers/gpu/drm/omapdrm/ F: Documentation/devicetree/bindings/display/ti/ +DRM DRIVERS FOR TI KEYSTONE +M: Jyri Sarha <jsarha@ti.com> +M: Tomi Valkeinen <tomi.valkeinen@ti.com> +L: dri-devel@lists.freedesktop.org +S: Maintained +F: drivers/gpu/drm/tidss/ +F: Documentation/devicetree/bindings/display/ti/ti,k2g-dss.yaml +F: Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml +F: Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml +T: git git://anongit.freedesktop.org/drm/drm-misc + DRM DRIVERS FOR V3D M: Eric Anholt <eric@anholt.net> S: Supported diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index d0aa6cff2e02..4bffa08f610a 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -389,6 +389,8 @@ source "drivers/gpu/drm/aspeed/Kconfig" source "drivers/gpu/drm/mcde/Kconfig" +source "drivers/gpu/drm/tidss/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6493088a0fdd..ca0ca775d37f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -122,3 +122,4 @@ obj-$(CONFIG_DRM_LIMA) += lima/ obj-$(CONFIG_DRM_PANFROST) += panfrost/ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ obj-$(CONFIG_DRM_MCDE) += mcde/ +obj-$(CONFIG_DRM_TIDSS) += tidss/ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 39cd545976b7..53fdafd85cb8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1136,7 +1136,7 @@ static bool amdgpu_switcheroo_can_switch(struct pci_dev *pdev) * locking inversion with the driver load path. And the access here is * completely racy anyway. So don't bother with locking for now. */ - return dev->open_count == 0; + return atomic_read(&dev->open_count) == 0; } static const struct vga_switcheroo_client_ops amdgpu_switcheroo_ops = { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index dee446278417..3ab46d4647e4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -69,11 +69,6 @@ static int amdgpu_map_buffer(struct ttm_buffer_object *bo, static int amdgpu_ttm_debugfs_init(struct amdgpu_device *adev); static void amdgpu_ttm_debugfs_fini(struct amdgpu_device *adev); -static int amdgpu_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) -{ - return 0; -} - /** * amdgpu_init_mem_type - Initialize a memory manager for a specific type of * memory request. @@ -1638,7 +1633,6 @@ static struct ttm_bo_driver amdgpu_bo_driver = { .ttm_tt_create = &amdgpu_ttm_tt_create, .ttm_tt_populate = &amdgpu_ttm_tt_populate, .ttm_tt_unpopulate = &amdgpu_ttm_tt_unpopulate, - .invalidate_caches = &amdgpu_invalidate_caches, .init_mem_type = &amdgpu_init_mem_type, .eviction_valuable = amdgpu_ttm_bo_eviction_valuable, .evict_flags = &amdgpu_evict_flags, diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c index 8ae1e1f97a73..be7c29cec318 100644 --- a/drivers/gpu/drm/arc/arcpgu_crtc.c +++ b/drivers/gpu/drm/arc/arcpgu_crtc.c @@ -9,7 +9,6 @@ #include <drm/drm_device.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> -#include <drm/drm_vblank.h> #include <drm/drm_plane_helper.h> #include <drm/drm_probe_helper.h> #include <linux/clk.h> @@ -138,24 +137,9 @@ static void arc_pgu_crtc_atomic_disable(struct drm_crtc *crtc, ~ARCPGU_CTRL_ENABLE_MASK); } -static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - struct drm_pending_vblank_event *event = crtc->state->event; - - if (event) { - crtc->state->event = NULL; - - spin_lock_irq(&crtc->dev->event_lock); - drm_crtc_send_vblank_event(crtc, event); - spin_unlock_irq(&crtc->dev->event_lock); - } -} - static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = { .mode_valid = arc_pgu_crtc_mode_valid, .mode_set_nofb = arc_pgu_crtc_mode_set_nofb, - .atomic_begin = arc_pgu_crtc_atomic_begin, .atomic_enable = arc_pgu_crtc_atomic_enable, .atomic_disable = arc_pgu_crtc_atomic_disable, }; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index b79f484e9bd2..18a0a4ce00f6 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -388,31 +388,9 @@ static int ast_get_dram_info(struct drm_device *dev) return 0; } -enum drm_mode_status ast_mode_config_mode_valid(struct drm_device *dev, - const struct drm_display_mode *mode) -{ - static const unsigned long max_bpp = 4; /* DRM_FORMAT_XRGBA8888 */ - - struct ast_private *ast = dev->dev_private; - unsigned long fbsize, fbpages, max_fbpages; - - /* To support double buffering, a framebuffer may not - * consume more than half of the available VRAM. - */ - max_fbpages = (ast->vram_size / 2) >> PAGE_SHIFT; - - fbsize = mode->hdisplay * mode->vdisplay * max_bpp; - fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE); - - if (fbpages > max_fbpages) - return MODE_MEM; - - return MODE_OK; -} - static const struct drm_mode_config_funcs ast_mode_funcs = { .fb_create = drm_gem_fb_create, - .mode_valid = ast_mode_config_mode_valid, + .mode_valid = drm_vram_helper_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 34608f0499eb..7810a84e7e9e 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -833,8 +833,6 @@ static void ast_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mode_info; struct drm_display_mode *adjusted_mode; - crtc->state->no_vblank = true; - ast_state = to_ast_crtc_state(crtc->state); format = ast_state->format; diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index 3f0006c2470d..7410b0ea4623 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -7,7 +7,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_vblank.h> #include "bochs.h" @@ -57,16 +56,8 @@ static void bochs_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct bochs_device *bochs = pipe->crtc.dev->dev_private; - struct drm_crtc *crtc = &pipe->crtc; bochs_plane_update(bochs, pipe->plane.state); - - if (crtc->state->event) { - spin_lock_irq(&crtc->dev->event_lock); - drm_crtc_send_vblank_event(crtc, crtc->state->event); - crtc->state->event = NULL; - spin_unlock_irq(&crtc->dev->event_lock); - } } static const struct drm_simple_display_pipe_funcs bochs_pipe_funcs = { @@ -92,32 +83,11 @@ static int bochs_connector_get_modes(struct drm_connector *connector) return count; } -static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct bochs_device *bochs = - container_of(connector, struct bochs_device, connector); - unsigned long size = mode->hdisplay * mode->vdisplay * 4; - - /* - * Make sure we can fit two framebuffers into video memory. - * This allows up to 1600x1200 with 16 MB (default size). - * If you want more try this: - * 'qemu -vga std -global VGA.vgamem_mb=32 $otherargs' - */ - if (size * 2 > bochs->fb_size) - return MODE_BAD; - - return MODE_OK; -} - static const struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = { .get_modes = bochs_connector_get_modes, - .mode_valid = bochs_connector_mode_valid, }; static const struct drm_connector_funcs bochs_connector_connector_funcs = { - .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, @@ -157,6 +127,7 @@ bochs_gem_fb_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_config_funcs bochs_mode_funcs = { .fb_create = bochs_gem_fb_create, + .mode_valid = drm_vram_helper_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 0b9ca5862455..8397bf72d2f3 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -72,6 +72,17 @@ config DRM_PARADE_PS8622 ---help--- Parade eDP-LVDS bridge chip driver. +config DRM_PARADE_PS8640 + tristate "Parade PS8640 MIPI DSI to eDP Converter" + depends on OF + select DRM_KMS_HELPER + select DRM_MIPI_DSI + select DRM_PANEL + help + Choose this option if you have PS8640 for display + The PS8640 is a high-performance and low-power + MIPI DSI to eDP converter + config DRM_SIL_SII8620 tristate "Silicon Image SII8620 HDMI/MHL bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index cd16ce830270..1eb5376c5d68 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o +obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o obj-$(CONFIG_DRM_SII902X) += sii902x.o obj-$(CONFIG_DRM_SII9234) += sii9234.o diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 6effe532f820..dfb59a5fefea 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1289,19 +1289,21 @@ struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp, return conn_state->crtc; } -static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void +analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *old_state = old_bridge_state->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; int ret; - crtc = analogix_dp_get_new_crtc(dp, state); + crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) return; - old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); /* Don't touch the panel if we're coming back from PSR */ if (old_crtc_state && old_crtc_state->self_refresh_active) return; @@ -1366,20 +1368,22 @@ out_dp_clk_pre: return ret; } -static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void +analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *old_state = old_bridge_state->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; int timeout_loop = 0; int ret; - crtc = analogix_dp_get_new_crtc(dp, state); + crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) return; - old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); /* Not a full enable, just disable PSR and continue */ if (old_crtc_state && old_crtc_state->self_refresh_active) { ret = analogix_dp_disable_psr(dp); @@ -1440,18 +1444,20 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge) dp->dpms_mode = DRM_MODE_DPMS_OFF; } -static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void +analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *old_state = old_bridge_state->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state = NULL; - crtc = analogix_dp_get_new_crtc(dp, state); + crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) goto out; - new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc); if (!new_crtc_state) goto out; @@ -1463,20 +1469,21 @@ out: analogix_dp_bridge_disable(bridge); } -static -void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void +analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *old_state = old_bridge_state->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; int ret; - crtc = analogix_dp_get_new_crtc(dp, state); + crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) return; - new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc); if (!new_crtc_state || !new_crtc_state->self_refresh_active) return; @@ -1563,6 +1570,9 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge, } static const struct drm_bridge_funcs analogix_dp_bridge_funcs = { + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, .atomic_pre_enable = analogix_dp_bridge_atomic_pre_enable, .atomic_enable = analogix_dp_bridge_atomic_enable, .atomic_disable = analogix_dp_bridge_atomic_disable, diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c new file mode 100644 index 000000000000..c6c06688aff2 --- /dev/null +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016 MediaTek Inc. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_bridge.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> + +#define PAGE2_GPIO_H 0xa7 +#define PS_GPIO9 BIT(1) +#define PAGE2_I2C_BYPASS 0xea +#define I2C_BYPASS_EN 0xd0 +#define PAGE2_MCS_EN 0xf3 +#define MCS_EN BIT(0) +#define PAGE3_SET_ADD 0xfe +#define VDO_CTL_ADD 0x13 +#define VDO_DIS 0x18 +#define VDO_EN 0x1c +#define DP_NUM_LANES 4 + +/* + * PS8640 uses multiple addresses: + * page[0]: for DP control + * page[1]: for VIDEO Bridge + * page[2]: for control top + * page[3]: for DSI Link Control1 + * page[4]: for MIPI Phy + * page[5]: for VPLL + * page[6]: for DSI Link Control2 + * page[7]: for SPI ROM mapping + */ +enum page_addr_offset { + PAGE0_DP_CNTL = 0, + PAGE1_VDO_BDG, + PAGE2_TOP_CNTL, + PAGE3_DSI_CNTL1, + PAGE4_MIPI_PHY, + PAGE5_VPLL, + PAGE6_DSI_CNTL2, + PAGE7_SPI_CNTL, + MAX_DEVS +}; + +enum ps8640_vdo_control { + DISABLE = VDO_DIS, + ENABLE = VDO_EN, +}; + +struct ps8640 { + struct drm_bridge bridge; + struct drm_bridge *panel_bridge; + struct mipi_dsi_device *dsi; + struct i2c_client *page[MAX_DEVS]; + struct regulator_bulk_data supplies[2]; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_powerdown; +}; + +static inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e) +{ + return container_of(e, struct ps8640, bridge); +} + +static int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge, + const enum ps8640_vdo_control ctrl) +{ + struct i2c_client *client = ps_bridge->page[PAGE3_DSI_CNTL1]; + u8 vdo_ctrl_buf[] = { VDO_CTL_ADD, ctrl }; + int ret; + + ret = i2c_smbus_write_i2c_block_data(client, PAGE3_SET_ADD, + sizeof(vdo_ctrl_buf), + vdo_ctrl_buf); + if (ret < 0) + return ret; + + return 0; +} + +static void ps8640_pre_enable(struct drm_bridge *bridge) +{ + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); + struct i2c_client *client = ps_bridge->page[PAGE2_TOP_CNTL]; + unsigned long timeout; + int ret, status; + + ret = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies), + ps_bridge->supplies); + if (ret < 0) { + DRM_ERROR("cannot enable regulators %d\n", ret); + return; + } + + gpiod_set_value(ps_bridge->gpio_powerdown, 0); + gpiod_set_value(ps_bridge->gpio_reset, 1); + usleep_range(2000, 2500); + gpiod_set_value(ps_bridge->gpio_reset, 0); + + /* + * Wait for the ps8640 embedded MCU to be ready + * First wait 200ms and then check the MCU ready flag every 20ms + */ + msleep(200); + + timeout = jiffies + msecs_to_jiffies(200) + 1; + + while (time_is_after_jiffies(timeout)) { + status = i2c_smbus_read_byte_data(client, PAGE2_GPIO_H); + if (status < 0) { + DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", status); + goto err_regulators_disable; + } + if ((status & PS_GPIO9) == PS_GPIO9) + break; + + msleep(20); + } + + msleep(50); + + /* + * The Manufacturer Command Set (MCS) is a device dependent interface + * intended for factory programming of the display module default + * parameters. Once the display module is configured, the MCS shall be + * disabled by the manufacturer. Once disabled, all MCS commands are + * ignored by the display interface. + */ + status = i2c_smbus_read_byte_data(client, PAGE2_MCS_EN); + if (status < 0) { + DRM_ERROR("failed read PAGE2_MCS_EN: %d\n", status); + goto err_regulators_disable; + } + + ret = i2c_smbus_write_byte_data(client, PAGE2_MCS_EN, + status & ~MCS_EN); + if (ret < 0) { + DRM_ERROR("failed write PAGE2_MCS_EN: %d\n", ret); + goto err_regulators_disable; + } + + ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE); + if (ret) { + DRM_ERROR("failed to enable VDO: %d\n", ret); + goto err_regulators_disable; + } + + /* Switch access edp panel's edid through i2c */ + ret = i2c_smbus_write_byte_data(client, PAGE2_I2C_BYPASS, + I2C_BYPASS_EN); + if (ret < 0) { + DRM_ERROR("failed write PAGE2_I2C_BYPASS: %d\n", ret); + goto err_regulators_disable; + } + + return; + +err_regulators_disable: + regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies), + ps_bridge->supplies); +} + +static void ps8640_post_disable(struct drm_bridge *bridge) +{ + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); + int ret; + + ret = ps8640_bridge_vdo_control(ps_bridge, DISABLE); + if (ret < 0) + DRM_ERROR("failed to disable VDO: %d\n", ret); + + gpiod_set_value(ps_bridge->gpio_reset, 1); + gpiod_set_value(ps_bridge->gpio_powerdown, 1); + ret = regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies), + ps_bridge->supplies); + if (ret < 0) + DRM_ERROR("cannot disable regulators %d\n", ret); +} + +static int ps8640_bridge_attach(struct drm_bridge *bridge) +{ + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); + struct device *dev = &ps_bridge->page[0]->dev; + struct device_node *in_ep, *dsi_node; + struct mipi_dsi_device *dsi; + struct mipi_dsi_host *host; + int ret; + const struct mipi_dsi_device_info info = { .type = "ps8640", + .channel = 0, + .node = NULL, + }; + /* port@0 is ps8640 dsi input port */ + in_ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); + if (!in_ep) + return -ENODEV; + + dsi_node = of_graph_get_remote_port_parent(in_ep); + of_node_put(in_ep); + if (!dsi_node) + return -ENODEV; + + host = of_find_mipi_dsi_host_by_node(dsi_node); + of_node_put(dsi_node); + if (!host) + return -ENODEV; + + dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dsi)) { + dev_err(dev, "failed to create dsi device\n"); + ret = PTR_ERR(dsi); + return ret; + } + + ps_bridge->dsi = dsi; + + dsi->host = host; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->lanes = DP_NUM_LANES; + ret = mipi_dsi_attach(dsi); + if (ret) + goto err_dsi_attach; + + /* Attach the panel-bridge to the dsi bridge */ + return drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge, + &ps_bridge->bridge); + +err_dsi_attach: + mipi_dsi_device_unregister(dsi); + return ret; +} + +static const struct drm_bridge_funcs ps8640_bridge_funcs = { + .attach = ps8640_bridge_attach, + .post_disable = ps8640_post_disable, + .pre_enable = ps8640_pre_enable, +}; + +static int ps8640_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct ps8640 *ps_bridge; + struct drm_panel *panel; + int ret; + u32 i; + + ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL); + if (!ps_bridge) + return -ENOMEM; + + /* port@1 is ps8640 output port */ + ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL); + if (ret < 0) + return ret; + if (!panel) + return -ENODEV; + + panel->connector_type = DRM_MODE_CONNECTOR_eDP; + + ps_bridge->panel_bridge = devm_drm_panel_bridge_add(dev, panel); + if (IS_ERR(ps_bridge->panel_bridge)) + return PTR_ERR(ps_bridge->panel_bridge); + + ps_bridge->supplies[0].supply = "vdd33"; + ps_bridge->supplies[1].supply = "vdd12"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies), + ps_bridge->supplies); + if (ret) + return ret; + + ps_bridge->gpio_powerdown = devm_gpiod_get(&client->dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(ps_bridge->gpio_powerdown)) + return PTR_ERR(ps_bridge->gpio_powerdown); + + /* + * Assert the reset to avoid the bridge being initialized prematurely + */ + ps_bridge->gpio_reset = devm_gpiod_get(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ps_bridge->gpio_reset)) + return PTR_ERR(ps_bridge->gpio_reset); + + ps_bridge->bridge.funcs = &ps8640_bridge_funcs; + ps_bridge->bridge.of_node = dev->of_node; + + ps_bridge->page[PAGE0_DP_CNTL] = client; + + for (i = 1; i < ARRAY_SIZE(ps_bridge->page); i++) { + ps_bridge->page[i] = devm_i2c_new_dummy_device(&client->dev, + client->adapter, + client->addr + i); + if (IS_ERR(ps_bridge->page[i])) { + dev_err(dev, "failed i2c dummy device, address %02x\n", + client->addr + i); + return PTR_ERR(ps_bridge->page[i]); + } + } + + i2c_set_clientdata(client, ps_bridge); + + drm_bridge_add(&ps_bridge->bridge); + + return 0; +} + +static int ps8640_remove(struct i2c_client *client) +{ + struct ps8640 *ps_bridge = i2c_get_clientdata(client); + + drm_bridge_remove(&ps_bridge->bridge); + + return 0; +} + +static const struct of_device_id ps8640_match[] = { + { .compatible = "parade,ps8640" }, + { } +}; +MODULE_DEVICE_TABLE(of, ps8640_match); + +static struct i2c_driver ps8640_driver = { + .probe_new = ps8640_probe, + .remove = ps8640_remove, + .driver = { + .name = "ps8640", + .of_match_table = ps8640_match, + }, +}; +module_i2c_driver(ps8640_driver); + +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>"); +MODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>"); +MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>"); +MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c index 248c9f765c45..a91fb0d7282c 100644 --- a/drivers/gpu/drm/cirrus/cirrus.c +++ b/drivers/gpu/drm/cirrus/cirrus.c @@ -38,7 +38,6 @@ #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> -#include <drm/drm_vblank.h> #define DRIVER_NAME "cirrus" #define DRIVER_DESC "qemu cirrus vga" @@ -434,13 +433,6 @@ static void cirrus_pipe_update(struct drm_simple_display_pipe *pipe, if (drm_atomic_helper_damage_merged(old_state, state, &rect)) cirrus_fb_blit_rect(pipe->plane.state->fb, &rect); - - if (crtc->state->event) { - spin_lock_irq(&crtc->dev->event_lock); - drm_crtc_send_vblank_event(crtc, crtc->state->event); - crtc->state->event = NULL; - spin_unlock_irq(&crtc->dev->event_lock); - } } static const struct drm_simple_display_pipe_funcs cirrus_pipe_funcs = { diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index d33691512a8e..65c46ed049c5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -30,6 +30,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_uapi.h> +#include <drm/drm_bridge.h> #include <drm/drm_debugfs.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> @@ -1018,6 +1019,121 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, } /** + * drm_atomic_get_bridge_state - get bridge state + * @state: global atomic state object + * @bridge: bridge to get state object for + * + * This function returns the bridge state for the given bridge, allocating it + * if needed. It will also grab the relevant bridge lock to make sure that the + * state is consistent. + * + * Returns: + * + * Either the allocated state or the error code encoded into the pointer. When + * the error is EDEADLK then the w/w mutex code has detected a deadlock and the + * entire atomic sequence must be restarted. + */ +struct drm_bridge_state * +drm_atomic_get_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_private_obj_state(state, &bridge->base); + if (IS_ERR(obj_state)) + return ERR_CAST(obj_state); + + return drm_priv_to_bridge_state(obj_state); +} +EXPORT_SYMBOL(drm_atomic_get_bridge_state); + +/** + * drm_atomic_get_old_bridge_state - get old bridge state, if it exists + * @state: global atomic state object + * @bridge: bridge to grab + * + * This function returns the old bridge state for the given bridge, or NULL if + * the bridge is not part of the global atomic state. + */ +struct drm_bridge_state * +drm_atomic_get_old_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_old_private_obj_state(state, &bridge->base); + if (!obj_state) + return NULL; + + return drm_priv_to_bridge_state(obj_state); +} +EXPORT_SYMBOL(drm_atomic_get_old_bridge_state); + +/** + * drm_atomic_get_new_bridge_state - get new bridge state, if it exists + * @state: global atomic state object + * @bridge: bridge to grab + * + * This function returns the new bridge state for the given bridge, or NULL if + * the bridge is not part of the global atomic state. + */ +struct drm_bridge_state * +drm_atomic_get_new_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_new_private_obj_state(state, &bridge->base); + if (!obj_state) + return NULL; + + return drm_priv_to_bridge_state(obj_state); +} +EXPORT_SYMBOL(drm_atomic_get_new_bridge_state); + +/** + * drm_atomic_add_encoder_bridges - add bridges attached to an encoder + * @state: atomic state + * @encoder: DRM encoder + * + * This function adds all bridges attached to @encoder. This is needed to add + * bridge states to @state and make them available when + * &bridge_funcs.atomic_{check,pre_enable,enable,disable_post_disable}() are + * called + * + * Returns: + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK + * then the w/w mutex code has detected a deadlock and the entire atomic + * sequence must be restarted. All other errors are fatal. + */ +int +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, + struct drm_encoder *encoder) +{ + struct drm_bridge_state *bridge_state; + struct drm_bridge *bridge; + + if (!encoder) + return 0; + + DRM_DEBUG_ATOMIC("Adding all bridges for [encoder:%d:%s] to %p\n", + encoder->base.id, encoder->name, state); + + drm_for_each_bridge_in_chain(encoder, bridge) { + /* Skip bridges that don't implement the atomic state hooks. */ + if (!bridge->funcs->atomic_duplicate_state) + continue; + + bridge_state = drm_atomic_get_bridge_state(state, bridge); + if (IS_ERR(bridge_state)) + return PTR_ERR(bridge_state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_encoder_bridges); + +/** * drm_atomic_add_affected_connectors - add connectors for CRTC * @state: atomic state * @crtc: DRM CRTC diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 4511c2e07bb9..85d163f16801 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -437,12 +437,12 @@ mode_fixup(struct drm_atomic_state *state) funcs = encoder->helper_private; bridge = drm_bridge_chain_get_first_bridge(encoder); - ret = drm_bridge_chain_mode_fixup(bridge, - &new_crtc_state->mode, - &new_crtc_state->adjusted_mode); - if (!ret) { - DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); - return -EINVAL; + ret = drm_atomic_bridge_chain_check(bridge, + new_crtc_state, + new_conn_state); + if (ret) { + DRM_DEBUG_ATOMIC("Bridge atomic check failed\n"); + return ret; } if (funcs && funcs->atomic_check) { @@ -583,6 +583,7 @@ mode_valid(struct drm_atomic_state *state) * &drm_crtc_state.connectors_changed is set when a connector is added or * removed from the CRTC. &drm_crtc_state.active_changed is set when * &drm_crtc_state.active changes, which is used for DPMS. + * &drm_crtc_state.no_vblank is set from the result of drm_dev_has_vblank(). * See also: drm_atomic_crtc_needs_modeset() * * IMPORTANT: @@ -649,6 +650,11 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return -EINVAL; } + + if (drm_dev_has_vblank(dev)) + new_crtc_state->no_vblank = false; + else + new_crtc_state->no_vblank = true; } ret = handle_conflicting_encoders(state, false); @@ -730,6 +736,26 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return ret; } + /* + * Iterate over all connectors again, and add all affected bridges to + * the state. + */ + for_each_oldnew_connector_in_state(state, connector, + old_connector_state, + new_connector_state, i) { + struct drm_encoder *encoder; + + encoder = old_connector_state->best_encoder; + ret = drm_atomic_add_encoder_bridges(state, encoder); + if (ret) + return ret; + + encoder = new_connector_state->best_encoder; + ret = drm_atomic_add_encoder_bridges(state, encoder); + if (ret) + return ret; + } + ret = mode_valid(state); if (ret) return ret; @@ -2215,7 +2241,9 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); * when a job is queued, and any change to the pipeline that does not touch the * connector is leading to timeouts when calling * drm_atomic_helper_wait_for_vblanks() or - * drm_atomic_helper_wait_for_flip_done(). + * drm_atomic_helper_wait_for_flip_done(). In addition to writeback + * connectors, this function can also fake VBLANK events for CRTCs without + * VBLANK interrupt. * * This is part of the atomic helper support for nonblocking commits, see * drm_atomic_helper_setup_commit() for an overview. @@ -3508,3 +3536,44 @@ fail: return ret; } EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); + +/** + * drm_atomic_helper_bridge_propagate_bus_fmt() - Propagate output format to + * the input end of a bridge + * @bridge: bridge control structure + * @bridge_state: new bridge state + * @crtc_state: new CRTC state + * @conn_state: new connector state + * @output_fmt: tested output bus format + * @num_input_fmts: will contain the size of the returned array + * + * This helper is a pluggable implementation of the + * &drm_bridge_funcs.atomic_get_input_bus_fmts operation for bridges that don't + * modify the bus configuration between their input and their output. It + * returns an array of input formats with a single element set to @output_fmt. + * + * RETURNS: + * a valid format array of size @num_input_fmts, or NULL if the allocation + * failed + */ +u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + + input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) { + *num_input_fmts = 0; + return NULL; + } + + *num_input_fmts = 1; + input_fmts[0] = output_fmt; + return input_fmts; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 7cf3cf936547..33141d2cdad4 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -26,6 +26,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_state_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_device.h> @@ -551,3 +552,105 @@ void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj memcpy(state, obj->state, sizeof(*state)); } EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state); + +/** + * __drm_atomic_helper_bridge_duplicate_state() - Copy atomic bridge state + * @bridge: bridge object + * @state: atomic bridge state + * + * Copies atomic state from a bridge's current state and resets inferred values. + * This is useful for drivers that subclass the bridge state. + */ +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + __drm_atomic_helper_private_obj_duplicate_state(&bridge->base, + &state->base); + state->bridge = bridge; +} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state); + +/** + * drm_atomic_helper_bridge_duplicate_state() - Duplicate a bridge state object + * @bridge: bridge object + * + * Allocates a new bridge state and initializes it with the current bridge + * state values. This helper is meant to be used as a bridge + * &drm_bridge_funcs.atomic_duplicate_state hook for bridges that don't + * subclass the bridge state. + */ +struct drm_bridge_state * +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge) +{ + struct drm_bridge_state *new; + + if (WARN_ON(!bridge->base.state)) + return NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (new) + __drm_atomic_helper_bridge_duplicate_state(bridge, new); + + return new; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state); + +/** + * drm_atomic_helper_bridge_destroy_state() - Destroy a bridge state object + * @bridge: the bridge this state refers to + * @state: bridge state to destroy + * + * Destroys a bridge state previously created by + * &drm_atomic_helper_bridge_reset() or + * &drm_atomic_helper_bridge_duplicate_state(). This helper is meant to be + * used as a bridge &drm_bridge_funcs.atomic_destroy_state hook for bridges + * that don't subclass the bridge state. + */ +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); + +/** + * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its + * default + * @bridge: the bridge this state refers to + * @state: bridge state to initialize + * + * Initializes the bridge state to default values. This is meant to be called + * by the bridge &drm_bridge_funcs.atomic_reset hook for bridges that subclass + * the bridge state. + */ +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + memset(state, 0, sizeof(*state)); + state->bridge = bridge; +} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset); + +/** + * drm_atomic_helper_bridge_reset() - Allocate and initialize a bridge state + * to its default + * @bridge: the bridge this state refers to + * @state: bridge state to initialize + * + * Allocates the bridge state and initializes it to default values. This helper + * is meant to be used as a bridge &drm_bridge_funcs.atomic_reset hook for + * bridges that don't subclass the bridge state. + */ +struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) +{ + struct drm_bridge_state *bridge_state; + + bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL); + if (!bridge_state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_bridge_reset(bridge, bridge_state); + return bridge_state; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index cc9acd986c68..531b876d0ed8 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -153,11 +153,6 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) return -ENOMEM; } - if (dev->driver->master_create) { - ret = dev->driver->master_create(dev, fpriv->master); - if (ret) - goto out_err; - } fpriv->is_master = 1; fpriv->authenticated = 1; @@ -332,9 +327,6 @@ static void drm_master_destroy(struct kref *kref) if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_lease_destroy(master); - if (dev->driver->master_destroy) - dev->driver->master_destroy(dev, master); - drm_legacy_master_rmmaps(dev, master); idr_destroy(&master->magic_map); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index c2cf0c90fa26..68ab933ee430 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/mutex.h> +#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_encoder.h> @@ -89,6 +90,31 @@ void drm_bridge_remove(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_remove); +static struct drm_private_state * +drm_bridge_atomic_duplicate_priv_state(struct drm_private_obj *obj) +{ + struct drm_bridge *bridge = drm_priv_to_bridge(obj); + struct drm_bridge_state *state; + + state = bridge->funcs->atomic_duplicate_state(bridge); + return state ? &state->base : NULL; +} + +static void +drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj, + struct drm_private_state *s) +{ + struct drm_bridge_state *state = drm_priv_to_bridge_state(s); + struct drm_bridge *bridge = drm_priv_to_bridge(obj); + + bridge->funcs->atomic_destroy_state(bridge, state); +} + +static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { + .atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state, + .atomic_destroy_state = drm_bridge_atomic_destroy_priv_state, +}; + /** * drm_bridge_attach - attach the bridge to an encoder's chain * @@ -135,15 +161,35 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, if (bridge->funcs->attach) { ret = bridge->funcs->attach(bridge); - if (ret < 0) { - list_del(&bridge->chain_node); - bridge->dev = NULL; - bridge->encoder = NULL; - return ret; + if (ret < 0) + goto err_reset_bridge; + } + + if (bridge->funcs->atomic_reset) { + struct drm_bridge_state *state; + + state = bridge->funcs->atomic_reset(bridge); + if (IS_ERR(state)) { + ret = PTR_ERR(state); + goto err_detach_bridge; } + + drm_atomic_private_obj_init(bridge->dev, &bridge->base, + &state->base, + &drm_bridge_priv_state_funcs); } return 0; + +err_detach_bridge: + if (bridge->funcs->detach) + bridge->funcs->detach(bridge); + +err_reset_bridge: + bridge->dev = NULL; + bridge->encoder = NULL; + list_del(&bridge->chain_node); + return ret; } EXPORT_SYMBOL(drm_bridge_attach); @@ -155,6 +201,9 @@ void drm_bridge_detach(struct drm_bridge *bridge) if (WARN_ON(!bridge->dev)) return; + if (bridge->funcs->atomic_reset) + drm_atomic_private_obj_fini(&bridge->base); + if (bridge->funcs->detach) bridge->funcs->detach(bridge); @@ -409,10 +458,19 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_disable) - iter->funcs->atomic_disable(iter, old_state); - else if (iter->funcs->disable) + if (iter->funcs->atomic_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + iter); + if (WARN_ON(!old_bridge_state)) + return; + + iter->funcs->atomic_disable(iter, old_bridge_state); + } else if (iter->funcs->disable) { iter->funcs->disable(iter); + } if (iter == bridge) break; @@ -443,10 +501,20 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_post_disable) - bridge->funcs->atomic_post_disable(bridge, old_state); - else if (bridge->funcs->post_disable) + if (bridge->funcs->atomic_post_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_post_disable(bridge, + old_bridge_state); + } else if (bridge->funcs->post_disable) { bridge->funcs->post_disable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); @@ -475,10 +543,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_pre_enable) - iter->funcs->atomic_pre_enable(iter, old_state); - else if (iter->funcs->pre_enable) + if (iter->funcs->atomic_pre_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + iter); + if (WARN_ON(!old_bridge_state)) + return; + + iter->funcs->atomic_pre_enable(iter, old_bridge_state); + } else if (iter->funcs->pre_enable) { iter->funcs->pre_enable(iter); + } if (iter == bridge) break; @@ -508,14 +585,340 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_enable) - bridge->funcs->atomic_enable(bridge, old_state); - else if (bridge->funcs->enable) + if (bridge->funcs->atomic_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_enable(bridge, old_bridge_state); + } else if (bridge->funcs->enable) { bridge->funcs->enable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable); +static int drm_atomic_bridge_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + if (bridge->funcs->atomic_check) { + struct drm_bridge_state *bridge_state; + int ret; + + bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + bridge); + if (WARN_ON(!bridge_state)) + return -EINVAL; + + ret = bridge->funcs->atomic_check(bridge, bridge_state, + crtc_state, conn_state); + if (ret) + return ret; + } else if (bridge->funcs->mode_fixup) { + if (!bridge->funcs->mode_fixup(bridge, &crtc_state->mode, + &crtc_state->adjusted_mode)) + return -EINVAL; + } + + return 0; +} + +static int select_bus_fmt_recursive(struct drm_bridge *first_bridge, + struct drm_bridge *cur_bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 out_bus_fmt) +{ + struct drm_bridge_state *cur_state; + unsigned int num_in_bus_fmts, i; + struct drm_bridge *prev_bridge; + u32 *in_bus_fmts; + int ret; + + prev_bridge = drm_bridge_get_prev_bridge(cur_bridge); + cur_state = drm_atomic_get_new_bridge_state(crtc_state->state, + cur_bridge); + + /* + * If bus format negotiation is not supported by this bridge, let's + * pass MEDIA_BUS_FMT_FIXED to the previous bridge in the chain and + * hope that it can handle this situation gracefully (by providing + * appropriate default values). + */ + if (!cur_bridge->funcs->atomic_get_input_bus_fmts) { + if (cur_bridge != first_bridge) { + ret = select_bus_fmt_recursive(first_bridge, + prev_bridge, crtc_state, + conn_state, + MEDIA_BUS_FMT_FIXED); + if (ret) + return ret; + } + + /* + * Driver does not implement the atomic state hooks, but that's + * fine, as long as it does not access the bridge state. + */ + if (cur_state) { + cur_state->input_bus_cfg.format = MEDIA_BUS_FMT_FIXED; + cur_state->output_bus_cfg.format = out_bus_fmt; + } + + return 0; + } + + /* + * If the driver implements ->atomic_get_input_bus_fmts() it + * should also implement the atomic state hooks. + */ + if (WARN_ON(!cur_state)) + return -EINVAL; + + in_bus_fmts = cur_bridge->funcs->atomic_get_input_bus_fmts(cur_bridge, + cur_state, + crtc_state, + conn_state, + out_bus_fmt, + &num_in_bus_fmts); + if (!num_in_bus_fmts) + return -ENOTSUPP; + else if (!in_bus_fmts) + return -ENOMEM; + + if (first_bridge == cur_bridge) { + cur_state->input_bus_cfg.format = in_bus_fmts[0]; + cur_state->output_bus_cfg.format = out_bus_fmt; + kfree(in_bus_fmts); + return 0; + } + + for (i = 0; i < num_in_bus_fmts; i++) { + ret = select_bus_fmt_recursive(first_bridge, prev_bridge, + crtc_state, conn_state, + in_bus_fmts[i]); + if (ret != -ENOTSUPP) + break; + } + + if (!ret) { + cur_state->input_bus_cfg.format = in_bus_fmts[i]; + cur_state->output_bus_cfg.format = out_bus_fmt; + } + + kfree(in_bus_fmts); + return ret; +} + +/* + * This function is called by &drm_atomic_bridge_chain_check() just before + * calling &drm_bridge_funcs.atomic_check() on all elements of the chain. + * It performs bus format negotiation between bridge elements. The negotiation + * happens in reverse order, starting from the last element in the chain up to + * @bridge. + * + * Negotiation starts by retrieving supported output bus formats on the last + * bridge element and testing them one by one. The test is recursive, meaning + * that for each tested output format, the whole chain will be walked backward, + * and each element will have to choose an input bus format that can be + * transcoded to the requested output format. When a bridge element does not + * support transcoding into a specific output format -ENOTSUPP is returned and + * the next bridge element will have to try a different format. If none of the + * combinations worked, -ENOTSUPP is returned and the atomic modeset will fail. + * + * This implementation is relying on + * &drm_bridge_funcs.atomic_get_output_bus_fmts() and + * &drm_bridge_funcs.atomic_get_input_bus_fmts() to gather supported + * input/output formats. + * + * When &drm_bridge_funcs.atomic_get_output_bus_fmts() is not implemented by + * the last element of the chain, &drm_atomic_bridge_chain_select_bus_fmts() + * tries a single format: &drm_connector.display_info.bus_formats[0] if + * available, MEDIA_BUS_FMT_FIXED otherwise. + * + * When &drm_bridge_funcs.atomic_get_input_bus_fmts() is not implemented, + * &drm_atomic_bridge_chain_select_bus_fmts() skips the negotiation on the + * bridge element that lacks this hook and asks the previous element in the + * chain to try MEDIA_BUS_FMT_FIXED. It's up to bridge drivers to decide what + * to do in that case (fail if they want to enforce bus format negotiation, or + * provide a reasonable default if they need to support pipelines where not + * all elements support bus format negotiation). + */ +static int +drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_connector *conn = conn_state->connector; + struct drm_encoder *encoder = bridge->encoder; + struct drm_bridge_state *last_bridge_state; + unsigned int i, num_out_bus_fmts; + struct drm_bridge *last_bridge; + u32 *out_bus_fmts; + int ret = 0; + + last_bridge = list_last_entry(&encoder->bridge_chain, + struct drm_bridge, chain_node); + last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + last_bridge); + + if (last_bridge->funcs->atomic_get_output_bus_fmts) { + const struct drm_bridge_funcs *funcs = last_bridge->funcs; + + /* + * If the driver implements ->atomic_get_output_bus_fmts() it + * should also implement the atomic state hooks. + */ + if (WARN_ON(!last_bridge_state)) + return -EINVAL; + + out_bus_fmts = funcs->atomic_get_output_bus_fmts(last_bridge, + last_bridge_state, + crtc_state, + conn_state, + &num_out_bus_fmts); + if (!num_out_bus_fmts) + return -ENOTSUPP; + else if (!out_bus_fmts) + return -ENOMEM; + } else { + num_out_bus_fmts = 1; + out_bus_fmts = kmalloc(sizeof(*out_bus_fmts), GFP_KERNEL); + if (!out_bus_fmts) + return -ENOMEM; + + if (conn->display_info.num_bus_formats && + conn->display_info.bus_formats) + out_bus_fmts[0] = conn->display_info.bus_formats[0]; + else + out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED; + } + + for (i = 0; i < num_out_bus_fmts; i++) { + ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state, + conn_state, out_bus_fmts[i]); + if (ret != -ENOTSUPP) + break; + } + + kfree(out_bus_fmts); + + return ret; +} + +static void +drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge, + struct drm_connector *conn, + struct drm_atomic_state *state) +{ + struct drm_bridge_state *bridge_state, *next_bridge_state; + struct drm_bridge *next_bridge; + u32 output_flags = 0; + + bridge_state = drm_atomic_get_new_bridge_state(state, bridge); + + /* No bridge state attached to this bridge => nothing to propagate. */ + if (!bridge_state) + return; + + next_bridge = drm_bridge_get_next_bridge(bridge); + + /* + * Let's try to apply the most common case here, that is, propagate + * display_info flags for the last bridge, and propagate the input + * flags of the next bridge element to the output end of the current + * bridge when the bridge is not the last one. + * There are exceptions to this rule, like when signal inversion is + * happening at the board level, but that's something drivers can deal + * with from their &drm_bridge_funcs.atomic_check() implementation by + * simply overriding the flags value we've set here. + */ + if (!next_bridge) { + output_flags = conn->display_info.bus_flags; + } else { + next_bridge_state = drm_atomic_get_new_bridge_state(state, + next_bridge); + /* + * No bridge state attached to the next bridge, just leave the + * flags to 0. + */ + if (next_bridge_state) + output_flags = next_bridge_state->input_bus_cfg.flags; + } + + bridge_state->output_bus_cfg.flags = output_flags; + + /* + * Propage the output flags to the input end of the bridge. Again, it's + * not necessarily what all bridges want, but that's what most of them + * do, and by doing that by default we avoid forcing drivers to + * duplicate the "dummy propagation" logic. + */ + bridge_state->input_bus_cfg.flags = output_flags; +} + +/** + * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain + * @bridge: bridge control structure + * @crtc_state: new CRTC state + * @conn_state: new connector state + * + * First trigger a bus format negotiation before calling + * &drm_bridge_funcs.atomic_check() (falls back on + * &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_check() + * + * RETURNS: + * 0 on success, a negative error code on failure + */ +int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_connector *conn = conn_state->connector; + struct drm_encoder *encoder; + struct drm_bridge *iter; + int ret; + + if (!bridge) + return 0; + + ret = drm_atomic_bridge_chain_select_bus_fmts(bridge, crtc_state, + conn_state); + if (ret) + return ret; + + encoder = bridge->encoder; + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { + int ret; + + /* + * Bus flags are propagated by default. If a bridge needs to + * tweak the input bus flags for any reason, it should happen + * in its &drm_bridge_funcs.atomic_check() implementation such + * that preceding bridges in the chain can propagate the new + * bus flags. + */ + drm_atomic_bridge_propagate_bus_flags(iter, conn, + crtc_state->state); + + ret = drm_atomic_bridge_check(iter, crtc_state, conn_state); + if (ret) + return ret; + + if (iter == bridge) + break; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_bridge_chain_check); + #ifdef CONFIG_OF /** * of_drm_find_bridge - find the bridge corresponding to the device node in diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 8ce9d73fab4f..19297e58b232 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -149,7 +149,6 @@ static int drm_addmap_core(struct drm_device *dev, resource_size_t offset, { struct drm_local_map *map; struct drm_map_list *list; - drm_dma_handle_t *dmah; unsigned long user_token; int ret; @@ -324,14 +323,14 @@ static int drm_addmap_core(struct drm_device *dev, resource_size_t offset, * As we're limiting the address to 2^32-1 (or less), * casting it down to 32 bits is no problem, but we * need to point to a 64bit variable first. */ - dmah = drm_pci_alloc(dev, map->size, map->size); - if (!dmah) { + map->handle = dma_alloc_coherent(&dev->pdev->dev, + map->size, + &map->offset, + GFP_KERNEL); + if (!map->handle) { kfree(map); return -ENOMEM; } - map->handle = dmah->vaddr; - map->offset = (unsigned long)dmah->busaddr; - kfree(dmah); break; default: kfree(map); @@ -513,7 +512,6 @@ int drm_legacy_getmap_ioctl(struct drm_device *dev, void *data, int drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) { struct drm_map_list *r_list = NULL, *list_t; - drm_dma_handle_t dmah; int found = 0; struct drm_master *master; @@ -554,10 +552,10 @@ int drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) case _DRM_SCATTER_GATHER: break; case _DRM_CONSISTENT: - dmah.vaddr = map->handle; - dmah.busaddr = map->offset; - dmah.size = map->size; - __drm_legacy_pci_free(dev, &dmah); + dma_free_coherent(&dev->pdev->dev, + map->size, + map->handle, + map->offset); break; } kfree(map); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 2166000ed057..f632ca05960e 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -140,6 +140,13 @@ static void drm_connector_get_cmdline_mode(struct drm_connector *connector) connector->force = mode->force; } + if (mode->panel_orientation != DRM_MODE_PANEL_ORIENTATION_UNKNOWN) { + DRM_INFO("cmdline forces connector %s panel_orientation to %d\n", + connector->name, mode->panel_orientation); + drm_connector_set_panel_orientation(connector, + mode->panel_orientation); + } + DRM_DEBUG_KMS("cmdline mode for connector %s %s %dx%d@%dHz%s%s%s\n", connector->name, mode->name, mode->xres, mode->yres, @@ -1139,7 +1146,8 @@ static const struct drm_prop_enum_list dp_colorspaces[] = { * coordinates, so if userspace rotates the picture to adjust for * the orientation it must also apply the same transformation to the * touchscreen input coordinates. This property is initialized by calling - * drm_connector_init_panel_orientation_property(). + * drm_connector_set_panel_orientation() or + * drm_connector_set_panel_orientation_with_quirk() * * scaling mode: * This property defines how a non-native mode is upscaled to the native @@ -2046,38 +2054,41 @@ void drm_connector_set_vrr_capable_property( EXPORT_SYMBOL(drm_connector_set_vrr_capable_property); /** - * drm_connector_init_panel_orientation_property - - * initialize the connecters panel_orientation property - * @connector: connector for which to init the panel-orientation property. - * @width: width in pixels of the panel, used for panel quirk detection - * @height: height in pixels of the panel, used for panel quirk detection + * drm_connector_set_panel_orientation - sets the connecter's panel_orientation + * @connector: connector for which to set the panel-orientation property. + * @panel_orientation: drm_panel_orientation value to set + * + * This function sets the connector's panel_orientation and attaches + * a "panel orientation" property to the connector. * - * This function should only be called for built-in panels, after setting - * connector->display_info.panel_orientation first (if known). + * Calling this function on a connector where the panel_orientation has + * already been set is a no-op (e.g. the orientation has been overridden with + * a kernel commandline option). * - * This function will check for platform specific (e.g. DMI based) quirks - * overriding display_info.panel_orientation first, then if panel_orientation - * is not DRM_MODE_PANEL_ORIENTATION_UNKNOWN it will attach the - * "panel orientation" property to the connector. + * It is allowed to call this function with a panel_orientation of + * DRM_MODE_PANEL_ORIENTATION_UNKNOWN, in which case it is a no-op. * * Returns: * Zero on success, negative errno on failure. */ -int drm_connector_init_panel_orientation_property( - struct drm_connector *connector, int width, int height) +int drm_connector_set_panel_orientation( + struct drm_connector *connector, + enum drm_panel_orientation panel_orientation) { struct drm_device *dev = connector->dev; struct drm_display_info *info = &connector->display_info; struct drm_property *prop; - int orientation_quirk; - orientation_quirk = drm_get_panel_orientation_quirk(width, height); - if (orientation_quirk != DRM_MODE_PANEL_ORIENTATION_UNKNOWN) - info->panel_orientation = orientation_quirk; + /* Already set? */ + if (info->panel_orientation != DRM_MODE_PANEL_ORIENTATION_UNKNOWN) + return 0; - if (info->panel_orientation == DRM_MODE_PANEL_ORIENTATION_UNKNOWN) + /* Don't attach the property if the orientation is unknown */ + if (panel_orientation == DRM_MODE_PANEL_ORIENTATION_UNKNOWN) return 0; + info->panel_orientation = panel_orientation; + prop = dev->mode_config.panel_orientation_property; if (!prop) { prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, @@ -2094,7 +2105,37 @@ int drm_connector_init_panel_orientation_property( info->panel_orientation); return 0; } -EXPORT_SYMBOL(drm_connector_init_panel_orientation_property); +EXPORT_SYMBOL(drm_connector_set_panel_orientation); + +/** + * drm_connector_set_panel_orientation_with_quirk - + * set the connecter's panel_orientation after checking for quirks + * @connector: connector for which to init the panel-orientation property. + * @panel_orientation: drm_panel_orientation value to set + * @width: width in pixels of the panel, used for panel quirk detection + * @height: height in pixels of the panel, used for panel quirk detection + * + * Like drm_connector_set_panel_orientation(), but with a check for platform + * specific (e.g. DMI based) quirks overriding the passed in panel_orientation. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_set_panel_orientation_with_quirk( + struct drm_connector *connector, + enum drm_panel_orientation panel_orientation, + int width, int height) +{ + int orientation_quirk; + + orientation_quirk = drm_get_panel_orientation_quirk(width, height); + if (orientation_quirk != DRM_MODE_PANEL_ORIENTATION_UNKNOWN) + panel_orientation = orientation_quirk; + + return drm_connector_set_panel_orientation(connector, + panel_orientation); +} +EXPORT_SYMBOL(drm_connector_set_panel_orientation_with_quirk); int drm_connector_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index c7d5e4c21423..16f2413403aa 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -216,6 +216,8 @@ int drm_mode_rmfb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_getfb(struct drm_device *dev, void *data, struct drm_file *file_priv); +int drm_mode_getfb2_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); int drm_mode_dirtyfb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index eab0f2687cd6..4e673d318503 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -182,8 +182,7 @@ int drm_debugfs_create_files(const struct drm_info_list *files, int count, for (i = 0; i < count; i++) { u32 features = files[i].driver_features; - if (features != 0 && - (dev->driver->driver_features & features) != features) + if (features && !drm_core_check_all_features(dev, features)) continue; tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index e22b812c4b80..5d67a41f7c3a 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c @@ -372,7 +372,7 @@ void drm_debugfs_crtc_crc_add(struct drm_crtc *crtc) crc_ent = debugfs_create_dir("crc", crtc->debugfs_entry); - debugfs_create_file("control", S_IRUGO, crc_ent, crtc, + debugfs_create_file("control", S_IRUGO | S_IWUSR, crc_ent, crtc, &drm_crtc_crc_control_fops); debugfs_create_file("data", S_IRUGO, crc_ent, crtc, &drm_crtc_crc_data_fops); diff --git a/drivers/gpu/drm/drm_dma.c b/drivers/gpu/drm/drm_dma.c index e45b07890c5a..a7add55a85b4 100644 --- a/drivers/gpu/drm/drm_dma.c +++ b/drivers/gpu/drm/drm_dma.c @@ -42,10 +42,10 @@ #include "drm_legacy.h" /** - * Initialize the DMA data. + * drm_legacy_dma_setup() - Initialize the DMA data. * - * \param dev DRM device. - * \return zero on success or a negative value on failure. + * @dev: DRM device. + * Return: zero on success or a negative value on failure. * * Allocate and initialize a drm_device_dma structure. */ @@ -71,9 +71,9 @@ int drm_legacy_dma_setup(struct drm_device *dev) } /** - * Cleanup the DMA resources. + * drm_legacy_dma_takedown() - Cleanup the DMA resources. * - * \param dev DRM device. + * @dev: DRM device. * * Free all pages associated with DMA buffers, the buffers and pages lists, and * finally the drm_device::dma structure itself. @@ -120,10 +120,10 @@ void drm_legacy_dma_takedown(struct drm_device *dev) } /** - * Free a buffer. + * drm_legacy_free_buffer() - Free a buffer. * - * \param dev DRM device. - * \param buf buffer to free. + * @dev: DRM device. + * @buf: buffer to free. * * Resets the fields of \p buf. */ @@ -139,9 +139,10 @@ void drm_legacy_free_buffer(struct drm_device *dev, struct drm_buf * buf) } /** - * Reclaim the buffers. + * drm_legacy_reclaim_buffers() - Reclaim the buffers. * - * \param file_priv DRM file private. + * @dev: DRM device. + * @file_priv: DRM file private. * * Frees each buffer associated with \p file_priv not already on the hardware. */ diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index a5364b5192b8..5a103e9b3c86 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -470,8 +470,7 @@ void drm_dp_downstream_debug(struct seq_file *m, int len; uint8_t rev[2]; int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; - bool branch_device = dpcd[DP_DOWNSTREAMPORT_PRESENT] & - DP_DWN_STRM_PORT_PRESENT; + bool branch_device = drm_dp_is_branch(dpcd); seq_printf(m, "\tDP branch device present: %s\n", branch_device ? "yes" : "no"); diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index cce0b1bba591..a811247cecfe 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -3499,9 +3499,9 @@ static int drm_dp_get_vc_payload_bw(u8 dp_link_bw, u8 dp_link_count) int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state) { int ret = 0; - int i = 0; struct drm_dp_mst_branch *mstb = NULL; + mutex_lock(&mgr->payload_lock); mutex_lock(&mgr->lock); if (mst_state == mgr->mst_state) goto out_unlock; @@ -3509,6 +3509,8 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms mgr->mst_state = mst_state; /* set the device into MST mode */ if (mst_state) { + struct drm_dp_payload reset_pay; + WARN_ON(mgr->mst_primary); /* get dpcd info */ @@ -3538,17 +3540,15 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms drm_dp_mst_topology_get_mstb(mgr->mst_primary); ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, - DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); - if (ret < 0) { + DP_MST_EN | + DP_UP_REQ_EN | + DP_UPSTREAM_IS_SRC); + if (ret < 0) goto out_unlock; - } - { - struct drm_dp_payload reset_pay; - reset_pay.start_slot = 0; - reset_pay.num_slots = 0x3f; - drm_dp_dpcd_write_payload(mgr, 0, &reset_pay); - } + reset_pay.start_slot = 0; + reset_pay.num_slots = 0x3f; + drm_dp_dpcd_write_payload(mgr, 0, &reset_pay); queue_work(system_long_wq, &mgr->work); @@ -3560,27 +3560,19 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms /* this can fail if the device is gone */ drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); ret = 0; - mutex_lock(&mgr->payload_lock); - memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); + memset(mgr->payloads, 0, + mgr->max_payloads * sizeof(mgr->payloads[0])); + memset(mgr->proposed_vcpis, 0, + mgr->max_payloads * sizeof(mgr->proposed_vcpis[0])); mgr->payload_mask = 0; set_bit(0, &mgr->payload_mask); - for (i = 0; i < mgr->max_payloads; i++) { - struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i]; - - if (vcpi) { - vcpi->vcpi = 0; - vcpi->num_slots = 0; - } - mgr->proposed_vcpis[i] = NULL; - } mgr->vcpi_mask = 0; - mutex_unlock(&mgr->payload_lock); - mgr->payload_id_table_cleared = false; } out_unlock: mutex_unlock(&mgr->lock); + mutex_unlock(&mgr->payload_lock); if (mstb) drm_dp_mst_topology_put_mstb(mstb); return ret; @@ -3708,7 +3700,7 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) { int len; u8 replyblock[32]; - int replylen, origlen, curreply; + int replylen, curreply; int ret; struct drm_dp_sideband_msg_rx *msg; int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE; @@ -3728,7 +3720,6 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) } replylen = msg->curchunk_len + msg->curchunk_hdrlen; - origlen = replylen; replylen -= len; curreply = len; while (replylen > 0) { diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 92d16724f949..1075b3a8b5b1 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -220,7 +220,7 @@ void drm_file_free(struct drm_file *file) DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", task_pid_nr(current), (long)old_encode_dev(file->minor->kdev->devt), - dev->open_count); + atomic_read(&dev->open_count)); if (drm_core_check_feature(dev, DRIVER_LEGACY) && dev->driver->preclose) @@ -379,7 +379,7 @@ int drm_open(struct inode *inode, struct file *filp) return PTR_ERR(minor); dev = minor->dev; - if (!dev->open_count++) + if (!atomic_fetch_inc(&dev->open_count)) need_setup = 1; /* share address_space across all char-devs of a single device */ @@ -398,7 +398,7 @@ int drm_open(struct inode *inode, struct file *filp) return 0; err_undo: - dev->open_count--; + atomic_dec(&dev->open_count); drm_minor_release(minor); return retcode; } @@ -440,11 +440,11 @@ int drm_release(struct inode *inode, struct file *filp) mutex_lock(&drm_global_mutex); - DRM_DEBUG("open_count = %d\n", dev->open_count); + DRM_DEBUG("open_count = %d\n", atomic_read(&dev->open_count)); drm_close_helper(filp); - if (!--dev->open_count) + if (atomic_dec_and_test(&dev->open_count)) drm_lastclose(dev); mutex_unlock(&drm_global_mutex); @@ -456,6 +456,40 @@ int drm_release(struct inode *inode, struct file *filp) EXPORT_SYMBOL(drm_release); /** + * drm_release_noglobal - release method for DRM file + * @inode: device inode + * @filp: file pointer. + * + * This function may be used by drivers as their &file_operations.release + * method. It frees any resources associated with the open file prior to taking + * the drm_global_mutex, which then calls the &drm_driver.postclose driver + * callback. If this is the last open file for the DRM device also proceeds to + * call the &drm_driver.lastclose driver callback. + * + * RETURNS: + * + * Always succeeds and returns 0. + */ +int drm_release_noglobal(struct inode *inode, struct file *filp) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_minor *minor = file_priv->minor; + struct drm_device *dev = minor->dev; + + drm_close_helper(filp); + + if (atomic_dec_and_mutex_lock(&dev->open_count, &drm_global_mutex)) { + drm_lastclose(dev); + mutex_unlock(&drm_global_mutex); + } + + drm_minor_release(minor); + + return 0; +} +EXPORT_SYMBOL(drm_release_noglobal); + +/** * drm_read - read method for DRM file * @filp: file pointer * @buffer: userspace destination pointer for the read diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 57564318ceea..57ac94ce9b9e 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -31,6 +31,7 @@ #include <drm/drm_file.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> +#include <drm/drm_gem.h> #include <drm/drm_print.h> #include <drm/drm_util.h> @@ -548,7 +549,128 @@ int drm_mode_getfb(struct drm_device *dev, out: drm_framebuffer_put(fb); + return ret; +} + +/** + * drm_mode_getfb2 - get extended FB info + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Lookup the FB given its ID and return info about it. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_getfb2_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_fb_cmd2 *r = data; + struct drm_framebuffer *fb; + unsigned int i; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); + if (!fb) + return -ENOENT; + + /* For multi-plane framebuffers, we require the driver to place the + * GEM objects directly in the drm_framebuffer. For single-plane + * framebuffers, we can fall back to create_handle. + */ + if (!fb->obj[0] && + (fb->format->num_planes > 1 || !fb->funcs->create_handle)) { + ret = -ENODEV; + goto out; + } + + r->height = fb->height; + r->width = fb->width; + r->pixel_format = fb->format->format; + + r->flags = 0; + if (dev->mode_config.allow_fb_modifiers) + r->flags |= DRM_MODE_FB_MODIFIERS; + + for (i = 0; i < ARRAY_SIZE(r->handles); i++) { + r->handles[i] = 0; + r->pitches[i] = 0; + r->offsets[i] = 0; + r->modifier[i] = 0; + } + + for (i = 0; i < fb->format->num_planes; i++) { + r->pitches[i] = fb->pitches[i]; + r->offsets[i] = fb->offsets[i]; + if (dev->mode_config.allow_fb_modifiers) + r->modifier[i] = fb->modifier; + } + + /* GET_FB2() is an unprivileged ioctl so we must not return a + * buffer-handle to non master/root processes! To match GET_FB() + * just return invalid handles (0) for non masters/root + * rather than making GET_FB2() privileged. + */ + if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN)) { + ret = 0; + goto out; + } + for (i = 0; i < fb->format->num_planes; i++) { + int j; + + /* If we reuse the same object for multiple planes, also + * return the same handle. + */ + for (j = 0; j < i; j++) { + if (fb->obj[i] == fb->obj[j]) { + r->handles[i] = r->handles[j]; + break; + } + } + + if (r->handles[i]) + continue; + + if (fb->obj[i]) { + ret = drm_gem_handle_create(file_priv, fb->obj[i], + &r->handles[i]); + } else { + WARN_ON(i > 0); + ret = fb->funcs->create_handle(fb, file_priv, + &r->handles[i]); + } + + if (ret != 0) + goto out; + } + +out: + if (ret != 0) { + /* Delete any previously-created handles on failure. */ + for (i = 0; i < ARRAY_SIZE(r->handles); i++) { + int j; + + if (r->handles[i]) + drm_gem_handle_delete(file_priv, r->handles[i]); + + /* Zero out any handles identical to the one we just + * deleted. + */ + for (j = i + 1; j < ARRAY_SIZE(r->handles); j++) { + if (r->handles[j] == r->handles[i]) + r->handles[j] = 0; + } + } + } + + drm_framebuffer_put(fb); return ret; } diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index a4863326061a..92a11bb42365 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -1141,3 +1141,64 @@ void drm_vram_helper_release_mm(struct drm_device *dev) dev->vram_mm = NULL; } EXPORT_SYMBOL(drm_vram_helper_release_mm); + +/* + * Mode-config helpers + */ + +static enum drm_mode_status +drm_vram_helper_mode_valid_internal(struct drm_device *dev, + const struct drm_display_mode *mode, + unsigned long max_bpp) +{ + struct drm_vram_mm *vmm = dev->vram_mm; + unsigned long fbsize, fbpages, max_fbpages; + + if (WARN_ON(!dev->vram_mm)) + return MODE_BAD; + + max_fbpages = (vmm->vram_size / 2) >> PAGE_SHIFT; + + fbsize = mode->hdisplay * mode->vdisplay * max_bpp; + fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE); + + if (fbpages > max_fbpages) + return MODE_MEM; + + return MODE_OK; +} + +/** + * drm_vram_helper_mode_valid - Tests if a display mode's + * framebuffer fits into the available video memory. + * @dev: the DRM device + * @mode: the mode to test + * + * This function tests if enough video memory is available for using the + * specified display mode. Atomic modesetting requires importing the + * designated framebuffer into video memory before evicting the active + * one. Hence, any framebuffer may consume at most half of the available + * VRAM. Display modes that require a larger framebuffer can not be used, + * even if the CRTC does support them. Each framebuffer is assumed to + * have 32-bit color depth. + * + * Note: + * The function can only test if the display mode is supported in + * general. If there are too many framebuffers pinned to video memory, + * a display mode may still not be usable in practice. The color depth of + * 32-bit fits all current use case. A more flexible test can be added + * when necessary. + * + * Returns: + * MODE_OK if the display mode is supported, or an error code of type + * enum drm_mode_status otherwise. + */ +enum drm_mode_status +drm_vram_helper_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode) +{ + static const unsigned long max_bpp = 4; /* DRM_FORMAT_XRGB8888 */ + + return drm_vram_helper_mode_valid_internal(dev, mode, max_bpp); +} +EXPORT_SYMBOL(drm_vram_helper_mode_valid); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 5afb39688b55..9e41972c4bbc 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -671,6 +671,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_connector_property_set_ioctl, DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, 0), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB2, drm_mode_getfb2_ioctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb_ioctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2_ioctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb_ioctl, 0), diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 16bff1be4b8a..558baf989f5a 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -24,7 +24,6 @@ #include <drm/drm_modes.h> #include <drm/drm_probe_helper.h> #include <drm/drm_rect.h> -#include <drm/drm_vblank.h> #include <video/mipi_display.h> #define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */ @@ -238,6 +237,23 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, } EXPORT_SYMBOL(mipi_dbi_buf_copy); +static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev, + unsigned int xs, unsigned int xe, + unsigned int ys, unsigned int ye) +{ + struct mipi_dbi *dbi = &dbidev->dbi; + + xs += dbidev->left_offset; + xe += dbidev->left_offset; + ys += dbidev->top_offset; + ye += dbidev->top_offset; + + mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xff, + xs & 0xff, (xe >> 8) & 0xff, xe & 0xff); + mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, (ys >> 8) & 0xff, + ys & 0xff, (ye >> 8) & 0xff, ye & 0xff); +} + static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) { struct drm_gem_object *gem = drm_gem_fb_get_obj(fb, 0); @@ -271,12 +287,8 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) tr = cma_obj->vaddr; } - mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, - (rect->x1 >> 8) & 0xff, rect->x1 & 0xff, - ((rect->x2 - 1) >> 8) & 0xff, (rect->x2 - 1) & 0xff); - mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, - (rect->y1 >> 8) & 0xff, rect->y1 & 0xff, - ((rect->y2 - 1) >> 8) & 0xff, (rect->y2 - 1) & 0xff); + mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1, + rect->y2 - 1); ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, tr, width * height * 2); @@ -299,18 +311,10 @@ void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; - struct drm_crtc *crtc = &pipe->crtc; struct drm_rect rect; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) mipi_dbi_fb_dirty(state->fb, &rect); - - if (crtc->state->event) { - spin_lock_irq(&crtc->dev->event_lock); - drm_crtc_send_vblank_event(crtc, crtc->state->event); - spin_unlock_irq(&crtc->dev->event_lock); - crtc->state->event = NULL; - } } EXPORT_SYMBOL(mipi_dbi_pipe_update); @@ -366,10 +370,7 @@ static void mipi_dbi_blank(struct mipi_dbi_dev *dbidev) memset(dbidev->tx_buf, 0, len); - mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, 0, 0, - ((width - 1) >> 8) & 0xFF, (width - 1) & 0xFF); - mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, 0, 0, - ((height - 1) >> 8) & 0xFF, (height - 1) & 0xFF); + mipi_dbi_set_window_address(dbidev, 0, width - 1, 0, height - 1); mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, (u8 *)dbidev->tx_buf, len); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index f2e43d341980..c6bb98729a26 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -51,8 +51,6 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t align) { drm_dma_handle_t *dmah; - unsigned long addr; - size_t sz; /* pci_alloc_consistent only guarantees alignment to the smallest * PAGE_SIZE order which is greater than or equal to the requested size. @@ -68,47 +66,18 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali dmah->size = size; dmah->vaddr = dma_alloc_coherent(&dev->pdev->dev, size, &dmah->busaddr, - GFP_KERNEL | __GFP_COMP); + GFP_KERNEL); if (dmah->vaddr == NULL) { kfree(dmah); return NULL; } - /* XXX - Is virt_to_page() legal for consistent mem? */ - /* Reserve */ - for (addr = (unsigned long)dmah->vaddr, sz = size; - sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { - SetPageReserved(virt_to_page((void *)addr)); - } - return dmah; } EXPORT_SYMBOL(drm_pci_alloc); -/* - * Free a PCI consistent memory block without freeing its descriptor. - * - * This function is for internal use in the Linux-specific DRM core code. - */ -void __drm_legacy_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) -{ - unsigned long addr; - size_t sz; - - if (dmah->vaddr) { - /* XXX - Is virt_to_page() legal for consistent mem? */ - /* Unreserve */ - for (addr = (unsigned long)dmah->vaddr, sz = dmah->size; - sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { - ClearPageReserved(virt_to_page((void *)addr)); - } - dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr, - dmah->busaddr); - } -} - /** * drm_pci_free - Free a PCI consistent memory block * @dev: DRM device @@ -119,7 +88,8 @@ void __drm_legacy_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) */ void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) { - __drm_legacy_pci_free(dev, dmah); + dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr, + dmah->busaddr); kfree(dmah); } diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 669c93fe2500..42d46414f767 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -43,27 +43,66 @@ * - Signal a syncobj (set a trivially signaled fence) * - Wait for a syncobj's fence to appear and be signaled * + * The syncobj userspace API also provides operations to manipulate a syncobj + * in terms of a timeline of struct &dma_fence_chain rather than a single + * struct &dma_fence, through the following operations: + * + * - Signal a given point on the timeline + * - Wait for a given point to appear and/or be signaled + * - Import and export from/to a given point of a timeline + * * At it's core, a syncobj is simply a wrapper around a pointer to a struct * &dma_fence which may be NULL. * When a syncobj is first created, its pointer is either NULL or a pointer * to an already signaled fence depending on whether the * &DRM_SYNCOBJ_CREATE_SIGNALED flag is passed to * &DRM_IOCTL_SYNCOBJ_CREATE. - * When GPU work which signals a syncobj is enqueued in a DRM driver, - * the syncobj fence is replaced with a fence which will be signaled by the - * completion of that work. - * When GPU work which waits on a syncobj is enqueued in a DRM driver, the - * driver retrieves syncobj's current fence at the time the work is enqueued - * waits on that fence before submitting the work to hardware. - * If the syncobj's fence is NULL, the enqueue operation is expected to fail. - * All manipulation of the syncobjs's fence happens in terms of the current - * fence at the time the ioctl is called by userspace regardless of whether - * that operation is an immediate host-side operation (signal or reset) or - * or an operation which is enqueued in some driver queue. - * &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used to - * manipulate a syncobj from the host by resetting its pointer to NULL or + * + * If the syncobj is considered as a binary (its state is either signaled or + * unsignaled) primitive, when GPU work is enqueued in a DRM driver to signal + * the syncobj, the syncobj's fence is replaced with a fence which will be + * signaled by the completion of that work. + * If the syncobj is considered as a timeline primitive, when GPU work is + * enqueued in a DRM driver to signal the a given point of the syncobj, a new + * struct &dma_fence_chain pointing to the DRM driver's fence and also + * pointing to the previous fence that was in the syncobj. The new struct + * &dma_fence_chain fence replace the syncobj's fence and will be signaled by + * completion of the DRM driver's work and also any work associated with the + * fence previously in the syncobj. + * + * When GPU work which waits on a syncobj is enqueued in a DRM driver, at the + * time the work is enqueued, it waits on the syncobj's fence before + * submitting the work to hardware. That fence is either : + * + * - The syncobj's current fence if the syncobj is considered as a binary + * primitive. + * - The struct &dma_fence associated with a given point if the syncobj is + * considered as a timeline primitive. + * + * If the syncobj's fence is NULL or not present in the syncobj's timeline, + * the enqueue operation is expected to fail. + * + * With binary syncobj, all manipulation of the syncobjs's fence happens in + * terms of the current fence at the time the ioctl is called by userspace + * regardless of whether that operation is an immediate host-side operation + * (signal or reset) or or an operation which is enqueued in some driver + * queue. &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used + * to manipulate a syncobj from the host by resetting its pointer to NULL or * setting its pointer to a fence which is already signaled. * + * With a timeline syncobj, all manipulation of the synobj's fence happens in + * terms of a u64 value referring to point in the timeline. See + * dma_fence_chain_find_seqno() to see how a given point is found in the + * timeline. + * + * Note that applications should be careful to always use timeline set of + * ioctl() when dealing with syncobj considered as timeline. Using a binary + * set of ioctl() with a syncobj considered as timeline could result incorrect + * synchronization. The use of binary syncobj is supported through the + * timeline set of ioctl() by using a point value of 0, this will reproduce + * the behavior of the binary set of ioctl() (for example replace the + * syncobj's fence when signaling). + * * * Host-side wait on syncobjs * -------------------------- @@ -87,6 +126,16 @@ * synchronize between the two. * This requirement is inherited from the Vulkan fence API. * + * Similarly, &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT takes an array of syncobj + * handles as well as an array of u64 points and does a host-side wait on all + * of syncobj fences at the given points simultaneously. + * + * &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT also adds the ability to wait for a given + * fence to materialize on the timeline without waiting for the fence to be + * signaled by using the &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE flag. This + * requirement is inherited from the wait-before-signal behavior required by + * the Vulkan timeline semaphore API. + * * * Import/export of syncobjs * ------------------------- @@ -120,6 +169,18 @@ * Because sync files are immutable, resetting or signaling the syncobj * will not affect any sync files whose fences have been imported into the * syncobj. + * + * + * Import/export of timeline points in timeline syncobjs + * ----------------------------------------------------- + * + * &DRM_IOCTL_SYNCOBJ_TRANSFER provides a mechanism to transfer a struct + * &dma_fence_chain of a syncobj at a given u64 point to another u64 point + * into another syncobj. + * + * Note that if you want to transfer a struct &dma_fence_chain from a given + * point on a timeline syncobj from/into a binary syncobj, you can use the + * point 0 to mean take/replace the fence in the syncobj. */ #include <linux/anon_inodes.h> diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 1659b13b178c..27a99c486f02 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -69,6 +69,12 @@ * &drm_driver.max_vblank_count. In that case the vblank core only disables the * vblanks after a timer has expired, which can be configured through the * ``vblankoffdelay`` module parameter. + * + * Drivers for hardware without support for vertical-blanking interrupts + * must not call drm_vblank_init(). For such drivers, atomic helpers will + * automatically generate fake vblank events as part of the display update. + * This functionality also can be controlled by the driver by enabling and + * disabling struct drm_crtc_state.no_vblank. */ /* Retry timestamp calculation up to 3 times to satisfy @@ -502,6 +508,28 @@ err: EXPORT_SYMBOL(drm_vblank_init); /** + * drm_dev_has_vblank - test if vblanking has been initialized for + * a device + * @dev: the device + * + * Drivers may call this function to test if vblank support is + * initialized for a device. For most hardware this means that vblanking + * can also be enabled. + * + * Atomic helpers use this function to initialize + * &drm_crtc_state.no_vblank. See also drm_atomic_helper_check_modeset(). + * + * Returns: + * True if vblanking has been initialized for the given device, false + * otherwise. + */ +bool drm_dev_has_vblank(const struct drm_device *dev) +{ + return dev->num_crtcs != 0; +} +EXPORT_SYMBOL(drm_dev_has_vblank); + +/** * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC * @crtc: which CRTC's vblank waitqueue to retrieve * diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 52e87e4869a5..64619fe90046 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -269,8 +269,6 @@ static void drm_vm_shm_close(struct vm_area_struct *vma) } if (!found_maps) { - drm_dma_handle_t dmah; - switch (map->type) { case _DRM_REGISTERS: case _DRM_FRAME_BUFFER: @@ -284,10 +282,10 @@ static void drm_vm_shm_close(struct vm_area_struct *vma) case _DRM_SCATTER_GATHER: break; case _DRM_CONSISTENT: - dmah.vaddr = map->handle; - dmah.busaddr = map->offset; - dmah.size = map->size; - __drm_legacy_pci_free(dev, &dmah); + dma_free_coherent(&dev->pdev->dev, + map->size, + map->handle, + map->offset); break; } kfree(map); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c index 50b988fdd5cc..99397ac3b363 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c @@ -54,6 +54,7 @@ int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, } const struct drm_mode_config_funcs hibmc_mode_funcs = { + .mode_valid = drm_vram_helper_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, .fb_create = drm_gem_fb_create, diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index f8e882101396..a7457303c62e 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1668,9 +1668,8 @@ static void icl_dsi_add_properties(struct intel_connector *connector) connector->base.state->scaling_mode = DRM_MODE_SCALE_ASPECT; - connector->base.display_info.panel_orientation = - intel_dsi_get_panel_orientation(connector); - drm_connector_init_panel_orientation_property(&connector->base, + drm_connector_set_panel_orientation_with_quirk(&connector->base, + intel_dsi_get_panel_orientation(connector), connector->panel.fixed_mode->hdisplay, connector->panel.fixed_mode->vdisplay); } diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 888ea8a170d1..fdd943a17de3 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1181,8 +1181,6 @@ struct intel_hdmi { }; struct intel_dp_mst_encoder; -#define DP_MAX_DOWNSTREAM_PORTS 0x10 - /* * enum link_m_n_set: * When platform provides two set of M_N registers for dp, we can diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c7424e2a04a3..4074d83b1a5f 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3149,7 +3149,7 @@ static bool downstream_hpd_needs_d0(struct intel_dp *intel_dp) * FIXME should really check all downstream ports... */ return intel_dp->dpcd[DP_DPCD_REV] == 0x11 && - intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT && + drm_dp_is_branch(intel_dp->dpcd) && intel_dp->downstream_ports[0] & DP_DS_PORT_HPD; } @@ -7401,9 +7401,12 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, intel_connector->panel.backlight.power = intel_edp_backlight_power; intel_panel_setup_backlight(connector, pipe); - if (fixed_mode) - drm_connector_init_panel_orientation_property( - connector, fixed_mode->hdisplay, fixed_mode->vdisplay); + if (fixed_mode) { + /* We do not know the orientation, but their might be a quirk */ + drm_connector_set_panel_orientation_with_quirk(connector, + DRM_MODE_PANEL_ORIENTATION_UNKNOWN, + fixed_mode->hdisplay, fixed_mode->vdisplay); + } return true; diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index daf4fc3dab6f..4514529ff6f3 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -1628,10 +1628,9 @@ static void vlv_dsi_add_properties(struct intel_connector *connector) connector->base.state->scaling_mode = DRM_MODE_SCALE_ASPECT; - connector->base.display_info.panel_orientation = - vlv_dsi_get_panel_orientation(connector); - drm_connector_init_panel_orientation_property( + drm_connector_set_panel_orientation_with_quirk( &connector->base, + vlv_dsi_get_panel_orientation(connector), connector->panel.fixed_mode->hdisplay, connector->panel.fixed_mode->vdisplay); } diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f7385abdd74b..2312b95fec33 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -2672,7 +2672,7 @@ const struct dev_pm_ops i915_pm_ops = { static const struct file_operations i915_driver_fops = { .owner = THIS_MODULE, .open = drm_open, - .release = drm_release, + .release = drm_release_noglobal, .unlocked_ioctl = drm_ioctl, .mmap = i915_gem_mmap, .poll = drm_poll, diff --git a/drivers/gpu/drm/i915/i915_switcheroo.c b/drivers/gpu/drm/i915/i915_switcheroo.c index 39c79e1c5b52..ed69b5d4a375 100644 --- a/drivers/gpu/drm/i915/i915_switcheroo.c +++ b/drivers/gpu/drm/i915/i915_switcheroo.c @@ -43,7 +43,7 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) * locking inversion with the driver load path. And the access here is * completely racy anyway. So don't bother with locking for now. */ - return i915 && i915->drm.open_count == 0; + return i915 && atomic_read(&i915->drm.open_count) == 0; } static const struct vga_switcheroo_client_ops i915_switcheroo_ops = { diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 28826c0aa24a..6776ebb3246d 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -359,7 +359,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, if (!fb) return 0; - if (!state->crtc) + if (WARN_ON(!state->crtc)) return -EINVAL; crtc_state = diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 3dca424059f7..142acff5ab37 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -24,6 +24,7 @@ struct imx_parallel_display { struct drm_connector connector; struct drm_encoder encoder; + struct drm_bridge bridge; struct device *dev; void *edid; int edid_len; @@ -31,7 +32,7 @@ struct imx_parallel_display { u32 bus_flags; struct drm_display_mode mode; struct drm_panel *panel; - struct drm_bridge *bridge; + struct drm_bridge *next_bridge; }; static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c) @@ -44,6 +45,11 @@ static inline struct imx_parallel_display *enc_to_imxpd(struct drm_encoder *e) return container_of(e, struct imx_parallel_display, encoder); } +static inline struct imx_parallel_display *bridge_to_imxpd(struct drm_bridge *b) +{ + return container_of(b, struct imx_parallel_display, bridge); +} + static int imx_pd_connector_get_modes(struct drm_connector *connector) { struct imx_parallel_display *imxpd = con_to_imxpd(connector); @@ -89,37 +95,148 @@ static struct drm_encoder *imx_pd_connector_best_encoder( return &imxpd->encoder; } -static void imx_pd_encoder_enable(struct drm_encoder *encoder) +static void imx_pd_bridge_enable(struct drm_bridge *bridge) { - struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge); drm_panel_prepare(imxpd->panel); drm_panel_enable(imxpd->panel); } -static void imx_pd_encoder_disable(struct drm_encoder *encoder) +static void imx_pd_bridge_disable(struct drm_bridge *bridge) { - struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge); drm_panel_disable(imxpd->panel); drm_panel_unprepare(imxpd->panel); } -static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +static const u32 imx_pd_bus_fmts[] = { + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_BGR888_1X24, + MEDIA_BUS_FMT_GBR888_1X24, + MEDIA_BUS_FMT_RGB666_1X18, + MEDIA_BUS_FMT_RGB666_1X24_CPADHI, + MEDIA_BUS_FMT_RGB565_1X16, +}; + +static u32 * +imx_pd_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) { - struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); struct drm_display_info *di = &conn_state->connector->display_info; - struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge); + u32 *output_fmts; - if (!imxpd->bus_format && di->num_bus_formats) { - imx_crtc_state->bus_flags = di->bus_flags; - imx_crtc_state->bus_format = di->bus_formats[0]; - } else { - imx_crtc_state->bus_flags = imxpd->bus_flags; - imx_crtc_state->bus_format = imxpd->bus_format; + if (!imxpd->bus_format && !di->num_bus_formats) { + *num_output_fmts = ARRAY_SIZE(imx_pd_bus_fmts); + return kmemdup(imx_pd_bus_fmts, sizeof(imx_pd_bus_fmts), + GFP_KERNEL); + } + + *num_output_fmts = 1; + output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL); + if (!output_fmts) + return NULL; + + if (!imxpd->bus_format && di->num_bus_formats) + output_fmts[0] = di->bus_formats[0]; + else + output_fmts[0] = imxpd->bus_format; + + return output_fmts; +} + +static bool imx_pd_format_supported(u32 output_fmt) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) { + if (imx_pd_bus_fmts[i] == output_fmt) + return true; + } + + return false; +} + +static u32 * +imx_pd_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge); + u32 *input_fmts; + + /* + * If the next bridge does not support bus format negotiation, let's + * use the static bus format definition (imxpd->bus_format) if it's + * specified, RGB888 when it's not. + */ + if (output_fmt == MEDIA_BUS_FMT_FIXED) + output_fmt = imxpd->bus_format ? : MEDIA_BUS_FMT_RGB888_1X24; + + /* Now make sure the requested output format is supported. */ + if ((imxpd->bus_format && imxpd->bus_format != output_fmt) || + !imx_pd_format_supported(output_fmt)) { + *num_input_fmts = 0; + return NULL; + } + + *num_input_fmts = 1; + input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + + input_fmts[0] = output_fmt; + return input_fmts; +} + +static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); + struct drm_display_info *di = &conn_state->connector->display_info; + struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge); + struct drm_bridge_state *next_bridge_state = NULL; + struct drm_bridge *next_bridge; + u32 bus_flags, bus_fmt; + + next_bridge = drm_bridge_get_next_bridge(bridge); + if (next_bridge) + next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + next_bridge); + + if (next_bridge_state) + bus_flags = next_bridge_state->input_bus_cfg.flags; + else if (!imxpd->bus_format && di->num_bus_formats) + bus_flags = di->bus_flags; + else + bus_flags = imxpd->bus_flags; + + bus_fmt = bridge_state->input_bus_cfg.format; + if (!imx_pd_format_supported(bus_fmt)) + return -EINVAL; + + if (bus_flags & + ~(DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE | + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)) { + dev_warn(imxpd->dev, "invalid bus_flags (%x)\n", bus_flags); + return -EINVAL; } + + bridge_state->output_bus_cfg.flags = bus_flags; + bridge_state->input_bus_cfg.flags = bus_flags; + imx_crtc_state->bus_flags = bus_flags; + imx_crtc_state->bus_format = bridge_state->input_bus_cfg.format; imx_crtc_state->di_hsync_pin = 2; imx_crtc_state->di_vsync_pin = 3; @@ -143,10 +260,15 @@ static const struct drm_encoder_funcs imx_pd_encoder_funcs = { .destroy = imx_drm_encoder_destroy, }; -static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { - .enable = imx_pd_encoder_enable, - .disable = imx_pd_encoder_disable, - .atomic_check = imx_pd_encoder_atomic_check, +static const struct drm_bridge_funcs imx_pd_bridge_funcs = { + .enable = imx_pd_bridge_enable, + .disable = imx_pd_bridge_disable, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_check = imx_pd_bridge_atomic_check, + .atomic_get_input_bus_fmts = imx_pd_bridge_atomic_get_input_bus_fmts, + .atomic_get_output_bus_fmts = imx_pd_bridge_atomic_get_output_bus_fmts, }; static int imx_pd_register(struct drm_device *drm, @@ -166,11 +288,13 @@ static int imx_pd_register(struct drm_device *drm, */ imxpd->connector.dpms = DRM_MODE_DPMS_OFF; - drm_encoder_helper_add(encoder, &imx_pd_encoder_helper_funcs); drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); - if (!imxpd->bridge) { + imxpd->bridge.funcs = &imx_pd_bridge_funcs; + drm_bridge_attach(encoder, &imxpd->bridge, NULL); + + if (!imxpd->next_bridge) { drm_connector_helper_add(&imxpd->connector, &imx_pd_connector_helper_funcs); drm_connector_init(drm, &imxpd->connector, @@ -181,8 +305,9 @@ static int imx_pd_register(struct drm_device *drm, if (imxpd->panel) drm_panel_attach(imxpd->panel, &imxpd->connector); - if (imxpd->bridge) { - ret = drm_bridge_attach(encoder, imxpd->bridge, NULL); + if (imxpd->next_bridge) { + ret = drm_bridge_attach(encoder, imxpd->next_bridge, + &imxpd->bridge); if (ret < 0) { dev_err(imxpd->dev, "failed to attach bridge: %d\n", ret); @@ -227,7 +352,8 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) imxpd->bus_format = bus_format; /* port@1 is the output port */ - ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, &imxpd->bridge); + ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, + &imxpd->next_bridge); if (ret && ret != -ENODEV) return ret; diff --git a/drivers/gpu/drm/lima/lima_drv.c b/drivers/gpu/drm/lima/lima_drv.c index 124efe4fa97b..2daac64d8955 100644 --- a/drivers/gpu/drm/lima/lima_drv.c +++ b/drivers/gpu/drm/lima/lima_drv.c @@ -15,10 +15,14 @@ #include "lima_vm.h" int lima_sched_timeout_ms; +uint lima_heap_init_nr_pages = 8; MODULE_PARM_DESC(sched_timeout_ms, "task run timeout in ms"); module_param_named(sched_timeout_ms, lima_sched_timeout_ms, int, 0444); +MODULE_PARM_DESC(heap_init_nr_pages, "heap buffer init number of pages"); +module_param_named(heap_init_nr_pages, lima_heap_init_nr_pages, uint, 0444); + static int lima_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_lima_get_param *args = data; @@ -68,7 +72,7 @@ static int lima_ioctl_gem_create(struct drm_device *dev, void *data, struct drm_ if (args->pad) return -EINVAL; - if (args->flags) + if (args->flags & ~(LIMA_BO_FLAG_HEAP)) return -EINVAL; if (args->size == 0) @@ -241,6 +245,12 @@ static const struct drm_ioctl_desc lima_drm_driver_ioctls[] = { DEFINE_DRM_GEM_FOPS(lima_drm_driver_fops); +/** + * Changelog: + * + * - 1.1.0 - add heap buffer support + */ + static struct drm_driver lima_drm_driver = { .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ, .open = lima_drm_driver_open, @@ -250,9 +260,9 @@ static struct drm_driver lima_drm_driver = { .fops = &lima_drm_driver_fops, .name = "lima", .desc = "lima DRM", - .date = "20190217", + .date = "20191231", .major = 1, - .minor = 0, + .minor = 1, .patchlevel = 0, .gem_create_object = lima_gem_create_object, diff --git a/drivers/gpu/drm/lima/lima_drv.h b/drivers/gpu/drm/lima/lima_drv.h index 69c7344715c9..f492ecc6a5d9 100644 --- a/drivers/gpu/drm/lima/lima_drv.h +++ b/drivers/gpu/drm/lima/lima_drv.h @@ -9,6 +9,7 @@ #include "lima_ctx.h" extern int lima_sched_timeout_ms; +extern uint lima_heap_init_nr_pages; struct lima_vm; struct lima_bo; diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c index d0059d8c97d8..5404e0d668db 100644 --- a/drivers/gpu/drm/lima/lima_gem.c +++ b/drivers/gpu/drm/lima/lima_gem.c @@ -4,6 +4,8 @@ #include <linux/mm.h> #include <linux/sync_file.h> #include <linux/pagemap.h> +#include <linux/shmem_fs.h> +#include <linux/dma-mapping.h> #include <drm/drm_file.h> #include <drm/drm_syncobj.h> @@ -15,6 +17,83 @@ #include "lima_gem.h" #include "lima_vm.h" +int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm) +{ + struct page **pages; + struct address_space *mapping = bo->base.base.filp->f_mapping; + struct device *dev = bo->base.base.dev->dev; + size_t old_size = bo->heap_size; + size_t new_size = bo->heap_size ? bo->heap_size * 2 : + (lima_heap_init_nr_pages << PAGE_SHIFT); + struct sg_table sgt; + int i, ret; + + if (bo->heap_size >= bo->base.base.size) + return -ENOSPC; + + new_size = min(new_size, bo->base.base.size); + + mutex_lock(&bo->base.pages_lock); + + if (bo->base.pages) { + pages = bo->base.pages; + } else { + pages = kvmalloc_array(bo->base.base.size >> PAGE_SHIFT, + sizeof(*pages), GFP_KERNEL | __GFP_ZERO); + if (!pages) { + mutex_unlock(&bo->base.pages_lock); + return -ENOMEM; + } + + bo->base.pages = pages; + bo->base.pages_use_count = 1; + + mapping_set_unevictable(mapping); + } + + for (i = old_size >> PAGE_SHIFT; i < new_size >> PAGE_SHIFT; i++) { + struct page *page = shmem_read_mapping_page(mapping, i); + + if (IS_ERR(page)) { + mutex_unlock(&bo->base.pages_lock); + return PTR_ERR(page); + } + pages[i] = page; + } + + mutex_unlock(&bo->base.pages_lock); + + ret = sg_alloc_table_from_pages(&sgt, pages, i, 0, + new_size, GFP_KERNEL); + if (ret) + return ret; + + if (bo->base.sgt) { + dma_unmap_sg(dev, bo->base.sgt->sgl, + bo->base.sgt->nents, DMA_BIDIRECTIONAL); + sg_free_table(bo->base.sgt); + } else { + bo->base.sgt = kmalloc(sizeof(*bo->base.sgt), GFP_KERNEL); + if (!bo->base.sgt) { + sg_free_table(&sgt); + return -ENOMEM; + } + } + + dma_map_sg(dev, sgt.sgl, sgt.nents, DMA_BIDIRECTIONAL); + + *bo->base.sgt = sgt; + + if (vm) { + ret = lima_vm_map_bo(vm, bo, old_size >> PAGE_SHIFT); + if (ret) + return ret; + } + + bo->heap_size = new_size; + return 0; +} + int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file, u32 size, u32 flags, u32 *handle) { @@ -22,7 +101,8 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file, gfp_t mask; struct drm_gem_shmem_object *shmem; struct drm_gem_object *obj; - struct sg_table *sgt; + struct lima_bo *bo; + bool is_heap = flags & LIMA_BO_FLAG_HEAP; shmem = drm_gem_shmem_create(dev, size); if (IS_ERR(shmem)) @@ -36,10 +116,18 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file, mask |= __GFP_DMA32; mapping_set_gfp_mask(obj->filp->f_mapping, mask); - sgt = drm_gem_shmem_get_pages_sgt(obj); - if (IS_ERR(sgt)) { - err = PTR_ERR(sgt); - goto out; + if (is_heap) { + bo = to_lima_bo(obj); + err = lima_heap_alloc(bo, NULL); + if (err) + goto out; + } else { + struct sg_table *sgt = drm_gem_shmem_get_pages_sgt(obj); + + if (IS_ERR(sgt)) { + err = PTR_ERR(sgt); + goto out; + } } err = drm_gem_handle_create(file, obj, handle); @@ -79,17 +167,47 @@ static void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *f lima_vm_bo_del(vm, bo); } +static int lima_gem_pin(struct drm_gem_object *obj) +{ + struct lima_bo *bo = to_lima_bo(obj); + + if (bo->heap_size) + return -EINVAL; + + return drm_gem_shmem_pin(obj); +} + +static void *lima_gem_vmap(struct drm_gem_object *obj) +{ + struct lima_bo *bo = to_lima_bo(obj); + + if (bo->heap_size) + return ERR_PTR(-EINVAL); + + return drm_gem_shmem_vmap(obj); +} + +static int lima_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +{ + struct lima_bo *bo = to_lima_bo(obj); + + if (bo->heap_size) + return -EINVAL; + + return drm_gem_shmem_mmap(obj, vma); +} + static const struct drm_gem_object_funcs lima_gem_funcs = { .free = lima_gem_free_object, .open = lima_gem_object_open, .close = lima_gem_object_close, .print_info = drm_gem_shmem_print_info, - .pin = drm_gem_shmem_pin, + .pin = lima_gem_pin, .unpin = drm_gem_shmem_unpin, .get_sg_table = drm_gem_shmem_get_sg_table, - .vmap = drm_gem_shmem_vmap, + .vmap = lima_gem_vmap, .vunmap = drm_gem_shmem_vunmap, - .mmap = drm_gem_shmem_mmap, + .mmap = lima_gem_mmap, }; struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size) diff --git a/drivers/gpu/drm/lima/lima_gem.h b/drivers/gpu/drm/lima/lima_gem.h index 1800feb3e47f..ccea06142f4b 100644 --- a/drivers/gpu/drm/lima/lima_gem.h +++ b/drivers/gpu/drm/lima/lima_gem.h @@ -7,12 +7,15 @@ #include <drm/drm_gem_shmem_helper.h> struct lima_submit; +struct lima_vm; struct lima_bo { struct drm_gem_shmem_object base; struct mutex lock; struct list_head va; + + size_t heap_size; }; static inline struct lima_bo * @@ -31,6 +34,7 @@ static inline struct dma_resv *lima_bo_resv(struct lima_bo *bo) return bo->base.base.resv; } +int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm); struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size); int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file, u32 size, u32 flags, u32 *handle); diff --git a/drivers/gpu/drm/lima/lima_gp.c b/drivers/gpu/drm/lima/lima_gp.c index ccf49faedebf..52b210f9a605 100644 --- a/drivers/gpu/drm/lima/lima_gp.c +++ b/drivers/gpu/drm/lima/lima_gp.c @@ -11,6 +11,8 @@ #include "lima_device.h" #include "lima_gp.h" #include "lima_regs.h" +#include "lima_gem.h" +#include "lima_vm.h" #define gp_write(reg, data) writel(data, ip->iomem + reg) #define gp_read(reg) readl(ip->iomem + reg) @@ -20,6 +22,7 @@ static irqreturn_t lima_gp_irq_handler(int irq, void *data) struct lima_ip *ip = data; struct lima_device *dev = ip->dev; struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp; + struct lima_sched_task *task = pipe->current_task; u32 state = gp_read(LIMA_GP_INT_STAT); u32 status = gp_read(LIMA_GP_STATUS); bool done = false; @@ -29,8 +32,16 @@ static irqreturn_t lima_gp_irq_handler(int irq, void *data) return IRQ_NONE; if (state & LIMA_GP_IRQ_MASK_ERROR) { - dev_err(dev->dev, "gp error irq state=%x status=%x\n", - state, status); + if ((state & LIMA_GP_IRQ_MASK_ERROR) == + LIMA_GP_IRQ_PLBU_OUT_OF_MEM) { + dev_dbg(dev->dev, "gp out of heap irq status=%x\n", + status); + } else { + dev_err(dev->dev, "gp error irq state=%x status=%x\n", + state, status); + if (task) + task->recoverable = false; + } /* mask all interrupts before hard reset */ gp_write(LIMA_GP_INT_MASK, 0); @@ -43,6 +54,7 @@ static irqreturn_t lima_gp_irq_handler(int irq, void *data) bool active = status & (LIMA_GP_STATUS_VS_ACTIVE | LIMA_GP_STATUS_PLBU_ACTIVE); done = valid && !active; + pipe->error = false; } gp_write(LIMA_GP_INT_CLEAR, state); @@ -121,6 +133,22 @@ static void lima_gp_task_run(struct lima_sched_pipe *pipe, u32 cmd = 0; int i; + /* update real heap buffer size for GP */ + for (i = 0; i < task->num_bos; i++) { + struct lima_bo *bo = task->bos[i]; + + if (bo->heap_size && + lima_vm_get_va(task->vm, bo) == + f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2]) { + f[LIMA_GP_PLBU_ALLOC_END_ADDR >> 2] = + f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2] + + bo->heap_size; + task->recoverable = true; + task->heap = bo; + break; + } + } + if (f[LIMA_GP_VSCL_START_ADDR >> 2] != f[LIMA_GP_VSCL_END_ADDR >> 2]) cmd |= LIMA_GP_CMD_START_VS; @@ -184,6 +212,31 @@ static void lima_gp_task_mmu_error(struct lima_sched_pipe *pipe) lima_sched_pipe_task_done(pipe); } +static int lima_gp_task_recover(struct lima_sched_pipe *pipe) +{ + struct lima_ip *ip = pipe->processor[0]; + struct lima_sched_task *task = pipe->current_task; + struct drm_lima_gp_frame *frame = task->frame; + u32 *f = frame->frame; + size_t fail_size = + f[LIMA_GP_PLBU_ALLOC_END_ADDR >> 2] - + f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2]; + + if (fail_size == task->heap->heap_size) { + int ret; + + ret = lima_heap_alloc(task->heap, task->vm); + if (ret < 0) + return ret; + } + + gp_write(LIMA_GP_INT_MASK, LIMA_GP_IRQ_MASK_USED); + gp_write(LIMA_GP_PLBU_ALLOC_END_ADDR, + f[LIMA_GP_PLBU_ALLOC_START_ADDR >> 2] + task->heap->heap_size); + gp_write(LIMA_GP_CMD, LIMA_GP_CMD_UPDATE_PLBU_ALLOC); + return 0; +} + static void lima_gp_print_version(struct lima_ip *ip) { u32 version, major, minor; @@ -270,6 +323,7 @@ int lima_gp_pipe_init(struct lima_device *dev) pipe->task_fini = lima_gp_task_fini; pipe->task_error = lima_gp_task_error; pipe->task_mmu_error = lima_gp_task_mmu_error; + pipe->task_recover = lima_gp_task_recover; return 0; } diff --git a/drivers/gpu/drm/lima/lima_mmu.c b/drivers/gpu/drm/lima/lima_mmu.c index 97ec09dee572..f79d2af427e7 100644 --- a/drivers/gpu/drm/lima/lima_mmu.c +++ b/drivers/gpu/drm/lima/lima_mmu.c @@ -99,6 +99,11 @@ void lima_mmu_fini(struct lima_ip *ip) } +void lima_mmu_flush_tlb(struct lima_ip *ip) +{ + mmu_write(LIMA_MMU_COMMAND, LIMA_MMU_COMMAND_ZAP_CACHE); +} + void lima_mmu_switch_vm(struct lima_ip *ip, struct lima_vm *vm) { struct lima_device *dev = ip->dev; diff --git a/drivers/gpu/drm/lima/lima_mmu.h b/drivers/gpu/drm/lima/lima_mmu.h index 8c78319bcc8e..4f8ccbebcba1 100644 --- a/drivers/gpu/drm/lima/lima_mmu.h +++ b/drivers/gpu/drm/lima/lima_mmu.h @@ -10,6 +10,7 @@ struct lima_vm; int lima_mmu_init(struct lima_ip *ip); void lima_mmu_fini(struct lima_ip *ip); +void lima_mmu_flush_tlb(struct lima_ip *ip); void lima_mmu_switch_vm(struct lima_ip *ip, struct lima_vm *vm); void lima_mmu_page_fault_resume(struct lima_ip *ip); diff --git a/drivers/gpu/drm/lima/lima_regs.h b/drivers/gpu/drm/lima/lima_regs.h index ace8ecefbe90..0124c90e0153 100644 --- a/drivers/gpu/drm/lima/lima_regs.h +++ b/drivers/gpu/drm/lima/lima_regs.h @@ -239,6 +239,7 @@ #define LIMA_MMU_STATUS_REPLAY_BUFFER_EMPTY BIT(4) #define LIMA_MMU_STATUS_PAGE_FAULT_IS_WRITE BIT(5) #define LIMA_MMU_STATUS_BUS_ID(x) ((x >> 6) & 0x1F) +#define LIMA_MMU_STATUS_STALL_NOT_ACTIVE BIT(31) #define LIMA_MMU_COMMAND 0x0008 #define LIMA_MMU_COMMAND_ENABLE_PAGING 0x00 #define LIMA_MMU_COMMAND_DISABLE_PAGING 0x01 diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index b561dd05bd62..3886999b4533 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c @@ -313,6 +313,26 @@ static const struct drm_sched_backend_ops lima_sched_ops = { .free_job = lima_sched_free_job, }; +static void lima_sched_recover_work(struct work_struct *work) +{ + struct lima_sched_pipe *pipe = + container_of(work, struct lima_sched_pipe, recover_work); + int i; + + for (i = 0; i < pipe->num_l2_cache; i++) + lima_l2_cache_flush(pipe->l2_cache[i]); + + if (pipe->bcast_mmu) { + lima_mmu_flush_tlb(pipe->bcast_mmu); + } else { + for (i = 0; i < pipe->num_mmu; i++) + lima_mmu_flush_tlb(pipe->mmu[i]); + } + + if (pipe->task_recover(pipe)) + drm_sched_fault(&pipe->base); +} + int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name) { unsigned int timeout = lima_sched_timeout_ms > 0 ? @@ -321,6 +341,8 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name) pipe->fence_context = dma_fence_context_alloc(1); spin_lock_init(&pipe->fence_lock); + INIT_WORK(&pipe->recover_work, lima_sched_recover_work); + return drm_sched_init(&pipe->base, &lima_sched_ops, 1, 0, msecs_to_jiffies(timeout), name); } @@ -332,11 +354,14 @@ void lima_sched_pipe_fini(struct lima_sched_pipe *pipe) void lima_sched_pipe_task_done(struct lima_sched_pipe *pipe) { - if (pipe->error) - drm_sched_fault(&pipe->base); - else { - struct lima_sched_task *task = pipe->current_task; - + struct lima_sched_task *task = pipe->current_task; + + if (pipe->error) { + if (task && task->recoverable) + schedule_work(&pipe->recover_work); + else + drm_sched_fault(&pipe->base); + } else { pipe->task_fini(pipe); dma_fence_signal(task->fence); } diff --git a/drivers/gpu/drm/lima/lima_sched.h b/drivers/gpu/drm/lima/lima_sched.h index 1d814fecbcc0..d64393fb50a9 100644 --- a/drivers/gpu/drm/lima/lima_sched.h +++ b/drivers/gpu/drm/lima/lima_sched.h @@ -20,6 +20,9 @@ struct lima_sched_task { struct lima_bo **bos; int num_bos; + bool recoverable; + struct lima_bo *heap; + /* pipe fence */ struct dma_fence *fence; }; @@ -68,6 +71,9 @@ struct lima_sched_pipe { void (*task_fini)(struct lima_sched_pipe *pipe); void (*task_error)(struct lima_sched_pipe *pipe); void (*task_mmu_error)(struct lima_sched_pipe *pipe); + int (*task_recover)(struct lima_sched_pipe *pipe); + + struct work_struct recover_work; }; int lima_sched_task_init(struct lima_sched_task *task, diff --git a/drivers/gpu/drm/lima/lima_vm.c b/drivers/gpu/drm/lima/lima_vm.c index 840e2350d872..5b92fb82674a 100644 --- a/drivers/gpu/drm/lima/lima_vm.c +++ b/drivers/gpu/drm/lima/lima_vm.c @@ -155,6 +155,7 @@ err_out0: void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo) { struct lima_bo_va *bo_va; + u32 size; mutex_lock(&bo->lock); @@ -166,8 +167,9 @@ void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo) mutex_lock(&vm->lock); + size = bo->heap_size ? bo->heap_size : bo_va->node.size; lima_vm_unmap_range(vm, bo_va->node.start, - bo_va->node.start + bo_va->node.size - 1); + bo_va->node.start + size - 1); drm_mm_remove_node(&bo_va->node); @@ -277,3 +279,45 @@ void lima_vm_print(struct lima_vm *vm) } } } + +int lima_vm_map_bo(struct lima_vm *vm, struct lima_bo *bo, int pageoff) +{ + struct lima_bo_va *bo_va; + struct sg_dma_page_iter sg_iter; + int offset = 0, err; + u32 base; + + mutex_lock(&bo->lock); + + bo_va = lima_vm_bo_find(vm, bo); + if (!bo_va) { + err = -ENOENT; + goto err_out0; + } + + mutex_lock(&vm->lock); + + base = bo_va->node.start + (pageoff << PAGE_SHIFT); + for_each_sg_dma_page(bo->base.sgt->sgl, &sg_iter, + bo->base.sgt->nents, pageoff) { + err = lima_vm_map_page(vm, sg_page_iter_dma_address(&sg_iter), + base + offset); + if (err) + goto err_out1; + + offset += PAGE_SIZE; + } + + mutex_unlock(&vm->lock); + + mutex_unlock(&bo->lock); + return 0; + +err_out1: + if (offset) + lima_vm_unmap_range(vm, base, base + offset - 1); + mutex_unlock(&vm->lock); +err_out0: + mutex_unlock(&bo->lock); + return err; +} diff --git a/drivers/gpu/drm/lima/lima_vm.h b/drivers/gpu/drm/lima/lima_vm.h index e0bdedcf14dd..22aeec77d84d 100644 --- a/drivers/gpu/drm/lima/lima_vm.h +++ b/drivers/gpu/drm/lima/lima_vm.h @@ -58,5 +58,6 @@ static inline void lima_vm_put(struct lima_vm *vm) } void lima_vm_print(struct lima_vm *vm); +int lima_vm_map_bo(struct lima_vm *vm, struct lima_bo *bo, int pageoff); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 1b62ccc57aef..2b4b21b02e40 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -647,13 +647,6 @@ nouveau_ttm_tt_create(struct ttm_buffer_object *bo, uint32_t page_flags) } static int -nouveau_bo_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) -{ - /* We'll do this from user space. */ - return 0; -} - -static int nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, struct ttm_mem_type_manager *man) { @@ -1697,7 +1690,6 @@ struct ttm_bo_driver nouveau_bo_driver = { .ttm_tt_create = &nouveau_ttm_tt_create, .ttm_tt_populate = &nouveau_ttm_tt_populate, .ttm_tt_unpopulate = &nouveau_ttm_tt_unpopulate, - .invalidate_caches = nouveau_bo_invalidate_caches, .init_mem_type = nouveau_bo_init_mem_type, .eviction_valuable = ttm_bo_eviction_valuable, .evict_flags = nouveau_bo_evict_flags, diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index d865d8aeac3c..c85dd8afa3c3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -72,7 +72,7 @@ nouveau_switcheroo_can_switch(struct pci_dev *pdev) * locking inversion with the driver load path. And the access here is * completely racy anyway. So don't bother with locking for now. */ - return dev->open_count == 0; + return atomic_read(&dev->open_count) == 0; } static const struct vga_switcheroo_client_ops diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index ae44ac2ec106..da3b84602cdd 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -29,6 +29,15 @@ config DRM_PANEL_BOE_HIMAX8279D 24 bit RGB per pixel. It provides a MIPI DSI interface to the host and has a built-in LED backlight. +config DRM_PANEL_BOE_TV101WUM_NL6 + tristate "BOE TV101WUM and AUO KD101N80 45NA 1200x1920 panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to support for BOE TV101WUM and AUO KD101N80 + 45NA WUXGA PANEL DSI Video Mode panel + config DRM_PANEL_LVDS tristate "Generic LVDS panel driver" depends on OF @@ -50,6 +59,15 @@ config DRM_PANEL_SIMPLE that it can be automatically turned off when the panel goes into a low power state. +config DRM_PANEL_FEIXIN_K101_IM2BA02 + tristate "Feixin K101 IM2BA02 panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for the Feixin K101 IM2BA02 + 4-lane 800x1280 MIPI DSI panel. + config DRM_PANEL_FEIYANG_FY07024DI26A30D tristate "Feiyang FY07024DI26A30-D MIPI-DSI LCD panel" depends on OF @@ -275,6 +293,12 @@ config DRM_PANEL_SAMSUNG_S6E63M0 Say Y here if you want to enable support for Samsung S6E63M0 AMOLED LCD panel. +config DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 + tristate "Samsung AMS452EF01 panel with S6E88A0 DSI video mode controller" + depends on OF + select DRM_MIPI_DSI + select VIDEOMODE_HELPERS + config DRM_PANEL_SAMSUNG_S6E8AA0 tristate "Samsung S6E8AA0 DSI video mode panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 7c4d3c581fd4..af1e2a3cc5fc 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o +obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o +obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o @@ -28,6 +30,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c new file mode 100644 index 000000000000..48a164257d18 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -0,0 +1,854 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Jitao Shi <jitao.shi@mediatek.com> + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> + +#include <video/mipi_display.h> + +struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + + /** + * @width_mm: width of the panel's active display area + * @height_mm: height of the panel's active display area + */ + struct { + unsigned int width_mm; + unsigned int height_mm; + } size; + + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + const struct panel_init_cmd *init_cmds; + unsigned int lanes; + bool discharge_on_disable; +}; + +struct boe_panel { + struct drm_panel base; + struct mipi_dsi_device *dsi; + + const struct panel_desc *desc; + + struct regulator *pp1800; + struct regulator *avee; + struct regulator *avdd; + struct gpio_desc *enable_gpio; + + bool prepared; +}; + +enum dsi_cmd_type { + INIT_DCS_CMD, + DELAY_CMD, +}; + +struct panel_init_cmd { + enum dsi_cmd_type type; + size_t len; + const char *data; +}; + +#define _INIT_DCS_CMD(...) { \ + .type = INIT_DCS_CMD, \ + .len = sizeof((char[]){__VA_ARGS__}), \ + .data = (char[]){__VA_ARGS__} } + +#define _INIT_DELAY_CMD(...) { \ + .type = DELAY_CMD,\ + .len = sizeof((char[]){__VA_ARGS__}), \ + .data = (char[]){__VA_ARGS__} } + +static const struct panel_init_cmd boe_init_cmd[] = { + _INIT_DELAY_CMD(24), + _INIT_DCS_CMD(0xB0, 0x05), + _INIT_DCS_CMD(0xB1, 0xE5), + _INIT_DCS_CMD(0xB3, 0x52), + _INIT_DCS_CMD(0xB0, 0x00), + _INIT_DCS_CMD(0xB3, 0x88), + _INIT_DCS_CMD(0xB0, 0x04), + _INIT_DCS_CMD(0xB8, 0x00), + _INIT_DCS_CMD(0xB0, 0x00), + _INIT_DCS_CMD(0xB6, 0x03), + _INIT_DCS_CMD(0xBA, 0x8B), + _INIT_DCS_CMD(0xBF, 0x1A), + _INIT_DCS_CMD(0xC0, 0x0F), + _INIT_DCS_CMD(0xC2, 0x0C), + _INIT_DCS_CMD(0xC3, 0x02), + _INIT_DCS_CMD(0xC4, 0x0C), + _INIT_DCS_CMD(0xC5, 0x02), + _INIT_DCS_CMD(0xB0, 0x01), + _INIT_DCS_CMD(0xE0, 0x26), + _INIT_DCS_CMD(0xE1, 0x26), + _INIT_DCS_CMD(0xDC, 0x00), + _INIT_DCS_CMD(0xDD, 0x00), + _INIT_DCS_CMD(0xCC, 0x26), + _INIT_DCS_CMD(0xCD, 0x26), + _INIT_DCS_CMD(0xC8, 0x00), + _INIT_DCS_CMD(0xC9, 0x00), + _INIT_DCS_CMD(0xD2, 0x03), + _INIT_DCS_CMD(0xD3, 0x03), + _INIT_DCS_CMD(0xE6, 0x04), + _INIT_DCS_CMD(0xE7, 0x04), + _INIT_DCS_CMD(0xC4, 0x09), + _INIT_DCS_CMD(0xC5, 0x09), + _INIT_DCS_CMD(0xD8, 0x0A), + _INIT_DCS_CMD(0xD9, 0x0A), + _INIT_DCS_CMD(0xC2, 0x0B), + _INIT_DCS_CMD(0xC3, 0x0B), + _INIT_DCS_CMD(0xD6, 0x0C), + _INIT_DCS_CMD(0xD7, 0x0C), + _INIT_DCS_CMD(0xC0, 0x05), + _INIT_DCS_CMD(0xC1, 0x05), + _INIT_DCS_CMD(0xD4, 0x06), + _INIT_DCS_CMD(0xD5, 0x06), + _INIT_DCS_CMD(0xCA, 0x07), + _INIT_DCS_CMD(0xCB, 0x07), + _INIT_DCS_CMD(0xDE, 0x08), + _INIT_DCS_CMD(0xDF, 0x08), + _INIT_DCS_CMD(0xB0, 0x02), + _INIT_DCS_CMD(0xC0, 0x00), + _INIT_DCS_CMD(0xC1, 0x0D), + _INIT_DCS_CMD(0xC2, 0x17), + _INIT_DCS_CMD(0xC3, 0x26), + _INIT_DCS_CMD(0xC4, 0x31), + _INIT_DCS_CMD(0xC5, 0x1C), + _INIT_DCS_CMD(0xC6, 0x2C), + _INIT_DCS_CMD(0xC7, 0x33), + _INIT_DCS_CMD(0xC8, 0x31), + _INIT_DCS_CMD(0xC9, 0x37), + _INIT_DCS_CMD(0xCA, 0x37), + _INIT_DCS_CMD(0xCB, 0x37), + _INIT_DCS_CMD(0xCC, 0x39), + _INIT_DCS_CMD(0xCD, 0x2E), + _INIT_DCS_CMD(0xCE, 0x2F), + _INIT_DCS_CMD(0xCF, 0x2F), + _INIT_DCS_CMD(0xD0, 0x07), + _INIT_DCS_CMD(0xD2, 0x00), + _INIT_DCS_CMD(0xD3, 0x0D), + _INIT_DCS_CMD(0xD4, 0x17), + _INIT_DCS_CMD(0xD5, 0x26), + _INIT_DCS_CMD(0xD6, 0x31), + _INIT_DCS_CMD(0xD7, 0x3F), + _INIT_DCS_CMD(0xD8, 0x3F), + _INIT_DCS_CMD(0xD9, 0x3F), + _INIT_DCS_CMD(0xDA, 0x3F), + _INIT_DCS_CMD(0xDB, 0x37), + _INIT_DCS_CMD(0xDC, 0x37), + _INIT_DCS_CMD(0xDD, 0x37), + _INIT_DCS_CMD(0xDE, 0x39), + _INIT_DCS_CMD(0xDF, 0x2E), + _INIT_DCS_CMD(0xE0, 0x2F), + _INIT_DCS_CMD(0xE1, 0x2F), + _INIT_DCS_CMD(0xE2, 0x07), + _INIT_DCS_CMD(0xB0, 0x03), + _INIT_DCS_CMD(0xC8, 0x0B), + _INIT_DCS_CMD(0xC9, 0x07), + _INIT_DCS_CMD(0xC3, 0x00), + _INIT_DCS_CMD(0xE7, 0x00), + _INIT_DCS_CMD(0xC5, 0x2A), + _INIT_DCS_CMD(0xDE, 0x2A), + _INIT_DCS_CMD(0xCA, 0x43), + _INIT_DCS_CMD(0xC9, 0x07), + _INIT_DCS_CMD(0xE4, 0xC0), + _INIT_DCS_CMD(0xE5, 0x0D), + _INIT_DCS_CMD(0xCB, 0x00), + _INIT_DCS_CMD(0xB0, 0x06), + _INIT_DCS_CMD(0xB8, 0xA5), + _INIT_DCS_CMD(0xC0, 0xA5), + _INIT_DCS_CMD(0xC7, 0x0F), + _INIT_DCS_CMD(0xD5, 0x32), + _INIT_DCS_CMD(0xB8, 0x00), + _INIT_DCS_CMD(0xC0, 0x00), + _INIT_DCS_CMD(0xBC, 0x00), + _INIT_DCS_CMD(0xB0, 0x07), + _INIT_DCS_CMD(0xB1, 0x00), + _INIT_DCS_CMD(0xB2, 0x02), + _INIT_DCS_CMD(0xB3, 0x0F), + _INIT_DCS_CMD(0xB4, 0x25), + _INIT_DCS_CMD(0xB5, 0x39), + _INIT_DCS_CMD(0xB6, 0x4E), + _INIT_DCS_CMD(0xB7, 0x72), + _INIT_DCS_CMD(0xB8, 0x97), + _INIT_DCS_CMD(0xB9, 0xDC), + _INIT_DCS_CMD(0xBA, 0x22), + _INIT_DCS_CMD(0xBB, 0xA4), + _INIT_DCS_CMD(0xBC, 0x2B), + _INIT_DCS_CMD(0xBD, 0x2F), + _INIT_DCS_CMD(0xBE, 0xA9), + _INIT_DCS_CMD(0xBF, 0x25), + _INIT_DCS_CMD(0xC0, 0x61), + _INIT_DCS_CMD(0xC1, 0x97), + _INIT_DCS_CMD(0xC2, 0xB2), + _INIT_DCS_CMD(0xC3, 0xCD), + _INIT_DCS_CMD(0xC4, 0xD9), + _INIT_DCS_CMD(0xC5, 0xE7), + _INIT_DCS_CMD(0xC6, 0xF4), + _INIT_DCS_CMD(0xC7, 0xFA), + _INIT_DCS_CMD(0xC8, 0xFC), + _INIT_DCS_CMD(0xC9, 0x00), + _INIT_DCS_CMD(0xCA, 0x00), + _INIT_DCS_CMD(0xCB, 0x16), + _INIT_DCS_CMD(0xCC, 0xAF), + _INIT_DCS_CMD(0xCD, 0xFF), + _INIT_DCS_CMD(0xCE, 0xFF), + _INIT_DCS_CMD(0xB0, 0x08), + _INIT_DCS_CMD(0xB1, 0x04), + _INIT_DCS_CMD(0xB2, 0x05), + _INIT_DCS_CMD(0xB3, 0x11), + _INIT_DCS_CMD(0xB4, 0x24), + _INIT_DCS_CMD(0xB5, 0x39), + _INIT_DCS_CMD(0xB6, 0x4F), + _INIT_DCS_CMD(0xB7, 0x72), + _INIT_DCS_CMD(0xB8, 0x98), + _INIT_DCS_CMD(0xB9, 0xDC), + _INIT_DCS_CMD(0xBA, 0x23), + _INIT_DCS_CMD(0xBB, 0xA6), + _INIT_DCS_CMD(0xBC, 0x2C), + _INIT_DCS_CMD(0xBD, 0x30), + _INIT_DCS_CMD(0xBE, 0xAA), + _INIT_DCS_CMD(0xBF, 0x26), + _INIT_DCS_CMD(0xC0, 0x62), + _INIT_DCS_CMD(0xC1, 0x9B), + _INIT_DCS_CMD(0xC2, 0xB5), + _INIT_DCS_CMD(0xC3, 0xCF), + _INIT_DCS_CMD(0xC4, 0xDB), + _INIT_DCS_CMD(0xC5, 0xE8), + _INIT_DCS_CMD(0xC6, 0xF5), + _INIT_DCS_CMD(0xC7, 0xFA), + _INIT_DCS_CMD(0xC8, 0xFC), + _INIT_DCS_CMD(0xC9, 0x00), + _INIT_DCS_CMD(0xCA, 0x00), + _INIT_DCS_CMD(0xCB, 0x16), + _INIT_DCS_CMD(0xCC, 0xAF), + _INIT_DCS_CMD(0xCD, 0xFF), + _INIT_DCS_CMD(0xCE, 0xFF), + _INIT_DCS_CMD(0xB0, 0x09), + _INIT_DCS_CMD(0xB1, 0x04), + _INIT_DCS_CMD(0xB2, 0x02), + _INIT_DCS_CMD(0xB3, 0x16), + _INIT_DCS_CMD(0xB4, 0x24), + _INIT_DCS_CMD(0xB5, 0x3B), + _INIT_DCS_CMD(0xB6, 0x4F), + _INIT_DCS_CMD(0xB7, 0x73), + _INIT_DCS_CMD(0xB8, 0x99), + _INIT_DCS_CMD(0xB9, 0xE0), + _INIT_DCS_CMD(0xBA, 0x26), + _INIT_DCS_CMD(0xBB, 0xAD), + _INIT_DCS_CMD(0xBC, 0x36), + _INIT_DCS_CMD(0xBD, 0x3A), + _INIT_DCS_CMD(0xBE, 0xAE), + _INIT_DCS_CMD(0xBF, 0x2A), + _INIT_DCS_CMD(0xC0, 0x66), + _INIT_DCS_CMD(0xC1, 0x9E), + _INIT_DCS_CMD(0xC2, 0xB8), + _INIT_DCS_CMD(0xC3, 0xD1), + _INIT_DCS_CMD(0xC4, 0xDD), + _INIT_DCS_CMD(0xC5, 0xE9), + _INIT_DCS_CMD(0xC6, 0xF6), + _INIT_DCS_CMD(0xC7, 0xFA), + _INIT_DCS_CMD(0xC8, 0xFC), + _INIT_DCS_CMD(0xC9, 0x00), + _INIT_DCS_CMD(0xCA, 0x00), + _INIT_DCS_CMD(0xCB, 0x16), + _INIT_DCS_CMD(0xCC, 0xAF), + _INIT_DCS_CMD(0xCD, 0xFF), + _INIT_DCS_CMD(0xCE, 0xFF), + _INIT_DCS_CMD(0xB0, 0x0A), + _INIT_DCS_CMD(0xB1, 0x00), + _INIT_DCS_CMD(0xB2, 0x02), + _INIT_DCS_CMD(0xB3, 0x0F), + _INIT_DCS_CMD(0xB4, 0x25), + _INIT_DCS_CMD(0xB5, 0x39), + _INIT_DCS_CMD(0xB6, 0x4E), + _INIT_DCS_CMD(0xB7, 0x72), + _INIT_DCS_CMD(0xB8, 0x97), + _INIT_DCS_CMD(0xB9, 0xDC), + _INIT_DCS_CMD(0xBA, 0x22), + _INIT_DCS_CMD(0xBB, 0xA4), + _INIT_DCS_CMD(0xBC, 0x2B), + _INIT_DCS_CMD(0xBD, 0x2F), + _INIT_DCS_CMD(0xBE, 0xA9), + _INIT_DCS_CMD(0xBF, 0x25), + _INIT_DCS_CMD(0xC0, 0x61), + _INIT_DCS_CMD(0xC1, 0x97), + _INIT_DCS_CMD(0xC2, 0xB2), + _INIT_DCS_CMD(0xC3, 0xCD), + _INIT_DCS_CMD(0xC4, 0xD9), + _INIT_DCS_CMD(0xC5, 0xE7), + _INIT_DCS_CMD(0xC6, 0xF4), + _INIT_DCS_CMD(0xC7, 0xFA), + _INIT_DCS_CMD(0xC8, 0xFC), + _INIT_DCS_CMD(0xC9, 0x00), + _INIT_DCS_CMD(0xCA, 0x00), + _INIT_DCS_CMD(0xCB, 0x16), + _INIT_DCS_CMD(0xCC, 0xAF), + _INIT_DCS_CMD(0xCD, 0xFF), + _INIT_DCS_CMD(0xCE, 0xFF), + _INIT_DCS_CMD(0xB0, 0x0B), + _INIT_DCS_CMD(0xB1, 0x04), + _INIT_DCS_CMD(0xB2, 0x05), + _INIT_DCS_CMD(0xB3, 0x11), + _INIT_DCS_CMD(0xB4, 0x24), + _INIT_DCS_CMD(0xB5, 0x39), + _INIT_DCS_CMD(0xB6, 0x4F), + _INIT_DCS_CMD(0xB7, 0x72), + _INIT_DCS_CMD(0xB8, 0x98), + _INIT_DCS_CMD(0xB9, 0xDC), + _INIT_DCS_CMD(0xBA, 0x23), + _INIT_DCS_CMD(0xBB, 0xA6), + _INIT_DCS_CMD(0xBC, 0x2C), + _INIT_DCS_CMD(0xBD, 0x30), + _INIT_DCS_CMD(0xBE, 0xAA), + _INIT_DCS_CMD(0xBF, 0x26), + _INIT_DCS_CMD(0xC0, 0x62), + _INIT_DCS_CMD(0xC1, 0x9B), + _INIT_DCS_CMD(0xC2, 0xB5), + _INIT_DCS_CMD(0xC3, 0xCF), + _INIT_DCS_CMD(0xC4, 0xDB), + _INIT_DCS_CMD(0xC5, 0xE8), + _INIT_DCS_CMD(0xC6, 0xF5), + _INIT_DCS_CMD(0xC7, 0xFA), + _INIT_DCS_CMD(0xC8, 0xFC), + _INIT_DCS_CMD(0xC9, 0x00), + _INIT_DCS_CMD(0xCA, 0x00), + _INIT_DCS_CMD(0xCB, 0x16), + _INIT_DCS_CMD(0xCC, 0xAF), + _INIT_DCS_CMD(0xCD, 0xFF), + _INIT_DCS_CMD(0xCE, 0xFF), + _INIT_DCS_CMD(0xB0, 0x0C), + _INIT_DCS_CMD(0xB1, 0x04), + _INIT_DCS_CMD(0xB2, 0x02), + _INIT_DCS_CMD(0xB3, 0x16), + _INIT_DCS_CMD(0xB4, 0x24), + _INIT_DCS_CMD(0xB5, 0x3B), + _INIT_DCS_CMD(0xB6, 0x4F), + _INIT_DCS_CMD(0xB7, 0x73), + _INIT_DCS_CMD(0xB8, 0x99), + _INIT_DCS_CMD(0xB9, 0xE0), + _INIT_DCS_CMD(0xBA, 0x26), + _INIT_DCS_CMD(0xBB, 0xAD), + _INIT_DCS_CMD(0xBC, 0x36), + _INIT_DCS_CMD(0xBD, 0x3A), + _INIT_DCS_CMD(0xBE, 0xAE), + _INIT_DCS_CMD(0xBF, 0x2A), + _INIT_DCS_CMD(0xC0, 0x66), + _INIT_DCS_CMD(0xC1, 0x9E), + _INIT_DCS_CMD(0xC2, 0xB8), + _INIT_DCS_CMD(0xC3, 0xD1), + _INIT_DCS_CMD(0xC4, 0xDD), + _INIT_DCS_CMD(0xC5, 0xE9), + _INIT_DCS_CMD(0xC6, 0xF6), + _INIT_DCS_CMD(0xC7, 0xFA), + _INIT_DCS_CMD(0xC8, 0xFC), + _INIT_DCS_CMD(0xC9, 0x00), + _INIT_DCS_CMD(0xCA, 0x00), + _INIT_DCS_CMD(0xCB, 0x16), + _INIT_DCS_CMD(0xCC, 0xAF), + _INIT_DCS_CMD(0xCD, 0xFF), + _INIT_DCS_CMD(0xCE, 0xFF), + _INIT_DCS_CMD(0xB0, 0x00), + _INIT_DCS_CMD(0xB3, 0x08), + _INIT_DCS_CMD(0xB0, 0x04), + _INIT_DCS_CMD(0xB8, 0x68), + _INIT_DELAY_CMD(150), + {}, +}; + +static const struct panel_init_cmd auo_kd101n80_45na_init_cmd[] = { + _INIT_DELAY_CMD(24), + _INIT_DCS_CMD(0x11), + _INIT_DELAY_CMD(120), + _INIT_DCS_CMD(0x29), + _INIT_DELAY_CMD(120), + {}, +}; + +static const struct panel_init_cmd auo_b101uan08_3_init_cmd[] = { + _INIT_DELAY_CMD(24), + _INIT_DCS_CMD(0xB0, 0x01), + _INIT_DCS_CMD(0xC0, 0x48), + _INIT_DCS_CMD(0xC1, 0x48), + _INIT_DCS_CMD(0xC2, 0x47), + _INIT_DCS_CMD(0xC3, 0x47), + _INIT_DCS_CMD(0xC4, 0x46), + _INIT_DCS_CMD(0xC5, 0x46), + _INIT_DCS_CMD(0xC6, 0x45), + _INIT_DCS_CMD(0xC7, 0x45), + _INIT_DCS_CMD(0xC8, 0x64), + _INIT_DCS_CMD(0xC9, 0x64), + _INIT_DCS_CMD(0xCA, 0x4F), + _INIT_DCS_CMD(0xCB, 0x4F), + _INIT_DCS_CMD(0xCC, 0x40), + _INIT_DCS_CMD(0xCD, 0x40), + _INIT_DCS_CMD(0xCE, 0x66), + _INIT_DCS_CMD(0xCF, 0x66), + _INIT_DCS_CMD(0xD0, 0x4F), + _INIT_DCS_CMD(0xD1, 0x4F), + _INIT_DCS_CMD(0xD2, 0x41), + _INIT_DCS_CMD(0xD3, 0x41), + _INIT_DCS_CMD(0xD4, 0x48), + _INIT_DCS_CMD(0xD5, 0x48), + _INIT_DCS_CMD(0xD6, 0x47), + _INIT_DCS_CMD(0xD7, 0x47), + _INIT_DCS_CMD(0xD8, 0x46), + _INIT_DCS_CMD(0xD9, 0x46), + _INIT_DCS_CMD(0xDA, 0x45), + _INIT_DCS_CMD(0xDB, 0x45), + _INIT_DCS_CMD(0xDC, 0x64), + _INIT_DCS_CMD(0xDD, 0x64), + _INIT_DCS_CMD(0xDE, 0x4F), + _INIT_DCS_CMD(0xDF, 0x4F), + _INIT_DCS_CMD(0xE0, 0x40), + _INIT_DCS_CMD(0xE1, 0x40), + _INIT_DCS_CMD(0xE2, 0x66), + _INIT_DCS_CMD(0xE3, 0x66), + _INIT_DCS_CMD(0xE4, 0x4F), + _INIT_DCS_CMD(0xE5, 0x4F), + _INIT_DCS_CMD(0xE6, 0x41), + _INIT_DCS_CMD(0xE7, 0x41), + _INIT_DELAY_CMD(150), + {}, +}; + +static inline struct boe_panel *to_boe_panel(struct drm_panel *panel) +{ + return container_of(panel, struct boe_panel, base); +} + +static int boe_panel_init_dcs_cmd(struct boe_panel *boe) +{ + struct mipi_dsi_device *dsi = boe->dsi; + struct drm_panel *panel = &boe->base; + int i, err = 0; + + if (boe->desc->init_cmds) { + const struct panel_init_cmd *init_cmds = boe->desc->init_cmds; + + for (i = 0; init_cmds[i].len != 0; i++) { + const struct panel_init_cmd *cmd = &init_cmds[i]; + + switch (cmd->type) { + case DELAY_CMD: + msleep(cmd->data[0]); + err = 0; + break; + + case INIT_DCS_CMD: + err = mipi_dsi_dcs_write(dsi, cmd->data[0], + cmd->len <= 1 ? NULL : + &cmd->data[1], + cmd->len - 1); + break; + + default: + err = -EINVAL; + } + + if (err < 0) { + dev_err(panel->dev, + "failed to write command %u\n", i); + return err; + } + } + } + return 0; +} + +static int boe_panel_enter_sleep_mode(struct boe_panel *boe) +{ + struct mipi_dsi_device *dsi = boe->dsi; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) + return ret; + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) + return ret; + + return 0; +} + +static int boe_panel_unprepare(struct drm_panel *panel) +{ + struct boe_panel *boe = to_boe_panel(panel); + int ret; + + if (!boe->prepared) + return 0; + + ret = boe_panel_enter_sleep_mode(boe); + if (ret < 0) { + dev_err(panel->dev, "failed to set panel off: %d\n", ret); + return ret; + } + + msleep(150); + + if (boe->desc->discharge_on_disable) { + regulator_disable(boe->avee); + regulator_disable(boe->avdd); + usleep_range(5000, 7000); + gpiod_set_value(boe->enable_gpio, 0); + usleep_range(5000, 7000); + regulator_disable(boe->pp1800); + } else { + gpiod_set_value(boe->enable_gpio, 0); + usleep_range(500, 1000); + regulator_disable(boe->avee); + regulator_disable(boe->avdd); + usleep_range(5000, 7000); + regulator_disable(boe->pp1800); + } + + boe->prepared = false; + + return 0; +} + +static int boe_panel_prepare(struct drm_panel *panel) +{ + struct boe_panel *boe = to_boe_panel(panel); + int ret; + + if (boe->prepared) + return 0; + + gpiod_set_value(boe->enable_gpio, 0); + usleep_range(1000, 1500); + + ret = regulator_enable(boe->pp1800); + if (ret < 0) + return ret; + + usleep_range(3000, 5000); + + ret = regulator_enable(boe->avdd); + if (ret < 0) + goto poweroff1v8; + ret = regulator_enable(boe->avee); + if (ret < 0) + goto poweroffavdd; + + usleep_range(5000, 10000); + + gpiod_set_value(boe->enable_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value(boe->enable_gpio, 0); + usleep_range(1000, 2000); + gpiod_set_value(boe->enable_gpio, 1); + usleep_range(6000, 10000); + + ret = boe_panel_init_dcs_cmd(boe); + if (ret < 0) { + dev_err(panel->dev, "failed to init panel: %d\n", ret); + goto poweroff; + } + + boe->prepared = true; + + return 0; + +poweroff: + regulator_disable(boe->avee); +poweroffavdd: + regulator_disable(boe->avdd); +poweroff1v8: + usleep_range(5000, 7000); + regulator_disable(boe->pp1800); + gpiod_set_value(boe->enable_gpio, 0); + + return ret; +} + +static int boe_panel_enable(struct drm_panel *panel) +{ + msleep(130); + return 0; +} + +static const struct drm_display_mode boe_tv101wum_nl6_default_mode = { + .clock = 159425, + .hdisplay = 1200, + .hsync_start = 1200 + 100, + .hsync_end = 1200 + 100 + 40, + .htotal = 1200 + 100 + 40 + 24, + .vdisplay = 1920, + .vsync_start = 1920 + 10, + .vsync_end = 1920 + 10 + 14, + .vtotal = 1920 + 10 + 14 + 4, + .vrefresh = 60, +}; + +static const struct panel_desc boe_tv101wum_nl6_desc = { + .modes = &boe_tv101wum_nl6_default_mode, + .bpc = 8, + .size = { + .width_mm = 135, + .height_mm = 216, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .init_cmds = boe_init_cmd, + .discharge_on_disable = false, +}; + +static const struct drm_display_mode auo_kd101n80_45na_default_mode = { + .clock = 157000, + .hdisplay = 1200, + .hsync_start = 1200 + 80, + .hsync_end = 1200 + 80 + 24, + .htotal = 1200 + 80 + 24 + 36, + .vdisplay = 1920, + .vsync_start = 1920 + 16, + .vsync_end = 1920 + 16 + 4, + .vtotal = 1920 + 16 + 4 + 16, + .vrefresh = 60, +}; + +static const struct panel_desc auo_kd101n80_45na_desc = { + .modes = &auo_kd101n80_45na_default_mode, + .bpc = 8, + .size = { + .width_mm = 135, + .height_mm = 216, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .init_cmds = auo_kd101n80_45na_init_cmd, + .discharge_on_disable = true, +}; + +static const struct drm_display_mode boe_tv101wum_n53_default_mode = { + .clock = 159916, + .hdisplay = 1200, + .hsync_start = 1200 + 80, + .hsync_end = 1200 + 80 + 24, + .htotal = 1200 + 80 + 24 + 60, + .vdisplay = 1920, + .vsync_start = 1920 + 20, + .vsync_end = 1920 + 20 + 4, + .vtotal = 1920 + 20 + 4 + 10, + .vrefresh = 60, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static const struct panel_desc boe_tv101wum_n53_desc = { + .modes = &boe_tv101wum_n53_default_mode, + .bpc = 8, + .size = { + .width_mm = 135, + .height_mm = 216, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .init_cmds = boe_init_cmd, +}; + +static const struct drm_display_mode auo_b101uan08_3_default_mode = { + .clock = 159667, + .hdisplay = 1200, + .hsync_start = 1200 + 60, + .hsync_end = 1200 + 60 + 4, + .htotal = 1200 + 60 + 4 + 80, + .vdisplay = 1920, + .vsync_start = 1920 + 34, + .vsync_end = 1920 + 34 + 2, + .vtotal = 1920 + 34 + 2 + 24, + .vrefresh = 60, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static const struct panel_desc auo_b101uan08_3_desc = { + .modes = &auo_b101uan08_3_default_mode, + .bpc = 8, + .size = { + .width_mm = 135, + .height_mm = 216, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .init_cmds = auo_b101uan08_3_init_cmd, +}; + +static int boe_panel_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct boe_panel *boe = to_boe_panel(panel); + const struct drm_display_mode *m = boe->desc->modes; + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, m); + if (!mode) { + dev_err(panel->dev, "failed to add mode %ux%u@%u\n", + m->hdisplay, m->vdisplay, m->vrefresh); + return -ENOMEM; + } + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + connector->display_info.width_mm = boe->desc->size.width_mm; + connector->display_info.height_mm = boe->desc->size.height_mm; + connector->display_info.bpc = boe->desc->bpc; + + return 1; +} + +static const struct drm_panel_funcs boe_panel_funcs = { + .unprepare = boe_panel_unprepare, + .prepare = boe_panel_prepare, + .enable = boe_panel_enable, + .get_modes = boe_panel_get_modes, +}; + +static int boe_panel_add(struct boe_panel *boe) +{ + struct device *dev = &boe->dsi->dev; + int err; + + boe->avdd = devm_regulator_get(dev, "avdd"); + if (IS_ERR(boe->avdd)) + return PTR_ERR(boe->avdd); + + boe->avee = devm_regulator_get(dev, "avee"); + if (IS_ERR(boe->avee)) + return PTR_ERR(boe->avee); + + boe->pp1800 = devm_regulator_get(dev, "pp1800"); + if (IS_ERR(boe->pp1800)) + return PTR_ERR(boe->pp1800); + + boe->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(boe->enable_gpio)) { + dev_err(dev, "cannot get reset-gpios %ld\n", + PTR_ERR(boe->enable_gpio)); + return PTR_ERR(boe->enable_gpio); + } + + gpiod_set_value(boe->enable_gpio, 0); + + drm_panel_init(&boe->base, dev, &boe_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + err = drm_panel_of_backlight(&boe->base); + if (err) + return err; + + boe->base.funcs = &boe_panel_funcs; + boe->base.dev = &boe->dsi->dev; + + return drm_panel_add(&boe->base); +} + +static int boe_panel_probe(struct mipi_dsi_device *dsi) +{ + struct boe_panel *boe; + int ret; + const struct panel_desc *desc; + + boe = devm_kzalloc(&dsi->dev, sizeof(*boe), GFP_KERNEL); + if (!boe) + return -ENOMEM; + + desc = of_device_get_match_data(&dsi->dev); + dsi->lanes = desc->lanes; + dsi->format = desc->format; + dsi->mode_flags = desc->mode_flags; + boe->desc = desc; + boe->dsi = dsi; + ret = boe_panel_add(boe); + if (ret < 0) + return ret; + + mipi_dsi_set_drvdata(dsi, boe); + + ret = mipi_dsi_attach(dsi); + if (ret) + drm_panel_remove(&boe->base); + + return ret; +} + +static void boe_panel_shutdown(struct mipi_dsi_device *dsi) +{ + struct boe_panel *boe = mipi_dsi_get_drvdata(dsi); + + drm_panel_disable(&boe->base); + drm_panel_unprepare(&boe->base); +} + +static int boe_panel_remove(struct mipi_dsi_device *dsi) +{ + struct boe_panel *boe = mipi_dsi_get_drvdata(dsi); + int ret; + + boe_panel_shutdown(dsi); + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); + + if (boe->base.dev) + drm_panel_remove(&boe->base); + + return 0; +} + +static const struct of_device_id boe_of_match[] = { + { .compatible = "boe,tv101wum-nl6", + .data = &boe_tv101wum_nl6_desc + }, + { .compatible = "auo,kd101n80-45na", + .data = &auo_kd101n80_45na_desc + }, + { .compatible = "boe,tv101wum-n53", + .data = &boe_tv101wum_n53_desc + }, + { .compatible = "auo,b101uan08.3", + .data = &auo_b101uan08_3_desc + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, boe_of_match); + +static struct mipi_dsi_driver boe_panel_driver = { + .driver = { + .name = "panel-boe-tv101wum-nl6", + .of_match_table = boe_of_match, + }, + .probe = boe_panel_probe, + .remove = boe_panel_remove, + .shutdown = boe_panel_shutdown, +}; +module_mipi_dsi_driver(boe_panel_driver); + +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>"); +MODULE_DESCRIPTION("BOE tv101wum-nl6 1200x1920 video mode panel driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c new file mode 100644 index 000000000000..fddbfddf6566 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2020 Icenowy Zheng <icenowy@aosc.io> + */ + +#include <linux/gpio/consumer.h> +#include <linux/delay.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> + +#define K101_IM2BA02_INIT_CMD_LEN 2 + +static const char * const regulator_names[] = { + "dvdd", + "avdd", + "cvdd" +}; + +struct k101_im2ba02 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + + struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)]; + struct gpio_desc *reset; +}; + +static inline struct k101_im2ba02 *panel_to_k101_im2ba02(struct drm_panel *panel) +{ + return container_of(panel, struct k101_im2ba02, panel); +} + +struct k101_im2ba02_init_cmd { + u8 data[K101_IM2BA02_INIT_CMD_LEN]; +}; + +static const struct k101_im2ba02_init_cmd k101_im2ba02_init_cmds[] = { + /* Switch to page 0 */ + { .data = { 0xE0, 0x00 } }, + + /* Seems to be some password */ + { .data = { 0xE1, 0x93} }, + { .data = { 0xE2, 0x65 } }, + { .data = { 0xE3, 0xF8 } }, + + /* Lane number, 0x02 - 3 lanes, 0x03 - 4 lanes */ + { .data = { 0x80, 0x03 } }, + + /* Sequence control */ + { .data = { 0x70, 0x02 } }, + { .data = { 0x71, 0x23 } }, + { .data = { 0x72, 0x06 } }, + + /* Switch to page 1 */ + { .data = { 0xE0, 0x01 } }, + + /* Set VCOM */ + { .data = { 0x00, 0x00 } }, + { .data = { 0x01, 0x66 } }, + /* Set VCOM_Reverse */ + { .data = { 0x03, 0x00 } }, + { .data = { 0x04, 0x25 } }, + + /* Set Gamma Power, VG[MS][PN] */ + { .data = { 0x17, 0x00 } }, + { .data = { 0x18, 0x6D } }, + { .data = { 0x19, 0x00 } }, + { .data = { 0x1A, 0x00 } }, + { .data = { 0x1B, 0xBF } }, /* VGMN = -4.5V */ + { .data = { 0x1C, 0x00 } }, + + /* Set Gate Power */ + { .data = { 0x1F, 0x3E } }, /* VGH_R = 15V */ + { .data = { 0x20, 0x28 } }, /* VGL_R = -11V */ + { .data = { 0x21, 0x28 } }, /* VGL_R2 = -11V */ + { .data = { 0x22, 0x0E } }, /* PA[6:4] = 0, PA[0] = 0 */ + + /* Set Panel */ + { .data = { 0x37, 0x09 } }, /* SS = 1, BGR = 1 */ + + /* Set RGBCYC */ + { .data = { 0x38, 0x04 } }, /* JDT = 100 column inversion */ + { .data = { 0x39, 0x08 } }, /* RGB_N_EQ1 */ + { .data = { 0x3A, 0x12 } }, /* RGB_N_EQ2 */ + { .data = { 0x3C, 0x78 } }, /* set EQ3 for TE_H */ + { .data = { 0x3D, 0xFF } }, /* set CHGEN_ON */ + { .data = { 0x3E, 0xFF } }, /* set CHGEN_OFF */ + { .data = { 0x3F, 0x7F } }, /* set CHGEN_OFF2 */ + + /* Set TCON parameter */ + { .data = { 0x40, 0x06 } }, /* RSO = 800 points */ + { .data = { 0x41, 0xA0 } }, /* LN = 1280 lines */ + + /* Set power voltage */ + { .data = { 0x55, 0x0F } }, /* DCDCM */ + { .data = { 0x56, 0x01 } }, + { .data = { 0x57, 0x69 } }, + { .data = { 0x58, 0x0A } }, + { .data = { 0x59, 0x0A } }, + { .data = { 0x5A, 0x45 } }, + { .data = { 0x5B, 0x15 } }, + + /* Set gamma */ + { .data = { 0x5D, 0x7C } }, + { .data = { 0x5E, 0x65 } }, + { .data = { 0x5F, 0x55 } }, + { .data = { 0x60, 0x49 } }, + { .data = { 0x61, 0x44 } }, + { .data = { 0x62, 0x35 } }, + { .data = { 0x63, 0x3A } }, + { .data = { 0x64, 0x23 } }, + { .data = { 0x65, 0x3D } }, + { .data = { 0x66, 0x3C } }, + { .data = { 0x67, 0x3D } }, + { .data = { 0x68, 0x5D } }, + { .data = { 0x69, 0x4D } }, + { .data = { 0x6A, 0x56 } }, + { .data = { 0x6B, 0x48 } }, + { .data = { 0x6C, 0x45 } }, + { .data = { 0x6D, 0x38 } }, + { .data = { 0x6E, 0x25 } }, + { .data = { 0x6F, 0x00 } }, + { .data = { 0x70, 0x7C } }, + { .data = { 0x71, 0x65 } }, + { .data = { 0x72, 0x55 } }, + { .data = { 0x73, 0x49 } }, + { .data = { 0x74, 0x44 } }, + { .data = { 0x75, 0x35 } }, + { .data = { 0x76, 0x3A } }, + { .data = { 0x77, 0x23 } }, + { .data = { 0x78, 0x3D } }, + { .data = { 0x79, 0x3C } }, + { .data = { 0x7A, 0x3D } }, + { .data = { 0x7B, 0x5D } }, + { .data = { 0x7C, 0x4D } }, + { .data = { 0x7D, 0x56 } }, + { .data = { 0x7E, 0x48 } }, + { .data = { 0x7F, 0x45 } }, + { .data = { 0x80, 0x38 } }, + { .data = { 0x81, 0x25 } }, + { .data = { 0x82, 0x00 } }, + + /* Switch to page 2, for GIP */ + { .data = { 0xE0, 0x02 } }, + + { .data = { 0x00, 0x1E } }, + { .data = { 0x01, 0x1E } }, + { .data = { 0x02, 0x41 } }, + { .data = { 0x03, 0x41 } }, + { .data = { 0x04, 0x43 } }, + { .data = { 0x05, 0x43 } }, + { .data = { 0x06, 0x1F } }, + { .data = { 0x07, 0x1F } }, + { .data = { 0x08, 0x1F } }, + { .data = { 0x09, 0x1F } }, + { .data = { 0x0A, 0x1E } }, + { .data = { 0x0B, 0x1E } }, + { .data = { 0x0C, 0x1F } }, + { .data = { 0x0D, 0x47 } }, + { .data = { 0x0E, 0x47 } }, + { .data = { 0x0F, 0x45 } }, + { .data = { 0x10, 0x45 } }, + { .data = { 0x11, 0x4B } }, + { .data = { 0x12, 0x4B } }, + { .data = { 0x13, 0x49 } }, + { .data = { 0x14, 0x49 } }, + { .data = { 0x15, 0x1F } }, + + { .data = { 0x16, 0x1E } }, + { .data = { 0x17, 0x1E } }, + { .data = { 0x18, 0x40 } }, + { .data = { 0x19, 0x40 } }, + { .data = { 0x1A, 0x42 } }, + { .data = { 0x1B, 0x42 } }, + { .data = { 0x1C, 0x1F } }, + { .data = { 0x1D, 0x1F } }, + { .data = { 0x1E, 0x1F } }, + { .data = { 0x1F, 0x1f } }, + { .data = { 0x20, 0x1E } }, + { .data = { 0x21, 0x1E } }, + { .data = { 0x22, 0x1f } }, + { .data = { 0x23, 0x46 } }, + { .data = { 0x24, 0x46 } }, + { .data = { 0x25, 0x44 } }, + { .data = { 0x26, 0x44 } }, + { .data = { 0x27, 0x4A } }, + { .data = { 0x28, 0x4A } }, + { .data = { 0x29, 0x48 } }, + { .data = { 0x2A, 0x48 } }, + { .data = { 0x2B, 0x1f } }, + + { .data = { 0x2C, 0x1F } }, + { .data = { 0x2D, 0x1F } }, + { .data = { 0x2E, 0x42 } }, + { .data = { 0x2F, 0x42 } }, + { .data = { 0x30, 0x40 } }, + { .data = { 0x31, 0x40 } }, + { .data = { 0x32, 0x1E } }, + { .data = { 0x33, 0x1E } }, + { .data = { 0x34, 0x1F } }, + { .data = { 0x35, 0x1F } }, + { .data = { 0x36, 0x1E } }, + { .data = { 0x37, 0x1E } }, + { .data = { 0x38, 0x1F } }, + { .data = { 0x39, 0x48 } }, + { .data = { 0x3A, 0x48 } }, + { .data = { 0x3B, 0x4A } }, + { .data = { 0x3C, 0x4A } }, + { .data = { 0x3D, 0x44 } }, + { .data = { 0x3E, 0x44 } }, + { .data = { 0x3F, 0x46 } }, + { .data = { 0x40, 0x46 } }, + { .data = { 0x41, 0x1F } }, + + { .data = { 0x42, 0x1F } }, + { .data = { 0x43, 0x1F } }, + { .data = { 0x44, 0x43 } }, + { .data = { 0x45, 0x43 } }, + { .data = { 0x46, 0x41 } }, + { .data = { 0x47, 0x41 } }, + { .data = { 0x48, 0x1E } }, + { .data = { 0x49, 0x1E } }, + { .data = { 0x4A, 0x1E } }, + { .data = { 0x4B, 0x1F } }, + { .data = { 0x4C, 0x1E } }, + { .data = { 0x4D, 0x1E } }, + { .data = { 0x4E, 0x1F } }, + { .data = { 0x4F, 0x49 } }, + { .data = { 0x50, 0x49 } }, + { .data = { 0x51, 0x4B } }, + { .data = { 0x52, 0x4B } }, + { .data = { 0x53, 0x45 } }, + { .data = { 0x54, 0x45 } }, + { .data = { 0x55, 0x47 } }, + { .data = { 0x56, 0x47 } }, + { .data = { 0x57, 0x1F } }, + + { .data = { 0x58, 0x10 } }, + { .data = { 0x59, 0x00 } }, + { .data = { 0x5A, 0x00 } }, + { .data = { 0x5B, 0x30 } }, + { .data = { 0x5C, 0x02 } }, + { .data = { 0x5D, 0x40 } }, + { .data = { 0x5E, 0x01 } }, + { .data = { 0x5F, 0x02 } }, + { .data = { 0x60, 0x30 } }, + { .data = { 0x61, 0x01 } }, + { .data = { 0x62, 0x02 } }, + { .data = { 0x63, 0x6A } }, + { .data = { 0x64, 0x6A } }, + { .data = { 0x65, 0x05 } }, + { .data = { 0x66, 0x12 } }, + { .data = { 0x67, 0x74 } }, + { .data = { 0x68, 0x04 } }, + { .data = { 0x69, 0x6A } }, + { .data = { 0x6A, 0x6A } }, + { .data = { 0x6B, 0x08 } }, + + { .data = { 0x6C, 0x00 } }, + { .data = { 0x6D, 0x04 } }, + { .data = { 0x6E, 0x04 } }, + { .data = { 0x6F, 0x88 } }, + { .data = { 0x70, 0x00 } }, + { .data = { 0x71, 0x00 } }, + { .data = { 0x72, 0x06 } }, + { .data = { 0x73, 0x7B } }, + { .data = { 0x74, 0x00 } }, + { .data = { 0x75, 0x07 } }, + { .data = { 0x76, 0x00 } }, + { .data = { 0x77, 0x5D } }, + { .data = { 0x78, 0x17 } }, + { .data = { 0x79, 0x1F } }, + { .data = { 0x7A, 0x00 } }, + { .data = { 0x7B, 0x00 } }, + { .data = { 0x7C, 0x00 } }, + { .data = { 0x7D, 0x03 } }, + { .data = { 0x7E, 0x7B } }, + + { .data = { 0xE0, 0x04 } }, + { .data = { 0x2B, 0x2B } }, + { .data = { 0x2E, 0x44 } }, + + { .data = { 0xE0, 0x01 } }, + { .data = { 0x0E, 0x01 } }, + + { .data = { 0xE0, 0x03 } }, + { .data = { 0x98, 0x2F } }, + + { .data = { 0xE0, 0x00 } }, + { .data = { 0xE6, 0x02 } }, + { .data = { 0xE7, 0x02 } }, + + { .data = { 0x11, 0x00 } }, +}; + +static const struct k101_im2ba02_init_cmd timed_cmds[] = { + { .data = { 0x29, 0x00 } }, + { .data = { 0x35, 0x00 } }, +}; + +static int k101_im2ba02_prepare(struct drm_panel *panel) +{ + struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel); + struct mipi_dsi_device *dsi = ctx->dsi; + unsigned int i; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret) + return ret; + + msleep(30); + + gpiod_set_value(ctx->reset, 1); + msleep(50); + + gpiod_set_value(ctx->reset, 0); + msleep(50); + + gpiod_set_value(ctx->reset, 1); + msleep(200); + + for (i = 0; i < ARRAY_SIZE(k101_im2ba02_init_cmds); i++) { + const struct k101_im2ba02_init_cmd *cmd = &k101_im2ba02_init_cmds[i]; + + ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data, K101_IM2BA02_INIT_CMD_LEN); + if (ret < 0) + goto powerdown; + } + + return 0; + +powerdown: + gpiod_set_value(ctx->reset, 0); + msleep(50); + + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); +} + +static int k101_im2ba02_enable(struct drm_panel *panel) +{ + struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel); + const struct k101_im2ba02_init_cmd *cmd = &timed_cmds[1]; + int ret; + + msleep(150); + + ret = mipi_dsi_dcs_set_display_on(ctx->dsi); + if (ret < 0) + return ret; + + msleep(50); + + return mipi_dsi_dcs_write_buffer(ctx->dsi, cmd->data, K101_IM2BA02_INIT_CMD_LEN); +} + +static int k101_im2ba02_disable(struct drm_panel *panel) +{ + struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel); + + return mipi_dsi_dcs_set_display_off(ctx->dsi); +} + +static int k101_im2ba02_unprepare(struct drm_panel *panel) +{ + struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel); + int ret; + + ret = mipi_dsi_dcs_set_display_off(ctx->dsi); + if (ret < 0) + DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n", + ret); + + ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi); + if (ret < 0) + DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n", + ret); + + msleep(200); + + gpiod_set_value(ctx->reset, 0); + msleep(20); + + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); +} + +static const struct drm_display_mode k101_im2ba02_default_mode = { + .clock = 70000, + .vrefresh = 60, + + .hdisplay = 800, + .hsync_start = 800 + 20, + .hsync_end = 800 + 20 + 20, + .htotal = 800 + 20 + 20 + 20, + + .vdisplay = 1280, + .vsync_start = 1280 + 16, + .vsync_end = 1280 + 16 + 4, + .vtotal = 1280 + 16 + 4 + 4, + + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + .width_mm = 136, + .height_mm = 217, +}; + +static int k101_im2ba02_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel); + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &k101_im2ba02_default_mode); + if (!mode) { + DRM_DEV_ERROR(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n", + k101_im2ba02_default_mode.hdisplay, + k101_im2ba02_default_mode.vdisplay, + k101_im2ba02_default_mode.vrefresh); + return -ENOMEM; + } + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs k101_im2ba02_funcs = { + .disable = k101_im2ba02_disable, + .unprepare = k101_im2ba02_unprepare, + .prepare = k101_im2ba02_prepare, + .enable = k101_im2ba02_enable, + .get_modes = k101_im2ba02_get_modes, +}; + +static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi) +{ + struct k101_im2ba02 *ctx; + unsigned int i; + int ret; + + ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mipi_dsi_set_drvdata(dsi, ctx); + ctx->dsi = dsi; + + for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) + ctx->supplies[i].supply = regulator_names[i]; + + ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret < 0) { + DRM_DEV_ERROR(&dsi->dev, "Couldn't get regulators\n"); + return ret; + } + + ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset)) { + DRM_DEV_ERROR(&dsi->dev, "Couldn't get our reset GPIO\n"); + return PTR_ERR(ctx->reset); + } + + drm_panel_init(&ctx->panel, &dsi->dev, &k101_im2ba02_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return ret; + + ret = drm_panel_add(&ctx->panel); + if (ret < 0) + return ret; + + dsi->mode_flags = MIPI_DSI_MODE_VIDEO; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->lanes = 4; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + drm_panel_remove(&ctx->panel); + return ret; + } + + return 0; +} + +static int k101_im2ba02_dsi_remove(struct mipi_dsi_device *dsi) +{ + struct k101_im2ba02 *ctx = mipi_dsi_get_drvdata(dsi); + + mipi_dsi_detach(dsi); + drm_panel_remove(&ctx->panel); + + return 0; +} + +static const struct of_device_id k101_im2ba02_of_match[] = { + { .compatible = "feixin,k101-im2ba02", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, k101_im2ba02_of_match); + +static struct mipi_dsi_driver k101_im2ba02_driver = { + .probe = k101_im2ba02_dsi_probe, + .remove = k101_im2ba02_dsi_remove, + .driver = { + .name = "feixin-k101-im2ba02", + .of_match_table = k101_im2ba02_of_match, + }, +}; +module_mipi_dsi_driver(k101_im2ba02_driver); + +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); +MODULE_DESCRIPTION("Feixin K101 IM2BA02 MIPI-DSI LCD panel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c new file mode 100644 index 000000000000..9d843fcc3a22 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2019, Michael Srba + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +struct s6e88a0_ams452ef01 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data supplies[2]; + struct gpio_desc *reset_gpio; + + bool prepared; +}; + +static inline struct +s6e88a0_ams452ef01 *to_s6e88a0_ams452ef01(struct drm_panel *panel) +{ + return container_of(panel, struct s6e88a0_ams452ef01, panel); +} + +#define dsi_dcs_write_seq(dsi, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static void s6e88a0_ams452ef01_reset(struct s6e88a0_ams452ef01 *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(5000, 6000); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(1000, 2000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(10000, 11000); +} + +static int s6e88a0_ams452ef01_on(struct s6e88a0_ams452ef01 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a); // enable LEVEL2 commands + dsi_dcs_write_seq(dsi, 0xcc, 0x4c); // set Pixel Clock Divider polarity + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to exit sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + // set default brightness/gama + dsi_dcs_write_seq(dsi, 0xca, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, // V255 RR,GG,BB + 0x80, 0x80, 0x80, // V203 R,G,B + 0x80, 0x80, 0x80, // V151 R,G,B + 0x80, 0x80, 0x80, // V87 R,G,B + 0x80, 0x80, 0x80, // V51 R,G,B + 0x80, 0x80, 0x80, // V35 R,G,B + 0x80, 0x80, 0x80, // V23 R,G,B + 0x80, 0x80, 0x80, // V11 R,G,B + 0x6b, 0x68, 0x71, // V3 R,G,B + 0x00, 0x00, 0x00); // V1 R,G,B + // set default Amoled Off Ratio + dsi_dcs_write_seq(dsi, 0xb2, 0x40, 0x0a, 0x17, 0x00, 0x0a); + dsi_dcs_write_seq(dsi, 0xb6, 0x2c, 0x0b); // set default elvss voltage + dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00); + dsi_dcs_write_seq(dsi, 0xf7, 0x03); // gamma/aor update + dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5); // disable LEVEL2 commands + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + + return 0; +} + +static int s6e88a0_ams452ef01_off(struct s6e88a0_ams452ef01 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + return ret; + } + msleep(35); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + return 0; +} + +static int s6e88a0_ams452ef01_prepare(struct drm_panel *panel) +{ + struct s6e88a0_ams452ef01 *ctx = to_s6e88a0_ams452ef01(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + s6e88a0_ams452ef01_reset(ctx); + + ret = s6e88a0_ams452ef01_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), + ctx->supplies); + return ret; + } + + ctx->prepared = true; + return 0; +} + +static int s6e88a0_ams452ef01_unprepare(struct drm_panel *panel) +{ + struct s6e88a0_ams452ef01 *ctx = to_s6e88a0_ams452ef01(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = s6e88a0_ams452ef01_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode s6e88a0_ams452ef01_mode = { + .clock = (540 + 88 + 4 + 20) * (960 + 14 + 2 + 8) * 60 / 1000, + .hdisplay = 540, + .hsync_start = 540 + 88, + .hsync_end = 540 + 88 + 4, + .htotal = 540 + 88 + 4 + 20, + .vdisplay = 960, + .vsync_start = 960 + 14, + .vsync_end = 960 + 14 + 2, + .vtotal = 960 + 14 + 2 + 8, + .vrefresh = 60, + .width_mm = 56, + .height_mm = 100, +}; + +static int s6e88a0_ams452ef01_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &s6e88a0_ams452ef01_mode); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs s6e88a0_ams452ef01_panel_funcs = { + .unprepare = s6e88a0_ams452ef01_unprepare, + .prepare = s6e88a0_ams452ef01_prepare, + .get_modes = s6e88a0_ams452ef01_get_modes, +}; + +static int s6e88a0_ams452ef01_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct s6e88a0_ams452ef01 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->supplies[0].supply = "vdd3"; + ctx->supplies[1].supply = "vci"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) { + ret = PTR_ERR(ctx->reset_gpio); + dev_err(dev, "Failed to get reset-gpios: %d\n", ret); + return ret; + } + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 2; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST; + + drm_panel_init(&ctx->panel, dev, &s6e88a0_ams452ef01_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_add(&ctx->panel); + if (ret < 0) { + dev_err(dev, "Failed to add panel: %d\n", ret); + return ret; + } + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + return ret; + } + + return 0; +} + +static int s6e88a0_ams452ef01_remove(struct mipi_dsi_device *dsi) +{ + struct s6e88a0_ams452ef01 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); + + return 0; +} + +static const struct of_device_id s6e88a0_ams452ef01_of_match[] = { + { .compatible = "samsung,s6e88a0-ams452ef01" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s6e88a0_ams452ef01_of_match); + +static struct mipi_dsi_driver s6e88a0_ams452ef01_driver = { + .probe = s6e88a0_ams452ef01_probe, + .remove = s6e88a0_ams452ef01_remove, + .driver = { + .name = "panel-s6e88a0-ams452ef01", + .of_match_table = s6e88a0_ams452ef01_of_match, + }, +}; +module_mipi_dsi_driver(s6e88a0_ams452ef01_driver); + +MODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>"); +MODULE_DESCRIPTION("MIPI-DSI based Panel Driver for AMS452EF01 AMOLED LCD with a S6E88A0 controller"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index e14c14ac62b5..d901ccb5cf9b 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1301,6 +1301,37 @@ static const struct panel_desc edt_et035012dm6 = { .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_NEGEDGE, }; +static const struct drm_display_mode edt_etm043080dh6gp_mode = { + .clock = 10870, + .hdisplay = 480, + .hsync_start = 480 + 8, + .hsync_end = 480 + 8 + 4, + .htotal = 480 + 8 + 4 + 41, + + /* + * IWG22M: Y resolution changed for "dc_linuxfb" module crashing while + * fb_align + */ + + .vdisplay = 288, + .vsync_start = 288 + 2, + .vsync_end = 288 + 2 + 4, + .vtotal = 288 + 2 + 4 + 10, + .vrefresh = 60, +}; + +static const struct panel_desc edt_etm043080dh6gp = { + .modes = &edt_etm043080dh6gp_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 100, + .height = 65, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct drm_display_mode edt_etm0430g0dh6_mode = { .clock = 9000, .hdisplay = 480, @@ -1440,6 +1471,33 @@ static const struct panel_desc foxlink_fl500wvr00_a0t = { .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; +static const struct drm_display_mode frida_frd350h54004_mode = { + .clock = 6000, + .hdisplay = 320, + .hsync_start = 320 + 44, + .hsync_end = 320 + 44 + 16, + .htotal = 320 + 44 + 16 + 20, + .vdisplay = 240, + .vsync_start = 240 + 2, + .vsync_end = 240 + 2 + 6, + .vtotal = 240 + 2 + 6 + 2, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +}; + +static const struct panel_desc frida_frd350h54004 = { + .modes = &frida_frd350h54004_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 77, + .height = 64, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct drm_display_mode friendlyarm_hd702e_mode = { .clock = 67185, .hdisplay = 800, @@ -2080,6 +2138,64 @@ static const struct panel_desc lg_lp129qe = { }, }; +static const struct display_timing logictechno_lt161010_2nh_timing = { + .pixelclock = { 26400000, 33300000, 46800000 }, + .hactive = { 800, 800, 800 }, + .hfront_porch = { 16, 210, 354 }, + .hback_porch = { 46, 46, 46 }, + .hsync_len = { 1, 20, 40 }, + .vactive = { 480, 480, 480 }, + .vfront_porch = { 7, 22, 147 }, + .vback_porch = { 23, 23, 23 }, + .vsync_len = { 1, 10, 20 }, + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | + DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE | + DISPLAY_FLAGS_SYNC_POSEDGE, +}; + +static const struct panel_desc logictechno_lt161010_2nh = { + .timings = &logictechno_lt161010_2nh_timing, + .num_timings = 1, + .size = { + .width = 154, + .height = 86, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | + DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + +static const struct display_timing logictechno_lt170410_2whc_timing = { + .pixelclock = { 68900000, 71100000, 73400000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 23, 60, 71 }, + .hback_porch = { 23, 60, 71 }, + .hsync_len = { 15, 40, 47 }, + .vactive = { 800, 800, 800 }, + .vfront_porch = { 5, 7, 10 }, + .vback_porch = { 5, 7, 10 }, + .vsync_len = { 6, 9, 12 }, + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | + DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE | + DISPLAY_FLAGS_SYNC_POSEDGE, +}; + +static const struct panel_desc logictechno_lt170410_2whc = { + .timings = &logictechno_lt170410_2whc_timing, + .num_timings = 1, + .size = { + .width = 217, + .height = 136, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | + DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode mitsubishi_aa070mc01_mode = { .clock = 30400, .hdisplay = 800, @@ -3023,7 +3139,7 @@ static const struct panel_desc toshiba_lt089ac29000 = { .width = 194, .height = 116, }, - .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -3286,6 +3402,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "edt,et035012dm6", .data = &edt_et035012dm6, }, { + .compatible = "edt,etm043080dh6gp", + .data = &edt_etm043080dh6gp, + }, { .compatible = "edt,etm0430g0dh6", .data = &edt_etm0430g0dh6, }, { @@ -3310,6 +3429,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "foxlink,fl500wvr00-a0t", .data = &foxlink_fl500wvr00_a0t, }, { + .compatible = "frida,frd350h54004", + .data = &frida_frd350h54004, + }, { .compatible = "friendlyarm,hd702e", .data = &friendlyarm_hd702e, }, { @@ -3388,6 +3510,15 @@ static const struct of_device_id platform_of_match[] = { .compatible = "logicpd,type28", .data = &logicpd_type_28, }, { + .compatible = "logictechno,lt161010-2nhc", + .data = &logictechno_lt161010_2nh, + }, { + .compatible = "logictechno,lt161010-2nhr", + .data = &logictechno_lt161010_2nh, + }, { + .compatible = "logictechno,lt170410-2whc", + .data = &logictechno_lt170410_2whc, + }, { .compatible = "mitsubishi,aa070mc01-ca1", .data = &mitsubishi_aa070mc01, }, { diff --git a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c index cf29405a2dbe..aeca15dfeb3c 100644 --- a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c +++ b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c @@ -86,7 +86,12 @@ struct td028ttec1_panel { #define to_td028ttec1_device(p) container_of(p, struct td028ttec1_panel, panel) -static int jbt_ret_write_0(struct td028ttec1_panel *lcd, u8 reg, int *err) +/* + * noinline_for_stack so we don't get multiple copies of tx_buf + * on the stack in case of gcc-plugin-structleak + */ +static int noinline_for_stack +jbt_ret_write_0(struct td028ttec1_panel *lcd, u8 reg, int *err) { struct spi_device *spi = lcd->spi; u16 tx_buf = JBT_COMMAND | reg; @@ -105,8 +110,9 @@ static int jbt_ret_write_0(struct td028ttec1_panel *lcd, u8 reg, int *err) return ret; } -static int jbt_reg_write_1(struct td028ttec1_panel *lcd, - u8 reg, u8 data, int *err) +static int noinline_for_stack +jbt_reg_write_1(struct td028ttec1_panel *lcd, + u8 reg, u8 data, int *err) { struct spi_device *spi = lcd->spi; u16 tx_buf[2]; @@ -128,8 +134,9 @@ static int jbt_reg_write_1(struct td028ttec1_panel *lcd, return ret; } -static int jbt_reg_write_2(struct td028ttec1_panel *lcd, - u8 reg, u16 data, int *err) +static int noinline_for_stack +jbt_reg_write_2(struct td028ttec1_panel *lcd, + u8 reg, u16 data, int *err) { struct spi_device *spi = lcd->spi; u16 tx_buf[3]; diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c index 8822ec13a0d6..1b9b79cd5804 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c @@ -309,10 +309,6 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev) ret = readl_relaxed_poll_timeout(pfdev->iomem + L2_READY_LO, val, val == pfdev->features.l2_present, 100, 1000); - gpu_write(pfdev, STACK_PWRON_LO, pfdev->features.stack_present); - ret |= readl_relaxed_poll_timeout(pfdev->iomem + STACK_READY_LO, - val, val == pfdev->features.stack_present, 100, 1000); - gpu_write(pfdev, SHADER_PWRON_LO, pfdev->features.shader_present); ret |= readl_relaxed_poll_timeout(pfdev->iomem + SHADER_READY_LO, val, val == pfdev->features.shader_present, 100, 1000); @@ -329,7 +325,6 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev) { gpu_write(pfdev, TILER_PWROFF_LO, 0); gpu_write(pfdev, SHADER_PWROFF_LO, 0); - gpu_write(pfdev, STACK_PWROFF_LO, 0); gpu_write(pfdev, L2_PWROFF_LO, 0); } @@ -351,7 +346,7 @@ int panfrost_gpu_init(struct panfrost_device *pfdev) return -ENODEV; err = devm_request_irq(pfdev->dev, irq, panfrost_gpu_irq_handler, - IRQF_SHARED, "gpu", pfdev); + IRQF_SHARED, KBUILD_MODNAME "-gpu", pfdev); if (err) { dev_err(pfdev->dev, "failed to request gpu irq"); return err; diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index 7157dfd7dea3..8fd8726313ae 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -512,7 +512,7 @@ int panfrost_job_init(struct panfrost_device *pfdev) return -ENODEV; ret = devm_request_irq(pfdev->dev, irq, panfrost_job_irq_handler, - IRQF_SHARED, "job", pfdev); + IRQF_SHARED, KBUILD_MODNAME "-job", pfdev); if (ret) { dev_err(pfdev->dev, "failed to request job irq"); return ret; diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 763cfca886a7..23314f41717b 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -641,9 +641,11 @@ int panfrost_mmu_init(struct panfrost_device *pfdev) if (irq <= 0) return -ENODEV; - err = devm_request_threaded_irq(pfdev->dev, irq, panfrost_mmu_irq_handler, + err = devm_request_threaded_irq(pfdev->dev, irq, + panfrost_mmu_irq_handler, panfrost_mmu_irq_handler_thread, - IRQF_SHARED, "mmu", pfdev); + IRQF_SHARED, KBUILD_MODNAME "-mmu", + pfdev); if (err) { dev_err(pfdev->dev, "failed to request mmu irq"); diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 16d73b22f3f5..ab4f8dd00400 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -31,7 +31,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_vblank.h> #include "qxl_drv.h" #include "qxl_object.h" @@ -372,19 +371,6 @@ static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc, static void qxl_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { - struct drm_device *dev = crtc->dev; - struct drm_pending_vblank_event *event; - unsigned long flags; - - if (crtc->state && crtc->state->event) { - event = crtc->state->event; - crtc->state->event = NULL; - - spin_lock_irqsave(&dev->event_lock, flags); - drm_crtc_send_vblank_event(crtc, event); - spin_unlock_irqrestore(&dev->event_lock, flags); - } - qxl_crtc_update_monitors_config(crtc, "flush"); } diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 16a5e903533d..62a5e424971b 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -48,11 +48,6 @@ static struct qxl_device *qxl_get_qdev(struct ttm_bo_device *bdev) return qdev; } -static int qxl_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) -{ - return 0; -} - static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, struct ttm_mem_type_manager *man) { @@ -256,7 +251,6 @@ static void qxl_bo_move_notify(struct ttm_buffer_object *bo, static struct ttm_bo_driver qxl_bo_driver = { .ttm_tt_create = &qxl_ttm_tt_create, - .invalidate_caches = &qxl_invalidate_caches, .init_mem_type = &qxl_init_mem_type, .eviction_valuable = ttm_bo_eviction_valuable, .evict_flags = &qxl_evict_flags, diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index a522e092038b..266e3cbbd09b 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1263,7 +1263,7 @@ static bool radeon_switcheroo_can_switch(struct pci_dev *pdev) * locking inversion with the driver load path. And the access here is * completely racy anyway. So don't bother with locking for now. */ - return dev->open_count == 0; + return atomic_read(&dev->open_count) == 0; } static const struct vga_switcheroo_client_ops radeon_switcheroo_ops = { diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 3b92311d30b9..badf1b6d1549 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -66,11 +66,6 @@ static struct radeon_device *radeon_get_rdev(struct ttm_bo_device *bdev) return rdev; } -static int radeon_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) -{ - return 0; -} - static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, struct ttm_mem_type_manager *man) { @@ -774,7 +769,6 @@ static struct ttm_bo_driver radeon_bo_driver = { .ttm_tt_create = &radeon_ttm_tt_create, .ttm_tt_populate = &radeon_ttm_tt_populate, .ttm_tt_unpopulate = &radeon_ttm_tt_unpopulate, - .invalidate_caches = &radeon_invalidate_caches, .init_mem_type = &radeon_init_mem_type, .eviction_valuable = ttm_bo_eviction_valuable, .evict_flags = &radeon_evict_flags, diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 8ffa4fbbdeb3..06432c881e07 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -590,8 +590,9 @@ static void __rcar_lvds_atomic_enable(struct drm_bridge *bridge, } static void rcar_lvds_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *state = old_bridge_state->base.state; struct drm_connector *connector; struct drm_crtc *crtc; @@ -603,7 +604,7 @@ static void rcar_lvds_atomic_enable(struct drm_bridge *bridge, } static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_bridge_state *old_bridge_state) { struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); @@ -618,7 +619,8 @@ static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, /* Disable the companion LVDS encoder in dual-link mode. */ if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion) - lvds->companion->funcs->atomic_disable(lvds->companion, state); + lvds->companion->funcs->atomic_disable(lvds->companion, + old_bridge_state); clk_disable_unprepare(lvds->clocks.mod); } @@ -682,6 +684,9 @@ static void rcar_lvds_detach(struct drm_bridge *bridge) static const struct drm_bridge_funcs rcar_lvds_bridge_ops = { .attach = rcar_lvds_attach, .detach = rcar_lvds_detach, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, .atomic_enable = rcar_lvds_atomic_enable, .atomic_disable = rcar_lvds_atomic_disable, .mode_fixup = rcar_lvds_mode_fixup, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 7582d0e6a60a..0d1884684dcb 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -6,6 +6,7 @@ #include <linux/dma-buf.h> #include <linux/iommu.h> +#include <linux/vmalloc.h> #include <drm/drm.h> #include <drm/drm_gem.h> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index d04b3492bdac..cecb2cc781f5 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -724,7 +724,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : DRM_PLANE_HELPER_NO_SCALING; - if (!crtc || !fb) + if (!crtc || WARN_ON(!fb)) return 0; crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 0b3d18c457b2..cc672620d6e0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -328,7 +328,7 @@ static inline uint16_t scl_get_bili_dn_vskip(int src_h, int dst_h, { int act_height; - act_height = (src_h + vskiplines - 1) / vskiplines; + act_height = DIV_ROUND_UP(src_h, vskiplines); if (act_height == dst_h) return GET_SCL_FT_BILI_DN(src_h, dst_h) / vskiplines; diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c index 4b165635b2d4..2e1f2664495d 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -377,7 +377,9 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) dsi->pllref_clk = devm_clk_get(dev, "ref"); if (IS_ERR(dsi->pllref_clk)) { ret = PTR_ERR(dsi->pllref_clk); - DRM_ERROR("Unable to get pll reference clock: %d\n", ret); + if (ret != -EPROBE_DEFER) + DRM_ERROR("Unable to get pll reference clock: %d\n", + ret); goto err_clk_get; } diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index c2815e8ae1da..8750ee831bea 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -648,9 +648,14 @@ static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = { static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); + struct drm_crtc_state *state = crtc->state; DRM_DEBUG_DRIVER("\n"); - reg_set(ldev->regs, LTDC_IER, IER_LIE); + + if (state->enable) + reg_set(ldev->regs, LTDC_IER, IER_LIE); + else + return -EPERM; return 0; } @@ -1146,12 +1151,14 @@ static int ltdc_get_caps(struct drm_device *ddev) ldev->caps.pad_max_freq_hz = 90000000; if (ldev->caps.hw_version == HWVER_10200) ldev->caps.pad_max_freq_hz = 65000000; + ldev->caps.nb_irq = 2; break; case HWVER_20101: ldev->caps.reg_ofs = REG_OFS_4; ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1; ldev->caps.non_alpha_only_l1 = false; ldev->caps.pad_max_freq_hz = 150000000; + ldev->caps.nb_irq = 4; break; default: return -ENODEV; @@ -1251,13 +1258,21 @@ int ltdc_load(struct drm_device *ddev) reg_clear(ldev->regs, LTDC_IER, IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE); - for (i = 0; i < MAX_IRQ; i++) { + ret = ltdc_get_caps(ddev); + if (ret) { + DRM_ERROR("hardware identifier (0x%08x) not supported!\n", + ldev->caps.hw_version); + goto err; + } + + DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version); + + for (i = 0; i < ldev->caps.nb_irq; i++) { irq = platform_get_irq(pdev, i); - if (irq == -EPROBE_DEFER) + if (irq < 0) { + ret = irq; goto err; - - if (irq < 0) - continue; + } ret = devm_request_threaded_irq(dev, irq, ltdc_irq, ltdc_irq_thread, IRQF_ONESHOT, @@ -1268,16 +1283,6 @@ int ltdc_load(struct drm_device *ddev) } } - - ret = ltdc_get_caps(ddev); - if (ret) { - DRM_ERROR("hardware identifier (0x%08x) not supported!\n", - ldev->caps.hw_version); - goto err; - } - - DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version); - /* Add endpoints panels or bridges if any */ for (i = 0; i < MAX_ENDPOINTS; i++) { if (panel[i]) { diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index a1ad0ae3b006..310e87f0667c 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -19,6 +19,7 @@ struct ltdc_caps { const u32 *pix_fmt_hw; /* supported pixel formats */ bool non_alpha_only_l1; /* non-native no-alpha formats on layer 1 */ int pad_max_freq_hz; /* max frequency supported by pad */ + int nb_irq; /* number of hardware interrupts */ }; #define LTDC_MAX_LAYER 4 diff --git a/drivers/gpu/drm/tidss/Kconfig b/drivers/gpu/drm/tidss/Kconfig new file mode 100644 index 000000000000..f790a5215302 --- /dev/null +++ b/drivers/gpu/drm/tidss/Kconfig @@ -0,0 +1,14 @@ +config DRM_TIDSS + tristate "DRM Support for TI Keystone" + depends on DRM && OF + depends on ARM || ARM64 || COMPILE_TEST + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + help + The TI Keystone family SoCs introduced a new generation of + Display SubSystem. There is currently three Keystone family + SoCs released with DSS. Each with somewhat different version + of it. The SoCs are 66AK2Gx, AM65x, and J721E. Set this to Y + or M to add display support for TI Keystone family + platforms. diff --git a/drivers/gpu/drm/tidss/Makefile b/drivers/gpu/drm/tidss/Makefile new file mode 100644 index 000000000000..312645271014 --- /dev/null +++ b/drivers/gpu/drm/tidss/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 + +tidss-y := tidss_crtc.o \ + tidss_drv.o \ + tidss_encoder.o \ + tidss_kms.o \ + tidss_irq.o \ + tidss_plane.o \ + tidss_scale_coefs.o \ + tidss_dispc.o + +obj-$(CONFIG_DRM_TIDSS) += tidss.o diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c new file mode 100644 index 000000000000..032c31ee2820 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_vblank.h> + +#include "tidss_crtc.h" +#include "tidss_dispc.h" +#include "tidss_drv.h" +#include "tidss_irq.h" + +/* Page flip and frame done IRQs */ + +static void tidss_crtc_finish_page_flip(struct tidss_crtc *tcrtc) +{ + struct drm_device *ddev = tcrtc->crtc.dev; + struct tidss_device *tidss = ddev->dev_private; + struct drm_pending_vblank_event *event; + unsigned long flags; + bool busy; + + spin_lock_irqsave(&ddev->event_lock, flags); + + /* + * New settings are taken into use at VFP, and GO bit is cleared at + * the same time. This happens before the vertical blank interrupt. + * So there is a small change that the driver sets GO bit after VFP, but + * before vblank, and we have to check for that case here. + */ + busy = dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport); + if (busy) { + spin_unlock_irqrestore(&ddev->event_lock, flags); + return; + } + + event = tcrtc->event; + tcrtc->event = NULL; + + if (!event) { + spin_unlock_irqrestore(&ddev->event_lock, flags); + return; + } + + drm_crtc_send_vblank_event(&tcrtc->crtc, event); + + spin_unlock_irqrestore(&ddev->event_lock, flags); + + drm_crtc_vblank_put(&tcrtc->crtc); +} + +void tidss_crtc_vblank_irq(struct drm_crtc *crtc) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + + drm_crtc_handle_vblank(crtc); + + tidss_crtc_finish_page_flip(tcrtc); +} + +void tidss_crtc_framedone_irq(struct drm_crtc *crtc) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + + complete(&tcrtc->framedone_completion); +} + +void tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + + dev_err_ratelimited(crtc->dev->dev, "CRTC%u SYNC LOST: (irq %llx)\n", + tcrtc->hw_videoport, irqstatus); +} + +/* drm_crtc_helper_funcs */ + +static int tidss_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + struct dispc_device *dispc = tidss->dispc; + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + u32 hw_videoport = tcrtc->hw_videoport; + const struct drm_display_mode *mode; + enum drm_mode_status ok; + + dev_dbg(ddev->dev, "%s\n", __func__); + + if (!state->enable) + return 0; + + mode = &state->adjusted_mode; + + ok = dispc_vp_mode_valid(dispc, hw_videoport, mode); + if (ok != MODE_OK) { + dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n", + __func__, mode->hdisplay, mode->vdisplay, mode->clock); + return -EINVAL; + } + + return dispc_vp_bus_check(dispc, hw_videoport, state); +} + +static void tidss_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + unsigned long flags; + + dev_dbg(ddev->dev, + "%s: %s enabled %d, needs modeset %d, event %p\n", __func__, + crtc->name, drm_atomic_crtc_needs_modeset(crtc->state), + crtc->state->enable, crtc->state->event); + + /* There is nothing to do if CRTC is not going to be enabled. */ + if (!crtc->state->enable) + return; + + /* + * Flush CRTC changes with go bit only if new modeset is not + * coming, so CRTC is enabled trough out the commit. + */ + if (drm_atomic_crtc_needs_modeset(crtc->state)) + return; + + /* If the GO bit is stuck we better quit here. */ + if (WARN_ON(dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport))) + return; + + /* We should have event if CRTC is enabled through out this commit. */ + if (WARN_ON(!crtc->state->event)) + return; + + /* Write vp properties to HW if needed. */ + dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, false); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&ddev->event_lock, flags); + dispc_vp_go(tidss->dispc, tcrtc->hw_videoport); + + WARN_ON(tcrtc->event); + + tcrtc->event = crtc->state->event; + crtc->state->event = NULL; + + spin_unlock_irqrestore(&ddev->event_lock, flags); +} + +static void tidss_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + unsigned long flags; + int r; + + dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event); + + tidss_runtime_get(tidss); + + r = dispc_vp_set_clk_rate(tidss->dispc, tcrtc->hw_videoport, + mode->clock * 1000); + if (r != 0) + return; + + r = dispc_vp_enable_clk(tidss->dispc, tcrtc->hw_videoport); + if (r != 0) + return; + + dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, true); + + /* Turn vertical blanking interrupt reporting on. */ + drm_crtc_vblank_on(crtc); + + dispc_vp_prepare(tidss->dispc, tcrtc->hw_videoport, crtc->state); + + dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport, crtc->state); + + spin_lock_irqsave(&ddev->event_lock, flags); + + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + + spin_unlock_irqrestore(&ddev->event_lock, flags); +} + +static void tidss_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + unsigned long flags; + + dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event); + + reinit_completion(&tcrtc->framedone_completion); + + dispc_vp_disable(tidss->dispc, tcrtc->hw_videoport); + + if (!wait_for_completion_timeout(&tcrtc->framedone_completion, + msecs_to_jiffies(500))) + dev_err(tidss->dev, "Timeout waiting for framedone on crtc %d", + tcrtc->hw_videoport); + + dispc_vp_unprepare(tidss->dispc, tcrtc->hw_videoport); + + spin_lock_irqsave(&ddev->event_lock, flags); + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irqrestore(&ddev->event_lock, flags); + + drm_crtc_vblank_off(crtc); + + dispc_vp_disable_clk(tidss->dispc, tcrtc->hw_videoport); + + tidss_runtime_put(tidss); +} + +static +enum drm_mode_status tidss_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + + return dispc_vp_mode_valid(tidss->dispc, tcrtc->hw_videoport, mode); +} + +static const struct drm_crtc_helper_funcs tidss_crtc_helper_funcs = { + .atomic_check = tidss_crtc_atomic_check, + .atomic_flush = tidss_crtc_atomic_flush, + .atomic_enable = tidss_crtc_atomic_enable, + .atomic_disable = tidss_crtc_atomic_disable, + + .mode_valid = tidss_crtc_mode_valid, +}; + +/* drm_crtc_funcs */ + +static int tidss_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + + dev_dbg(ddev->dev, "%s\n", __func__); + + tidss_runtime_get(tidss); + + tidss_irq_enable_vblank(crtc); + + return 0; +} + +static void tidss_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + + dev_dbg(ddev->dev, "%s\n", __func__); + + tidss_irq_disable_vblank(crtc); + + tidss_runtime_put(tidss); +} + +static void tidss_crtc_reset(struct drm_crtc *crtc) +{ + struct tidss_crtc_state *tcrtc; + + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(crtc->state); + + tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL); + if (!tcrtc) { + crtc->state = NULL; + return; + } + + crtc->state = &tcrtc->base; + crtc->state->crtc = crtc; +} + +static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct tidss_crtc_state *state, *current_state; + + if (WARN_ON(!crtc->state)) + return NULL; + + current_state = to_tidss_crtc_state(crtc->state); + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); + + state->bus_format = current_state->bus_format; + state->bus_flags = current_state->bus_flags; + + return &state->base; +} + +static const struct drm_crtc_funcs tidss_crtc_funcs = { + .reset = tidss_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = tidss_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = tidss_crtc_enable_vblank, + .disable_vblank = tidss_crtc_disable_vblank, +}; + +struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss, + u32 hw_videoport, + struct drm_plane *primary) +{ + struct tidss_crtc *tcrtc; + struct drm_crtc *crtc; + unsigned int gamma_lut_size = 0; + bool has_ctm = tidss->feat->vp_feat.color.has_ctm; + int ret; + + tcrtc = devm_kzalloc(tidss->dev, sizeof(*tcrtc), GFP_KERNEL); + if (!tcrtc) + return ERR_PTR(-ENOMEM); + + tcrtc->hw_videoport = hw_videoport; + init_completion(&tcrtc->framedone_completion); + + crtc = &tcrtc->crtc; + + ret = drm_crtc_init_with_planes(&tidss->ddev, crtc, primary, + NULL, &tidss_crtc_funcs, NULL); + if (ret < 0) + return ERR_PTR(ret); + + drm_crtc_helper_add(crtc, &tidss_crtc_helper_funcs); + + /* + * The dispc gamma functions adapt to what ever size we ask + * from it no matter what HW supports. X-server assumes 256 + * element gamma tables so lets use that. + */ + if (tidss->feat->vp_feat.color.gamma_size) + gamma_lut_size = 256; + + drm_crtc_enable_color_mgmt(crtc, 0, has_ctm, gamma_lut_size); + if (gamma_lut_size) + drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); + + return tcrtc; +} diff --git a/drivers/gpu/drm/tidss/tidss_crtc.h b/drivers/gpu/drm/tidss/tidss_crtc.h new file mode 100644 index 000000000000..df9d90b1ad2d --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_crtc.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#ifndef __TIDSS_CRTC_H__ +#define __TIDSS_CRTC_H__ + +#include <linux/completion.h> +#include <linux/wait.h> + +#include <drm/drm_crtc.h> + +#define to_tidss_crtc(c) container_of((c), struct tidss_crtc, crtc) + +struct tidss_device; + +struct tidss_crtc { + struct drm_crtc crtc; + + u32 hw_videoport; + + struct drm_pending_vblank_event *event; + + struct completion framedone_completion; +}; + +#define to_tidss_crtc_state(x) container_of(x, struct tidss_crtc_state, base) + +struct tidss_crtc_state { + /* Must be first. */ + struct drm_crtc_state base; + + u32 bus_format; + u32 bus_flags; +}; + +void tidss_crtc_vblank_irq(struct drm_crtc *crtc); +void tidss_crtc_framedone_irq(struct drm_crtc *crtc); +void tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus); + +struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss, + u32 hw_videoport, + struct drm_plane *primary); +#endif diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c new file mode 100644 index 000000000000..eeb160dc047b --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -0,0 +1,2768 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Jyri Sarha <jsarha@ti.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include <drm/drm_fourcc.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_panel.h> + +#include "tidss_crtc.h" +#include "tidss_dispc.h" +#include "tidss_drv.h" +#include "tidss_irq.h" +#include "tidss_plane.h" + +#include "tidss_dispc_regs.h" +#include "tidss_scale_coefs.h" + +static const u16 tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = { + [DSS_REVISION_OFF] = 0x00, + [DSS_SYSCONFIG_OFF] = 0x04, + [DSS_SYSSTATUS_OFF] = 0x08, + [DISPC_IRQ_EOI_OFF] = 0x20, + [DISPC_IRQSTATUS_RAW_OFF] = 0x24, + [DISPC_IRQSTATUS_OFF] = 0x28, + [DISPC_IRQENABLE_SET_OFF] = 0x2c, + [DISPC_IRQENABLE_CLR_OFF] = 0x30, + + [DISPC_GLOBAL_MFLAG_ATTRIBUTE_OFF] = 0x40, + [DISPC_GLOBAL_BUFFER_OFF] = 0x44, + + [DISPC_DBG_CONTROL_OFF] = 0x4c, + [DISPC_DBG_STATUS_OFF] = 0x50, + + [DISPC_CLKGATING_DISABLE_OFF] = 0x54, +}; + +const struct dispc_features dispc_k2g_feats = { + .min_pclk_khz = 4375, + + .max_pclk_khz = { + [DISPC_VP_DPI] = 150000, + }, + + /* + * XXX According TRM the RGB input buffer width up to 2560 should + * work on 3 taps, but in practice it only works up to 1280. + */ + .scaling = { + .in_width_max_5tap_rgb = 1280, + .in_width_max_3tap_rgb = 1280, + .in_width_max_5tap_yuv = 2560, + .in_width_max_3tap_yuv = 2560, + .upscale_limit = 16, + .downscale_limit_5tap = 4, + .downscale_limit_3tap = 2, + /* + * The max supported pixel inc value is 255. The value + * of pixel inc is calculated like this: 1+(xinc-1)*bpp. + * The maximum bpp of all formats supported by the HW + * is 8. So the maximum supported xinc value is 32, + * because 1+(32-1)*8 < 255 < 1+(33-1)*4. + */ + .xinc_max = 32, + }, + + .subrev = DISPC_K2G, + + .common = "common", + + .common_regs = tidss_k2g_common_regs, + + .num_vps = 1, + .vp_name = { "vp1" }, + .ovr_name = { "ovr1" }, + .vpclk_name = { "vp1" }, + .vp_bus_type = { DISPC_VP_DPI }, + + .vp_feat = { .color = { + .has_ctm = true, + .gamma_size = 256, + .gamma_type = TIDSS_GAMMA_8BIT, + }, + }, + + .num_planes = 1, + .vid_name = { "vid1" }, + .vid_lite = { false }, + .vid_order = { 0 }, +}; + +static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = { + [DSS_REVISION_OFF] = 0x4, + [DSS_SYSCONFIG_OFF] = 0x8, + [DSS_SYSSTATUS_OFF] = 0x20, + [DISPC_IRQ_EOI_OFF] = 0x24, + [DISPC_IRQSTATUS_RAW_OFF] = 0x28, + [DISPC_IRQSTATUS_OFF] = 0x2c, + [DISPC_IRQENABLE_SET_OFF] = 0x30, + [DISPC_IRQENABLE_CLR_OFF] = 0x40, + [DISPC_VID_IRQENABLE_OFF] = 0x44, + [DISPC_VID_IRQSTATUS_OFF] = 0x58, + [DISPC_VP_IRQENABLE_OFF] = 0x70, + [DISPC_VP_IRQSTATUS_OFF] = 0x7c, + + [WB_IRQENABLE_OFF] = 0x88, + [WB_IRQSTATUS_OFF] = 0x8c, + + [DISPC_GLOBAL_MFLAG_ATTRIBUTE_OFF] = 0x90, + [DISPC_GLOBAL_OUTPUT_ENABLE_OFF] = 0x94, + [DISPC_GLOBAL_BUFFER_OFF] = 0x98, + [DSS_CBA_CFG_OFF] = 0x9c, + [DISPC_DBG_CONTROL_OFF] = 0xa0, + [DISPC_DBG_STATUS_OFF] = 0xa4, + [DISPC_CLKGATING_DISABLE_OFF] = 0xa8, + [DISPC_SECURE_DISABLE_OFF] = 0xac, +}; + +const struct dispc_features dispc_am65x_feats = { + .max_pclk_khz = { + [DISPC_VP_DPI] = 165000, + [DISPC_VP_OLDI] = 165000, + }, + + .scaling = { + .in_width_max_5tap_rgb = 1280, + .in_width_max_3tap_rgb = 2560, + .in_width_max_5tap_yuv = 2560, + .in_width_max_3tap_yuv = 4096, + .upscale_limit = 16, + .downscale_limit_5tap = 4, + .downscale_limit_3tap = 2, + /* + * The max supported pixel inc value is 255. The value + * of pixel inc is calculated like this: 1+(xinc-1)*bpp. + * The maximum bpp of all formats supported by the HW + * is 8. So the maximum supported xinc value is 32, + * because 1+(32-1)*8 < 255 < 1+(33-1)*4. + */ + .xinc_max = 32, + }, + + .subrev = DISPC_AM65X, + + .common = "common", + .common_regs = tidss_am65x_common_regs, + + .num_vps = 2, + .vp_name = { "vp1", "vp2" }, + .ovr_name = { "ovr1", "ovr2" }, + .vpclk_name = { "vp1", "vp2" }, + .vp_bus_type = { DISPC_VP_OLDI, DISPC_VP_DPI }, + + .vp_feat = { .color = { + .has_ctm = true, + .gamma_size = 256, + .gamma_type = TIDSS_GAMMA_8BIT, + }, + }, + + .num_planes = 2, + /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ + .vid_name = { "vid", "vidl1" }, + .vid_lite = { false, true, }, + .vid_order = { 1, 0 }, + + .errata = { + .i2000 = true, + }, +}; + +static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = { + [DSS_REVISION_OFF] = 0x4, + [DSS_SYSCONFIG_OFF] = 0x8, + [DSS_SYSSTATUS_OFF] = 0x20, + [DISPC_IRQ_EOI_OFF] = 0x80, + [DISPC_IRQSTATUS_RAW_OFF] = 0x28, + [DISPC_IRQSTATUS_OFF] = 0x2c, + [DISPC_IRQENABLE_SET_OFF] = 0x30, + [DISPC_IRQENABLE_CLR_OFF] = 0x34, + [DISPC_VID_IRQENABLE_OFF] = 0x38, + [DISPC_VID_IRQSTATUS_OFF] = 0x48, + [DISPC_VP_IRQENABLE_OFF] = 0x58, + [DISPC_VP_IRQSTATUS_OFF] = 0x68, + + [WB_IRQENABLE_OFF] = 0x78, + [WB_IRQSTATUS_OFF] = 0x7c, + + [DISPC_GLOBAL_MFLAG_ATTRIBUTE_OFF] = 0x98, + [DISPC_GLOBAL_OUTPUT_ENABLE_OFF] = 0x9c, + [DISPC_GLOBAL_BUFFER_OFF] = 0xa0, + [DSS_CBA_CFG_OFF] = 0xa4, + [DISPC_DBG_CONTROL_OFF] = 0xa8, + [DISPC_DBG_STATUS_OFF] = 0xac, + [DISPC_CLKGATING_DISABLE_OFF] = 0xb0, + [DISPC_SECURE_DISABLE_OFF] = 0x90, + + [FBDC_REVISION_1_OFF] = 0xb8, + [FBDC_REVISION_2_OFF] = 0xbc, + [FBDC_REVISION_3_OFF] = 0xc0, + [FBDC_REVISION_4_OFF] = 0xc4, + [FBDC_REVISION_5_OFF] = 0xc8, + [FBDC_REVISION_6_OFF] = 0xcc, + [FBDC_COMMON_CONTROL_OFF] = 0xd0, + [FBDC_CONSTANT_COLOR_0_OFF] = 0xd4, + [FBDC_CONSTANT_COLOR_1_OFF] = 0xd8, + [DISPC_CONNECTIONS_OFF] = 0xe4, + [DISPC_MSS_VP1_OFF] = 0xe8, + [DISPC_MSS_VP3_OFF] = 0xec, +}; + +const struct dispc_features dispc_j721e_feats = { + .max_pclk_khz = { + [DISPC_VP_DPI] = 170000, + [DISPC_VP_INTERNAL] = 600000, + }, + + .scaling = { + .in_width_max_5tap_rgb = 2048, + .in_width_max_3tap_rgb = 4096, + .in_width_max_5tap_yuv = 4096, + .in_width_max_3tap_yuv = 4096, + .upscale_limit = 16, + .downscale_limit_5tap = 4, + .downscale_limit_3tap = 2, + /* + * The max supported pixel inc value is 255. The value + * of pixel inc is calculated like this: 1+(xinc-1)*bpp. + * The maximum bpp of all formats supported by the HW + * is 8. So the maximum supported xinc value is 32, + * because 1+(32-1)*8 < 255 < 1+(33-1)*4. + */ + .xinc_max = 32, + }, + + .subrev = DISPC_J721E, + + .common = "common_m", + .common_regs = tidss_j721e_common_regs, + + .num_vps = 4, + .vp_name = { "vp1", "vp2", "vp3", "vp4" }, + .ovr_name = { "ovr1", "ovr2", "ovr3", "ovr4" }, + .vpclk_name = { "vp1", "vp2", "vp3", "vp4" }, + /* Currently hard coded VP routing (see dispc_initial_config()) */ + .vp_bus_type = { DISPC_VP_INTERNAL, DISPC_VP_DPI, + DISPC_VP_INTERNAL, DISPC_VP_DPI, }, + .vp_feat = { .color = { + .has_ctm = true, + .gamma_size = 1024, + .gamma_type = TIDSS_GAMMA_10BIT, + }, + }, + .num_planes = 4, + .vid_name = { "vid1", "vidl1", "vid2", "vidl2" }, + .vid_lite = { 0, 1, 0, 1, }, + .vid_order = { 1, 3, 0, 2 }, +}; + +static const u16 *dispc_common_regmap; + +struct dss_vp_data { + u32 *gamma_table; +}; + +struct dss_plane_data { + u32 zorder; + u32 hw_videoport; +}; + +struct dispc_device { + struct tidss_device *tidss; + struct device *dev; + + void __iomem *base_common; + void __iomem *base_vid[TIDSS_MAX_PLANES]; + void __iomem *base_ovr[TIDSS_MAX_PORTS]; + void __iomem *base_vp[TIDSS_MAX_PORTS]; + + struct regmap *oldi_io_ctrl; + + struct clk *vp_clk[TIDSS_MAX_PORTS]; + + const struct dispc_features *feat; + + struct clk *fclk; + + bool is_enabled; + + struct dss_vp_data vp_data[TIDSS_MAX_PORTS]; + + struct dss_plane_data plane_data[TIDSS_MAX_PLANES]; + + u32 *fourccs; + u32 num_fourccs; + + u32 memory_bandwidth_limit; +}; + +static void dispc_write(struct dispc_device *dispc, u16 reg, u32 val) +{ + iowrite32(val, dispc->base_common + reg); +} + +static u32 dispc_read(struct dispc_device *dispc, u16 reg) +{ + return ioread32(dispc->base_common + reg); +} + +static +void dispc_vid_write(struct dispc_device *dispc, u32 hw_plane, u16 reg, u32 val) +{ + void __iomem *base = dispc->base_vid[hw_plane]; + + iowrite32(val, base + reg); +} + +static u32 dispc_vid_read(struct dispc_device *dispc, u32 hw_plane, u16 reg) +{ + void __iomem *base = dispc->base_vid[hw_plane]; + + return ioread32(base + reg); +} + +static void dispc_ovr_write(struct dispc_device *dispc, u32 hw_videoport, + u16 reg, u32 val) +{ + void __iomem *base = dispc->base_ovr[hw_videoport]; + + iowrite32(val, base + reg); +} + +static u32 dispc_ovr_read(struct dispc_device *dispc, u32 hw_videoport, u16 reg) +{ + void __iomem *base = dispc->base_ovr[hw_videoport]; + + return ioread32(base + reg); +} + +static void dispc_vp_write(struct dispc_device *dispc, u32 hw_videoport, + u16 reg, u32 val) +{ + void __iomem *base = dispc->base_vp[hw_videoport]; + + iowrite32(val, base + reg); +} + +static u32 dispc_vp_read(struct dispc_device *dispc, u32 hw_videoport, u16 reg) +{ + void __iomem *base = dispc->base_vp[hw_videoport]; + + return ioread32(base + reg); +} + +/* + * TRM gives bitfields as start:end, where start is the higher bit + * number. For example 7:0 + */ + +static u32 FLD_MASK(u32 start, u32 end) +{ + return ((1 << (start - end + 1)) - 1) << end; +} + +static u32 FLD_VAL(u32 val, u32 start, u32 end) +{ + return (val << end) & FLD_MASK(start, end); +} + +static u32 FLD_GET(u32 val, u32 start, u32 end) +{ + return (val & FLD_MASK(start, end)) >> end; +} + +static u32 FLD_MOD(u32 orig, u32 val, u32 start, u32 end) +{ + return (orig & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end); +} + +static u32 REG_GET(struct dispc_device *dispc, u32 idx, u32 start, u32 end) +{ + return FLD_GET(dispc_read(dispc, idx), start, end); +} + +static void REG_FLD_MOD(struct dispc_device *dispc, u32 idx, u32 val, + u32 start, u32 end) +{ + dispc_write(dispc, idx, FLD_MOD(dispc_read(dispc, idx), val, + start, end)); +} + +static u32 VID_REG_GET(struct dispc_device *dispc, u32 hw_plane, u32 idx, + u32 start, u32 end) +{ + return FLD_GET(dispc_vid_read(dispc, hw_plane, idx), start, end); +} + +static void VID_REG_FLD_MOD(struct dispc_device *dispc, u32 hw_plane, u32 idx, + u32 val, u32 start, u32 end) +{ + dispc_vid_write(dispc, hw_plane, idx, + FLD_MOD(dispc_vid_read(dispc, hw_plane, idx), + val, start, end)); +} + +static u32 VP_REG_GET(struct dispc_device *dispc, u32 vp, u32 idx, + u32 start, u32 end) +{ + return FLD_GET(dispc_vp_read(dispc, vp, idx), start, end); +} + +static void VP_REG_FLD_MOD(struct dispc_device *dispc, u32 vp, u32 idx, u32 val, + u32 start, u32 end) +{ + dispc_vp_write(dispc, vp, idx, FLD_MOD(dispc_vp_read(dispc, vp, idx), + val, start, end)); +} + +__maybe_unused +static u32 OVR_REG_GET(struct dispc_device *dispc, u32 ovr, u32 idx, + u32 start, u32 end) +{ + return FLD_GET(dispc_ovr_read(dispc, ovr, idx), start, end); +} + +static void OVR_REG_FLD_MOD(struct dispc_device *dispc, u32 ovr, u32 idx, + u32 val, u32 start, u32 end) +{ + dispc_ovr_write(dispc, ovr, idx, + FLD_MOD(dispc_ovr_read(dispc, ovr, idx), + val, start, end)); +} + +static dispc_irq_t dispc_vp_irq_from_raw(u32 stat, u32 hw_videoport) +{ + dispc_irq_t vp_stat = 0; + + if (stat & BIT(0)) + vp_stat |= DSS_IRQ_VP_FRAME_DONE(hw_videoport); + if (stat & BIT(1)) + vp_stat |= DSS_IRQ_VP_VSYNC_EVEN(hw_videoport); + if (stat & BIT(2)) + vp_stat |= DSS_IRQ_VP_VSYNC_ODD(hw_videoport); + if (stat & BIT(4)) + vp_stat |= DSS_IRQ_VP_SYNC_LOST(hw_videoport); + + return vp_stat; +} + +static u32 dispc_vp_irq_to_raw(dispc_irq_t vpstat, u32 hw_videoport) +{ + u32 stat = 0; + + if (vpstat & DSS_IRQ_VP_FRAME_DONE(hw_videoport)) + stat |= BIT(0); + if (vpstat & DSS_IRQ_VP_VSYNC_EVEN(hw_videoport)) + stat |= BIT(1); + if (vpstat & DSS_IRQ_VP_VSYNC_ODD(hw_videoport)) + stat |= BIT(2); + if (vpstat & DSS_IRQ_VP_SYNC_LOST(hw_videoport)) + stat |= BIT(4); + + return stat; +} + +static dispc_irq_t dispc_vid_irq_from_raw(u32 stat, u32 hw_plane) +{ + dispc_irq_t vid_stat = 0; + + if (stat & BIT(0)) + vid_stat |= DSS_IRQ_PLANE_FIFO_UNDERFLOW(hw_plane); + + return vid_stat; +} + +static u32 dispc_vid_irq_to_raw(dispc_irq_t vidstat, u32 hw_plane) +{ + u32 stat = 0; + + if (vidstat & DSS_IRQ_PLANE_FIFO_UNDERFLOW(hw_plane)) + stat |= BIT(0); + + return stat; +} + +static dispc_irq_t dispc_k2g_vp_read_irqstatus(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 stat = dispc_vp_read(dispc, hw_videoport, DISPC_VP_K2G_IRQSTATUS); + + return dispc_vp_irq_from_raw(stat, hw_videoport); +} + +static void dispc_k2g_vp_write_irqstatus(struct dispc_device *dispc, + u32 hw_videoport, dispc_irq_t vpstat) +{ + u32 stat = dispc_vp_irq_to_raw(vpstat, hw_videoport); + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_K2G_IRQSTATUS, stat); +} + +static dispc_irq_t dispc_k2g_vid_read_irqstatus(struct dispc_device *dispc, + u32 hw_plane) +{ + u32 stat = dispc_vid_read(dispc, hw_plane, DISPC_VID_K2G_IRQSTATUS); + + return dispc_vid_irq_from_raw(stat, hw_plane); +} + +static void dispc_k2g_vid_write_irqstatus(struct dispc_device *dispc, + u32 hw_plane, dispc_irq_t vidstat) +{ + u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); + + dispc_vid_write(dispc, hw_plane, DISPC_VID_K2G_IRQSTATUS, stat); +} + +static dispc_irq_t dispc_k2g_vp_read_irqenable(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 stat = dispc_vp_read(dispc, hw_videoport, DISPC_VP_K2G_IRQENABLE); + + return dispc_vp_irq_from_raw(stat, hw_videoport); +} + +static void dispc_k2g_vp_set_irqenable(struct dispc_device *dispc, + u32 hw_videoport, dispc_irq_t vpstat) +{ + u32 stat = dispc_vp_irq_to_raw(vpstat, hw_videoport); + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_K2G_IRQENABLE, stat); +} + +static dispc_irq_t dispc_k2g_vid_read_irqenable(struct dispc_device *dispc, + u32 hw_plane) +{ + u32 stat = dispc_vid_read(dispc, hw_plane, DISPC_VID_K2G_IRQENABLE); + + return dispc_vid_irq_from_raw(stat, hw_plane); +} + +static void dispc_k2g_vid_set_irqenable(struct dispc_device *dispc, + u32 hw_plane, dispc_irq_t vidstat) +{ + u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); + + dispc_vid_write(dispc, hw_plane, DISPC_VID_K2G_IRQENABLE, stat); +} + +static void dispc_k2g_clear_irqstatus(struct dispc_device *dispc, + dispc_irq_t mask) +{ + dispc_k2g_vp_write_irqstatus(dispc, 0, mask); + dispc_k2g_vid_write_irqstatus(dispc, 0, mask); +} + +static +dispc_irq_t dispc_k2g_read_and_clear_irqstatus(struct dispc_device *dispc) +{ + dispc_irq_t stat = 0; + + /* always clear the top level irqstatus */ + dispc_write(dispc, DISPC_IRQSTATUS, + dispc_read(dispc, DISPC_IRQSTATUS)); + + stat |= dispc_k2g_vp_read_irqstatus(dispc, 0); + stat |= dispc_k2g_vid_read_irqstatus(dispc, 0); + + dispc_k2g_clear_irqstatus(dispc, stat); + + return stat; +} + +static dispc_irq_t dispc_k2g_read_irqenable(struct dispc_device *dispc) +{ + dispc_irq_t stat = 0; + + stat |= dispc_k2g_vp_read_irqenable(dispc, 0); + stat |= dispc_k2g_vid_read_irqenable(dispc, 0); + + return stat; +} + +static +void dispc_k2g_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask) +{ + dispc_irq_t old_mask = dispc_k2g_read_irqenable(dispc); + + /* clear the irqstatus for newly enabled irqs */ + dispc_k2g_clear_irqstatus(dispc, (mask ^ old_mask) & mask); + + dispc_k2g_vp_set_irqenable(dispc, 0, mask); + dispc_k2g_vid_set_irqenable(dispc, 0, mask); + + dispc_write(dispc, DISPC_IRQENABLE_SET, (1 << 0) | (1 << 7)); + + /* flush posted write */ + dispc_k2g_read_irqenable(dispc); +} + +static dispc_irq_t dispc_k3_vp_read_irqstatus(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 stat = dispc_read(dispc, DISPC_VP_IRQSTATUS(hw_videoport)); + + return dispc_vp_irq_from_raw(stat, hw_videoport); +} + +static void dispc_k3_vp_write_irqstatus(struct dispc_device *dispc, + u32 hw_videoport, dispc_irq_t vpstat) +{ + u32 stat = dispc_vp_irq_to_raw(vpstat, hw_videoport); + + dispc_write(dispc, DISPC_VP_IRQSTATUS(hw_videoport), stat); +} + +static dispc_irq_t dispc_k3_vid_read_irqstatus(struct dispc_device *dispc, + u32 hw_plane) +{ + u32 stat = dispc_read(dispc, DISPC_VID_IRQSTATUS(hw_plane)); + + return dispc_vid_irq_from_raw(stat, hw_plane); +} + +static void dispc_k3_vid_write_irqstatus(struct dispc_device *dispc, + u32 hw_plane, dispc_irq_t vidstat) +{ + u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); + + dispc_write(dispc, DISPC_VID_IRQSTATUS(hw_plane), stat); +} + +static dispc_irq_t dispc_k3_vp_read_irqenable(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 stat = dispc_read(dispc, DISPC_VP_IRQENABLE(hw_videoport)); + + return dispc_vp_irq_from_raw(stat, hw_videoport); +} + +static void dispc_k3_vp_set_irqenable(struct dispc_device *dispc, + u32 hw_videoport, dispc_irq_t vpstat) +{ + u32 stat = dispc_vp_irq_to_raw(vpstat, hw_videoport); + + dispc_write(dispc, DISPC_VP_IRQENABLE(hw_videoport), stat); +} + +static dispc_irq_t dispc_k3_vid_read_irqenable(struct dispc_device *dispc, + u32 hw_plane) +{ + u32 stat = dispc_read(dispc, DISPC_VID_IRQENABLE(hw_plane)); + + return dispc_vid_irq_from_raw(stat, hw_plane); +} + +static void dispc_k3_vid_set_irqenable(struct dispc_device *dispc, + u32 hw_plane, dispc_irq_t vidstat) +{ + u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); + + dispc_write(dispc, DISPC_VID_IRQENABLE(hw_plane), stat); +} + +static +void dispc_k3_clear_irqstatus(struct dispc_device *dispc, dispc_irq_t clearmask) +{ + unsigned int i; + u32 top_clear = 0; + + for (i = 0; i < dispc->feat->num_vps; ++i) { + if (clearmask & DSS_IRQ_VP_MASK(i)) { + dispc_k3_vp_write_irqstatus(dispc, i, clearmask); + top_clear |= BIT(i); + } + } + for (i = 0; i < dispc->feat->num_planes; ++i) { + if (clearmask & DSS_IRQ_PLANE_MASK(i)) { + dispc_k3_vid_write_irqstatus(dispc, i, clearmask); + top_clear |= BIT(4 + i); + } + } + if (dispc->feat->subrev == DISPC_K2G) + return; + + dispc_write(dispc, DISPC_IRQSTATUS, top_clear); + + /* Flush posted writes */ + dispc_read(dispc, DISPC_IRQSTATUS); +} + +static +dispc_irq_t dispc_k3_read_and_clear_irqstatus(struct dispc_device *dispc) +{ + dispc_irq_t status = 0; + unsigned int i; + + for (i = 0; i < dispc->feat->num_vps; ++i) + status |= dispc_k3_vp_read_irqstatus(dispc, i); + + for (i = 0; i < dispc->feat->num_planes; ++i) + status |= dispc_k3_vid_read_irqstatus(dispc, i); + + dispc_k3_clear_irqstatus(dispc, status); + + return status; +} + +static dispc_irq_t dispc_k3_read_irqenable(struct dispc_device *dispc) +{ + dispc_irq_t enable = 0; + unsigned int i; + + for (i = 0; i < dispc->feat->num_vps; ++i) + enable |= dispc_k3_vp_read_irqenable(dispc, i); + + for (i = 0; i < dispc->feat->num_planes; ++i) + enable |= dispc_k3_vid_read_irqenable(dispc, i); + + return enable; +} + +static void dispc_k3_set_irqenable(struct dispc_device *dispc, + dispc_irq_t mask) +{ + unsigned int i; + u32 main_enable = 0, main_disable = 0; + dispc_irq_t old_mask; + + old_mask = dispc_k3_read_irqenable(dispc); + + /* clear the irqstatus for newly enabled irqs */ + dispc_k3_clear_irqstatus(dispc, (old_mask ^ mask) & mask); + + for (i = 0; i < dispc->feat->num_vps; ++i) { + dispc_k3_vp_set_irqenable(dispc, i, mask); + if (mask & DSS_IRQ_VP_MASK(i)) + main_enable |= BIT(i); /* VP IRQ */ + else + main_disable |= BIT(i); /* VP IRQ */ + } + + for (i = 0; i < dispc->feat->num_planes; ++i) { + dispc_k3_vid_set_irqenable(dispc, i, mask); + if (mask & DSS_IRQ_PLANE_MASK(i)) + main_enable |= BIT(i + 4); /* VID IRQ */ + else + main_disable |= BIT(i + 4); /* VID IRQ */ + } + + if (main_enable) + dispc_write(dispc, DISPC_IRQENABLE_SET, main_enable); + + if (main_disable) + dispc_write(dispc, DISPC_IRQENABLE_CLR, main_disable); + + /* Flush posted writes */ + dispc_read(dispc, DISPC_IRQENABLE_SET); +} + +dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc) +{ + switch (dispc->feat->subrev) { + case DISPC_K2G: + return dispc_k2g_read_and_clear_irqstatus(dispc); + case DISPC_AM65X: + case DISPC_J721E: + return dispc_k3_read_and_clear_irqstatus(dispc); + default: + WARN_ON(1); + return 0; + } +} + +void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask) +{ + switch (dispc->feat->subrev) { + case DISPC_K2G: + dispc_k2g_set_irqenable(dispc, mask); + break; + case DISPC_AM65X: + case DISPC_J721E: + dispc_k3_set_irqenable(dispc, mask); + break; + default: + WARN_ON(1); + break; + } +} + +enum dispc_oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 }; + +struct dispc_bus_format { + u32 bus_fmt; + u32 data_width; + bool is_oldi_fmt; + enum dispc_oldi_mode_reg_val oldi_mode_reg_val; +}; + +static const struct dispc_bus_format dispc_bus_formats[] = { + { MEDIA_BUS_FMT_RGB444_1X12, 12, false, 0 }, + { MEDIA_BUS_FMT_RGB565_1X16, 16, false, 0 }, + { MEDIA_BUS_FMT_RGB666_1X18, 18, false, 0 }, + { MEDIA_BUS_FMT_RGB888_1X24, 24, false, 0 }, + { MEDIA_BUS_FMT_RGB101010_1X30, 30, false, 0 }, + { MEDIA_BUS_FMT_RGB121212_1X36, 36, false, 0 }, + { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 18, true, SPWG_18 }, + { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 24, true, SPWG_24 }, + { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 24, true, JEIDA_24 }, +}; + +static const +struct dispc_bus_format *dispc_vp_find_bus_fmt(struct dispc_device *dispc, + u32 hw_videoport, + u32 bus_fmt, u32 bus_flags) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dispc_bus_formats); ++i) { + if (dispc_bus_formats[i].bus_fmt == bus_fmt) + return &dispc_bus_formats[i]; + } + + return NULL; +} + +int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_crtc_state *state) +{ + const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state); + const struct dispc_bus_format *fmt; + + fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format, + tstate->bus_flags); + if (!fmt) { + dev_dbg(dispc->dev, "%s: Unsupported bus format: %u\n", + __func__, tstate->bus_format); + return -EINVAL; + } + + if (dispc->feat->vp_bus_type[hw_videoport] != DISPC_VP_OLDI && + fmt->is_oldi_fmt) { + dev_dbg(dispc->dev, "%s: %s is not OLDI-port\n", + __func__, dispc->feat->vp_name[hw_videoport]); + return -EINVAL; + } + + return 0; +} + +static void dispc_oldi_tx_power(struct dispc_device *dispc, bool power) +{ + u32 val = power ? 0 : OLDI_PWRDN_TX; + + if (WARN_ON(!dispc->oldi_io_ctrl)) + return; + + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT0_IO_CTRL, + OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT1_IO_CTRL, + OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT2_IO_CTRL, + OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT3_IO_CTRL, + OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->oldi_io_ctrl, OLDI_CLK_IO_CTRL, + OLDI_PWRDN_TX, val); +} + +static void dispc_set_num_datalines(struct dispc_device *dispc, + u32 hw_videoport, int num_lines) +{ + int v; + + switch (num_lines) { + case 12: + v = 0; break; + case 16: + v = 1; break; + case 18: + v = 2; break; + case 24: + v = 3; break; + case 30: + v = 4; break; + case 36: + v = 5; break; + default: + WARN_ON(1); + v = 3; + } + + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, v, 10, 8); +} + +static void dispc_enable_oldi(struct dispc_device *dispc, u32 hw_videoport, + const struct dispc_bus_format *fmt) +{ + u32 oldi_cfg = 0; + u32 oldi_reset_bit = BIT(5 + hw_videoport); + int count = 0; + + /* + * For the moment DUALMODESYNC, MASTERSLAVE, MODE, and SRC + * bits of DISPC_VP_DSS_OLDI_CFG are set statically to 0. + */ + + if (fmt->data_width == 24) + oldi_cfg |= BIT(8); /* MSB */ + else if (fmt->data_width != 18) + dev_warn(dispc->dev, "%s: %d port width not supported\n", + __func__, fmt->data_width); + + oldi_cfg |= BIT(7); /* DEPOL */ + + oldi_cfg = FLD_MOD(oldi_cfg, fmt->oldi_mode_reg_val, 3, 1); + + oldi_cfg |= BIT(12); /* SOFTRST */ + + oldi_cfg |= BIT(0); /* ENABLE */ + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, oldi_cfg); + + while (!(oldi_reset_bit & dispc_read(dispc, DSS_SYSSTATUS)) && + count < 10000) + count++; + + if (!(oldi_reset_bit & dispc_read(dispc, DSS_SYSSTATUS))) + dev_warn(dispc->dev, "%s: timeout waiting OLDI reset done\n", + __func__); +} + +void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_crtc_state *state) +{ + const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state); + const struct dispc_bus_format *fmt; + + fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format, + tstate->bus_flags); + + if (WARN_ON(!fmt)) + return; + + if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) { + dispc_oldi_tx_power(dispc, true); + + dispc_enable_oldi(dispc, hw_videoport, fmt); + } +} + +void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_crtc_state *state) +{ + const struct drm_display_mode *mode = &state->adjusted_mode; + const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state); + bool align, onoff, rf, ieo, ipc, ihs, ivs; + const struct dispc_bus_format *fmt; + u32 hsw, hfp, hbp, vsw, vfp, vbp; + + fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format, + tstate->bus_flags); + + if (WARN_ON(!fmt)) + return; + + dispc_set_num_datalines(dispc, hw_videoport, fmt->data_width); + + hfp = mode->hsync_start - mode->hdisplay; + hsw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + vfp = mode->vsync_start - mode->vdisplay; + vsw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_end; + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_TIMING_H, + FLD_VAL(hsw - 1, 7, 0) | + FLD_VAL(hfp - 1, 19, 8) | + FLD_VAL(hbp - 1, 31, 20)); + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_TIMING_V, + FLD_VAL(vsw - 1, 7, 0) | + FLD_VAL(vfp, 19, 8) | + FLD_VAL(vbp, 31, 20)); + + ivs = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); + + ihs = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); + + ieo = !!(tstate->bus_flags & DRM_BUS_FLAG_DE_LOW); + + ipc = !!(tstate->bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE); + + /* always use the 'rf' setting */ + onoff = true; + + rf = !!(tstate->bus_flags & DRM_BUS_FLAG_SYNC_POSEDGE); + + /* always use aligned syncs */ + align = true; + + /* always use DE_HIGH for OLDI */ + if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) + ieo = false; + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_POL_FREQ, + FLD_VAL(align, 18, 18) | + FLD_VAL(onoff, 17, 17) | + FLD_VAL(rf, 16, 16) | + FLD_VAL(ieo, 15, 15) | + FLD_VAL(ipc, 14, 14) | + FLD_VAL(ihs, 13, 13) | + FLD_VAL(ivs, 12, 12)); + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_SIZE_SCREEN, + FLD_VAL(mode->hdisplay - 1, 11, 0) | + FLD_VAL(mode->vdisplay - 1, 27, 16)); + + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1, 0, 0); +} + +void dispc_vp_disable(struct dispc_device *dispc, u32 hw_videoport) +{ + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 0, 0, 0); +} + +void dispc_vp_unprepare(struct dispc_device *dispc, u32 hw_videoport) +{ + if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) { + dispc_vp_write(dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, 0); + + dispc_oldi_tx_power(dispc, false); + } +} + +bool dispc_vp_go_busy(struct dispc_device *dispc, u32 hw_videoport) +{ + return VP_REG_GET(dispc, hw_videoport, DISPC_VP_CONTROL, 5, 5); +} + +void dispc_vp_go(struct dispc_device *dispc, u32 hw_videoport) +{ + WARN_ON(VP_REG_GET(dispc, hw_videoport, DISPC_VP_CONTROL, 5, 5)); + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1, 5, 5); +} + +enum c8_to_c12_mode { C8_TO_C12_REPLICATE, C8_TO_C12_MAX, C8_TO_C12_MIN }; + +static u16 c8_to_c12(u8 c8, enum c8_to_c12_mode mode) +{ + u16 c12; + + c12 = c8 << 4; + + switch (mode) { + case C8_TO_C12_REPLICATE: + /* Copy c8 4 MSB to 4 LSB for full scale c12 */ + c12 |= c8 >> 4; + break; + case C8_TO_C12_MAX: + c12 |= 0xF; + break; + default: + case C8_TO_C12_MIN: + break; + } + + return c12; +} + +static u64 argb8888_to_argb12121212(u32 argb8888, enum c8_to_c12_mode m) +{ + u8 a, r, g, b; + u64 v; + + a = (argb8888 >> 24) & 0xff; + r = (argb8888 >> 16) & 0xff; + g = (argb8888 >> 8) & 0xff; + b = (argb8888 >> 0) & 0xff; + + v = ((u64)c8_to_c12(a, m) << 36) | ((u64)c8_to_c12(r, m) << 24) | + ((u64)c8_to_c12(g, m) << 12) | (u64)c8_to_c12(b, m); + + return v; +} + +static void dispc_vp_set_default_color(struct dispc_device *dispc, + u32 hw_videoport, u32 default_color) +{ + u64 v; + + v = argb8888_to_argb12121212(default_color, C8_TO_C12_REPLICATE); + + dispc_ovr_write(dispc, hw_videoport, + DISPC_OVR_DEFAULT_COLOR, v & 0xffffffff); + dispc_ovr_write(dispc, hw_videoport, + DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff); +} + +enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_display_mode *mode) +{ + u32 hsw, hfp, hbp, vsw, vfp, vbp; + enum dispc_vp_bus_type bus_type; + int max_pclk; + + bus_type = dispc->feat->vp_bus_type[hw_videoport]; + + max_pclk = dispc->feat->max_pclk_khz[bus_type]; + + if (WARN_ON(max_pclk == 0)) + return MODE_BAD; + + if (mode->clock < dispc->feat->min_pclk_khz) + return MODE_CLOCK_LOW; + + if (mode->clock > max_pclk) + return MODE_CLOCK_HIGH; + + if (mode->hdisplay > 4096) + return MODE_BAD; + + if (mode->vdisplay > 4096) + return MODE_BAD; + + /* TODO: add interlace support */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + /* + * Enforce the output width is divisible by 2. Actually this + * is only needed in following cases: + * - YUV output selected (BT656, BT1120) + * - Dithering enabled + * - TDM with TDMCycleFormat == 3 + * But for simplicity we enforce that always. + */ + if ((mode->hdisplay % 2) != 0) + return MODE_BAD_HVALUE; + + hfp = mode->hsync_start - mode->hdisplay; + hsw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + vfp = mode->vsync_start - mode->vdisplay; + vsw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_end; + + if (hsw < 1 || hsw > 256 || + hfp < 1 || hfp > 4096 || + hbp < 1 || hbp > 4096) + return MODE_BAD_HVALUE; + + if (vsw < 1 || vsw > 256 || + vfp > 4095 || vbp > 4095) + return MODE_BAD_VVALUE; + + if (dispc->memory_bandwidth_limit) { + const unsigned int bpp = 4; + u64 bandwidth; + + bandwidth = 1000 * mode->clock; + bandwidth = bandwidth * mode->hdisplay * mode->vdisplay * bpp; + bandwidth = div_u64(bandwidth, mode->htotal * mode->vtotal); + + if (dispc->memory_bandwidth_limit < bandwidth) + return MODE_BAD; + } + + return MODE_OK; +} + +int dispc_vp_enable_clk(struct dispc_device *dispc, u32 hw_videoport) +{ + int ret = clk_prepare_enable(dispc->vp_clk[hw_videoport]); + + if (ret) + dev_err(dispc->dev, "%s: enabling clk failed: %d\n", __func__, + ret); + + return ret; +} + +void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport) +{ + clk_disable_unprepare(dispc->vp_clk[hw_videoport]); +} + +/* + * Calculate the percentage difference between the requested pixel clock rate + * and the effective rate resulting from calculating the clock divider value. + */ +static +unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate) +{ + int r = rate / 100, rr = real_rate / 100; + + return (unsigned int)(abs(((rr - r) * 100) / r)); +} + +int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport, + unsigned long rate) +{ + int r; + unsigned long new_rate; + + r = clk_set_rate(dispc->vp_clk[hw_videoport], rate); + if (r) { + dev_err(dispc->dev, "vp%d: failed to set clk rate to %lu\n", + hw_videoport, rate); + return r; + } + + new_rate = clk_get_rate(dispc->vp_clk[hw_videoport]); + + if (dispc_pclk_diff(rate, new_rate) > 5) + dev_warn(dispc->dev, + "vp%d: Clock rate %lu differs over 5%% from requsted %lu\n", + hw_videoport, new_rate, rate); + + dev_dbg(dispc->dev, "vp%d: new rate %lu Hz (requested %lu Hz)\n", + hw_videoport, clk_get_rate(dispc->vp_clk[hw_videoport]), rate); + + return 0; +} + +/* OVR */ +static void dispc_k2g_ovr_set_plane(struct dispc_device *dispc, + u32 hw_plane, u32 hw_videoport, + u32 x, u32 y, u32 zpos) +{ + /* On k2g there is only one plane and no need for ovr */ + dispc_vid_write(dispc, hw_plane, DISPC_VID_K2G_POSITION, + x | (y << 16)); +} + +static void dispc_am65x_ovr_set_plane(struct dispc_device *dispc, + u32 hw_plane, u32 hw_videoport, + u32 x, u32 y, u32 zpos) +{ + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(zpos), + hw_plane, 4, 1); + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(zpos), + x, 17, 6); + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(zpos), + y, 30, 19); +} + +static void dispc_j721e_ovr_set_plane(struct dispc_device *dispc, + u32 hw_plane, u32 hw_videoport, + u32 x, u32 y, u32 zpos) +{ + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(zpos), + hw_plane, 4, 1); + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES2(zpos), + x, 13, 0); + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES2(zpos), + y, 29, 16); +} + +static void dispc_ovr_set_plane(struct dispc_device *dispc, + u32 hw_plane, u32 hw_videoport, + u32 x, u32 y, u32 zpos) +{ + switch (dispc->feat->subrev) { + case DISPC_K2G: + dispc_k2g_ovr_set_plane(dispc, hw_plane, hw_videoport, + x, y, zpos); + break; + case DISPC_AM65X: + dispc_am65x_ovr_set_plane(dispc, hw_plane, hw_videoport, + x, y, zpos); + break; + case DISPC_J721E: + dispc_j721e_ovr_set_plane(dispc, hw_plane, hw_videoport, + x, y, zpos); + break; + default: + WARN_ON(1); + break; + } +} + +static void dispc_ovr_enable_plane(struct dispc_device *dispc, + u32 hw_videoport, u32 zpos, bool enable) +{ + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(zpos), + !!enable, 0, 0); +} + +/* CSC */ +enum csc_ctm { + CSC_RR, CSC_RG, CSC_RB, + CSC_GR, CSC_GG, CSC_GB, + CSC_BR, CSC_BG, CSC_BB, +}; + +enum csc_yuv2rgb { + CSC_RY, CSC_RCB, CSC_RCR, + CSC_GY, CSC_GCB, CSC_GCR, + CSC_BY, CSC_BCB, CSC_BCR, +}; + +enum csc_rgb2yuv { + CSC_YR, CSC_YG, CSC_YB, + CSC_CBR, CSC_CBG, CSC_CBB, + CSC_CRR, CSC_CRG, CSC_CRB, +}; + +struct dispc_csc_coef { + void (*to_regval)(const struct dispc_csc_coef *csc, u32 *regval); + int m[9]; + int preoffset[3]; + int postoffset[3]; + enum { CLIP_LIMITED_RANGE = 0, CLIP_FULL_RANGE = 1, } cliping; + const char *name; +}; + +#define DISPC_CSC_REGVAL_LEN 8 + +static +void dispc_csc_offset_regval(const struct dispc_csc_coef *csc, u32 *regval) +{ +#define OVAL(x, y) (FLD_VAL(x, 15, 3) | FLD_VAL(y, 31, 19)) + regval[5] = OVAL(csc->preoffset[0], csc->preoffset[1]); + regval[6] = OVAL(csc->preoffset[2], csc->postoffset[0]); + regval[7] = OVAL(csc->postoffset[1], csc->postoffset[2]); +#undef OVAL +} + +#define CVAL(x, y) (FLD_VAL(x, 10, 0) | FLD_VAL(y, 26, 16)) +static +void dispc_csc_yuv2rgb_regval(const struct dispc_csc_coef *csc, u32 *regval) +{ + regval[0] = CVAL(csc->m[CSC_RY], csc->m[CSC_RCR]); + regval[1] = CVAL(csc->m[CSC_RCB], csc->m[CSC_GY]); + regval[2] = CVAL(csc->m[CSC_GCR], csc->m[CSC_GCB]); + regval[3] = CVAL(csc->m[CSC_BY], csc->m[CSC_BCR]); + regval[4] = CVAL(csc->m[CSC_BCB], 0); + + dispc_csc_offset_regval(csc, regval); +} + +__maybe_unused static +void dispc_csc_rgb2yuv_regval(const struct dispc_csc_coef *csc, u32 *regval) +{ + regval[0] = CVAL(csc->m[CSC_YR], csc->m[CSC_YG]); + regval[1] = CVAL(csc->m[CSC_YB], csc->m[CSC_CRR]); + regval[2] = CVAL(csc->m[CSC_CRG], csc->m[CSC_CRB]); + regval[3] = CVAL(csc->m[CSC_CBR], csc->m[CSC_CBG]); + regval[4] = CVAL(csc->m[CSC_CBB], 0); + + dispc_csc_offset_regval(csc, regval); +} + +static void dispc_csc_cpr_regval(const struct dispc_csc_coef *csc, + u32 *regval) +{ + regval[0] = CVAL(csc->m[CSC_RR], csc->m[CSC_RG]); + regval[1] = CVAL(csc->m[CSC_RB], csc->m[CSC_GR]); + regval[2] = CVAL(csc->m[CSC_GG], csc->m[CSC_GB]); + regval[3] = CVAL(csc->m[CSC_BR], csc->m[CSC_BG]); + regval[4] = CVAL(csc->m[CSC_BB], 0); + + dispc_csc_offset_regval(csc, regval); +} + +#undef CVAL + +static void dispc_k2g_vid_write_csc(struct dispc_device *dispc, u32 hw_plane, + const struct dispc_csc_coef *csc) +{ + static const u16 dispc_vid_csc_coef_reg[] = { + DISPC_VID_CSC_COEF(0), DISPC_VID_CSC_COEF(1), + DISPC_VID_CSC_COEF(2), DISPC_VID_CSC_COEF(3), + DISPC_VID_CSC_COEF(4), DISPC_VID_CSC_COEF(5), + DISPC_VID_CSC_COEF(6), /* K2G has no post offset support */ + }; + u32 regval[DISPC_CSC_REGVAL_LEN]; + unsigned int i; + + csc->to_regval(csc, regval); + + if (regval[7] != 0) + dev_warn(dispc->dev, "%s: No post offset support for %s\n", + __func__, csc->name); + + for (i = 0; i < ARRAY_SIZE(dispc_vid_csc_coef_reg); i++) + dispc_vid_write(dispc, hw_plane, dispc_vid_csc_coef_reg[i], + regval[i]); +} + +static void dispc_k3_vid_write_csc(struct dispc_device *dispc, u32 hw_plane, + const struct dispc_csc_coef *csc) +{ + static const u16 dispc_vid_csc_coef_reg[DISPC_CSC_REGVAL_LEN] = { + DISPC_VID_CSC_COEF(0), DISPC_VID_CSC_COEF(1), + DISPC_VID_CSC_COEF(2), DISPC_VID_CSC_COEF(3), + DISPC_VID_CSC_COEF(4), DISPC_VID_CSC_COEF(5), + DISPC_VID_CSC_COEF(6), DISPC_VID_CSC_COEF7, + }; + u32 regval[DISPC_CSC_REGVAL_LEN]; + unsigned int i; + + csc->to_regval(csc, regval); + + for (i = 0; i < ARRAY_SIZE(dispc_vid_csc_coef_reg); i++) + dispc_vid_write(dispc, hw_plane, dispc_vid_csc_coef_reg[i], + regval[i]); +} + +/* YUV -> RGB, ITU-R BT.601, full range */ +static const struct dispc_csc_coef csc_yuv2rgb_bt601_full = { + dispc_csc_yuv2rgb_regval, + { 256, 0, 358, /* ry, rcb, rcr |1.000 0.000 1.402|*/ + 256, -88, -182, /* gy, gcb, gcr |1.000 -0.344 -0.714|*/ + 256, 452, 0, }, /* by, bcb, bcr |1.000 1.772 0.000|*/ + { 0, -2048, -2048, }, /* full range */ + { 0, 0, 0, }, + CLIP_FULL_RANGE, + "BT.601 Full", +}; + +/* YUV -> RGB, ITU-R BT.601, limited range */ +static const struct dispc_csc_coef csc_yuv2rgb_bt601_lim = { + dispc_csc_yuv2rgb_regval, + { 298, 0, 409, /* ry, rcb, rcr |1.164 0.000 1.596|*/ + 298, -100, -208, /* gy, gcb, gcr |1.164 -0.392 -0.813|*/ + 298, 516, 0, }, /* by, bcb, bcr |1.164 2.017 0.000|*/ + { -256, -2048, -2048, }, /* limited range */ + { 0, 0, 0, }, + CLIP_FULL_RANGE, + "BT.601 Limited", +}; + +/* YUV -> RGB, ITU-R BT.709, full range */ +static const struct dispc_csc_coef csc_yuv2rgb_bt709_full = { + dispc_csc_yuv2rgb_regval, + { 256, 0, 402, /* ry, rcb, rcr |1.000 0.000 1.570|*/ + 256, -48, -120, /* gy, gcb, gcr |1.000 -0.187 -0.467|*/ + 256, 475, 0, }, /* by, bcb, bcr |1.000 1.856 0.000|*/ + { 0, -2048, -2048, }, /* full range */ + { 0, 0, 0, }, + CLIP_FULL_RANGE, + "BT.709 Full", +}; + +/* YUV -> RGB, ITU-R BT.709, limited range */ +static const struct dispc_csc_coef csc_yuv2rgb_bt709_lim = { + dispc_csc_yuv2rgb_regval, + { 298, 0, 459, /* ry, rcb, rcr |1.164 0.000 1.793|*/ + 298, -55, -136, /* gy, gcb, gcr |1.164 -0.213 -0.533|*/ + 298, 541, 0, }, /* by, bcb, bcr |1.164 2.112 0.000|*/ + { -256, -2048, -2048, }, /* limited range */ + { 0, 0, 0, }, + CLIP_FULL_RANGE, + "BT.709 Limited", +}; + +static const struct { + enum drm_color_encoding encoding; + enum drm_color_range range; + const struct dispc_csc_coef *csc; +} dispc_csc_table[] = { + { DRM_COLOR_YCBCR_BT601, DRM_COLOR_YCBCR_FULL_RANGE, + &csc_yuv2rgb_bt601_full, }, + { DRM_COLOR_YCBCR_BT601, DRM_COLOR_YCBCR_LIMITED_RANGE, + &csc_yuv2rgb_bt601_lim, }, + { DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_FULL_RANGE, + &csc_yuv2rgb_bt709_full, }, + { DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE, + &csc_yuv2rgb_bt709_lim, }, +}; + +static const +struct dispc_csc_coef *dispc_find_csc(enum drm_color_encoding encoding, + enum drm_color_range range) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dispc_csc_table); i++) { + if (dispc_csc_table[i].encoding == encoding && + dispc_csc_table[i].range == range) { + return dispc_csc_table[i].csc; + } + } + return NULL; +} + +static void dispc_vid_csc_setup(struct dispc_device *dispc, u32 hw_plane, + const struct drm_plane_state *state) +{ + static const struct dispc_csc_coef *coef; + + coef = dispc_find_csc(state->color_encoding, state->color_range); + if (!coef) { + dev_err(dispc->dev, "%s: CSC (%u,%u) not found\n", + __func__, state->color_encoding, state->color_range); + return; + } + + if (dispc->feat->subrev == DISPC_K2G) + dispc_k2g_vid_write_csc(dispc, hw_plane, coef); + else + dispc_k3_vid_write_csc(dispc, hw_plane, coef); +} + +static void dispc_vid_csc_enable(struct dispc_device *dispc, u32 hw_plane, + bool enable) +{ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, !!enable, 9, 9); +} + +/* SCALER */ + +static u32 dispc_calc_fir_inc(u32 in, u32 out) +{ + return (u32)div_u64(0x200000ull * in, out); +} + +enum dispc_vid_fir_coef_set { + DISPC_VID_FIR_COEF_HORIZ, + DISPC_VID_FIR_COEF_HORIZ_UV, + DISPC_VID_FIR_COEF_VERT, + DISPC_VID_FIR_COEF_VERT_UV, +}; + +static void dispc_vid_write_fir_coefs(struct dispc_device *dispc, + u32 hw_plane, + enum dispc_vid_fir_coef_set coef_set, + const struct tidss_scale_coefs *coefs) +{ + static const u16 c0_regs[] = { + [DISPC_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H0, + [DISPC_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H0_C, + [DISPC_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V0, + [DISPC_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V0_C, + }; + + static const u16 c12_regs[] = { + [DISPC_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H12, + [DISPC_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H12_C, + [DISPC_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V12, + [DISPC_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V12_C, + }; + + const u16 c0_base = c0_regs[coef_set]; + const u16 c12_base = c12_regs[coef_set]; + int phase; + + if (!coefs) { + dev_err(dispc->dev, "%s: No coefficients given.\n", __func__); + return; + } + + for (phase = 0; phase <= 8; ++phase) { + u16 reg = c0_base + phase * 4; + u16 c0 = coefs->c0[phase]; + + dispc_vid_write(dispc, hw_plane, reg, c0); + } + + for (phase = 0; phase <= 15; ++phase) { + u16 reg = c12_base + phase * 4; + s16 c1, c2; + u32 c12; + + c1 = coefs->c1[phase]; + c2 = coefs->c2[phase]; + c12 = FLD_VAL(c1, 19, 10) | FLD_VAL(c2, 29, 20); + + dispc_vid_write(dispc, hw_plane, reg, c12); + } +} + +static bool dispc_fourcc_is_yuv(u32 fourcc) +{ + switch (fourcc) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_NV12: + return true; + default: + return false; + } +} + +struct dispc_scaling_params { + int xinc, yinc; + u32 in_w, in_h, in_w_uv, in_h_uv; + u32 fir_xinc, fir_yinc, fir_xinc_uv, fir_yinc_uv; + bool scale_x, scale_y; + const struct tidss_scale_coefs *xcoef, *ycoef, *xcoef_uv, *ycoef_uv; + bool five_taps; +}; + +static int dispc_vid_calc_scaling(struct dispc_device *dispc, + const struct drm_plane_state *state, + struct dispc_scaling_params *sp, + bool lite_plane) +{ + const struct dispc_features_scaling *f = &dispc->feat->scaling; + u32 fourcc = state->fb->format->format; + u32 in_width_max_5tap = f->in_width_max_5tap_rgb; + u32 in_width_max_3tap = f->in_width_max_3tap_rgb; + u32 downscale_limit; + u32 in_width_max; + + memset(sp, 0, sizeof(*sp)); + sp->xinc = 1; + sp->yinc = 1; + sp->in_w = state->src_w >> 16; + sp->in_w_uv = sp->in_w; + sp->in_h = state->src_h >> 16; + sp->in_h_uv = sp->in_h; + + sp->scale_x = sp->in_w != state->crtc_w; + sp->scale_y = sp->in_h != state->crtc_h; + + if (dispc_fourcc_is_yuv(fourcc)) { + in_width_max_5tap = f->in_width_max_5tap_yuv; + in_width_max_3tap = f->in_width_max_3tap_yuv; + + sp->in_w_uv >>= 1; + sp->scale_x = true; + + if (fourcc == DRM_FORMAT_NV12) { + sp->in_h_uv >>= 1; + sp->scale_y = true; + } + } + + /* Skip the rest if no scaling is used */ + if ((!sp->scale_x && !sp->scale_y) || lite_plane) + return 0; + + if (sp->in_w > in_width_max_5tap) { + sp->five_taps = false; + in_width_max = in_width_max_3tap; + downscale_limit = f->downscale_limit_3tap; + } else { + sp->five_taps = true; + in_width_max = in_width_max_5tap; + downscale_limit = f->downscale_limit_5tap; + } + + if (sp->scale_x) { + sp->fir_xinc = dispc_calc_fir_inc(sp->in_w, state->crtc_w); + + if (sp->fir_xinc < dispc_calc_fir_inc(1, f->upscale_limit)) { + dev_dbg(dispc->dev, + "%s: X-scaling factor %u/%u > %u\n", + __func__, state->crtc_w, state->src_w >> 16, + f->upscale_limit); + return -EINVAL; + } + + if (sp->fir_xinc >= dispc_calc_fir_inc(downscale_limit, 1)) { + sp->xinc = DIV_ROUND_UP(DIV_ROUND_UP(sp->in_w, + state->crtc_w), + downscale_limit); + + if (sp->xinc > f->xinc_max) { + dev_dbg(dispc->dev, + "%s: X-scaling factor %u/%u < 1/%u\n", + __func__, state->crtc_w, + state->src_w >> 16, + downscale_limit * f->xinc_max); + return -EINVAL; + } + + sp->in_w = (state->src_w >> 16) / sp->xinc; + } + + while (sp->in_w > in_width_max) { + sp->xinc++; + sp->in_w = (state->src_w >> 16) / sp->xinc; + } + + if (sp->xinc > f->xinc_max) { + dev_dbg(dispc->dev, + "%s: Too wide input bufer %u > %u\n", __func__, + state->src_w >> 16, in_width_max * f->xinc_max); + return -EINVAL; + } + + /* + * We need even line length for YUV formats. Decimation + * can lead to odd length, so we need to make it even + * again. + */ + if (dispc_fourcc_is_yuv(fourcc)) + sp->in_w &= ~1; + + sp->fir_xinc = dispc_calc_fir_inc(sp->in_w, state->crtc_w); + } + + if (sp->scale_y) { + sp->fir_yinc = dispc_calc_fir_inc(sp->in_h, state->crtc_h); + + if (sp->fir_yinc < dispc_calc_fir_inc(1, f->upscale_limit)) { + dev_dbg(dispc->dev, + "%s: Y-scaling factor %u/%u > %u\n", + __func__, state->crtc_h, state->src_h >> 16, + f->upscale_limit); + return -EINVAL; + } + + if (sp->fir_yinc >= dispc_calc_fir_inc(downscale_limit, 1)) { + sp->yinc = DIV_ROUND_UP(DIV_ROUND_UP(sp->in_h, + state->crtc_h), + downscale_limit); + + sp->in_h /= sp->yinc; + sp->fir_yinc = dispc_calc_fir_inc(sp->in_h, + state->crtc_h); + } + } + + dev_dbg(dispc->dev, + "%s: %ux%u decim %ux%u -> %ux%u firinc %u.%03ux%u.%03u taps %u -> %ux%u\n", + __func__, state->src_w >> 16, state->src_h >> 16, + sp->xinc, sp->yinc, sp->in_w, sp->in_h, + sp->fir_xinc / 0x200000u, + ((sp->fir_xinc & 0x1FFFFFu) * 999u) / 0x1FFFFFu, + sp->fir_yinc / 0x200000u, + ((sp->fir_yinc & 0x1FFFFFu) * 999u) / 0x1FFFFFu, + sp->five_taps ? 5 : 3, + state->crtc_w, state->crtc_h); + + if (dispc_fourcc_is_yuv(fourcc)) { + if (sp->scale_x) { + sp->in_w_uv /= sp->xinc; + sp->fir_xinc_uv = dispc_calc_fir_inc(sp->in_w_uv, + state->crtc_w); + sp->xcoef_uv = tidss_get_scale_coefs(dispc->dev, + sp->fir_xinc_uv, + true); + } + if (sp->scale_y) { + sp->in_h_uv /= sp->yinc; + sp->fir_yinc_uv = dispc_calc_fir_inc(sp->in_h_uv, + state->crtc_h); + sp->ycoef_uv = tidss_get_scale_coefs(dispc->dev, + sp->fir_yinc_uv, + sp->five_taps); + } + } + + if (sp->scale_x) + sp->xcoef = tidss_get_scale_coefs(dispc->dev, sp->fir_xinc, + true); + + if (sp->scale_y) + sp->ycoef = tidss_get_scale_coefs(dispc->dev, sp->fir_yinc, + sp->five_taps); + + return 0; +} + +static void dispc_vid_set_scaling(struct dispc_device *dispc, + u32 hw_plane, + struct dispc_scaling_params *sp, + u32 fourcc) +{ + /* HORIZONTAL RESIZE ENABLE */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + sp->scale_x, 7, 7); + + /* VERTICAL RESIZE ENABLE */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + sp->scale_y, 8, 8); + + /* Skip the rest if no scaling is used */ + if (!sp->scale_x && !sp->scale_y) + return; + + /* VERTICAL 5-TAPS */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + sp->five_taps, 21, 21); + + if (dispc_fourcc_is_yuv(fourcc)) { + if (sp->scale_x) { + dispc_vid_write(dispc, hw_plane, DISPC_VID_FIRH2, + sp->fir_xinc_uv); + dispc_vid_write_fir_coefs(dispc, hw_plane, + DISPC_VID_FIR_COEF_HORIZ_UV, + sp->xcoef_uv); + } + if (sp->scale_y) { + dispc_vid_write(dispc, hw_plane, DISPC_VID_FIRV2, + sp->fir_yinc_uv); + dispc_vid_write_fir_coefs(dispc, hw_plane, + DISPC_VID_FIR_COEF_VERT_UV, + sp->ycoef_uv); + } + } + + if (sp->scale_x) { + dispc_vid_write(dispc, hw_plane, DISPC_VID_FIRH, sp->fir_xinc); + dispc_vid_write_fir_coefs(dispc, hw_plane, + DISPC_VID_FIR_COEF_HORIZ, + sp->xcoef); + } + + if (sp->scale_y) { + dispc_vid_write(dispc, hw_plane, DISPC_VID_FIRV, sp->fir_yinc); + dispc_vid_write_fir_coefs(dispc, hw_plane, + DISPC_VID_FIR_COEF_VERT, sp->ycoef); + } +} + +/* OTHER */ + +static const struct { + u32 fourcc; + u8 dss_code; +} dispc_color_formats[] = { + { DRM_FORMAT_ARGB4444, 0x0, }, + { DRM_FORMAT_ABGR4444, 0x1, }, + { DRM_FORMAT_RGBA4444, 0x2, }, + + { DRM_FORMAT_RGB565, 0x3, }, + { DRM_FORMAT_BGR565, 0x4, }, + + { DRM_FORMAT_ARGB1555, 0x5, }, + { DRM_FORMAT_ABGR1555, 0x6, }, + + { DRM_FORMAT_ARGB8888, 0x7, }, + { DRM_FORMAT_ABGR8888, 0x8, }, + { DRM_FORMAT_RGBA8888, 0x9, }, + { DRM_FORMAT_BGRA8888, 0xa, }, + + { DRM_FORMAT_RGB888, 0xb, }, + { DRM_FORMAT_BGR888, 0xc, }, + + { DRM_FORMAT_ARGB2101010, 0xe, }, + { DRM_FORMAT_ABGR2101010, 0xf, }, + + { DRM_FORMAT_XRGB4444, 0x20, }, + { DRM_FORMAT_XBGR4444, 0x21, }, + { DRM_FORMAT_RGBX4444, 0x22, }, + + { DRM_FORMAT_ARGB1555, 0x25, }, + { DRM_FORMAT_ABGR1555, 0x26, }, + + { DRM_FORMAT_XRGB8888, 0x27, }, + { DRM_FORMAT_XBGR8888, 0x28, }, + { DRM_FORMAT_RGBX8888, 0x29, }, + { DRM_FORMAT_BGRX8888, 0x2a, }, + + { DRM_FORMAT_XRGB2101010, 0x2e, }, + { DRM_FORMAT_XBGR2101010, 0x2f, }, + + { DRM_FORMAT_YUYV, 0x3e, }, + { DRM_FORMAT_UYVY, 0x3f, }, + + { DRM_FORMAT_NV12, 0x3d, }, +}; + +static void dispc_plane_set_pixel_format(struct dispc_device *dispc, + u32 hw_plane, u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dispc_color_formats); ++i) { + if (dispc_color_formats[i].fourcc == fourcc) { + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + dispc_color_formats[i].dss_code, + 6, 1); + return; + } + } + + WARN_ON(1); +} + +const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len) +{ + WARN_ON(!dispc->fourccs); + + *len = dispc->num_fourccs; + + return dispc->fourccs; +} + +static s32 pixinc(int pixels, u8 ps) +{ + if (pixels == 1) + return 1; + else if (pixels > 1) + return 1 + (pixels - 1) * ps; + else if (pixels < 0) + return 1 - (-pixels + 1) * ps; + + WARN_ON(1); + return 0; +} + +int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane, + const struct drm_plane_state *state, + u32 hw_videoport) +{ + bool lite = dispc->feat->vid_lite[hw_plane]; + u32 fourcc = state->fb->format->format; + bool need_scaling = state->src_w >> 16 != state->crtc_w || + state->src_h >> 16 != state->crtc_h; + struct dispc_scaling_params scaling; + int ret; + + if (dispc_fourcc_is_yuv(fourcc)) { + if (!dispc_find_csc(state->color_encoding, + state->color_range)) { + dev_dbg(dispc->dev, + "%s: Unsupported CSC (%u,%u) for HW plane %u\n", + __func__, state->color_encoding, + state->color_range, hw_plane); + return -EINVAL; + } + } + + if (need_scaling) { + if (lite) { + dev_dbg(dispc->dev, + "%s: Lite plane %u can't scale %ux%u!=%ux%u\n", + __func__, hw_plane, + state->src_w >> 16, state->src_h >> 16, + state->crtc_w, state->crtc_h); + return -EINVAL; + } + ret = dispc_vid_calc_scaling(dispc, state, &scaling, false); + if (ret) + return ret; + } + + return 0; +} + +static +dma_addr_t dispc_plane_state_paddr(const struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *gem; + u32 x = state->src_x >> 16; + u32 y = state->src_y >> 16; + + gem = drm_fb_cma_get_gem_obj(state->fb, 0); + + return gem->paddr + fb->offsets[0] + x * fb->format->cpp[0] + + y * fb->pitches[0]; +} + +static +dma_addr_t dispc_plane_state_p_uv_addr(const struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *gem; + u32 x = state->src_x >> 16; + u32 y = state->src_y >> 16; + + if (WARN_ON(state->fb->format->num_planes != 2)) + return 0; + + gem = drm_fb_cma_get_gem_obj(fb, 1); + + return gem->paddr + fb->offsets[1] + + (x * fb->format->cpp[1] / fb->format->hsub) + + (y * fb->pitches[1] / fb->format->vsub); +} + +int dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane, + const struct drm_plane_state *state, + u32 hw_videoport) +{ + bool lite = dispc->feat->vid_lite[hw_plane]; + u32 fourcc = state->fb->format->format; + u16 cpp = state->fb->format->cpp[0]; + u32 fb_width = state->fb->pitches[0] / cpp; + dma_addr_t paddr = dispc_plane_state_paddr(state); + struct dispc_scaling_params scale; + + dispc_vid_calc_scaling(dispc, state, &scale, lite); + + dispc_plane_set_pixel_format(dispc, hw_plane, fourcc); + + dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_0, paddr & 0xffffffff); + dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_EXT_0, (u64)paddr >> 32); + dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_1, paddr & 0xffffffff); + dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_EXT_1, (u64)paddr >> 32); + + dispc_vid_write(dispc, hw_plane, DISPC_VID_PICTURE_SIZE, + (scale.in_w - 1) | ((scale.in_h - 1) << 16)); + + /* For YUV422 format we use the macropixel size for pixel inc */ + if (fourcc == DRM_FORMAT_YUYV || fourcc == DRM_FORMAT_UYVY) + dispc_vid_write(dispc, hw_plane, DISPC_VID_PIXEL_INC, + pixinc(scale.xinc, cpp * 2)); + else + dispc_vid_write(dispc, hw_plane, DISPC_VID_PIXEL_INC, + pixinc(scale.xinc, cpp)); + + dispc_vid_write(dispc, hw_plane, DISPC_VID_ROW_INC, + pixinc(1 + (scale.yinc * fb_width - + scale.xinc * scale.in_w), + cpp)); + + if (state->fb->format->num_planes == 2) { + u16 cpp_uv = state->fb->format->cpp[1]; + u32 fb_width_uv = state->fb->pitches[1] / cpp_uv; + dma_addr_t p_uv_addr = dispc_plane_state_p_uv_addr(state); + + dispc_vid_write(dispc, hw_plane, + DISPC_VID_BA_UV_0, p_uv_addr & 0xffffffff); + dispc_vid_write(dispc, hw_plane, + DISPC_VID_BA_UV_EXT_0, (u64)p_uv_addr >> 32); + dispc_vid_write(dispc, hw_plane, + DISPC_VID_BA_UV_1, p_uv_addr & 0xffffffff); + dispc_vid_write(dispc, hw_plane, + DISPC_VID_BA_UV_EXT_1, (u64)p_uv_addr >> 32); + + dispc_vid_write(dispc, hw_plane, DISPC_VID_ROW_INC_UV, + pixinc(1 + (scale.yinc * fb_width_uv - + scale.xinc * scale.in_w_uv), + cpp_uv)); + } + + if (!lite) { + dispc_vid_write(dispc, hw_plane, DISPC_VID_SIZE, + (state->crtc_w - 1) | + ((state->crtc_h - 1) << 16)); + + dispc_vid_set_scaling(dispc, hw_plane, &scale, fourcc); + } + + /* enable YUV->RGB color conversion */ + if (dispc_fourcc_is_yuv(fourcc)) { + dispc_vid_csc_setup(dispc, hw_plane, state); + dispc_vid_csc_enable(dispc, hw_plane, true); + } else { + dispc_vid_csc_enable(dispc, hw_plane, false); + } + + dispc_vid_write(dispc, hw_plane, DISPC_VID_GLOBAL_ALPHA, + 0xFF & (state->alpha >> 8)); + + if (state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 1, + 28, 28); + else + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 0, + 28, 28); + + dispc_ovr_set_plane(dispc, hw_plane, hw_videoport, + state->crtc_x, state->crtc_y, + state->normalized_zpos); + + dispc->plane_data[hw_plane].zorder = state->normalized_zpos; + dispc->plane_data[hw_plane].hw_videoport = hw_videoport; + + return 0; +} + +int dispc_plane_enable(struct dispc_device *dispc, u32 hw_plane, bool enable) +{ + dispc_ovr_enable_plane(dispc, dispc->plane_data[hw_plane].hw_videoport, + dispc->plane_data[hw_plane].zorder, enable); + + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, !!enable, 0, 0); + + return 0; +} + +static u32 dispc_vid_get_fifo_size(struct dispc_device *dispc, u32 hw_plane) +{ + return VID_REG_GET(dispc, hw_plane, DISPC_VID_BUF_SIZE_STATUS, 15, 0); +} + +static void dispc_vid_set_mflag_threshold(struct dispc_device *dispc, + u32 hw_plane, u32 low, u32 high) +{ + dispc_vid_write(dispc, hw_plane, DISPC_VID_MFLAG_THRESHOLD, + FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); +} + +static void dispc_vid_set_buf_threshold(struct dispc_device *dispc, + u32 hw_plane, u32 low, u32 high) +{ + dispc_vid_write(dispc, hw_plane, DISPC_VID_BUF_THRESHOLD, + FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); +} + +static void dispc_k2g_plane_init(struct dispc_device *dispc) +{ + unsigned int hw_plane; + + dev_dbg(dispc->dev, "%s()\n", __func__); + + /* MFLAG_CTRL = ENABLED */ + REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 2, 1, 0); + /* MFLAG_START = MFLAGNORMALSTARTMODE */ + REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); + + for (hw_plane = 0; hw_plane < dispc->feat->num_planes; hw_plane++) { + u32 size = dispc_vid_get_fifo_size(dispc, hw_plane); + u32 thr_low, thr_high; + u32 mflag_low, mflag_high; + u32 preload; + + thr_high = size - 1; + thr_low = size / 2; + + mflag_high = size * 2 / 3; + mflag_low = size / 3; + + preload = thr_low; + + dev_dbg(dispc->dev, + "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", + dispc->feat->vid_name[hw_plane], + size, + thr_high, thr_low, + mflag_high, mflag_low, + preload); + + dispc_vid_set_buf_threshold(dispc, hw_plane, + thr_low, thr_high); + dispc_vid_set_mflag_threshold(dispc, hw_plane, + mflag_low, mflag_high); + + dispc_vid_write(dispc, hw_plane, DISPC_VID_PRELOAD, preload); + + /* + * Prefetch up to fifo high-threshold value to minimize the + * possibility of underflows. Note that this means the PRELOAD + * register is ignored. + */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 1, + 19, 19); + } +} + +static void dispc_k3_plane_init(struct dispc_device *dispc) +{ + unsigned int hw_plane; + u32 cba_lo_pri = 1; + u32 cba_hi_pri = 0; + + dev_dbg(dispc->dev, "%s()\n", __func__); + + REG_FLD_MOD(dispc, DSS_CBA_CFG, cba_lo_pri, 2, 0); + REG_FLD_MOD(dispc, DSS_CBA_CFG, cba_hi_pri, 5, 3); + + /* MFLAG_CTRL = ENABLED */ + REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 2, 1, 0); + /* MFLAG_START = MFLAGNORMALSTARTMODE */ + REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); + + for (hw_plane = 0; hw_plane < dispc->feat->num_planes; hw_plane++) { + u32 size = dispc_vid_get_fifo_size(dispc, hw_plane); + u32 thr_low, thr_high; + u32 mflag_low, mflag_high; + u32 preload; + + thr_high = size - 1; + thr_low = size / 2; + + mflag_high = size * 2 / 3; + mflag_low = size / 3; + + preload = thr_low; + + dev_dbg(dispc->dev, + "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", + dispc->feat->vid_name[hw_plane], + size, + thr_high, thr_low, + mflag_high, mflag_low, + preload); + + dispc_vid_set_buf_threshold(dispc, hw_plane, + thr_low, thr_high); + dispc_vid_set_mflag_threshold(dispc, hw_plane, + mflag_low, mflag_high); + + dispc_vid_write(dispc, hw_plane, DISPC_VID_PRELOAD, preload); + + /* Prefech up to PRELOAD value */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 0, + 19, 19); + } +} + +static void dispc_plane_init(struct dispc_device *dispc) +{ + switch (dispc->feat->subrev) { + case DISPC_K2G: + dispc_k2g_plane_init(dispc); + break; + case DISPC_AM65X: + case DISPC_J721E: + dispc_k3_plane_init(dispc); + break; + default: + WARN_ON(1); + } +} + +static void dispc_vp_init(struct dispc_device *dispc) +{ + unsigned int i; + + dev_dbg(dispc->dev, "%s()\n", __func__); + + /* Enable the gamma Shadow bit-field for all VPs*/ + for (i = 0; i < dispc->feat->num_vps; i++) + VP_REG_FLD_MOD(dispc, i, DISPC_VP_CONFIG, 1, 2, 2); +} + +static void dispc_initial_config(struct dispc_device *dispc) +{ + dispc_plane_init(dispc); + dispc_vp_init(dispc); + + /* Note: Hardcoded DPI routing on J721E for now */ + if (dispc->feat->subrev == DISPC_J721E) { + dispc_write(dispc, DISPC_CONNECTIONS, + FLD_VAL(2, 3, 0) | /* VP1 to DPI0 */ + FLD_VAL(8, 7, 4) /* VP3 to DPI1 */ + ); + } +} + +static void dispc_k2g_vp_write_gamma_table(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 *table = dispc->vp_data[hw_videoport].gamma_table; + u32 hwlen = dispc->feat->vp_feat.color.gamma_size; + unsigned int i; + + dev_dbg(dispc->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); + + if (WARN_ON(dispc->feat->vp_feat.color.gamma_type != TIDSS_GAMMA_8BIT)) + return; + + for (i = 0; i < hwlen; ++i) { + u32 v = table[i]; + + v |= i << 24; + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_K2G_GAMMA_TABLE, + v); + } +} + +static void dispc_am65x_vp_write_gamma_table(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 *table = dispc->vp_data[hw_videoport].gamma_table; + u32 hwlen = dispc->feat->vp_feat.color.gamma_size; + unsigned int i; + + dev_dbg(dispc->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); + + if (WARN_ON(dispc->feat->vp_feat.color.gamma_type != TIDSS_GAMMA_8BIT)) + return; + + for (i = 0; i < hwlen; ++i) { + u32 v = table[i]; + + v |= i << 24; + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_GAMMA_TABLE, v); + } +} + +static void dispc_j721e_vp_write_gamma_table(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 *table = dispc->vp_data[hw_videoport].gamma_table; + u32 hwlen = dispc->feat->vp_feat.color.gamma_size; + unsigned int i; + + dev_dbg(dispc->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); + + if (WARN_ON(dispc->feat->vp_feat.color.gamma_type != TIDSS_GAMMA_10BIT)) + return; + + for (i = 0; i < hwlen; ++i) { + u32 v = table[i]; + + if (i == 0) + v |= 1 << 31; + + dispc_vp_write(dispc, hw_videoport, DISPC_VP_GAMMA_TABLE, v); + } +} + +static void dispc_vp_write_gamma_table(struct dispc_device *dispc, + u32 hw_videoport) +{ + switch (dispc->feat->subrev) { + case DISPC_K2G: + dispc_k2g_vp_write_gamma_table(dispc, hw_videoport); + break; + case DISPC_AM65X: + dispc_am65x_vp_write_gamma_table(dispc, hw_videoport); + break; + case DISPC_J721E: + dispc_j721e_vp_write_gamma_table(dispc, hw_videoport); + break; + default: + WARN_ON(1); + break; + } +} + +static const struct drm_color_lut dispc_vp_gamma_default_lut[] = { + { .red = 0, .green = 0, .blue = 0, }, + { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, +}; + +static void dispc_vp_set_gamma(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_color_lut *lut, + unsigned int length) +{ + u32 *table = dispc->vp_data[hw_videoport].gamma_table; + u32 hwlen = dispc->feat->vp_feat.color.gamma_size; + u32 hwbits; + unsigned int i; + + dev_dbg(dispc->dev, "%s: hw_videoport %d, lut len %u, hw len %u\n", + __func__, hw_videoport, length, hwlen); + + if (dispc->feat->vp_feat.color.gamma_type == TIDSS_GAMMA_10BIT) + hwbits = 10; + else + hwbits = 8; + + if (!lut || length < 2) { + lut = dispc_vp_gamma_default_lut; + length = ARRAY_SIZE(dispc_vp_gamma_default_lut); + } + + for (i = 0; i < length - 1; ++i) { + unsigned int first = i * (hwlen - 1) / (length - 1); + unsigned int last = (i + 1) * (hwlen - 1) / (length - 1); + unsigned int w = last - first; + u16 r, g, b; + unsigned int j; + + if (w == 0) + continue; + + for (j = 0; j <= w; j++) { + r = (lut[i].red * (w - j) + lut[i + 1].red * j) / w; + g = (lut[i].green * (w - j) + lut[i + 1].green * j) / w; + b = (lut[i].blue * (w - j) + lut[i + 1].blue * j) / w; + + r >>= 16 - hwbits; + g >>= 16 - hwbits; + b >>= 16 - hwbits; + + table[first + j] = (r << (hwbits * 2)) | + (g << hwbits) | b; + } + } + + dispc_vp_write_gamma_table(dispc, hw_videoport); +} + +static s16 dispc_S31_32_to_s2_8(s64 coef) +{ + u64 sign_bit = 1ULL << 63; + u64 cbits = (u64)coef; + s16 ret; + + if (cbits & sign_bit) + ret = -clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x200); + else + ret = clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x1FF); + + return ret; +} + +static void dispc_k2g_cpr_from_ctm(const struct drm_color_ctm *ctm, + struct dispc_csc_coef *cpr) +{ + memset(cpr, 0, sizeof(*cpr)); + + cpr->to_regval = dispc_csc_cpr_regval; + cpr->m[CSC_RR] = dispc_S31_32_to_s2_8(ctm->matrix[0]); + cpr->m[CSC_RG] = dispc_S31_32_to_s2_8(ctm->matrix[1]); + cpr->m[CSC_RB] = dispc_S31_32_to_s2_8(ctm->matrix[2]); + cpr->m[CSC_GR] = dispc_S31_32_to_s2_8(ctm->matrix[3]); + cpr->m[CSC_GG] = dispc_S31_32_to_s2_8(ctm->matrix[4]); + cpr->m[CSC_GB] = dispc_S31_32_to_s2_8(ctm->matrix[5]); + cpr->m[CSC_BR] = dispc_S31_32_to_s2_8(ctm->matrix[6]); + cpr->m[CSC_BG] = dispc_S31_32_to_s2_8(ctm->matrix[7]); + cpr->m[CSC_BB] = dispc_S31_32_to_s2_8(ctm->matrix[8]); +} + +#define CVAL(xR, xG, xB) (FLD_VAL(xR, 9, 0) | FLD_VAL(xG, 20, 11) | \ + FLD_VAL(xB, 31, 22)) + +static void dispc_k2g_vp_csc_cpr_regval(const struct dispc_csc_coef *csc, + u32 *regval) +{ + regval[0] = CVAL(csc->m[CSC_BB], csc->m[CSC_BG], csc->m[CSC_BR]); + regval[1] = CVAL(csc->m[CSC_GB], csc->m[CSC_GG], csc->m[CSC_GR]); + regval[2] = CVAL(csc->m[CSC_RB], csc->m[CSC_RG], csc->m[CSC_RR]); +} + +#undef CVAL + +static void dispc_k2g_vp_write_csc(struct dispc_device *dispc, u32 hw_videoport, + const struct dispc_csc_coef *csc) +{ + static const u16 dispc_vp_cpr_coef_reg[] = { + DISPC_VP_CSC_COEF0, DISPC_VP_CSC_COEF1, DISPC_VP_CSC_COEF2, + /* K2G CPR is packed to three registers. */ + }; + u32 regval[DISPC_CSC_REGVAL_LEN]; + unsigned int i; + + dispc_k2g_vp_csc_cpr_regval(csc, regval); + + for (i = 0; i < ARRAY_SIZE(dispc_vp_cpr_coef_reg); i++) + dispc_vp_write(dispc, hw_videoport, dispc_vp_cpr_coef_reg[i], + regval[i]); +} + +static void dispc_k2g_vp_set_ctm(struct dispc_device *dispc, u32 hw_videoport, + struct drm_color_ctm *ctm) +{ + u32 cprenable = 0; + + if (ctm) { + struct dispc_csc_coef cpr; + + dispc_k2g_cpr_from_ctm(ctm, &cpr); + dispc_k2g_vp_write_csc(dispc, hw_videoport, &cpr); + cprenable = 1; + } + + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONFIG, + cprenable, 15, 15); +} + +static s16 dispc_S31_32_to_s3_8(s64 coef) +{ + u64 sign_bit = 1ULL << 63; + u64 cbits = (u64)coef; + s16 ret; + + if (cbits & sign_bit) + ret = -clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x400); + else + ret = clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x3FF); + + return ret; +} + +static void dispc_csc_from_ctm(const struct drm_color_ctm *ctm, + struct dispc_csc_coef *cpr) +{ + memset(cpr, 0, sizeof(*cpr)); + + cpr->to_regval = dispc_csc_cpr_regval; + cpr->m[CSC_RR] = dispc_S31_32_to_s3_8(ctm->matrix[0]); + cpr->m[CSC_RG] = dispc_S31_32_to_s3_8(ctm->matrix[1]); + cpr->m[CSC_RB] = dispc_S31_32_to_s3_8(ctm->matrix[2]); + cpr->m[CSC_GR] = dispc_S31_32_to_s3_8(ctm->matrix[3]); + cpr->m[CSC_GG] = dispc_S31_32_to_s3_8(ctm->matrix[4]); + cpr->m[CSC_GB] = dispc_S31_32_to_s3_8(ctm->matrix[5]); + cpr->m[CSC_BR] = dispc_S31_32_to_s3_8(ctm->matrix[6]); + cpr->m[CSC_BG] = dispc_S31_32_to_s3_8(ctm->matrix[7]); + cpr->m[CSC_BB] = dispc_S31_32_to_s3_8(ctm->matrix[8]); +} + +static void dispc_k3_vp_write_csc(struct dispc_device *dispc, u32 hw_videoport, + const struct dispc_csc_coef *csc) +{ + static const u16 dispc_vp_csc_coef_reg[DISPC_CSC_REGVAL_LEN] = { + DISPC_VP_CSC_COEF0, DISPC_VP_CSC_COEF1, DISPC_VP_CSC_COEF2, + DISPC_VP_CSC_COEF3, DISPC_VP_CSC_COEF4, DISPC_VP_CSC_COEF5, + DISPC_VP_CSC_COEF6, DISPC_VP_CSC_COEF7, + }; + u32 regval[DISPC_CSC_REGVAL_LEN]; + unsigned int i; + + csc->to_regval(csc, regval); + + for (i = 0; i < ARRAY_SIZE(regval); i++) + dispc_vp_write(dispc, hw_videoport, dispc_vp_csc_coef_reg[i], + regval[i]); +} + +static void dispc_k3_vp_set_ctm(struct dispc_device *dispc, u32 hw_videoport, + struct drm_color_ctm *ctm) +{ + u32 colorconvenable = 0; + + if (ctm) { + struct dispc_csc_coef csc; + + dispc_csc_from_ctm(ctm, &csc); + dispc_k3_vp_write_csc(dispc, hw_videoport, &csc); + colorconvenable = 1; + } + + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONFIG, + colorconvenable, 24, 24); +} + +static void dispc_vp_set_color_mgmt(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_crtc_state *state, + bool newmodeset) +{ + struct drm_color_lut *lut = NULL; + struct drm_color_ctm *ctm = NULL; + unsigned int length = 0; + + if (!(state->color_mgmt_changed || newmodeset)) + return; + + if (state->gamma_lut) { + lut = (struct drm_color_lut *)state->gamma_lut->data; + length = state->gamma_lut->length / sizeof(*lut); + } + + dispc_vp_set_gamma(dispc, hw_videoport, lut, length); + + if (state->ctm) + ctm = (struct drm_color_ctm *)state->ctm->data; + + if (dispc->feat->subrev == DISPC_K2G) + dispc_k2g_vp_set_ctm(dispc, hw_videoport, ctm); + else + dispc_k3_vp_set_ctm(dispc, hw_videoport, ctm); +} + +void dispc_vp_setup(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_crtc_state *state, bool newmodeset) +{ + dispc_vp_set_default_color(dispc, hw_videoport, 0); + dispc_vp_set_color_mgmt(dispc, hw_videoport, state, newmodeset); +} + +int dispc_runtime_suspend(struct dispc_device *dispc) +{ + dev_dbg(dispc->dev, "suspend\n"); + + dispc->is_enabled = false; + + clk_disable_unprepare(dispc->fclk); + + return 0; +} + +int dispc_runtime_resume(struct dispc_device *dispc) +{ + dev_dbg(dispc->dev, "resume\n"); + + clk_prepare_enable(dispc->fclk); + + if (REG_GET(dispc, DSS_SYSSTATUS, 0, 0) == 0) + dev_warn(dispc->dev, "DSS FUNC RESET not done!\n"); + + dev_dbg(dispc->dev, "OMAP DSS7 rev 0x%x\n", + dispc_read(dispc, DSS_REVISION)); + + dev_dbg(dispc->dev, "VP RESETDONE %d,%d,%d\n", + REG_GET(dispc, DSS_SYSSTATUS, 1, 1), + REG_GET(dispc, DSS_SYSSTATUS, 2, 2), + REG_GET(dispc, DSS_SYSSTATUS, 3, 3)); + + if (dispc->feat->subrev == DISPC_AM65X) + dev_dbg(dispc->dev, "OLDI RESETDONE %d,%d,%d\n", + REG_GET(dispc, DSS_SYSSTATUS, 5, 5), + REG_GET(dispc, DSS_SYSSTATUS, 6, 6), + REG_GET(dispc, DSS_SYSSTATUS, 7, 7)); + + dev_dbg(dispc->dev, "DISPC IDLE %d\n", + REG_GET(dispc, DSS_SYSSTATUS, 9, 9)); + + dispc_initial_config(dispc); + + dispc->is_enabled = true; + + tidss_irq_resume(dispc->tidss); + + return 0; +} + +void dispc_remove(struct tidss_device *tidss) +{ + dev_dbg(tidss->dev, "%s\n", __func__); + + tidss->dispc = NULL; +} + +static int dispc_iomap_resource(struct platform_device *pdev, const char *name, + void __iomem **base) +{ + struct resource *res; + void __iomem *b; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) { + dev_err(&pdev->dev, "cannot get mem resource '%s'\n", name); + return -EINVAL; + } + + b = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(b)) { + dev_err(&pdev->dev, "cannot ioremap resource '%s'\n", name); + return PTR_ERR(b); + } + + *base = b; + + return 0; +} + +static int dispc_init_am65x_oldi_io_ctrl(struct device *dev, + struct dispc_device *dispc) +{ + dispc->oldi_io_ctrl = + syscon_regmap_lookup_by_phandle(dev->of_node, + "ti,am65x-oldi-io-ctrl"); + if (PTR_ERR(dispc->oldi_io_ctrl) == -ENODEV) { + dispc->oldi_io_ctrl = NULL; + } else if (IS_ERR(dispc->oldi_io_ctrl)) { + dev_err(dev, "%s: syscon_regmap_lookup_by_phandle failed %ld\n", + __func__, PTR_ERR(dispc->oldi_io_ctrl)); + return PTR_ERR(dispc->oldi_io_ctrl); + } + return 0; +} + +int dispc_init(struct tidss_device *tidss) +{ + struct device *dev = tidss->dev; + struct platform_device *pdev = to_platform_device(dev); + struct dispc_device *dispc; + const struct dispc_features *feat; + unsigned int i, num_fourccs; + int r = 0; + + dev_dbg(dev, "%s\n", __func__); + + feat = tidss->feat; + + if (feat->subrev != DISPC_K2G) { + r = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + if (r) + dev_warn(dev, "cannot set DMA masks to 48-bit\n"); + } + + dispc = devm_kzalloc(dev, sizeof(*dispc), GFP_KERNEL); + if (!dispc) + return -ENOMEM; + + dispc->fourccs = devm_kcalloc(dev, ARRAY_SIZE(dispc_color_formats), + sizeof(*dispc->fourccs), GFP_KERNEL); + if (!dispc->fourccs) + return -ENOMEM; + + num_fourccs = 0; + for (i = 0; i < ARRAY_SIZE(dispc_color_formats); ++i) { + if (feat->errata.i2000 && + dispc_fourcc_is_yuv(dispc_color_formats[i].fourcc)) + continue; + dispc->fourccs[num_fourccs++] = dispc_color_formats[i].fourcc; + } + dispc->num_fourccs = num_fourccs; + dispc->tidss = tidss; + dispc->dev = dev; + dispc->feat = feat; + + dispc_common_regmap = dispc->feat->common_regs; + + r = dispc_iomap_resource(pdev, dispc->feat->common, + &dispc->base_common); + if (r) + return r; + + for (i = 0; i < dispc->feat->num_planes; i++) { + r = dispc_iomap_resource(pdev, dispc->feat->vid_name[i], + &dispc->base_vid[i]); + if (r) + return r; + } + + for (i = 0; i < dispc->feat->num_vps; i++) { + u32 gamma_size = dispc->feat->vp_feat.color.gamma_size; + u32 *gamma_table; + struct clk *clk; + + r = dispc_iomap_resource(pdev, dispc->feat->ovr_name[i], + &dispc->base_ovr[i]); + if (r) + return r; + + r = dispc_iomap_resource(pdev, dispc->feat->vp_name[i], + &dispc->base_vp[i]); + if (r) + return r; + + clk = devm_clk_get(dev, dispc->feat->vpclk_name[i]); + if (IS_ERR(clk)) { + dev_err(dev, "%s: Failed to get clk %s:%ld\n", __func__, + dispc->feat->vpclk_name[i], PTR_ERR(clk)); + return PTR_ERR(clk); + } + dispc->vp_clk[i] = clk; + + gamma_table = devm_kmalloc_array(dev, gamma_size, + sizeof(*gamma_table), + GFP_KERNEL); + if (!gamma_table) + return -ENOMEM; + dispc->vp_data[i].gamma_table = gamma_table; + } + + if (feat->subrev == DISPC_AM65X) { + r = dispc_init_am65x_oldi_io_ctrl(dev, dispc); + if (r) + return r; + } + + dispc->fclk = devm_clk_get(dev, "fck"); + if (IS_ERR(dispc->fclk)) { + dev_err(dev, "%s: Failed to get fclk: %ld\n", + __func__, PTR_ERR(dispc->fclk)); + return PTR_ERR(dispc->fclk); + } + dev_dbg(dev, "DSS fclk %lu Hz\n", clk_get_rate(dispc->fclk)); + + of_property_read_u32(dispc->dev->of_node, "max-memory-bandwidth", + &dispc->memory_bandwidth_limit); + + tidss->dispc = dispc; + + return 0; +} diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h new file mode 100644 index 000000000000..e65e6a2bb821 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#ifndef __TIDSS_DISPC_H__ +#define __TIDSS_DISPC_H__ + +#include "tidss_drv.h" + +struct dispc_device; + +struct drm_crtc_state; + +enum tidss_gamma_type { TIDSS_GAMMA_8BIT, TIDSS_GAMMA_10BIT }; + +struct tidss_vp_feat { + struct tidss_vp_color_feat { + u32 gamma_size; + enum tidss_gamma_type gamma_type; + bool has_ctm; + } color; +}; + +struct tidss_plane_feat { + struct tidss_plane_color_feat { + u32 encodings; + u32 ranges; + enum drm_color_encoding default_encoding; + enum drm_color_range default_range; + } color; + struct tidss_plane_blend_feat { + bool global_alpha; + } blend; +}; + +struct dispc_features_scaling { + u32 in_width_max_5tap_rgb; + u32 in_width_max_3tap_rgb; + u32 in_width_max_5tap_yuv; + u32 in_width_max_3tap_yuv; + u32 upscale_limit; + u32 downscale_limit_5tap; + u32 downscale_limit_3tap; + u32 xinc_max; +}; + +struct dispc_errata { + bool i2000; /* DSS Does Not Support YUV Pixel Data Formats */ +}; + +enum dispc_vp_bus_type { + DISPC_VP_DPI, /* DPI output */ + DISPC_VP_OLDI, /* OLDI (LVDS) output */ + DISPC_VP_INTERNAL, /* SoC internal routing */ + DISPC_VP_MAX_BUS_TYPE, +}; + +enum dispc_dss_subrevision { + DISPC_K2G, + DISPC_AM65X, + DISPC_J721E, +}; + +struct dispc_features { + int min_pclk_khz; + int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE]; + + struct dispc_features_scaling scaling; + + enum dispc_dss_subrevision subrev; + + const char *common; + const u16 *common_regs; + u32 num_vps; + const char *vp_name[TIDSS_MAX_PORTS]; /* Should match dt reg names */ + const char *ovr_name[TIDSS_MAX_PORTS]; /* Should match dt reg names */ + const char *vpclk_name[TIDSS_MAX_PORTS]; /* Should match dt clk names */ + const enum dispc_vp_bus_type vp_bus_type[TIDSS_MAX_PORTS]; + struct tidss_vp_feat vp_feat; + u32 num_planes; + const char *vid_name[TIDSS_MAX_PLANES]; /* Should match dt reg names */ + bool vid_lite[TIDSS_MAX_PLANES]; + u32 vid_order[TIDSS_MAX_PLANES]; + + struct dispc_errata errata; +}; + +extern const struct dispc_features dispc_k2g_feats; +extern const struct dispc_features dispc_am65x_feats; +extern const struct dispc_features dispc_j721e_feats; + +void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask); +dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc); + +void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_crtc_state *state); +void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_crtc_state *state); +void dispc_vp_disable(struct dispc_device *dispc, u32 hw_videoport); +void dispc_vp_unprepare(struct dispc_device *dispc, u32 hw_videoport); +bool dispc_vp_go_busy(struct dispc_device *dispc, u32 hw_videoport); +void dispc_vp_go(struct dispc_device *dispc, u32 hw_videoport); +int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_crtc_state *state); +enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_display_mode *mode); +int dispc_vp_enable_clk(struct dispc_device *dispc, u32 hw_videoport); +void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport); +int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport, + unsigned long rate); +void dispc_vp_setup(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_crtc_state *state, bool newmodeset); + +int dispc_runtime_suspend(struct dispc_device *dispc); +int dispc_runtime_resume(struct dispc_device *dispc); + +int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane, + const struct drm_plane_state *state, + u32 hw_videoport); +int dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane, + const struct drm_plane_state *state, + u32 hw_videoport); +int dispc_plane_enable(struct dispc_device *dispc, u32 hw_plane, bool enable); +const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len); + +int dispc_init(struct tidss_device *tidss); +void dispc_remove(struct tidss_device *tidss); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_dispc_regs.h b/drivers/gpu/drm/tidss/tidss_dispc_regs.h new file mode 100644 index 000000000000..88a83a41b6e3 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_dispc_regs.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Jyri Sarha <jsarha@ti.com> + */ + +#ifndef __TIDSS_DISPC_REGS_H +#define __TIDSS_DISPC_REGS_H + +enum dispc_common_regs { + NOT_APPLICABLE_OFF = 0, + DSS_REVISION_OFF, + DSS_SYSCONFIG_OFF, + DSS_SYSSTATUS_OFF, + DISPC_IRQ_EOI_OFF, + DISPC_IRQSTATUS_RAW_OFF, + DISPC_IRQSTATUS_OFF, + DISPC_IRQENABLE_SET_OFF, + DISPC_IRQENABLE_CLR_OFF, + DISPC_VID_IRQENABLE_OFF, + DISPC_VID_IRQSTATUS_OFF, + DISPC_VP_IRQENABLE_OFF, + DISPC_VP_IRQSTATUS_OFF, + WB_IRQENABLE_OFF, + WB_IRQSTATUS_OFF, + DISPC_GLOBAL_MFLAG_ATTRIBUTE_OFF, + DISPC_GLOBAL_OUTPUT_ENABLE_OFF, + DISPC_GLOBAL_BUFFER_OFF, + DSS_CBA_CFG_OFF, + DISPC_DBG_CONTROL_OFF, + DISPC_DBG_STATUS_OFF, + DISPC_CLKGATING_DISABLE_OFF, + DISPC_SECURE_DISABLE_OFF, + FBDC_REVISION_1_OFF, + FBDC_REVISION_2_OFF, + FBDC_REVISION_3_OFF, + FBDC_REVISION_4_OFF, + FBDC_REVISION_5_OFF, + FBDC_REVISION_6_OFF, + FBDC_COMMON_CONTROL_OFF, + FBDC_CONSTANT_COLOR_0_OFF, + FBDC_CONSTANT_COLOR_1_OFF, + DISPC_CONNECTIONS_OFF, + DISPC_MSS_VP1_OFF, + DISPC_MSS_VP3_OFF, + DISPC_COMMON_REG_TABLE_LEN, +}; + +/* + * dispc_common_regmap should be defined as const u16 * and pointing + * to a valid dss common register map for the platform, before the + * macros bellow can be used. + */ + +#define REG(r) (dispc_common_regmap[r ## _OFF]) + +#define DSS_REVISION REG(DSS_REVISION) +#define DSS_SYSCONFIG REG(DSS_SYSCONFIG) +#define DSS_SYSSTATUS REG(DSS_SYSSTATUS) +#define DISPC_IRQ_EOI REG(DISPC_IRQ_EOI) +#define DISPC_IRQSTATUS_RAW REG(DISPC_IRQSTATUS_RAW) +#define DISPC_IRQSTATUS REG(DISPC_IRQSTATUS) +#define DISPC_IRQENABLE_SET REG(DISPC_IRQENABLE_SET) +#define DISPC_IRQENABLE_CLR REG(DISPC_IRQENABLE_CLR) +#define DISPC_VID_IRQENABLE(n) (REG(DISPC_VID_IRQENABLE) + (n) * 4) +#define DISPC_VID_IRQSTATUS(n) (REG(DISPC_VID_IRQSTATUS) + (n) * 4) +#define DISPC_VP_IRQENABLE(n) (REG(DISPC_VP_IRQENABLE) + (n) * 4) +#define DISPC_VP_IRQSTATUS(n) (REG(DISPC_VP_IRQSTATUS) + (n) * 4) +#define WB_IRQENABLE REG(WB_IRQENABLE) +#define WB_IRQSTATUS REG(WB_IRQSTATUS) + +#define DISPC_GLOBAL_MFLAG_ATTRIBUTE REG(DISPC_GLOBAL_MFLAG_ATTRIBUTE) +#define DISPC_GLOBAL_OUTPUT_ENABLE REG(DISPC_GLOBAL_OUTPUT_ENABLE) +#define DISPC_GLOBAL_BUFFER REG(DISPC_GLOBAL_BUFFER) +#define DSS_CBA_CFG REG(DSS_CBA_CFG) +#define DISPC_DBG_CONTROL REG(DISPC_DBG_CONTROL) +#define DISPC_DBG_STATUS REG(DISPC_DBG_STATUS) +#define DISPC_CLKGATING_DISABLE REG(DISPC_CLKGATING_DISABLE) +#define DISPC_SECURE_DISABLE REG(DISPC_SECURE_DISABLE) + +#define FBDC_REVISION_1 REG(FBDC_REVISION_1) +#define FBDC_REVISION_2 REG(FBDC_REVISION_2) +#define FBDC_REVISION_3 REG(FBDC_REVISION_3) +#define FBDC_REVISION_4 REG(FBDC_REVISION_4) +#define FBDC_REVISION_5 REG(FBDC_REVISION_5) +#define FBDC_REVISION_6 REG(FBDC_REVISION_6) +#define FBDC_COMMON_CONTROL REG(FBDC_COMMON_CONTROL) +#define FBDC_CONSTANT_COLOR_0 REG(FBDC_CONSTANT_COLOR_0) +#define FBDC_CONSTANT_COLOR_1 REG(FBDC_CONSTANT_COLOR_1) +#define DISPC_CONNECTIONS REG(DISPC_CONNECTIONS) +#define DISPC_MSS_VP1 REG(DISPC_MSS_VP1) +#define DISPC_MSS_VP3 REG(DISPC_MSS_VP3) + +/* VID */ + +#define DISPC_VID_ACCUH_0 0x0 +#define DISPC_VID_ACCUH_1 0x4 +#define DISPC_VID_ACCUH2_0 0x8 +#define DISPC_VID_ACCUH2_1 0xc +#define DISPC_VID_ACCUV_0 0x10 +#define DISPC_VID_ACCUV_1 0x14 +#define DISPC_VID_ACCUV2_0 0x18 +#define DISPC_VID_ACCUV2_1 0x1c +#define DISPC_VID_ATTRIBUTES 0x20 +#define DISPC_VID_ATTRIBUTES2 0x24 +#define DISPC_VID_BA_0 0x28 +#define DISPC_VID_BA_1 0x2c +#define DISPC_VID_BA_UV_0 0x30 +#define DISPC_VID_BA_UV_1 0x34 +#define DISPC_VID_BUF_SIZE_STATUS 0x38 +#define DISPC_VID_BUF_THRESHOLD 0x3c +#define DISPC_VID_CSC_COEF(n) (0x40 + (n) * 4) + +#define DISPC_VID_FIRH 0x5c +#define DISPC_VID_FIRH2 0x60 +#define DISPC_VID_FIRV 0x64 +#define DISPC_VID_FIRV2 0x68 + +#define DISPC_VID_FIR_COEFS_H0 0x6c +#define DISPC_VID_FIR_COEF_H0(phase) (0x6c + (phase) * 4) +#define DISPC_VID_FIR_COEFS_H0_C 0x90 +#define DISPC_VID_FIR_COEF_H0_C(phase) (0x90 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_H12 0xb4 +#define DISPC_VID_FIR_COEF_H12(phase) (0xb4 + (phase) * 4) +#define DISPC_VID_FIR_COEFS_H12_C 0xf4 +#define DISPC_VID_FIR_COEF_H12_C(phase) (0xf4 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_V0 0x134 +#define DISPC_VID_FIR_COEF_V0(phase) (0x134 + (phase) * 4) +#define DISPC_VID_FIR_COEFS_V0_C 0x158 +#define DISPC_VID_FIR_COEF_V0_C(phase) (0x158 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_V12 0x17c +#define DISPC_VID_FIR_COEF_V12(phase) (0x17c + (phase) * 4) +#define DISPC_VID_FIR_COEFS_V12_C 0x1bc +#define DISPC_VID_FIR_COEF_V12_C(phase) (0x1bc + (phase) * 4) + +#define DISPC_VID_GLOBAL_ALPHA 0x1fc +#define DISPC_VID_K2G_IRQENABLE 0x200 /* K2G */ +#define DISPC_VID_K2G_IRQSTATUS 0x204 /* K2G */ +#define DISPC_VID_MFLAG_THRESHOLD 0x208 +#define DISPC_VID_PICTURE_SIZE 0x20c +#define DISPC_VID_PIXEL_INC 0x210 +#define DISPC_VID_K2G_POSITION 0x214 /* K2G */ +#define DISPC_VID_PRELOAD 0x218 +#define DISPC_VID_ROW_INC 0x21c +#define DISPC_VID_SIZE 0x220 +#define DISPC_VID_BA_EXT_0 0x22c +#define DISPC_VID_BA_EXT_1 0x230 +#define DISPC_VID_BA_UV_EXT_0 0x234 +#define DISPC_VID_BA_UV_EXT_1 0x238 +#define DISPC_VID_CSC_COEF7 0x23c +#define DISPC_VID_ROW_INC_UV 0x248 +#define DISPC_VID_CLUT 0x260 +#define DISPC_VID_SAFETY_ATTRIBUTES 0x2a0 +#define DISPC_VID_SAFETY_CAPT_SIGNATURE 0x2a4 +#define DISPC_VID_SAFETY_POSITION 0x2a8 +#define DISPC_VID_SAFETY_REF_SIGNATURE 0x2ac +#define DISPC_VID_SAFETY_SIZE 0x2b0 +#define DISPC_VID_SAFETY_LFSR_SEED 0x2b4 +#define DISPC_VID_LUMAKEY 0x2b8 +#define DISPC_VID_DMA_BUFSIZE 0x2bc /* J721E */ + +/* OVR */ + +#define DISPC_OVR_CONFIG 0x0 +#define DISPC_OVR_VIRTVP 0x4 /* J721E */ +#define DISPC_OVR_DEFAULT_COLOR 0x8 +#define DISPC_OVR_DEFAULT_COLOR2 0xc +#define DISPC_OVR_TRANS_COLOR_MAX 0x10 +#define DISPC_OVR_TRANS_COLOR_MAX2 0x14 +#define DISPC_OVR_TRANS_COLOR_MIN 0x18 +#define DISPC_OVR_TRANS_COLOR_MIN2 0x1c +#define DISPC_OVR_ATTRIBUTES(n) (0x20 + (n) * 4) +#define DISPC_OVR_ATTRIBUTES2(n) (0x34 + (n) * 4) /* J721E */ +/* VP */ + +#define DISPC_VP_CONFIG 0x0 +#define DISPC_VP_CONTROL 0x4 +#define DISPC_VP_CSC_COEF0 0x8 +#define DISPC_VP_CSC_COEF1 0xc +#define DISPC_VP_CSC_COEF2 0x10 +#define DISPC_VP_DATA_CYCLE_0 0x14 +#define DISPC_VP_DATA_CYCLE_1 0x18 +#define DISPC_VP_K2G_GAMMA_TABLE 0x20 /* K2G */ +#define DISPC_VP_K2G_IRQENABLE 0x3c /* K2G */ +#define DISPC_VP_K2G_IRQSTATUS 0x40 /* K2G */ +#define DISPC_VP_DATA_CYCLE_2 0x1c +#define DISPC_VP_LINE_NUMBER 0x44 +#define DISPC_VP_POL_FREQ 0x4c +#define DISPC_VP_SIZE_SCREEN 0x50 +#define DISPC_VP_TIMING_H 0x54 +#define DISPC_VP_TIMING_V 0x58 +#define DISPC_VP_CSC_COEF3 0x5c +#define DISPC_VP_CSC_COEF4 0x60 +#define DISPC_VP_CSC_COEF5 0x64 +#define DISPC_VP_CSC_COEF6 0x68 +#define DISPC_VP_CSC_COEF7 0x6c +#define DISPC_VP_SAFETY_ATTRIBUTES_0 0x70 +#define DISPC_VP_SAFETY_ATTRIBUTES_1 0x74 +#define DISPC_VP_SAFETY_ATTRIBUTES_2 0x78 +#define DISPC_VP_SAFETY_ATTRIBUTES_3 0x7c +#define DISPC_VP_SAFETY_CAPT_SIGNATURE_0 0x90 +#define DISPC_VP_SAFETY_CAPT_SIGNATURE_1 0x94 +#define DISPC_VP_SAFETY_CAPT_SIGNATURE_2 0x98 +#define DISPC_VP_SAFETY_CAPT_SIGNATURE_3 0x9c +#define DISPC_VP_SAFETY_POSITION_0 0xb0 +#define DISPC_VP_SAFETY_POSITION_1 0xb4 +#define DISPC_VP_SAFETY_POSITION_2 0xb8 +#define DISPC_VP_SAFETY_POSITION_3 0xbc +#define DISPC_VP_SAFETY_REF_SIGNATURE_0 0xd0 +#define DISPC_VP_SAFETY_REF_SIGNATURE_1 0xd4 +#define DISPC_VP_SAFETY_REF_SIGNATURE_2 0xd8 +#define DISPC_VP_SAFETY_REF_SIGNATURE_3 0xdc +#define DISPC_VP_SAFETY_SIZE_0 0xf0 +#define DISPC_VP_SAFETY_SIZE_1 0xf4 +#define DISPC_VP_SAFETY_SIZE_2 0xf8 +#define DISPC_VP_SAFETY_SIZE_3 0xfc +#define DISPC_VP_SAFETY_LFSR_SEED 0x110 +#define DISPC_VP_GAMMA_TABLE 0x120 +#define DISPC_VP_DSS_OLDI_CFG 0x160 +#define DISPC_VP_DSS_OLDI_STATUS 0x164 +#define DISPC_VP_DSS_OLDI_LB 0x168 +#define DISPC_VP_DSS_MERGE_SPLIT 0x16c /* J721E */ +#define DISPC_VP_DSS_DMA_THREADSIZE 0x170 /* J721E */ +#define DISPC_VP_DSS_DMA_THREADSIZE_STATUS 0x174 /* J721E */ + +/* + * OLDI IO_CTRL register offsets. On AM654 the registers are found + * from CTRL_MMR0, there the syscon regmap should map 0x14 bytes from + * CTRLMMR0P1_OLDI_DAT0_IO_CTRL to CTRLMMR0P1_OLDI_CLK_IO_CTRL + * register range. + */ +#define OLDI_DAT0_IO_CTRL 0x00 +#define OLDI_DAT1_IO_CTRL 0x04 +#define OLDI_DAT2_IO_CTRL 0x08 +#define OLDI_DAT3_IO_CTRL 0x0C +#define OLDI_CLK_IO_CTRL 0x10 + +#define OLDI_PWRDN_TX BIT(8) + +#endif /* __TIDSS_DISPC_REGS_H */ diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c new file mode 100644 index 000000000000..d95e4be2c7b9 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#include <linux/console.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_irq.h> +#include <drm/drm_probe_helper.h> + +#include "tidss_dispc.h" +#include "tidss_drv.h" +#include "tidss_kms.h" +#include "tidss_irq.h" + +/* Power management */ + +int tidss_runtime_get(struct tidss_device *tidss) +{ + int r; + + dev_dbg(tidss->dev, "%s\n", __func__); + + r = pm_runtime_get_sync(tidss->dev); + WARN_ON(r < 0); + return r < 0 ? r : 0; +} + +void tidss_runtime_put(struct tidss_device *tidss) +{ + int r; + + dev_dbg(tidss->dev, "%s\n", __func__); + + r = pm_runtime_put_sync(tidss->dev); + WARN_ON(r < 0); +} + +static int __maybe_unused tidss_pm_runtime_suspend(struct device *dev) +{ + struct tidss_device *tidss = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + return dispc_runtime_suspend(tidss->dispc); +} + +static int __maybe_unused tidss_pm_runtime_resume(struct device *dev) +{ + struct tidss_device *tidss = dev_get_drvdata(dev); + int r; + + dev_dbg(dev, "%s\n", __func__); + + r = dispc_runtime_resume(tidss->dispc); + if (r) + return r; + + return 0; +} + +static int __maybe_unused tidss_suspend(struct device *dev) +{ + struct tidss_device *tidss = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + return drm_mode_config_helper_suspend(&tidss->ddev); +} + +static int __maybe_unused tidss_resume(struct device *dev) +{ + struct tidss_device *tidss = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + return drm_mode_config_helper_resume(&tidss->ddev); +} + +#ifdef CONFIG_PM + +static const struct dev_pm_ops tidss_pm_ops = { + .runtime_suspend = tidss_pm_runtime_suspend, + .runtime_resume = tidss_pm_runtime_resume, + SET_SYSTEM_SLEEP_PM_OPS(tidss_suspend, tidss_resume) +}; + +#endif /* CONFIG_PM */ + +/* DRM device Information */ + +static void tidss_release(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + + drm_kms_helper_poll_fini(ddev); + + tidss_modeset_cleanup(tidss); + + drm_dev_fini(ddev); + + kfree(tidss); +} + +DEFINE_DRM_GEM_CMA_FOPS(tidss_fops); + +static struct drm_driver tidss_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &tidss_fops, + .release = tidss_release, + DRM_GEM_CMA_VMAP_DRIVER_OPS, + .name = "tidss", + .desc = "TI Keystone DSS", + .date = "20180215", + .major = 1, + .minor = 0, + + .irq_preinstall = tidss_irq_preinstall, + .irq_postinstall = tidss_irq_postinstall, + .irq_handler = tidss_irq_handler, + .irq_uninstall = tidss_irq_uninstall, +}; + +static int tidss_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tidss_device *tidss; + struct drm_device *ddev; + int ret; + int irq; + + dev_dbg(dev, "%s\n", __func__); + + /* Can't use devm_* since drm_device's lifetime may exceed dev's */ + tidss = kzalloc(sizeof(*tidss), GFP_KERNEL); + if (!tidss) + return -ENOMEM; + + ddev = &tidss->ddev; + + ret = devm_drm_dev_init(&pdev->dev, ddev, &tidss_driver); + if (ret) { + kfree(ddev); + return ret; + } + + tidss->dev = dev; + tidss->feat = of_device_get_match_data(dev); + + platform_set_drvdata(pdev, tidss); + + ddev->dev_private = tidss; + + ret = dispc_init(tidss); + if (ret) { + dev_err(dev, "failed to initialize dispc: %d\n", ret); + return ret; + } + + pm_runtime_enable(dev); + +#ifndef CONFIG_PM + /* If we don't have PM, we need to call resume manually */ + dispc_runtime_resume(tidss->dispc); +#endif + + ret = tidss_modeset_init(tidss); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to init DRM/KMS (%d)\n", ret); + goto err_runtime_suspend; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err_runtime_suspend; + } + + ret = drm_irq_install(ddev, irq); + if (ret) { + dev_err(dev, "drm_irq_install failed: %d\n", ret); + goto err_runtime_suspend; + } + + drm_kms_helper_poll_init(ddev); + + drm_mode_config_reset(ddev); + + ret = drm_dev_register(ddev, 0); + if (ret) { + dev_err(dev, "failed to register DRM device\n"); + goto err_irq_uninstall; + } + + drm_fbdev_generic_setup(ddev, 32); + + dev_dbg(dev, "%s done\n", __func__); + + return 0; + +err_irq_uninstall: + drm_irq_uninstall(ddev); + +err_runtime_suspend: +#ifndef CONFIG_PM + dispc_runtime_suspend(tidss->dispc); +#endif + pm_runtime_disable(dev); + + return ret; +} + +static int tidss_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tidss_device *tidss = platform_get_drvdata(pdev); + struct drm_device *ddev = &tidss->ddev; + + dev_dbg(dev, "%s\n", __func__); + + drm_dev_unregister(ddev); + + drm_atomic_helper_shutdown(ddev); + + drm_irq_uninstall(ddev); + +#ifndef CONFIG_PM + /* If we don't have PM, we need to call suspend manually */ + dispc_runtime_suspend(tidss->dispc); +#endif + pm_runtime_disable(dev); + + /* devm allocated dispc goes away with the dev so mark it NULL */ + dispc_remove(tidss); + + dev_dbg(dev, "%s done\n", __func__); + + return 0; +} + +static void tidss_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + +static const struct of_device_id tidss_of_table[] = { + { .compatible = "ti,k2g-dss", .data = &dispc_k2g_feats, }, + { .compatible = "ti,am65x-dss", .data = &dispc_am65x_feats, }, + { .compatible = "ti,j721e-dss", .data = &dispc_j721e_feats, }, + { } +}; + +MODULE_DEVICE_TABLE(of, tidss_of_table); + +static struct platform_driver tidss_platform_driver = { + .probe = tidss_probe, + .remove = tidss_remove, + .shutdown = tidss_shutdown, + .driver = { + .name = "tidss", +#ifdef CONFIG_PM + .pm = &tidss_pm_ops, +#endif + .of_match_table = tidss_of_table, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(tidss_platform_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("TI Keystone DSS Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h new file mode 100644 index 000000000000..e2aa6436ad18 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_drv.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#ifndef __TIDSS_DRV_H__ +#define __TIDSS_DRV_H__ + +#include <linux/spinlock.h> + +#define TIDSS_MAX_PORTS 4 +#define TIDSS_MAX_PLANES 4 + +typedef u32 dispc_irq_t; + +struct tidss_device { + struct drm_device ddev; /* DRM device for DSS */ + struct device *dev; /* Underlying DSS device */ + + const struct dispc_features *feat; + struct dispc_device *dispc; + + unsigned int num_crtcs; + struct drm_crtc *crtcs[TIDSS_MAX_PORTS]; + + unsigned int num_planes; + struct drm_plane *planes[TIDSS_MAX_PLANES]; + + spinlock_t wait_lock; /* protects the irq masks */ + dispc_irq_t irq_mask; /* enabled irqs in addition to wait_list */ + + struct drm_atomic_state *saved_state; +}; + +int tidss_runtime_get(struct tidss_device *tidss); +void tidss_runtime_put(struct tidss_device *tidss); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c new file mode 100644 index 000000000000..f7fe3a43ead0 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_encoder.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#include <linux/export.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_panel.h> +#include <drm/drm_of.h> + +#include "tidss_crtc.h" +#include "tidss_drv.h" +#include "tidss_encoder.h" + +static int tidss_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_device *ddev = encoder->dev; + struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(crtc_state); + struct drm_display_info *di = &conn_state->connector->display_info; + struct drm_bridge *bridge; + bool bus_flags_set = false; + + dev_dbg(ddev->dev, "%s\n", __func__); + + /* + * Take the bus_flags from the first bridge that defines + * bridge timings, or from the connector's display_info if no + * bridge defines the timings. + */ + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (!bridge->timings) + continue; + + tcrtc_state->bus_flags = bridge->timings->input_bus_flags; + bus_flags_set = true; + break; + } + + if (!di->bus_formats || di->num_bus_formats == 0) { + dev_err(ddev->dev, "%s: No bus_formats in connected display\n", + __func__); + return -EINVAL; + } + + // XXX any cleaner way to set bus format and flags? + tcrtc_state->bus_format = di->bus_formats[0]; + if (!bus_flags_set) + tcrtc_state->bus_flags = di->bus_flags; + + return 0; +} + +static const struct drm_encoder_helper_funcs encoder_helper_funcs = { + .atomic_check = tidss_encoder_atomic_check, +}; + +static const struct drm_encoder_funcs encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss, + u32 encoder_type, u32 possible_crtcs) +{ + struct drm_encoder *enc; + int ret; + + enc = devm_kzalloc(tidss->dev, sizeof(*enc), GFP_KERNEL); + if (!enc) + return ERR_PTR(-ENOMEM); + + enc->possible_crtcs = possible_crtcs; + + ret = drm_encoder_init(&tidss->ddev, enc, &encoder_funcs, + encoder_type, NULL); + if (ret < 0) + return ERR_PTR(ret); + + drm_encoder_helper_add(enc, &encoder_helper_funcs); + + dev_dbg(tidss->dev, "Encoder create done\n"); + + return enc; +} diff --git a/drivers/gpu/drm/tidss/tidss_encoder.h b/drivers/gpu/drm/tidss/tidss_encoder.h new file mode 100644 index 000000000000..06854d66e7e6 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_encoder.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#ifndef __TIDSS_ENCODER_H__ +#define __TIDSS_ENCODER_H__ + +#include <drm/drm_encoder.h> + +struct tidss_device; + +struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss, + u32 encoder_type, u32 possible_crtcs); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_irq.c b/drivers/gpu/drm/tidss/tidss_irq.c new file mode 100644 index 000000000000..612c046738e5 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_irq.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#include <drm/drm_print.h> + +#include "tidss_crtc.h" +#include "tidss_dispc.h" +#include "tidss_drv.h" +#include "tidss_irq.h" +#include "tidss_plane.h" + +/* call with wait_lock and dispc runtime held */ +static void tidss_irq_update(struct tidss_device *tidss) +{ + assert_spin_locked(&tidss->wait_lock); + + dispc_set_irqenable(tidss->dispc, tidss->irq_mask); +} + +void tidss_irq_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + u32 hw_videoport = tcrtc->hw_videoport; + unsigned long flags; + + spin_lock_irqsave(&tidss->wait_lock, flags); + tidss->irq_mask |= DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) | + DSS_IRQ_VP_VSYNC_ODD(hw_videoport); + tidss_irq_update(tidss); + spin_unlock_irqrestore(&tidss->wait_lock, flags); +} + +void tidss_irq_disable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + u32 hw_videoport = tcrtc->hw_videoport; + unsigned long flags; + + spin_lock_irqsave(&tidss->wait_lock, flags); + tidss->irq_mask &= ~(DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) | + DSS_IRQ_VP_VSYNC_ODD(hw_videoport)); + tidss_irq_update(tidss); + spin_unlock_irqrestore(&tidss->wait_lock, flags); +} + +irqreturn_t tidss_irq_handler(int irq, void *arg) +{ + struct drm_device *ddev = (struct drm_device *)arg; + struct tidss_device *tidss = ddev->dev_private; + unsigned int id; + dispc_irq_t irqstatus; + + if (WARN_ON(!ddev->irq_enabled)) + return IRQ_NONE; + + irqstatus = dispc_read_and_clear_irqstatus(tidss->dispc); + + for (id = 0; id < tidss->num_crtcs; id++) { + struct drm_crtc *crtc = tidss->crtcs[id]; + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + u32 hw_videoport = tcrtc->hw_videoport; + + if (irqstatus & (DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) | + DSS_IRQ_VP_VSYNC_ODD(hw_videoport))) + tidss_crtc_vblank_irq(crtc); + + if (irqstatus & (DSS_IRQ_VP_FRAME_DONE(hw_videoport))) + tidss_crtc_framedone_irq(crtc); + + if (irqstatus & DSS_IRQ_VP_SYNC_LOST(hw_videoport)) + tidss_crtc_error_irq(crtc, irqstatus); + } + + if (irqstatus & DSS_IRQ_DEVICE_OCP_ERR) + dev_err_ratelimited(tidss->dev, "OCP error\n"); + + return IRQ_HANDLED; +} + +void tidss_irq_resume(struct tidss_device *tidss) +{ + unsigned long flags; + + spin_lock_irqsave(&tidss->wait_lock, flags); + tidss_irq_update(tidss); + spin_unlock_irqrestore(&tidss->wait_lock, flags); +} + +void tidss_irq_preinstall(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + + spin_lock_init(&tidss->wait_lock); + + tidss_runtime_get(tidss); + + dispc_set_irqenable(tidss->dispc, 0); + dispc_read_and_clear_irqstatus(tidss->dispc); + + tidss_runtime_put(tidss); +} + +int tidss_irq_postinstall(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + unsigned long flags; + unsigned int i; + + tidss_runtime_get(tidss); + + spin_lock_irqsave(&tidss->wait_lock, flags); + + tidss->irq_mask = DSS_IRQ_DEVICE_OCP_ERR; + + for (i = 0; i < tidss->num_crtcs; ++i) { + struct tidss_crtc *tcrtc = to_tidss_crtc(tidss->crtcs[i]); + + tidss->irq_mask |= DSS_IRQ_VP_SYNC_LOST(tcrtc->hw_videoport); + + tidss->irq_mask |= DSS_IRQ_VP_FRAME_DONE(tcrtc->hw_videoport); + } + + tidss_irq_update(tidss); + + spin_unlock_irqrestore(&tidss->wait_lock, flags); + + tidss_runtime_put(tidss); + + return 0; +} + +void tidss_irq_uninstall(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + + tidss_runtime_get(tidss); + dispc_set_irqenable(tidss->dispc, 0); + tidss_runtime_put(tidss); +} diff --git a/drivers/gpu/drm/tidss/tidss_irq.h b/drivers/gpu/drm/tidss/tidss_irq.h new file mode 100644 index 000000000000..aa92db403cca --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_irq.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#ifndef __TIDSS_IRQ_H__ +#define __TIDSS_IRQ_H__ + +#include <linux/types.h> + +#include "tidss_drv.h" + +/* + * The IRQ status from various DISPC IRQ registers are packed into a single + * value, where the bits are defined as follows: + * + * bit group |dev|wb |mrg0|mrg1|mrg2|mrg3|plane0-3| <unused> | + * bit use |D |fou|FEOL|FEOL|FEOL|FEOL| UUUU | | + * bit number|0 |1-3|4-7 |8-11| 12-19 | 20-23 | 24-31 | + * + * device bits: D = OCP error + * WB bits: f = frame done wb, o = wb buffer overflow, + * u = wb buffer uncomplete + * vp bits: F = frame done, E = vsync even, O = vsync odd, L = sync lost + * plane bits: U = fifo underflow + */ + +#define DSS_IRQ_DEVICE_OCP_ERR BIT(0) + +#define DSS_IRQ_DEVICE_FRAMEDONEWB BIT(1) +#define DSS_IRQ_DEVICE_WBBUFFEROVERFLOW BIT(2) +#define DSS_IRQ_DEVICE_WBUNCOMPLETEERROR BIT(3) +#define DSS_IRQ_DEVICE_WB_MASK GENMASK(3, 1) + +#define DSS_IRQ_VP_BIT_N(ch, bit) (4 + 4 * (ch) + (bit)) +#define DSS_IRQ_PLANE_BIT_N(plane, bit) \ + (DSS_IRQ_VP_BIT_N(TIDSS_MAX_PORTS, 0) + 1 * (plane) + (bit)) + +#define DSS_IRQ_VP_BIT(ch, bit) BIT(DSS_IRQ_VP_BIT_N((ch), (bit))) +#define DSS_IRQ_PLANE_BIT(plane, bit) \ + BIT(DSS_IRQ_PLANE_BIT_N((plane), (bit))) + +static inline dispc_irq_t DSS_IRQ_VP_MASK(u32 ch) +{ + return GENMASK(DSS_IRQ_VP_BIT_N((ch), 3), DSS_IRQ_VP_BIT_N((ch), 0)); +} + +static inline dispc_irq_t DSS_IRQ_PLANE_MASK(u32 plane) +{ + return GENMASK(DSS_IRQ_PLANE_BIT_N((plane), 0), + DSS_IRQ_PLANE_BIT_N((plane), 0)); +} + +#define DSS_IRQ_VP_FRAME_DONE(ch) DSS_IRQ_VP_BIT((ch), 0) +#define DSS_IRQ_VP_VSYNC_EVEN(ch) DSS_IRQ_VP_BIT((ch), 1) +#define DSS_IRQ_VP_VSYNC_ODD(ch) DSS_IRQ_VP_BIT((ch), 2) +#define DSS_IRQ_VP_SYNC_LOST(ch) DSS_IRQ_VP_BIT((ch), 3) + +#define DSS_IRQ_PLANE_FIFO_UNDERFLOW(plane) DSS_IRQ_PLANE_BIT((plane), 0) + +struct drm_crtc; +struct drm_device; + +struct tidss_device; + +void tidss_irq_enable_vblank(struct drm_crtc *crtc); +void tidss_irq_disable_vblank(struct drm_crtc *crtc); + +void tidss_irq_preinstall(struct drm_device *ddev); +int tidss_irq_postinstall(struct drm_device *ddev); +void tidss_irq_uninstall(struct drm_device *ddev); +irqreturn_t tidss_irq_handler(int irq, void *arg); + +void tidss_irq_resume(struct tidss_device *tidss); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c new file mode 100644 index 000000000000..5311e0f1c551 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_vblank.h> + +#include "tidss_crtc.h" +#include "tidss_dispc.h" +#include "tidss_drv.h" +#include "tidss_encoder.h" +#include "tidss_kms.h" +#include "tidss_plane.h" + +static void tidss_atomic_commit_tail(struct drm_atomic_state *old_state) +{ + struct drm_device *ddev = old_state->dev; + struct tidss_device *tidss = ddev->dev_private; + + dev_dbg(ddev->dev, "%s\n", __func__); + + tidss_runtime_get(tidss); + + drm_atomic_helper_commit_modeset_disables(ddev, old_state); + drm_atomic_helper_commit_planes(ddev, old_state, 0); + drm_atomic_helper_commit_modeset_enables(ddev, old_state); + + drm_atomic_helper_commit_hw_done(old_state); + drm_atomic_helper_wait_for_flip_done(ddev, old_state); + + drm_atomic_helper_cleanup_planes(ddev, old_state); + + tidss_runtime_put(tidss); +} + +static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = { + .atomic_commit_tail = tidss_atomic_commit_tail, +}; + +static const struct drm_mode_config_funcs mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static int tidss_dispc_modeset_init(struct tidss_device *tidss) +{ + struct device *dev = tidss->dev; + unsigned int fourccs_len; + const u32 *fourccs = dispc_plane_formats(tidss->dispc, &fourccs_len); + unsigned int i; + + struct pipe { + u32 hw_videoport; + struct drm_bridge *bridge; + u32 enc_type; + }; + + const struct dispc_features *feat = tidss->feat; + u32 max_vps = feat->num_vps; + u32 max_planes = feat->num_planes; + + struct pipe pipes[TIDSS_MAX_PORTS]; + u32 num_pipes = 0; + u32 crtc_mask; + + /* first find all the connected panels & bridges */ + + for (i = 0; i < max_vps; i++) { + struct drm_panel *panel; + struct drm_bridge *bridge; + u32 enc_type = DRM_MODE_ENCODER_NONE; + int ret; + + ret = drm_of_find_panel_or_bridge(dev->of_node, i, 0, + &panel, &bridge); + if (ret == -ENODEV) { + dev_dbg(dev, "no panel/bridge for port %d\n", i); + continue; + } else if (ret) { + dev_dbg(dev, "port %d probe returned %d\n", i, ret); + return ret; + } + + if (panel) { + u32 conn_type; + + dev_dbg(dev, "Setting up panel for port %d\n", i); + + switch (feat->vp_bus_type[i]) { + case DISPC_VP_OLDI: + enc_type = DRM_MODE_ENCODER_LVDS; + conn_type = DRM_MODE_CONNECTOR_LVDS; + break; + case DISPC_VP_DPI: + enc_type = DRM_MODE_ENCODER_DPI; + conn_type = DRM_MODE_CONNECTOR_LVDS; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + if (panel->connector_type != conn_type) { + dev_err(dev, + "%s: Panel %s has incompatible connector type for vp%d (%d != %d)\n", + __func__, dev_name(panel->dev), i, + panel->connector_type, conn_type); + return -EINVAL; + } + + bridge = devm_drm_panel_bridge_add(dev, panel); + if (IS_ERR(bridge)) { + dev_err(dev, + "failed to set up panel bridge for port %d\n", + i); + return PTR_ERR(bridge); + } + } + + pipes[num_pipes].hw_videoport = i; + pipes[num_pipes].bridge = bridge; + pipes[num_pipes].enc_type = enc_type; + num_pipes++; + } + + /* all planes can be on any crtc */ + crtc_mask = (1 << num_pipes) - 1; + + /* then create a plane, a crtc and an encoder for each panel/bridge */ + + for (i = 0; i < num_pipes; ++i) { + struct tidss_plane *tplane; + struct tidss_crtc *tcrtc; + struct drm_encoder *enc; + u32 hw_plane_id = feat->vid_order[tidss->num_planes]; + int ret; + + tplane = tidss_plane_create(tidss, hw_plane_id, + DRM_PLANE_TYPE_PRIMARY, crtc_mask, + fourccs, fourccs_len); + if (IS_ERR(tplane)) { + dev_err(tidss->dev, "plane create failed\n"); + return PTR_ERR(tplane); + } + + tidss->planes[tidss->num_planes++] = &tplane->plane; + + tcrtc = tidss_crtc_create(tidss, pipes[i].hw_videoport, + &tplane->plane); + if (IS_ERR(tcrtc)) { + dev_err(tidss->dev, "crtc create failed\n"); + return PTR_ERR(tcrtc); + } + + tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc; + + enc = tidss_encoder_create(tidss, pipes[i].enc_type, + 1 << tcrtc->crtc.index); + if (IS_ERR(enc)) { + dev_err(tidss->dev, "encoder create failed\n"); + return PTR_ERR(enc); + } + + ret = drm_bridge_attach(enc, pipes[i].bridge, NULL); + if (ret) { + dev_err(tidss->dev, "bridge attach failed: %d\n", ret); + return ret; + } + } + + /* create overlay planes of the leftover planes */ + + while (tidss->num_planes < max_planes) { + struct tidss_plane *tplane; + u32 hw_plane_id = feat->vid_order[tidss->num_planes]; + + tplane = tidss_plane_create(tidss, hw_plane_id, + DRM_PLANE_TYPE_OVERLAY, crtc_mask, + fourccs, fourccs_len); + + if (IS_ERR(tplane)) { + dev_err(tidss->dev, "plane create failed\n"); + return PTR_ERR(tplane); + } + + tidss->planes[tidss->num_planes++] = &tplane->plane; + } + + return 0; +} + +int tidss_modeset_init(struct tidss_device *tidss) +{ + struct drm_device *ddev = &tidss->ddev; + unsigned int i; + int ret; + + dev_dbg(tidss->dev, "%s\n", __func__); + + drm_mode_config_init(ddev); + + ddev->mode_config.min_width = 8; + ddev->mode_config.min_height = 8; + ddev->mode_config.max_width = 8096; + ddev->mode_config.max_height = 8096; + ddev->mode_config.normalize_zpos = true; + ddev->mode_config.funcs = &mode_config_funcs; + ddev->mode_config.helper_private = &mode_config_helper_funcs; + + ret = tidss_dispc_modeset_init(tidss); + if (ret) + goto err_mode_config_cleanup; + + ret = drm_vblank_init(ddev, tidss->num_crtcs); + if (ret) + goto err_mode_config_cleanup; + + /* Start with vertical blanking interrupt reporting disabled. */ + for (i = 0; i < tidss->num_crtcs; ++i) + drm_crtc_vblank_reset(tidss->crtcs[i]); + + drm_mode_config_reset(ddev); + + dev_dbg(tidss->dev, "%s done\n", __func__); + + return 0; + +err_mode_config_cleanup: + drm_mode_config_cleanup(ddev); + return ret; +} + +void tidss_modeset_cleanup(struct tidss_device *tidss) +{ + struct drm_device *ddev = &tidss->ddev; + + drm_mode_config_cleanup(ddev); +} diff --git a/drivers/gpu/drm/tidss/tidss_kms.h b/drivers/gpu/drm/tidss/tidss_kms.h new file mode 100644 index 000000000000..dda5625d0128 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_kms.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#ifndef __TIDSS_KMS_H__ +#define __TIDSS_KMS_H__ + +struct tidss_device; + +int tidss_modeset_init(struct tidss_device *tidss); +void tidss_modeset_cleanup(struct tidss_device *tidss); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c new file mode 100644 index 000000000000..ff99b2dd4a17 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_fb_cma_helper.h> + +#include "tidss_crtc.h" +#include "tidss_dispc.h" +#include "tidss_drv.h" +#include "tidss_plane.h" + +/* drm_plane_helper_funcs */ + +static int tidss_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_device *ddev = plane->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_plane *tplane = to_tidss_plane(plane); + const struct drm_format_info *finfo; + struct drm_crtc_state *crtc_state; + u32 hw_plane = tplane->hw_plane_id; + u32 hw_videoport; + int ret; + + dev_dbg(ddev->dev, "%s\n", __func__); + + if (!state->crtc) { + /* + * The visible field is not reset by the DRM core but only + * updated by drm_plane_helper_check_state(), set it manually. + */ + state->visible = false; + return 0; + } + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, 0, + INT_MAX, true, true); + if (ret < 0) + return ret; + + /* + * The HW is only able to start drawing at subpixel boundary + * (the two first checks bellow). At the end of a row the HW + * can only jump integer number of subpixels forward to the + * beginning of the next row. So we can only show picture with + * integer subpixel width (the third check). However, after + * reaching the end of the drawn picture the drawing starts + * again at the absolute memory address where top left corner + * position of the drawn picture is (so there is no need to + * check for odd height). + */ + + finfo = drm_format_info(state->fb->format->format); + + if ((state->src_x >> 16) % finfo->hsub != 0) { + dev_dbg(ddev->dev, + "%s: x-position %u not divisible subpixel size %u\n", + __func__, (state->src_x >> 16), finfo->hsub); + return -EINVAL; + } + + if ((state->src_y >> 16) % finfo->vsub != 0) { + dev_dbg(ddev->dev, + "%s: y-position %u not divisible subpixel size %u\n", + __func__, (state->src_y >> 16), finfo->vsub); + return -EINVAL; + } + + if ((state->src_w >> 16) % finfo->hsub != 0) { + dev_dbg(ddev->dev, + "%s: src width %u not divisible by subpixel size %u\n", + __func__, (state->src_w >> 16), finfo->hsub); + return -EINVAL; + } + + if (!state->visible) + return 0; + + hw_videoport = to_tidss_crtc(state->crtc)->hw_videoport; + + ret = dispc_plane_check(tidss->dispc, hw_plane, state, hw_videoport); + if (ret) + return ret; + + return 0; +} + +static void tidss_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *ddev = plane->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_plane *tplane = to_tidss_plane(plane); + struct drm_plane_state *state = plane->state; + u32 hw_videoport; + int ret; + + dev_dbg(ddev->dev, "%s\n", __func__); + + if (!state->visible) { + dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false); + return; + } + + hw_videoport = to_tidss_crtc(state->crtc)->hw_videoport; + + ret = dispc_plane_setup(tidss->dispc, tplane->hw_plane_id, + state, hw_videoport); + + if (ret) { + dev_err(plane->dev->dev, "%s: Failed to setup plane %d\n", + __func__, tplane->hw_plane_id); + dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false); + return; + } + + dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, true); +} + +static void tidss_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *ddev = plane->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_plane *tplane = to_tidss_plane(plane); + + dev_dbg(ddev->dev, "%s\n", __func__); + + dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false); +} + +static const struct drm_plane_helper_funcs tidss_plane_helper_funcs = { + .atomic_check = tidss_plane_atomic_check, + .atomic_update = tidss_plane_atomic_update, + .atomic_disable = tidss_plane_atomic_disable, +}; + +static const struct drm_plane_funcs tidss_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .destroy = drm_plane_cleanup, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +struct tidss_plane *tidss_plane_create(struct tidss_device *tidss, + u32 hw_plane_id, u32 plane_type, + u32 crtc_mask, const u32 *formats, + u32 num_formats) +{ + struct tidss_plane *tplane; + enum drm_plane_type type; + u32 possible_crtcs; + u32 num_planes = tidss->feat->num_planes; + u32 color_encodings = (BIT(DRM_COLOR_YCBCR_BT601) | + BIT(DRM_COLOR_YCBCR_BT709)); + u32 color_ranges = (BIT(DRM_COLOR_YCBCR_FULL_RANGE) | + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE)); + u32 default_encoding = DRM_COLOR_YCBCR_BT601; + u32 default_range = DRM_COLOR_YCBCR_FULL_RANGE; + u32 blend_modes = (BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE)); + int ret; + + tplane = devm_kzalloc(tidss->dev, sizeof(*tplane), GFP_KERNEL); + if (!tplane) + return ERR_PTR(-ENOMEM); + + tplane->hw_plane_id = hw_plane_id; + + possible_crtcs = crtc_mask; + type = plane_type; + + ret = drm_universal_plane_init(&tidss->ddev, &tplane->plane, + possible_crtcs, + &tidss_plane_funcs, + formats, num_formats, + NULL, type, NULL); + if (ret < 0) + return ERR_PTR(ret); + + drm_plane_helper_add(&tplane->plane, &tidss_plane_helper_funcs); + + drm_plane_create_zpos_property(&tplane->plane, hw_plane_id, 0, + num_planes - 1); + + ret = drm_plane_create_color_properties(&tplane->plane, + color_encodings, + color_ranges, + default_encoding, + default_range); + if (ret) + return ERR_PTR(ret); + + ret = drm_plane_create_alpha_property(&tplane->plane); + if (ret) + return ERR_PTR(ret); + + ret = drm_plane_create_blend_mode_property(&tplane->plane, blend_modes); + if (ret) + return ERR_PTR(ret); + + return tplane; +} diff --git a/drivers/gpu/drm/tidss/tidss_plane.h b/drivers/gpu/drm/tidss/tidss_plane.h new file mode 100644 index 000000000000..80ff1c5a2535 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_plane.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#ifndef __TIDSS_PLANE_H__ +#define __TIDSS_PLANE_H__ + +#define to_tidss_plane(p) container_of((p), struct tidss_plane, plane) + +struct tidss_device; + +struct tidss_plane { + struct drm_plane plane; + + u32 hw_plane_id; +}; + +struct tidss_plane *tidss_plane_create(struct tidss_device *tidss, + u32 hw_plane_id, u32 plane_type, + u32 crtc_mask, const u32 *formats, + u32 num_formats); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_scale_coefs.c b/drivers/gpu/drm/tidss/tidss_scale_coefs.c new file mode 100644 index 000000000000..5ec68389cc68 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_scale_coefs.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Jyri Sarha <jsarha@ti.com> + */ + +#include <linux/device.h> +#include <linux/kernel.h> + +#include "tidss_scale_coefs.h" + +/* + * These are interpolated with a custom python script from DSS5 + * (drivers/gpu/drm/omapdrm/dss/dispc_coef.c) coefficients. + */ +static const struct tidss_scale_coefs coef5_m32 = { + .c2 = { 28, 34, 40, 46, 52, 58, 64, 70, 0, 2, 4, 8, 12, 16, 20, 24, }, + .c1 = { 132, 138, 144, 150, 156, 162, 168, 174, 76, 84, 92, 98, 104, 110, 116, 124, }, + .c0 = { 192, 192, 192, 190, 188, 186, 184, 182, 180, }, +}; + +static const struct tidss_scale_coefs coef5_m26 = { + .c2 = { 24, 28, 32, 38, 44, 50, 56, 64, 0, 2, 4, 6, 8, 12, 16, 20, }, + .c1 = { 132, 138, 144, 152, 160, 166, 172, 178, 72, 80, 88, 94, 100, 108, 116, 124, }, + .c0 = { 200, 202, 204, 202, 200, 196, 192, 188, 184, }, +}; + +static const struct tidss_scale_coefs coef5_m22 = { + .c2 = { 16, 20, 24, 30, 36, 42, 48, 56, 0, 0, 0, 2, 4, 8, 12, 14, }, + .c1 = { 132, 140, 148, 156, 164, 172, 180, 186, 64, 72, 80, 88, 96, 104, 112, 122, }, + .c0 = { 216, 216, 216, 214, 212, 208, 204, 198, 192, }, +}; + +static const struct tidss_scale_coefs coef5_m19 = { + .c2 = { 12, 14, 16, 22, 28, 34, 40, 48, 0, 0, 0, 2, 4, 4, 4, 8, }, + .c1 = { 128, 140, 152, 160, 168, 176, 184, 192, 56, 64, 72, 82, 92, 100, 108, 118, }, + .c0 = { 232, 232, 232, 226, 220, 218, 216, 208, 200, }, +}; + +static const struct tidss_scale_coefs coef5_m16 = { + .c2 = { 0, 2, 4, 8, 12, 18, 24, 32, 0, 0, 0, -2, -4, -4, -4, -2, }, + .c1 = { 124, 138, 152, 164, 176, 186, 196, 206, 40, 48, 56, 68, 80, 90, 100, 112, }, + .c0 = { 264, 262, 260, 254, 248, 242, 236, 226, 216, }, +}; + +static const struct tidss_scale_coefs coef5_m14 = { + .c2 = { -8, -6, -4, -2, 0, 6, 12, 18, 0, -2, -4, -6, -8, -8, -8, -8, }, + .c1 = { 120, 134, 148, 164, 180, 194, 208, 220, 24, 32, 40, 52, 64, 78, 92, 106, }, + .c0 = { 288, 286, 284, 280, 276, 266, 256, 244, 232, }, +}; + +static const struct tidss_scale_coefs coef5_m13 = { + .c2 = { -12, -12, -12, -10, -8, -4, 0, 6, 0, -2, -4, -6, -8, -10, -12, -12, }, + .c1 = { 112, 130, 148, 164, 180, 196, 212, 228, 12, 22, 32, 44, 56, 70, 84, 98, }, + .c0 = { 312, 308, 304, 298, 292, 282, 272, 258, 244, }, +}; + +static const struct tidss_scale_coefs coef5_m12 = { + .c2 = { -16, -18, -20, -18, -16, -14, -12, -6, 0, -2, -4, -6, -8, -10, -12, -14, }, + .c1 = { 104, 124, 144, 164, 184, 202, 220, 238, 0, 10, 20, 30, 40, 56, 72, 88, }, + .c0 = { 336, 332, 328, 320, 312, 300, 288, 272, 256, }, +}; + +static const struct tidss_scale_coefs coef5_m11 = { + .c2 = { -20, -22, -24, -24, -24, -24, -24, -20, 0, -2, -4, -6, -8, -10, -12, -16, }, + .c1 = { 92, 114, 136, 158, 180, 204, 228, 250, -16, -8, 0, 12, 24, 38, 52, 72, }, + .c0 = { 368, 364, 360, 350, 340, 326, 312, 292, 272, }, +}; + +static const struct tidss_scale_coefs coef5_m10 = { + .c2 = { -16, -20, -24, -28, -32, -34, -36, -34, 0, 0, 0, -2, -4, -8, -12, -14, }, + .c1 = { 72, 96, 120, 148, 176, 204, 232, 260, -32, -26, -20, -10, 0, 16, 32, 52, }, + .c0 = { 400, 398, 396, 384, 372, 354, 336, 312, 288, }, +}; + +static const struct tidss_scale_coefs coef5_m9 = { + .c2 = { -12, -18, -24, -28, -32, -38, -44, -46, 0, 2, 4, 2, 0, -2, -4, -8, }, + .c1 = { 40, 68, 96, 128, 160, 196, 232, 268, -48, -46, -44, -36, -28, -14, 0, 20, }, + .c0 = { 456, 450, 444, 428, 412, 388, 364, 334, 304, }, +}; + +static const struct tidss_scale_coefs coef5_m8 = { + .c2 = { 0, -4, -8, -16, -24, -32, -40, -48, 0, 2, 4, 6, 8, 6, 4, 2, }, + .c1 = { 0, 28, 56, 94, 132, 176, 220, 266, -56, -60, -64, -62, -60, -50, -40, -20, }, + .c0 = { 512, 506, 500, 478, 456, 424, 392, 352, 312, }, +}; + +static const struct tidss_scale_coefs coef3_m32 = { + .c1 = { 108, 92, 76, 62, 48, 36, 24, 140, 256, 236, 216, 198, 180, 162, 144, 126, }, + .c0 = { 296, 294, 292, 288, 284, 278, 272, 136, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m26 = { + .c1 = { 104, 90, 76, 60, 44, 32, 20, 138, 256, 236, 216, 198, 180, 160, 140, 122, }, + .c0 = { 304, 300, 296, 292, 288, 282, 276, 138, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m22 = { + .c1 = { 100, 84, 68, 54, 40, 30, 20, 138, 256, 236, 216, 196, 176, 156, 136, 118, }, + .c0 = { 312, 310, 308, 302, 296, 286, 276, 138, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m19 = { + .c1 = { 96, 80, 64, 50, 36, 26, 16, 136, 256, 236, 216, 194, 172, 152, 132, 114, }, + .c0 = { 320, 318, 316, 310, 304, 292, 280, 140, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m16 = { + .c1 = { 88, 72, 56, 44, 32, 22, 12, 134, 256, 234, 212, 190, 168, 148, 128, 108, }, + .c0 = { 336, 332, 328, 320, 312, 300, 288, 144, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m14 = { + .c1 = { 80, 64, 48, 36, 24, 16, 8, 132, 256, 232, 208, 186, 164, 142, 120, 100, }, + .c0 = { 352, 348, 344, 334, 324, 310, 296, 148, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m13 = { + .c1 = { 72, 56, 40, 30, 20, 12, 4, 130, 256, 232, 208, 184, 160, 136, 112, 92, }, + .c0 = { 368, 364, 360, 346, 332, 316, 300, 150, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m12 = { + .c1 = { 64, 50, 36, 26, 16, 10, 4, 130, 256, 230, 204, 178, 152, 128, 104, 84, }, + .c0 = { 384, 378, 372, 358, 344, 324, 304, 152, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m11 = { + .c1 = { 56, 40, 24, 16, 8, 4, 0, 128, 256, 228, 200, 172, 144, 120, 96, 76, }, + .c0 = { 400, 396, 392, 376, 360, 336, 312, 156, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m10 = { + .c1 = { 40, 26, 12, 6, 0, -2, -4, 126, 256, 226, 196, 166, 136, 110, 84, 62, }, + .c0 = { 432, 424, 416, 396, 376, 348, 320, 160, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m9 = { + .c1 = { 24, 12, 0, -4, -8, -8, -8, 124, 256, 222, 188, 154, 120, 92, 64, 44, }, + .c0 = { 464, 456, 448, 424, 400, 366, 332, 166, 256, }, +}; + +static const struct tidss_scale_coefs coef3_m8 = { + .c1 = { 0, -8, -16, -16, -16, -12, -8, 124, 256, 214, 172, 134, 96, 66, 36, 18, }, + .c0 = { 512, 502, 492, 462, 432, 390, 348, 174, 256, }, +}; + +const struct tidss_scale_coefs *tidss_get_scale_coefs(struct device *dev, + u32 firinc, + bool five_taps) +{ + int i; + int inc; + static const struct { + int mmin; + int mmax; + const struct tidss_scale_coefs *coef3; + const struct tidss_scale_coefs *coef5; + const char *name; + } coefs[] = { + { 27, 32, &coef3_m32, &coef5_m32, "M32" }, + { 23, 26, &coef3_m26, &coef5_m26, "M26" }, + { 20, 22, &coef3_m22, &coef5_m22, "M22" }, + { 17, 19, &coef3_m19, &coef5_m19, "M19" }, + { 15, 16, &coef3_m16, &coef5_m16, "M16" }, + { 14, 14, &coef3_m14, &coef5_m14, "M14" }, + { 13, 13, &coef3_m13, &coef5_m13, "M13" }, + { 12, 12, &coef3_m12, &coef5_m12, "M12" }, + { 11, 11, &coef3_m11, &coef5_m11, "M11" }, + { 10, 10, &coef3_m10, &coef5_m10, "M10" }, + { 9, 9, &coef3_m9, &coef5_m9, "M9" }, + { 4, 8, &coef3_m8, &coef5_m8, "M8" }, + /* + * When upscaling more than two times, blockiness and outlines + * around the image are observed when M8 tables are used. M11, + * M16 and M19 tables are used to prevent this. + */ + { 3, 3, &coef3_m11, &coef5_m11, "M11" }, + { 2, 2, &coef3_m16, &coef5_m16, "M16" }, + { 0, 1, &coef3_m19, &coef5_m19, "M19" }, + }; + + /* + * inc is result of 0x200000 * in_size / out_size. This dividing + * by 0x40000 scales it down to 8 * in_size / out_size. After + * division the actual scaling factor is 8/inc. + */ + inc = firinc / 0x40000; + for (i = 0; i < ARRAY_SIZE(coefs); ++i) { + if (inc >= coefs[i].mmin && inc <= coefs[i].mmax) { + if (five_taps) + return coefs[i].coef5; + else + return coefs[i].coef3; + } + } + + dev_err(dev, "%s: Coefficients not found for firinc 0x%08x, inc %d\n", + __func__, firinc, inc); + + return NULL; +} diff --git a/drivers/gpu/drm/tidss/tidss_scale_coefs.h b/drivers/gpu/drm/tidss/tidss_scale_coefs.h new file mode 100644 index 000000000000..64b5af5b5361 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_scale_coefs.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Jyri Sarha <jsarha@ti.com> + */ + +#ifndef __TIDSS_DISPC_COEF_H__ +#define __TIDSS_DISPC_COEF_H__ + +#include <linux/types.h> + +struct tidss_scale_coefs { + s16 c2[16]; + s16 c1[16]; + u16 c0[9]; +}; + +const struct tidss_scale_coefs *tidss_get_scale_coefs(struct device *dev, + u32 firinc, + bool five_taps); + +#endif diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index a46ac284dd5e..4160e74e4751 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -47,6 +47,20 @@ config TINYDRM_ILI9341 If M is selected the module will be called ili9341. +config TINYDRM_ILI9486 + tristate "DRM support for ILI9486 display panels" + depends on DRM && SPI + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_MIPI_DBI + select BACKLIGHT_CLASS_DEVICE + help + DRM driver for the following Ilitek ILI9486 panels: + * PISCREEN 3.5" 320x480 TFT (Ozzmaker 3.5") + * RPILCD 3.5" 320x480 TFT (Waveshare 3.5") + + If M is selected the module will be called ili9486. + config TINYDRM_MI0283QT tristate "DRM support for MI0283QT" depends on DRM && SPI @@ -85,14 +99,16 @@ config TINYDRM_ST7586 If M is selected the module will be called st7586. config TINYDRM_ST7735R - tristate "DRM support for Sitronix ST7735R display panels" + tristate "DRM support for Sitronix ST7715R/ST7735R display panels" depends on DRM && SPI select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_MIPI_DBI select BACKLIGHT_CLASS_DEVICE help - DRM driver Sitronix ST7735R with one of the following LCDs: - * JD-T18003-T01 1.8" 128x160 TFT + DRM driver for Sitronix ST7715R/ST7735R with one of the following + LCDs: + * Jianda JD-T18003-T01 1.8" 128x160 TFT + * Okaya RH128128T 1.44" 128x128 TFT If M is selected the module will be called st7735r. diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index 896cf31132d3..c96ceee71453 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_GM12U320) += gm12u320.o obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o obj-$(CONFIG_TINYDRM_ILI9341) += ili9341.o +obj-$(CONFIG_TINYDRM_ILI9486) += ili9486.o obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o obj-$(CONFIG_TINYDRM_ST7586) += st7586.o diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 94fb1f593564..a48173441ae0 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -22,7 +22,6 @@ #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> -#include <drm/drm_vblank.h> static bool eco_mode; module_param(eco_mode, bool, 0644); @@ -610,18 +609,10 @@ static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; - struct drm_crtc *crtc = &pipe->crtc; struct drm_rect rect; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) gm12u320_fb_mark_dirty(pipe->plane.state->fb, &rect); - - if (crtc->state->event) { - spin_lock_irq(&crtc->dev->event_lock); - drm_crtc_send_vblank_event(crtc, crtc->state->event); - crtc->state->event = NULL; - spin_unlock_irq(&crtc->dev->event_lock); - } } static const struct drm_simple_display_pipe_funcs gm12u320_pipe_funcs = { diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index c66acc566c2b..802fb8dde1b6 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -26,7 +26,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_rect.h> -#include <drm/drm_vblank.h> #define ILI9225_DRIVER_READ_CODE 0x00 #define ILI9225_DRIVER_OUTPUT_CONTROL 0x01 @@ -165,18 +164,10 @@ static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; - struct drm_crtc *crtc = &pipe->crtc; struct drm_rect rect; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) ili9225_fb_dirty(state->fb, &rect); - - if (crtc->state->event) { - spin_lock_irq(&crtc->dev->event_lock); - drm_crtc_send_vblank_event(crtc, crtc->state->event); - spin_unlock_irq(&crtc->dev->event_lock); - crtc->state->event = NULL; - } } static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c new file mode 100644 index 000000000000..5084b38c1a71 --- /dev/null +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DRM driver for Ilitek ILI9486 panels + * + * Copyright 2020 Kamlesh Gurudasani <kamlesh.gurudasani@gmail.com> + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/spi/spi.h> + +#include <video/mipi_display.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_mipi_dbi.h> +#include <drm/drm_modeset_helper.h> + +#define ILI9486_ITFCTR1 0xb0 +#define ILI9486_PWCTRL1 0xc2 +#define ILI9486_VMCTRL1 0xc5 +#define ILI9486_PGAMCTRL 0xe0 +#define ILI9486_NGAMCTRL 0xe1 +#define ILI9486_DGAMCTRL 0xe2 +#define ILI9486_MADCTL_BGR BIT(3) +#define ILI9486_MADCTL_MV BIT(5) +#define ILI9486_MADCTL_MX BIT(6) +#define ILI9486_MADCTL_MY BIT(7) + +/* + * The PiScreen/waveshare rpi-lcd-35 has a SPI to 16-bit parallel bus converter + * in front of the display controller. This means that 8-bit values have to be + * transferred as 16-bit. + */ +static int waveshare_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par, + size_t num) +{ + struct spi_device *spi = mipi->spi; + void *data = par; + u32 speed_hz; + int i, ret; + u16 *buf; + + buf = kmalloc(32 * sizeof(u16), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * The displays are Raspberry Pi HATs and connected to the 8-bit only + * SPI controller, so 16-bit command and parameters need byte swapping + * before being transferred as 8-bit on the big endian SPI bus. + * Pixel data bytes have already been swapped before this function is + * called. + */ + buf[0] = cpu_to_be16(*cmd); + gpiod_set_value_cansleep(mipi->dc, 0); + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 2); + ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, buf, 2); + if (ret || !num) + goto free; + + /* 8-bit configuration data, not 16-bit pixel data */ + if (num <= 32) { + for (i = 0; i < num; i++) + buf[i] = cpu_to_be16(par[i]); + num *= 2; + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); + data = buf; + } + + gpiod_set_value_cansleep(mipi->dc, 1); + ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, data, num); + free: + kfree(buf); + + return ret; +} + +static void waveshare_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); + struct mipi_dbi *dbi = &dbidev->dbi; + u8 addr_mode; + int ret, idx; + + if (!drm_dev_enter(pipe->crtc.dev, &idx)) + return; + + DRM_DEBUG_KMS("\n"); + + ret = mipi_dbi_poweron_conditional_reset(dbidev); + if (ret < 0) + goto out_exit; + if (ret == 1) + goto out_enable; + + mipi_dbi_command(dbi, ILI9486_ITFCTR1); + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(250); + + mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, 0x55); + + mipi_dbi_command(dbi, ILI9486_PWCTRL1, 0x44); + + mipi_dbi_command(dbi, ILI9486_VMCTRL1, 0x00, 0x00, 0x00, 0x00); + + mipi_dbi_command(dbi, ILI9486_PGAMCTRL, + 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, + 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x0); + mipi_dbi_command(dbi, ILI9486_NGAMCTRL, + 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, + 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00); + mipi_dbi_command(dbi, ILI9486_DGAMCTRL, + 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, + 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00); + + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); + msleep(100); + + out_enable: + switch (dbidev->rotation) { + case 90: + addr_mode = ILI9486_MADCTL_MY; + break; + case 180: + addr_mode = ILI9486_MADCTL_MV; + break; + case 270: + addr_mode = ILI9486_MADCTL_MX; + break; + default: + addr_mode = ILI9486_MADCTL_MV | ILI9486_MADCTL_MY | + ILI9486_MADCTL_MX; + break; + } + addr_mode |= ILI9486_MADCTL_BGR; + mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); + mipi_dbi_enable_flush(dbidev, crtc_state, plane_state); + out_exit: + drm_dev_exit(idx); +} + +static const struct drm_simple_display_pipe_funcs waveshare_pipe_funcs = { + .enable = waveshare_enable, + .disable = mipi_dbi_pipe_disable, + .update = mipi_dbi_pipe_update, + .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, +}; + +static const struct drm_display_mode waveshare_mode = { + DRM_SIMPLE_MODE(480, 320, 73, 49), +}; + +DEFINE_DRM_GEM_CMA_FOPS(ili9486_fops); + +static struct drm_driver ili9486_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &ili9486_fops, + .release = mipi_dbi_release, + DRM_GEM_CMA_VMAP_DRIVER_OPS, + .debugfs_init = mipi_dbi_debugfs_init, + .name = "ili9486", + .desc = "Ilitek ILI9486", + .date = "20200118", + .major = 1, + .minor = 0, +}; + +static const struct of_device_id ili9486_of_match[] = { + { .compatible = "waveshare,rpi-lcd-35" }, + { .compatible = "ozzmaker,piscreen" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ili9486_of_match); + +static const struct spi_device_id ili9486_id[] = { + { "ili9486", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ili9486_id); + +static int ili9486_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct mipi_dbi_dev *dbidev; + struct drm_device *drm; + struct mipi_dbi *dbi; + struct gpio_desc *dc; + u32 rotation = 0; + int ret; + + dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL); + if (!dbidev) + return -ENOMEM; + + dbi = &dbidev->dbi; + drm = &dbidev->drm; + ret = devm_drm_dev_init(dev, drm, &ili9486_driver); + if (ret) { + kfree(dbidev); + return ret; + } + + drm_mode_config_init(drm); + + dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(dbi->reset)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); + return PTR_ERR(dbi->reset); + } + + dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); + if (IS_ERR(dc)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); + return PTR_ERR(dc); + } + + dbidev->backlight = devm_of_find_backlight(dev); + if (IS_ERR(dbidev->backlight)) + return PTR_ERR(dbidev->backlight); + + device_property_read_u32(dev, "rotation", &rotation); + + ret = mipi_dbi_spi_init(spi, dbi, dc); + if (ret) + return ret; + + dbi->command = waveshare_command; + dbi->read_commands = NULL; + + ret = mipi_dbi_dev_init(dbidev, &waveshare_pipe_funcs, + &waveshare_mode, rotation); + if (ret) + return ret; + + drm_mode_config_reset(drm); + + ret = drm_dev_register(drm, 0); + if (ret) + return ret; + + spi_set_drvdata(spi, drm); + + drm_fbdev_generic_setup(drm, 0); + + return 0; +} + +static int ili9486_remove(struct spi_device *spi) +{ + struct drm_device *drm = spi_get_drvdata(spi); + + drm_dev_unplug(drm); + drm_atomic_helper_shutdown(drm); + + return 0; +} + +static void ili9486_shutdown(struct spi_device *spi) +{ + drm_atomic_helper_shutdown(spi_get_drvdata(spi)); +} + +static struct spi_driver ili9486_spi_driver = { + .driver = { + .name = "ili9486", + .of_match_table = ili9486_of_match, + }, + .id_table = ili9486_id, + .probe = ili9486_probe, + .remove = ili9486_remove, + .shutdown = ili9486_shutdown, +}; +module_spi_driver(ili9486_spi_driver); + +MODULE_DESCRIPTION("Ilitek ILI9486 DRM driver"); +MODULE_AUTHOR("Kamlesh Gurudasani <kamlesh.gurudasani@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index 76d179200775..f5ebcaf7ee3a 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -17,7 +17,7 @@ #include <linux/dma-buf.h> #include <linux/gpio/consumer.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/property.h> #include <linux/sched/clock.h> #include <linux/spi/spi.h> #include <linux/thermal.h> @@ -33,13 +33,13 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_modes.h> #include <drm/drm_rect.h> -#include <drm/drm_vblank.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #define REPAPER_RID_G2_COG_ID 0x12 enum repaper_model { + /* 0 is reserved to avoid clashing with NULL */ E1144CS021 = 1, E1190CS021, E2200CS021, @@ -856,18 +856,10 @@ static void repaper_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; - struct drm_crtc *crtc = &pipe->crtc; struct drm_rect rect; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) repaper_fb_dirty(state->fb); - - if (crtc->state->event) { - spin_lock_irq(&crtc->dev->event_lock); - drm_crtc_send_vblank_event(crtc, crtc->state->event); - spin_unlock_irq(&crtc->dev->event_lock); - crtc->state->event = NULL; - } } static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = { @@ -995,21 +987,21 @@ static int repaper_probe(struct spi_device *spi) { const struct drm_display_mode *mode; const struct spi_device_id *spi_id; - const struct of_device_id *match; struct device *dev = &spi->dev; enum repaper_model model; const char *thermal_zone; struct repaper_epd *epd; size_t line_buffer_size; struct drm_device *drm; + const void *match; int ret; - match = of_match_device(repaper_of_match, dev); + match = device_get_match_data(dev); if (match) { - model = (enum repaper_model)match->data; + model = (enum repaper_model)match; } else { spi_id = spi_get_device_id(spi); - model = spi_id->driver_data; + model = (enum repaper_model)spi_id->driver_data; } /* The SPI device is used to allocate dma memory */ @@ -1197,7 +1189,6 @@ static void repaper_shutdown(struct spi_device *spi) static struct spi_driver repaper_spi_driver = { .driver = { .name = "repaper", - .owner = THIS_MODULE, .of_match_table = repaper_of_match, }, .id_table = repaper_id, diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index 060cc756194f..9ef559dd3191 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -23,7 +23,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_rect.h> -#include <drm/drm_vblank.h> /* controller-specific commands */ #define ST7586_DISP_MODE_GRAY 0x38 @@ -159,18 +158,10 @@ static void st7586_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; - struct drm_crtc *crtc = &pipe->crtc; struct drm_rect rect; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) st7586_fb_dirty(state->fb, &rect); - - if (crtc->state->event) { - spin_lock_irq(&crtc->dev->event_lock); - drm_crtc_send_vblank_event(crtc, crtc->state->event); - spin_unlock_irq(&crtc->dev->event_lock); - crtc->state->event = NULL; - } } static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index 3f4487c71684..3cd9b8d9888d 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * DRM driver for Sitronix ST7735R panels + * DRM driver for display panels connected to a Sitronix ST7715R or ST7735R + * display controller in SPI mode. * * Copyright 2017 David Lechner <david@lechnology.com> + * Copyright (C) 2019 Glider bvba */ #include <linux/backlight.h> @@ -37,12 +39,28 @@ #define ST7735R_MY BIT(7) #define ST7735R_MX BIT(6) #define ST7735R_MV BIT(5) +#define ST7735R_RGB BIT(3) + +struct st7735r_cfg { + const struct drm_display_mode mode; + unsigned int left_offset; + unsigned int top_offset; + unsigned int write_only:1; + unsigned int rgb:1; /* RGB (vs. BGR) */ +}; + +struct st7735r_priv { + struct mipi_dbi_dev dbidev; /* Must be first for .release() */ + const struct st7735r_cfg *cfg; +}; -static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plane_state) +static void st7735r_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); + struct st7735r_priv *priv = container_of(dbidev, struct st7735r_priv, + dbidev); struct mipi_dbi *dbi = &dbidev->dbi; int ret, idx; u8 addr_mode; @@ -87,6 +105,10 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe, addr_mode = ST7735R_MY | ST7735R_MV; break; } + + if (priv->cfg->rgb) + addr_mode |= ST7735R_RGB; + mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); @@ -109,15 +131,24 @@ out_exit: drm_dev_exit(idx); } -static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = { - .enable = jd_t18003_t01_pipe_enable, +static const struct drm_simple_display_pipe_funcs st7735r_pipe_funcs = { + .enable = st7735r_pipe_enable, .disable = mipi_dbi_pipe_disable, .update = mipi_dbi_pipe_update, .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, }; -static const struct drm_display_mode jd_t18003_t01_mode = { - DRM_SIMPLE_MODE(128, 160, 28, 35), +static const struct st7735r_cfg jd_t18003_t01_cfg = { + .mode = { DRM_SIMPLE_MODE(128, 160, 28, 35) }, + /* Cannot read from Adafruit 1.8" display via SPI */ + .write_only = true, +}; + +static const struct st7735r_cfg rh128128t_cfg = { + .mode = { DRM_SIMPLE_MODE(128, 128, 25, 26) }, + .left_offset = 2, + .top_offset = 3, + .rgb = true, }; DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops); @@ -136,13 +167,14 @@ static struct drm_driver st7735r_driver = { }; static const struct of_device_id st7735r_of_match[] = { - { .compatible = "jianda,jd-t18003-t01" }, + { .compatible = "jianda,jd-t18003-t01", .data = &jd_t18003_t01_cfg }, + { .compatible = "okaya,rh128128t", .data = &rh128128t_cfg }, { }, }; MODULE_DEVICE_TABLE(of, st7735r_of_match); static const struct spi_device_id st7735r_id[] = { - { "jd-t18003-t01", 0 }, + { "jd-t18003-t01", (uintptr_t)&jd_t18003_t01_cfg }, { }, }; MODULE_DEVICE_TABLE(spi, st7735r_id); @@ -150,17 +182,26 @@ MODULE_DEVICE_TABLE(spi, st7735r_id); static int st7735r_probe(struct spi_device *spi) { struct device *dev = &spi->dev; + const struct st7735r_cfg *cfg; struct mipi_dbi_dev *dbidev; + struct st7735r_priv *priv; struct drm_device *drm; struct mipi_dbi *dbi; struct gpio_desc *dc; u32 rotation = 0; int ret; - dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL); - if (!dbidev) + cfg = device_get_match_data(&spi->dev); + if (!cfg) + cfg = (void *)spi_get_device_id(spi)->driver_data; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; + dbidev = &priv->dbidev; + priv->cfg = cfg; + dbi = &dbidev->dbi; drm = &dbidev->drm; ret = devm_drm_dev_init(dev, drm, &st7735r_driver); @@ -193,10 +234,14 @@ static int st7735r_probe(struct spi_device *spi) if (ret) return ret; - /* Cannot read from Adafruit 1.8" display via SPI */ - dbi->read_commands = NULL; + if (cfg->write_only) + dbi->read_commands = NULL; + + dbidev->left_offset = cfg->left_offset; + dbidev->top_offset = cfg->top_offset; - ret = mipi_dbi_dev_init(dbidev, &jd_t18003_t01_pipe_funcs, &jd_t18003_t01_mode, rotation); + ret = mipi_dbi_dev_init(dbidev, &st7735r_pipe_funcs, &cfg->mode, + rotation); if (ret) return ret; @@ -231,7 +276,6 @@ static void st7735r_shutdown(struct spi_device *spi) static struct spi_driver st7735r_spi_driver = { .driver = { .name = "st7735r", - .owner = THIS_MODULE, .of_match_table = st7735r_of_match, }, .id_table = st7735r_id, diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 5df596fb0280..1fbc36f05d89 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -372,14 +372,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, } moved: - if (bo->evicted) { - if (bdev->driver->invalidate_caches) { - ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement); - if (ret) - pr_err("Can not flush read caches\n"); - } - bo->evicted = false; - } + bo->evicted = false; if (bo->mem.mm_node) bo->offset = (bo->mem.start << PAGE_SHIFT) + @@ -498,8 +491,10 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) dma_resv_unlock(bo->base.resv); } - if (bo->base.resv != &bo->base._resv) + if (bo->base.resv != &bo->base._resv) { + ttm_bo_flush_all_fences(bo); dma_resv_unlock(&bo->base._resv); + } error: kref_get(&bo->list_kref); diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index e9671d38b4a0..0afdfb0d1fe1 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -109,7 +109,6 @@ static const struct drm_connector_helper_funcs udl_connector_helper_funcs = { }; static const struct drm_connector_funcs udl_connector_funcs = { - .dpms = drm_helper_connector_dpms, .reset = drm_atomic_helper_connector_reset, .detect = udl_detect, .fill_modes = drm_helper_probe_single_connector_modes, diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index 22af17959053..d59ebac70b15 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -375,8 +375,6 @@ udl_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, char *wrptr; int color_depth = UDL_COLOR_DEPTH_16BPP; - crtc_state->no_vblank = true; - buf = (char *)udl->mode_buf; /* This first section has to do with setting the base address on the @@ -428,14 +426,6 @@ udl_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe) udl_submit_urb(dev, urb, buf - (char *)urb->transfer_buffer); } -static int -udl_simple_display_pipe_check(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state, - struct drm_crtc_state *crtc_state) -{ - return 0; -} - static void udl_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_plane_state) @@ -457,7 +447,6 @@ struct drm_simple_display_pipe_funcs udl_simple_display_pipe_funcs = { .mode_valid = udl_simple_display_pipe_mode_valid, .enable = udl_simple_display_pipe_enable, .disable = udl_simple_display_pipe_disable, - .check = udl_simple_display_pipe_check, .update = udl_simple_display_pipe_update, .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, }; diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c index 19612132c8a3..0883a435e62b 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_mode.c +++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c @@ -18,7 +18,6 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_vblank.h> #include "hgsmi_channels.h" #include "vbox_drv.h" @@ -226,17 +225,6 @@ static void vbox_crtc_atomic_disable(struct drm_crtc *crtc, static void vbox_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { - struct drm_pending_vblank_event *event; - unsigned long flags; - - if (crtc->state && crtc->state->event) { - event = crtc->state->event; - crtc->state->event = NULL; - - spin_lock_irqsave(&crtc->dev->event_lock, flags); - drm_crtc_send_vblank_event(crtc, event); - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - } } static const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = { @@ -838,6 +826,7 @@ static int vbox_connector_init(struct drm_device *dev, static const struct drm_mode_config_funcs vbox_mode_funcs = { .fb_create = drm_gem_fb_create_with_dirty, + .mode_valid = drm_vram_helper_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 4934127f0d76..91e408f7a56e 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -139,7 +139,7 @@ static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) static bool plane_enabled(struct drm_plane_state *state) { - return state->fb && state->crtc; + return state->fb && !WARN_ON(!state->crtc); } static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane) diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c index 5156e6b279db..e27120d512b0 100644 --- a/drivers/gpu/drm/virtio/virtgpu_debugfs.c +++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c @@ -47,6 +47,7 @@ static int virtio_gpu_features(struct seq_file *m, void *data) virtio_add_bool(m, "virgl", vgdev->has_virgl_3d); virtio_add_bool(m, "edid", vgdev->has_edid); + virtio_add_bool(m, "indirect", vgdev->has_indirect); virtio_add_int(m, "cap sets", vgdev->num_capsets); virtio_add_int(m, "scanouts", vgdev->num_scanouts); return 0; diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 0966208ec30d..7b0f0643bb2d 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -30,7 +30,6 @@ #include <drm/drm_fourcc.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_vblank.h> #include "virtgpu_drv.h" @@ -121,13 +120,6 @@ static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc, static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { - unsigned long flags; - - spin_lock_irqsave(&crtc->dev->event_lock, flags); - if (crtc->state->event) - drm_crtc_send_vblank_event(crtc, crtc->state->event); - crtc->state->event = NULL; - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = { @@ -332,6 +324,7 @@ static void vgdev_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_enables(dev, state); drm_atomic_helper_commit_planes(dev, state, 0); + drm_atomic_helper_fake_vblank(state); drm_atomic_helper_commit_hw_done(state); drm_atomic_helper_wait_for_vblanks(dev, state); diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 7e69c06e168e..d278c8c50f39 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -193,6 +193,7 @@ struct virtio_gpu_device { bool has_virgl_3d; bool has_edid; + bool has_indirect; struct work_struct config_changed_work; diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 2f5773e43557..c1086df49816 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -159,6 +159,9 @@ int virtio_gpu_init(struct drm_device *dev) if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_EDID)) { vgdev->has_edid = true; } + if (virtio_has_feature(vgdev->vdev, VIRTIO_RING_F_INDIRECT_DESC)) { + vgdev->has_indirect = true; + } DRM_INFO("features: %cvirgl %cedid\n", vgdev->has_virgl_3d ? '+' : '-', diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 5914e79d3429..cc02fc4bab2a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -95,7 +95,8 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, if (!vbuf) return ERR_PTR(-ENOMEM); - BUG_ON(size > MAX_INLINE_CMD_SIZE); + BUG_ON(size > MAX_INLINE_CMD_SIZE || + size < sizeof(struct virtio_gpu_ctrl_hdr)); vbuf->buf = (void *)vbuf + sizeof(*vbuf); vbuf->size = size; @@ -109,6 +110,16 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, return vbuf; } +static struct virtio_gpu_ctrl_hdr * +virtio_gpu_vbuf_ctrl_hdr(struct virtio_gpu_vbuffer *vbuf) +{ + /* this assumes a vbuf contains a command that starts with a + * virtio_gpu_ctrl_hdr, which is true for both ctrl and cursor + * virtqueues. + */ + return (struct virtio_gpu_ctrl_hdr *)vbuf->buf; +} + static void *virtio_gpu_alloc_cmd(struct virtio_gpu_device *vgdev, struct virtio_gpu_vbuffer **vbuffer_p, int size) @@ -211,10 +222,10 @@ void virtio_gpu_dequeue_ctrl_func(struct work_struct *work) if (resp->type != cpu_to_le32(VIRTIO_GPU_RESP_OK_NODATA)) { if (resp->type >= cpu_to_le32(VIRTIO_GPU_RESP_ERR_UNSPEC)) { struct virtio_gpu_ctrl_hdr *cmd; - cmd = (struct virtio_gpu_ctrl_hdr *)entry->buf; - DRM_ERROR("response 0x%x (command 0x%x)\n", - le32_to_cpu(resp->type), - le32_to_cpu(cmd->type)); + cmd = virtio_gpu_vbuf_ctrl_hdr(entry); + DRM_ERROR_RATELIMITED("response 0x%x (command 0x%x)\n", + le32_to_cpu(resp->type), + le32_to_cpu(cmd->type)); } else DRM_DEBUG("response 0x%x\n", le32_to_cpu(resp->type)); } @@ -307,109 +318,113 @@ static struct sg_table *vmalloc_to_sgt(char *data, uint32_t size, int *sg_ents) return sgt; } -static bool virtio_gpu_queue_ctrl_buffer_locked(struct virtio_gpu_device *vgdev, - struct virtio_gpu_vbuffer *vbuf, - struct scatterlist *vout) - __releases(&vgdev->ctrlq.qlock) - __acquires(&vgdev->ctrlq.qlock) +static void virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf, + struct virtio_gpu_fence *fence, + int elemcnt, + struct scatterlist **sgs, + int outcnt, + int incnt) { struct virtqueue *vq = vgdev->ctrlq.vq; - struct scatterlist *sgs[3], vcmd, vresp; - int outcnt = 0, incnt = 0; bool notify = false; int ret; - if (!vgdev->vqs_ready) - return notify; + if (vgdev->has_indirect) + elemcnt = 1; - sg_init_one(&vcmd, vbuf->buf, vbuf->size); - sgs[outcnt + incnt] = &vcmd; - outcnt++; +again: + spin_lock(&vgdev->ctrlq.qlock); - if (vout) { - sgs[outcnt + incnt] = vout; - outcnt++; + if (!vgdev->vqs_ready) { + spin_unlock(&vgdev->ctrlq.qlock); + + if (fence && vbuf->objs) + virtio_gpu_array_unlock_resv(vbuf->objs); + return; } - if (vbuf->resp_size) { - sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size); - sgs[outcnt + incnt] = &vresp; - incnt++; + if (vq->num_free < elemcnt) { + spin_unlock(&vgdev->ctrlq.qlock); + wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= elemcnt); + goto again; + } + + /* now that the position of the vbuf in the virtqueue is known, we can + * finally set the fence id + */ + if (fence) { + virtio_gpu_fence_emit(vgdev, virtio_gpu_vbuf_ctrl_hdr(vbuf), + fence); + if (vbuf->objs) { + virtio_gpu_array_add_fence(vbuf->objs, &fence->f); + virtio_gpu_array_unlock_resv(vbuf->objs); + } } -retry: ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC); - if (ret == -ENOSPC) { - spin_unlock(&vgdev->ctrlq.qlock); - wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= outcnt + incnt); - spin_lock(&vgdev->ctrlq.qlock); - goto retry; - } else { - trace_virtio_gpu_cmd_queue(vq, - (struct virtio_gpu_ctrl_hdr *)vbuf->buf); + WARN_ON(ret); - notify = virtqueue_kick_prepare(vq); + trace_virtio_gpu_cmd_queue(vq, virtio_gpu_vbuf_ctrl_hdr(vbuf)); + + notify = virtqueue_kick_prepare(vq); + + spin_unlock(&vgdev->ctrlq.qlock); + + if (notify) { + if (vgdev->disable_notify) + vgdev->pending_notify = true; + else + virtqueue_notify(vq); } - return notify; } static void virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev, struct virtio_gpu_vbuffer *vbuf, - struct virtio_gpu_ctrl_hdr *hdr, struct virtio_gpu_fence *fence) { - struct virtqueue *vq = vgdev->ctrlq.vq; - struct scatterlist *vout = NULL, sg; + struct scatterlist *sgs[3], vcmd, vout, vresp; struct sg_table *sgt = NULL; - bool notify; - int outcnt = 0; + int elemcnt = 0, outcnt = 0, incnt = 0; + /* set up vcmd */ + sg_init_one(&vcmd, vbuf->buf, vbuf->size); + elemcnt++; + sgs[outcnt] = &vcmd; + outcnt++; + + /* set up vout */ if (vbuf->data_size) { if (is_vmalloc_addr(vbuf->data_buf)) { + int sg_ents; sgt = vmalloc_to_sgt(vbuf->data_buf, vbuf->data_size, - &outcnt); - if (!sgt) + &sg_ents); + if (!sgt) { + if (fence && vbuf->objs) + virtio_gpu_array_unlock_resv(vbuf->objs); return; - vout = sgt->sgl; + } + + elemcnt += sg_ents; + sgs[outcnt] = sgt->sgl; } else { - sg_init_one(&sg, vbuf->data_buf, vbuf->data_size); - vout = &sg; - outcnt = 1; + sg_init_one(&vout, vbuf->data_buf, vbuf->data_size); + elemcnt++; + sgs[outcnt] = &vout; } + outcnt++; } -again: - spin_lock(&vgdev->ctrlq.qlock); - - /* - * Make sure we have enouth space in the virtqueue. If not - * wait here until we have. - * - * Without that virtio_gpu_queue_ctrl_buffer_nolock might have - * to wait for free space, which can result in fence ids being - * submitted out-of-order. - */ - if (vq->num_free < 2 + outcnt) { - spin_unlock(&vgdev->ctrlq.qlock); - wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= 3); - goto again; + /* set up vresp */ + if (vbuf->resp_size) { + sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size); + elemcnt++; + sgs[outcnt + incnt] = &vresp; + incnt++; } - if (hdr && fence) { - virtio_gpu_fence_emit(vgdev, hdr, fence); - if (vbuf->objs) { - virtio_gpu_array_add_fence(vbuf->objs, &fence->f); - virtio_gpu_array_unlock_resv(vbuf->objs); - } - } - notify = virtio_gpu_queue_ctrl_buffer_locked(vgdev, vbuf, vout); - spin_unlock(&vgdev->ctrlq.qlock); - if (notify) { - if (vgdev->disable_notify) - vgdev->pending_notify = true; - else - virtqueue_notify(vgdev->ctrlq.vq); - } + virtio_gpu_queue_ctrl_sgs(vgdev, vbuf, fence, elemcnt, sgs, outcnt, + incnt); if (sgt) { sg_free_table(sgt); @@ -435,7 +450,7 @@ void virtio_gpu_enable_notify(struct virtio_gpu_device *vgdev) static void virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev, struct virtio_gpu_vbuffer *vbuf) { - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, NULL, NULL); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, NULL); } static void virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev, @@ -464,7 +479,7 @@ retry: goto retry; } else { trace_virtio_gpu_cmd_queue(vq, - (struct virtio_gpu_ctrl_hdr *)vbuf->buf); + virtio_gpu_vbuf_ctrl_hdr(vbuf)); notify = virtqueue_kick_prepare(vq); } @@ -499,7 +514,7 @@ void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, cmd_p->width = cpu_to_le32(params->width); cmd_p->height = cpu_to_le32(params->height); - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence); bo->created = true; } @@ -531,7 +546,7 @@ static void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgde cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING); cmd_p->resource_id = cpu_to_le32(resource_id); - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence); } void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, @@ -606,7 +621,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, cmd_p->r.x = cpu_to_le32(x); cmd_p->r.y = cpu_to_le32(y); - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence); } static void @@ -629,7 +644,7 @@ virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev, vbuf->data_buf = ents; vbuf->data_size = sizeof(*ents) * nents; - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence); } static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev, @@ -988,7 +1003,7 @@ virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev, cmd_p->nr_samples = cpu_to_le32(params->nr_samples); cmd_p->flags = cpu_to_le32(params->flags); - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence); bo->created = true; } @@ -1021,7 +1036,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev, cmd_p->offset = cpu_to_le64(offset); cmd_p->level = cpu_to_le32(level); - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence); } void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev, @@ -1047,7 +1062,7 @@ void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev, cmd_p->offset = cpu_to_le64(offset); cmd_p->level = cpu_to_le32(level); - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence); } void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev, @@ -1070,7 +1085,7 @@ void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev, cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id); cmd_p->size = cpu_to_le32(data_size); - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence); } int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index 5fc8f85aaf3d..6d31265a2ab7 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -117,7 +117,7 @@ static int vkms_plane_atomic_check(struct drm_plane *plane, bool can_position = false; int ret; - if (!state->fb | !state->crtc) + if (!state->fb || WARN_ON(!state->crtc)) return 0; crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index d8ea3dd10af0..3f3b2c7a208a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -736,11 +736,6 @@ out_no_init: return NULL; } -static int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) -{ - return 0; -} - static int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, struct ttm_mem_type_manager *man) { @@ -866,7 +861,6 @@ struct ttm_bo_driver vmw_bo_driver = { .ttm_tt_create = &vmw_ttm_tt_create, .ttm_tt_populate = &vmw_ttm_populate, .ttm_tt_unpopulate = &vmw_ttm_unpopulate, - .invalidate_caches = vmw_invalidate_caches, .init_mem_type = vmw_init_mem_type, .eviction_valuable = ttm_bo_eviction_valuable, .evict_flags = vmw_evict_flags, diff --git a/drivers/gpu/drm/xen/xen_drm_front_kms.c b/drivers/gpu/drm/xen/xen_drm_front_kms.c index 4f34c5208180..78096bbcd226 100644 --- a/drivers/gpu/drm/xen/xen_drm_front_kms.c +++ b/drivers/gpu/drm/xen/xen_drm_front_kms.c @@ -220,6 +220,24 @@ static bool display_send_page_flip(struct drm_simple_display_pipe *pipe, return false; } +static int display_check(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state, + struct drm_crtc_state *crtc_state) +{ + /* + * Xen doesn't initialize vblanking via drm_vblank_init(), so + * DRM helpers assume that it doesn't handle vblanking and start + * sending out fake VBLANK events automatically. + * + * As xen contains it's own logic for sending out VBLANK events + * in send_pending_event(), disable no_vblank (i.e., the xen + * driver has vblanking support). + */ + crtc_state->no_vblank = false; + + return 0; +} + static void display_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_plane_state) { @@ -284,6 +302,7 @@ static const struct drm_simple_display_pipe_funcs display_funcs = { .enable = display_enable, .disable = display_disable, .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, + .check = display_check, .update = display_update, }; diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index 086c50fac689..c8f7b21fa09e 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -54,7 +54,7 @@ static int zx_vl_plane_atomic_check(struct drm_plane *plane, int min_scale = FRAC_16_16(1, 8); int max_scale = FRAC_16_16(8, 1); - if (!crtc || !fb) + if (!crtc || WARN_ON(!fb)) return 0; crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state, @@ -281,7 +281,7 @@ static int zx_gl_plane_atomic_check(struct drm_plane *plane, struct drm_crtc *crtc = plane_state->crtc; struct drm_crtc_state *crtc_state; - if (!crtc || !fb) + if (!crtc || WARN_ON(!fb)) return 0; crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state, diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 403707a3e503..95e2000c1491 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -9,7 +9,7 @@ menu "Backlight & LCD device support" # LCD # config LCD_CLASS_DEVICE - tristate "Lowlevel LCD controls" + tristate "Lowlevel LCD controls" help This framework adds support for low-level control of LCD. Some framebuffer devices connect to platform-specific LCD modules @@ -141,10 +141,10 @@ endif # LCD_CLASS_DEVICE # Backlight # config BACKLIGHT_CLASS_DEVICE - tristate "Lowlevel Backlight controls" + tristate "Lowlevel Backlight controls" help This framework adds support for low-level control of the LCD - backlight. This includes support for brightness and power. + backlight. This includes support for brightness and power. To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. @@ -272,7 +272,7 @@ config BACKLIGHT_APPLE tristate "Apple Backlight Driver" depends on X86 && ACPI help - If you have an Intel-based Apple say Y to enable a driver for its + If you have an Intel-based Apple say Y to enable a driver for its backlight. config BACKLIGHT_TOSA diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 70c10ea1c38b..3c01b0d2414f 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -27,7 +27,7 @@ config VGACON_SOFT_SCROLLBACK depends on VGA_CONSOLE default n help - The scrollback buffer of the standard VGA console is located in + The scrollback buffer of the standard VGA console is located in the VGA RAM. The size of this RAM is fixed and is quite small. If you require a larger scrollback buffer, this can be placed in System RAM which is dynamically allocated during initialization. @@ -84,36 +84,36 @@ config MDA_CONSOLE If unsure, say N. config SGI_NEWPORT_CONSOLE - tristate "SGI Newport Console support" + tristate "SGI Newport Console support" depends on SGI_IP22 && HAS_IOMEM - select FONT_SUPPORT - help - Say Y here if you want the console on the Newport aka XL graphics - card of your Indy. Most people say Y here. + select FONT_SUPPORT + help + Say Y here if you want the console on the Newport aka XL graphics + card of your Indy. Most people say Y here. config DUMMY_CONSOLE bool default y config DUMMY_CONSOLE_COLUMNS - int "Initial number of console screen columns" - depends on DUMMY_CONSOLE && !ARM - default 160 if PARISC - default 80 - help - On PA-RISC, the default value is 160, which should fit a 1280x1024 - monitor. - Select 80 if you use a 640x480 resolution by default. + int "Initial number of console screen columns" + depends on DUMMY_CONSOLE && !ARM + default 160 if PARISC + default 80 + help + On PA-RISC, the default value is 160, which should fit a 1280x1024 + monitor. + Select 80 if you use a 640x480 resolution by default. config DUMMY_CONSOLE_ROWS - int "Initial number of console screen rows" - depends on DUMMY_CONSOLE && !ARM - default 64 if PARISC - default 25 - help - On PA-RISC, the default value is 64, which should fit a 1280x1024 - monitor. - Select 25 if you use a 640x480 resolution by default. + int "Initial number of console screen rows" + depends on DUMMY_CONSOLE && !ARM + default 64 if PARISC + default 25 + help + On PA-RISC, the default value is 64, which should fit a 1280x1024 + monitor. + Select 25 if you use a 640x480 resolution by default. config FRAMEBUFFER_CONSOLE bool "Framebuffer Console support" @@ -129,11 +129,11 @@ config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY depends on FRAMEBUFFER_CONSOLE default n ---help--- - If this option is selected, the framebuffer console will - automatically select the primary display device (if the architecture + If this option is selected, the framebuffer console will + automatically select the primary display device (if the architecture supports this feature). Otherwise, the framebuffer console will - always select the first framebuffer driver that is loaded. The latter - is the default behavior. + always select the first framebuffer driver that is loaded. The latter + is the default behavior. You can always override the automatic selection of the primary device by using the fbcon=map: boot option. @@ -144,11 +144,11 @@ config FRAMEBUFFER_CONSOLE_ROTATION bool "Framebuffer Console Rotation" depends on FRAMEBUFFER_CONSOLE help - Enable display rotation for the framebuffer console. This is done - in software and may be significantly slower than a normally oriented - display. Note that the rotation is done at the console level only - such that other users of the framebuffer will remain normally - oriented. + Enable display rotation for the framebuffer console. This is done + in software and may be significantly slower than a normally oriented + display. Note that the rotation is done at the console level only + such that other users of the framebuffer will remain normally + oriented. config FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER bool "Framebuffer Console Deferred Takeover" @@ -162,14 +162,14 @@ config FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER black screen as soon as fbcon loads. config STI_CONSOLE - bool "STI text console" + bool "STI text console" depends on PARISC && HAS_IOMEM - select FONT_SUPPORT - default y - help - The STI console is the builtin display/keyboard on HP-PARISC - machines. Say Y here to build support for it into your kernel. - The alternative is to use your primary serial port as a console. + select FONT_SUPPORT + default y + help + The STI console is the builtin display/keyboard on HP-PARISC + machines. Say Y here to build support for it into your kernel. + The alternative is to use your primary serial port as a console. endmenu diff --git a/drivers/video/fbdev/cg14.c b/drivers/video/fbdev/cg14.c index a620b51cf7d0..6a745eb46ca1 100644 --- a/drivers/video/fbdev/cg14.c +++ b/drivers/video/fbdev/cg14.c @@ -509,8 +509,7 @@ static int cg14_probe(struct platform_device *op) if (!par->regs || !par->clut || !par->cursor || !info->screen_base) goto out_unmap_regs; - is_8mb = (((op->resource[1].end - op->resource[1].start) + 1) == - (8 * 1024 * 1024)); + is_8mb = (resource_size(&op->resource[1]) == (8 * 1024 * 1024)); BUILD_BUG_ON(sizeof(par->mmap_map) != sizeof(__cg14_mmap_map)); diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile index 37710316a680..26cbc965497c 100644 --- a/drivers/video/fbdev/core/Makefile +++ b/drivers/video/fbdev/core/Makefile @@ -16,7 +16,6 @@ fb-y += fbcon_rotate.o fbcon_cw.o fbcon_ud.o \ fbcon_ccw.o endif endif -fb-objs := $(fb-y) obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index bb6ae995c2e5..28335788e76e 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -873,7 +873,7 @@ static int set_con2fb_map(int unit, int newidx, int user) int oldidx = con2fb_map[unit]; struct fb_info *info = registered_fb[newidx]; struct fb_info *oldinfo = NULL; - int found, err = 0; + int found, err = 0; WARN_CONSOLE_UNLOCKED(); @@ -895,31 +895,30 @@ static int set_con2fb_map(int unit, int newidx, int user) con2fb_map[unit] = newidx; if (!err && !found) - err = con2fb_acquire_newinfo(vc, info, unit, oldidx); - + err = con2fb_acquire_newinfo(vc, info, unit, oldidx); /* * If old fb is not mapped to any of the consoles, * fbcon should release it. */ - if (!err && oldinfo && !search_fb_in_map(oldidx)) - err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx, - found); + if (!err && oldinfo && !search_fb_in_map(oldidx)) + err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx, + found); - if (!err) { - int show_logo = (fg_console == 0 && !user && - logo_shown != FBCON_LOGO_DONTSHOW); + if (!err) { + int show_logo = (fg_console == 0 && !user && + logo_shown != FBCON_LOGO_DONTSHOW); - if (!found) - fbcon_add_cursor_timer(info); - con2fb_map_boot[unit] = newidx; - con2fb_init_display(vc, info, unit, show_logo); + if (!found) + fbcon_add_cursor_timer(info); + con2fb_map_boot[unit] = newidx; + con2fb_init_display(vc, info, unit, show_logo); } if (!search_fb_in_map(info_idx)) info_idx = newidx; - return err; + return err; } /* diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index d04554959ea7..30e73ec4ad5c 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -663,20 +663,20 @@ int fb_prepare_logo(struct fb_info *info, int rotate) fb_logo.depth = 1; - if (fb_logo.depth > 4 && depth > 4) { - switch (info->fix.visual) { - case FB_VISUAL_TRUECOLOR: - fb_logo.needs_truepalette = 1; - break; - case FB_VISUAL_DIRECTCOLOR: - fb_logo.needs_directpalette = 1; - fb_logo.needs_cmapreset = 1; - break; - case FB_VISUAL_PSEUDOCOLOR: - fb_logo.needs_cmapreset = 1; - break; - } - } + if (fb_logo.depth > 4 && depth > 4) { + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + fb_logo.needs_truepalette = 1; + break; + case FB_VISUAL_DIRECTCOLOR: + fb_logo.needs_directpalette = 1; + fb_logo.needs_cmapreset = 1; + break; + case FB_VISUAL_PSEUDOCOLOR: + fb_logo.needs_cmapreset = 1; + break; + } + } height = fb_logo.logo->height; if (fb_center_logo) @@ -1065,19 +1065,19 @@ fb_blank(struct fb_info *info, int blank) struct fb_event event; int ret = -EINVAL; - if (blank > FB_BLANK_POWERDOWN) - blank = FB_BLANK_POWERDOWN; + if (blank > FB_BLANK_POWERDOWN) + blank = FB_BLANK_POWERDOWN; event.info = info; event.data = ␣ if (info->fbops->fb_blank) - ret = info->fbops->fb_blank(blank, info); + ret = info->fbops->fb_blank(blank, info); if (!ret) fb_notifier_call_chain(FB_EVENT_BLANK, &event); - return ret; + return ret; } EXPORT_SYMBOL(fb_blank); @@ -1115,7 +1115,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, break; case FBIOGET_FSCREENINFO: lock_fb_info(info); - fix = info->fix; + memcpy(&fix, &info->fix, sizeof(fix)); if (info->flags & FBINFO_HIDE_SMEM_START) fix.smem_start = 0; unlock_fb_info(info); diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index f47d50e560c0..e4c3c8b65da4 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -594,8 +594,8 @@ static int synthvid_get_supported_resolution(struct hv_device *hdev) t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); if (!t) { pr_err("Time out on waiting resolution response\n"); - ret = -ETIMEDOUT; - goto out; + ret = -ETIMEDOUT; + goto out; } if (msg->resolution_resp.resolution_count == 0) { diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c index c583c018304d..c24de9107958 100644 --- a/drivers/video/fbdev/nvidia/nvidia.c +++ b/drivers/video/fbdev/nvidia/nvidia.c @@ -168,27 +168,26 @@ static int nvidia_panel_tweak(struct nvidia_par *par, { int tweak = 0; - if (par->paneltweak) { - tweak = par->paneltweak; - } else { - /* begin flat panel hacks */ - /* This is unfortunate, but some chips need this register - tweaked or else you get artifacts where adjacent pixels are - swapped. There are no hard rules for what to set here so all - we can do is experiment and apply hacks. */ - - if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) { - /* At least one NV34 laptop needs this workaround. */ - tweak = -1; - } - - if((par->Chipset & 0xfff0) == 0x0310) { - tweak = 1; - } - /* end flat panel hacks */ - } - - return tweak; + if (par->paneltweak) { + tweak = par->paneltweak; + } else { + /* Begin flat panel hacks. + * This is unfortunate, but some chips need this register + * tweaked or else you get artifacts where adjacent pixels are + * swapped. There are no hard rules for what to set here so all + * we can do is experiment and apply hacks. + */ + if (((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) { + /* At least one NV34 laptop needs this workaround. */ + tweak = -1; + } + + if ((par->Chipset & 0xfff0) == 0x0310) + tweak = 1; + /* end flat panel hacks */ + } + + return tweak; } static void nvidia_screen_off(struct nvidia_par *par, int on) diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c index 8dfa9158ba78..836e7b1639ce 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c @@ -1154,16 +1154,12 @@ static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, r = fbdev->ctrl->setcolreg(regno, red, green, blue, transp, update_hw_pal); */ - /* Fallthrough */ r = -EINVAL; break; case OMAPFB_COLOR_RGB565: case OMAPFB_COLOR_RGB444: case OMAPFB_COLOR_RGB24P: case OMAPFB_COLOR_RGB24U: - if (r != 0) - break; - if (regno < 16) { u32 pal; pal = ((red >> (16 - var->red.length)) << diff --git a/drivers/video/fbdev/pxa168fb.c b/drivers/video/fbdev/pxa168fb.c index 9b9ec1468347..706c69430818 100644 --- a/drivers/video/fbdev/pxa168fb.c +++ b/drivers/video/fbdev/pxa168fb.c @@ -769,7 +769,7 @@ failed_free_fbmem: dma_free_wc(fbi->dev, info->fix.smem_len, info->screen_base, fbi->fb_start_dma); failed_free_info: - kfree(info); + framebuffer_release(info); dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret); return ret; diff --git a/drivers/video/fbdev/s1d13xxxfb.c b/drivers/video/fbdev/s1d13xxxfb.c index 8048499e398d..eaea8c373753 100644 --- a/drivers/video/fbdev/s1d13xxxfb.c +++ b/drivers/video/fbdev/s1d13xxxfb.c @@ -746,9 +746,9 @@ s1d13xxxfb_remove(struct platform_device *pdev) } release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start +1); + resource_size(&pdev->resource[0])); release_mem_region(pdev->resource[1].start, - pdev->resource[1].end - pdev->resource[1].start +1); + resource_size(&pdev->resource[1])); return 0; } @@ -788,14 +788,14 @@ static int s1d13xxxfb_probe(struct platform_device *pdev) } if (!request_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start +1, "s1d13xxxfb mem")) { + resource_size(&pdev->resource[0]), "s1d13xxxfb mem")) { dev_dbg(&pdev->dev, "request_mem_region failed\n"); ret = -EBUSY; goto bail; } if (!request_mem_region(pdev->resource[1].start, - pdev->resource[1].end - pdev->resource[1].start +1, "s1d13xxxfb regs")) { + resource_size(&pdev->resource[1]), "s1d13xxxfb regs")) { dev_dbg(&pdev->dev, "request_mem_region failed\n"); ret = -EBUSY; goto bail; @@ -810,7 +810,7 @@ static int s1d13xxxfb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); default_par = info->par; default_par->regs = ioremap(pdev->resource[1].start, - pdev->resource[1].end - pdev->resource[1].start +1); + resource_size(&pdev->resource[1])); if (!default_par->regs) { printk(KERN_ERR PFX "unable to map registers\n"); ret = -ENOMEM; @@ -819,7 +819,7 @@ static int s1d13xxxfb_probe(struct platform_device *pdev) info->pseudo_palette = default_par->pseudo_palette; info->screen_base = ioremap(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start +1); + resource_size(&pdev->resource[0])); if (!info->screen_base) { printk(KERN_ERR PFX "unable to map framebuffer\n"); @@ -857,9 +857,9 @@ static int s1d13xxxfb_probe(struct platform_device *pdev) info->fix = s1d13xxxfb_fix; info->fix.mmio_start = pdev->resource[1].start; - info->fix.mmio_len = pdev->resource[1].end - pdev->resource[1].start + 1; + info->fix.mmio_len = resource_size(&pdev->resource[1]); info->fix.smem_start = pdev->resource[0].start; - info->fix.smem_len = pdev->resource[0].end - pdev->resource[0].start + 1; + info->fix.smem_len = resource_size(&pdev->resource[0]); printk(KERN_INFO PFX "regs mapped at 0x%p, fb %d KiB mapped at 0x%p\n", default_par->regs, info->fix.smem_len / 1024, info->screen_base); diff --git a/drivers/video/fbdev/sa1100fb.c b/drivers/video/fbdev/sa1100fb.c index 5bb653db0cec..2d285cc384cf 100644 --- a/drivers/video/fbdev/sa1100fb.c +++ b/drivers/video/fbdev/sa1100fb.c @@ -1053,7 +1053,7 @@ static int sa1100fb_map_video_memory(struct sa1100fb_info *fbi) } /* Fake monspecs to fill in fbinfo structure */ -static struct fb_monspecs monspecs = { +static const struct fb_monspecs monspecs = { .hfmin = 30000, .hfmax = 70000, .vfmin = 50, diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 142535267fec..1a0bd78f9345 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -791,6 +791,8 @@ static int ssd1307fb_remove(struct i2c_client *client) pwm_disable(par->pwm); pwm_put(par->pwm); } + if (par->vbat_reg) + regulator_disable(par->vbat_reg); fb_deferred_io_cleanup(info); __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len)); framebuffer_release(info); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 951dfb15c27b..52d65a055491 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -670,6 +670,9 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state, } int __must_check +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, + struct drm_encoder *encoder); +int __must_check drm_atomic_add_affected_connectors(struct drm_atomic_state *state, struct drm_crtc *crtc); int __must_check @@ -992,4 +995,77 @@ drm_atomic_crtc_effectively_active(const struct drm_crtc_state *state) return state->active || state->self_refresh_active; } +/** + * struct drm_bus_cfg - bus configuration + * + * This structure stores the configuration of a physical bus between two + * components in an output pipeline, usually between two bridges, an encoder + * and a bridge, or a bridge and a connector. + * + * The bus configuration is stored in &drm_bridge_state separately for the + * input and output buses, as seen from the point of view of each bridge. The + * bus configuration of a bridge output is usually identical to the + * configuration of the next bridge's input, but may differ if the signals are + * modified between the two bridges, for instance by an inverter on the board. + * The input and output configurations of a bridge may differ if the bridge + * modifies the signals internally, for instance by performing format + * conversion, or modifying signals polarities. + */ +struct drm_bus_cfg { + /** + * @format: format used on this bus (one of the MEDIA_BUS_FMT_* format) + * + * This field should not be directly modified by drivers + * (&drm_atomic_bridge_chain_select_bus_fmts() takes care of the bus + * format negotiation). + */ + u32 format; + + /** + * @flags: DRM_BUS_* flags used on this bus + */ + u32 flags; +}; + +/** + * struct drm_bridge_state - Atomic bridge state object + */ +struct drm_bridge_state { + /** + * @base: inherit from &drm_private_state + */ + struct drm_private_state base; + + /** + * @bridge: the bridge this state refers to + */ + struct drm_bridge *bridge; + + /** + * @input_bus_cfg: input bus configuration + */ + struct drm_bus_cfg input_bus_cfg; + + /** + * @output_bus_cfg: input bus configuration + */ + struct drm_bus_cfg output_bus_cfg; +}; + +static inline struct drm_bridge_state * +drm_priv_to_bridge_state(struct drm_private_state *priv) +{ + return container_of(priv, struct drm_bridge_state, base); +} + +struct drm_bridge_state * +drm_atomic_get_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge); +struct drm_bridge_state * +drm_atomic_get_old_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge); +struct drm_bridge_state * +drm_atomic_get_new_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge); + #endif /* DRM_ATOMIC_H_ */ diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 9db3cac48f4f..b268180c97eb 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -224,4 +224,12 @@ drm_atomic_plane_disabling(struct drm_plane_state *old_plane_state, return old_plane_state->crtc && !new_plane_state->crtc; } +u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + #endif /* DRM_ATOMIC_HELPER_H_ */ diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 8171dea4cc22..3f8f1d627f7c 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -26,6 +26,8 @@ #include <linux/types.h> +struct drm_bridge; +struct drm_bridge_state; struct drm_crtc; struct drm_crtc_state; struct drm_plane; @@ -80,3 +82,14 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, struct drm_private_state *state); + +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge, + struct drm_bridge_state *state); +struct drm_bridge_state * +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge); +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, + struct drm_bridge_state *state); +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, + struct drm_bridge_state *state); +struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 694e153a7531..45626ecf20f8 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -25,6 +25,8 @@ #include <linux/list.h> #include <linux/ctype.h> + +#include <drm/drm_atomic.h> #include <drm/drm_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h> @@ -109,7 +111,9 @@ struct drm_bridge_funcs { * this function passes all other callbacks must succeed for this * configuration. * - * The @mode_fixup callback is optional. + * The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup() + * is not called when &drm_bridge_funcs.atomic_check() is implemented, + * so only one of them should be provided. * * NOTE: * @@ -263,7 +267,7 @@ struct drm_bridge_funcs { * The @atomic_pre_enable callback is optional. */ void (*atomic_pre_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); /** * @atomic_enable: @@ -288,7 +292,7 @@ struct drm_bridge_funcs { * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); /** * @atomic_disable: * @@ -311,7 +315,7 @@ struct drm_bridge_funcs { * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); /** * @atomic_post_disable: @@ -337,7 +341,153 @@ struct drm_bridge_funcs { * The @atomic_post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); + + /** + * @atomic_duplicate_state: + * + * Duplicate the current bridge state object (which is guaranteed to be + * non-NULL). + * + * The atomic_duplicate_state() is optional. When not implemented the + * core allocates a drm_bridge_state object and calls + * &__drm_atomic_helper_bridge_duplicate_state() to initialize it. + * + * RETURNS: + * A valid drm_bridge_state object or NULL if the allocation fails. + */ + struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge); + + /** + * @atomic_destroy_state: + * + * Destroy a bridge state object previously allocated by + * &drm_bridge_funcs.atomic_duplicate_state(). + * + * The atomic_destroy_state hook is optional. When not implemented the + * core calls kfree() on the state. + */ + void (*atomic_destroy_state)(struct drm_bridge *bridge, + struct drm_bridge_state *state); + + /** + * @atomic_get_output_bus_fmts: + * + * Return the supported bus formats on the output end of a bridge. + * The returned array must be allocated with kmalloc() and will be + * freed by the caller. If the allocation fails, NULL should be + * returned. num_output_fmts must be set to the returned array size. + * Formats listed in the returned array should be listed in decreasing + * preference order (the core will try all formats until it finds one + * that works). + * + * This method is only called on the last element of the bridge chain + * as part of the bus format negotiation process that happens in + * &drm_atomic_bridge_chain_select_bus_fmts(). + * This method is optional. When not implemented, the core will + * fall back to &drm_connector.display_info.bus_formats[0] if + * &drm_connector.display_info.num_bus_formats > 0, + * or to MEDIA_BUS_FMT_FIXED otherwise. + */ + u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts); + + /** + * @atomic_get_input_bus_fmts: + * + * Return the supported bus formats on the input end of a bridge for + * a specific output bus format. + * + * The returned array must be allocated with kmalloc() and will be + * freed by the caller. If the allocation fails, NULL should be + * returned. num_output_fmts must be set to the returned array size. + * Formats listed in the returned array should be listed in decreasing + * preference order (the core will try all formats until it finds one + * that works). When the format is not supported NULL should be + * returned and *num_output_fmts should be set to 0. + * + * This method is called on all elements of the bridge chain as part of + * the bus format negotiation process that happens in + * &drm_atomic_bridge_chain_select_bus_fmts(). + * This method is optional. When not implemented, the core will bypass + * bus format negotiation on this element of the bridge without + * failing, and the previous element in the chain will be passed + * MEDIA_BUS_FMT_FIXED as its output bus format. + * + * Bridge drivers that need to support being linked to bridges that are + * not supporting bus format negotiation should handle the + * output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a + * sensible default value or extracting this information from somewhere + * else (FW property, &drm_display_mode, &drm_display_info, ...) + * + * Note: Even if input format selection on the first bridge has no + * impact on the negotiation process (bus format negotiation stops once + * we reach the first element of the chain), drivers are expected to + * return accurate input formats as the input format may be used to + * configure the CRTC output appropriately. + */ + u32 *(*atomic_get_input_bus_fmts)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + + /** + * @atomic_check: + * + * This method is responsible for checking bridge state correctness. + * It can also check the state of the surrounding components in chain + * to make sure the whole pipeline can work properly. + * + * &drm_bridge_funcs.atomic_check() hooks are called in reverse + * order (from the last to the first bridge). + * + * This method is optional. &drm_bridge_funcs.mode_fixup() is not + * called when &drm_bridge_funcs.atomic_check() is implemented, so only + * one of them should be provided. + * + * If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or + * &drm_bridge_state.output_bus_cfg.flags it should should happen in + * this function. By default the &drm_bridge_state.output_bus_cfg.flags + * field is set to the next bridge + * &drm_bridge_state.input_bus_cfg.flags value or + * &drm_connector.display_info.bus_flags if the bridge is the last + * element in the chain. + * + * RETURNS: + * zero if the check passed, a negative error code otherwise. + */ + int (*atomic_check)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); + + /** + * @atomic_reset: + * + * Reset the bridge to a predefined state (or retrieve its current + * state) and return a &drm_bridge_state object matching this state. + * This function is called at attach time. + * + * The atomic_reset hook is mandatory if the bridge implements any of + * the atomic hooks, and should be left unassigned otherwise. + * + * Note that the atomic_reset() semantics is not exactly matching the + * reset() semantics found on other components (connector, plane, ...). + * 1/ The reset operation happens when the bridge is attached, not when + * drm_mode_config_reset() is called + * 2/ It's meant to be used exclusively on bridges that have been + * converted to the ATOMIC API + * + * RETURNS: + * A valid drm_bridge_state object in case of success, an ERR_PTR() + * giving the reason of the failure otherwise. + */ + struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge); }; /** @@ -380,6 +530,8 @@ struct drm_bridge_timings { * struct drm_bridge - central DRM bridge control structure */ struct drm_bridge { + /** @base: inherit from &drm_private_object */ + struct drm_private_obj base; /** @dev: DRM device this bridge belongs to */ struct drm_device *dev; /** @encoder: encoder to which this bridge is connected */ @@ -404,6 +556,12 @@ struct drm_bridge { void *driver_private; }; +static inline struct drm_bridge * +drm_priv_to_bridge(struct drm_private_obj *priv) +{ + return container_of(priv, struct drm_bridge, base); +} + void drm_bridge_add(struct drm_bridge *bridge); void drm_bridge_remove(struct drm_bridge *bridge); struct drm_bridge *of_drm_find_bridge(struct device_node *np); @@ -482,6 +640,9 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); void drm_bridge_chain_enable(struct drm_bridge *bridge); +int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *state); void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, @@ -491,6 +652,14 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state); +u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + #ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel); struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 221910948b37..2113500b4075 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1552,8 +1552,13 @@ void drm_connector_set_link_status_property(struct drm_connector *connector, uint64_t link_status); void drm_connector_set_vrr_capable_property( struct drm_connector *connector, bool capable); -int drm_connector_init_panel_orientation_property( - struct drm_connector *connector, int width, int height); +int drm_connector_set_panel_orientation( + struct drm_connector *connector, + enum drm_panel_orientation panel_orientation); +int drm_connector_set_panel_orientation_with_quirk( + struct drm_connector *connector, + enum drm_panel_orientation panel_orientation, + int width, int height); int drm_connector_attach_max_bpc_property(struct drm_connector *connector, int min, int max); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 5e9b15a0e8c5..6bef2f41d4d7 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -174,12 +174,25 @@ struct drm_crtc_state { * @no_vblank: * * Reflects the ability of a CRTC to send VBLANK events. This state - * usually depends on the pipeline configuration, and the main usuage - * is CRTCs feeding a writeback connector operating in oneshot mode. - * In this case the VBLANK event is only generated when a job is queued - * to the writeback connector, and we want the core to fake VBLANK - * events when this part of the pipeline hasn't changed but others had - * or when the CRTC and connectors are being disabled. + * usually depends on the pipeline configuration. If set to true, DRM + * atomic helpers will send out a fake VBLANK event during display + * updates after all hardware changes have been committed. This is + * implemented in drm_atomic_helper_fake_vblank(). + * + * One usage is for drivers and/or hardware without support for VBLANK + * interrupts. Such drivers typically do not initialize vblanking + * (i.e., call drm_vblank_init() with the number of CRTCs). For CRTCs + * without initialized vblanking, this field is set to true in + * drm_atomic_helper_check_modeset(), and a fake VBLANK event will be + * send out on each update of the display pipeline by + * drm_atomic_helper_fake_vblank(). + * + * Another usage is CRTCs feeding a writeback connector operating in + * oneshot mode. In this case the fake VBLANK event is only generated + * when a job is queued to the writeback connector, and we want the + * core to fake VBLANK events when this part of the pipeline hasn't + * changed but others had or when the CRTC and connectors are being + * disabled. * * __drm_atomic_helper_crtc_duplicate_state() will not reset the value * from the current state, the CRTC driver is then responsible for @@ -335,7 +348,14 @@ struct drm_crtc_state { * - Events for disabled CRTCs are not allowed, and drivers can ignore * that case. * - * This can be handled by the drm_crtc_send_vblank_event() function, + * For very simple hardware without VBLANK interrupt, enabling + * &struct drm_crtc_state.no_vblank makes DRM's atomic commit helpers + * send a fake VBLANK event at the end of the display update after all + * hardware changes have been applied. See + * drm_atomic_helper_fake_vblank(). + * + * For more complex hardware this + * can be handled by the drm_crtc_send_vblank_event() function, * which the driver should call on the provided event upon completion of * the atomic commit. Note that if the driver supports vblank signalling * and timestamping the vblank counters and timestamps must agree with diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 1acfc3bbd3fb..bb60a949f416 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -144,7 +144,7 @@ struct drm_device { * Usage counter for outstanding files open, * protected by drm_global_mutex */ - int open_count; + atomic_t open_count; /** @filelist_mutex: Protects @filelist. */ struct mutex filelist_mutex; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index bc04467f7c3a..262faf9e5e94 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -392,6 +392,8 @@ # define DP_DS_12BPC 2 # define DP_DS_16BPC 3 +#define DP_MAX_DOWNSTREAM_PORTS 0x10 + /* DP Forward error Correction Registers */ #define DP_FEC_CAPABILITY 0x090 /* 1.4 */ # define DP_FEC_CAPABLE (1 << 0) diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index bcb39da9adb4..5483f888712a 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -635,11 +635,13 @@ struct drm_dp_mst_topology_mgr { struct mutex payload_lock; /** * @proposed_vcpis: Array of pointers for the new VCPI allocation. The - * VCPI structure itself is &drm_dp_mst_port.vcpi. + * VCPI structure itself is &drm_dp_mst_port.vcpi, and the size of + * this array is determined by @max_payloads. */ struct drm_dp_vcpi **proposed_vcpis; /** - * @payloads: Array of payloads. + * @payloads: Array of payloads. The size of this array is determined + * by @max_payloads. */ struct drm_dp_payload *payloads; /** diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index cf13470810a5..77685ed7aa65 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -458,20 +458,6 @@ struct drm_driver { void (*irq_uninstall) (struct drm_device *dev); /** - * @master_create: - * - * Called whenever a new master is created. Only used by vmwgfx. - */ - int (*master_create)(struct drm_device *dev, struct drm_master *master); - - /** - * @master_destroy: - * - * Called whenever a master is destroyed. Only used by vmwgfx. - */ - void (*master_destroy)(struct drm_device *dev, struct drm_master *master); - - /** * @master_set: * * Called whenever the minor master is set. Only used by vmwgfx. @@ -824,6 +810,25 @@ static inline bool drm_dev_is_unplugged(struct drm_device *dev) } /** + * drm_core_check_all_features - check driver feature flags mask + * @dev: DRM device to check + * @features: feature flag(s) mask + * + * This checks @dev for driver features, see &drm_driver.driver_features, + * &drm_device.driver_features, and the various &enum drm_driver_feature flags. + * + * Returns true if all features in the @features mask are supported, false + * otherwise. + */ +static inline bool drm_core_check_all_features(const struct drm_device *dev, + u32 features) +{ + u32 supported = dev->driver->driver_features & dev->driver_features; + + return features && (supported & features) == features; +} + +/** * drm_core_check_feature - check driver feature flags * @dev: DRM device to check * @feature: feature flag @@ -833,9 +838,10 @@ static inline bool drm_dev_is_unplugged(struct drm_device *dev) * * Returns true if the @feature is supported, false otherwise. */ -static inline bool drm_core_check_feature(const struct drm_device *dev, u32 feature) +static inline bool drm_core_check_feature(const struct drm_device *dev, + enum drm_driver_feature feature) { - return dev->driver->driver_features & dev->driver_features & feature; + return drm_core_check_all_features(dev, feature); } /** diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 8b099b347817..19df8028a6c4 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -374,6 +374,7 @@ int drm_open(struct inode *inode, struct file *filp); ssize_t drm_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset); int drm_release(struct inode *inode, struct file *filp); +int drm_release_noglobal(struct inode *inode, struct file *filp); __poll_t drm_poll(struct file *filp, struct poll_table_struct *wait); int drm_event_reserve_init_locked(struct drm_device *dev, struct drm_file *file_priv, diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 573e9fd109bf..0f6e47213d8d 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -6,6 +6,7 @@ #include <drm/drm_file.h> #include <drm/drm_gem.h> #include <drm/drm_ioctl.h> +#include <drm/drm_modes.h> #include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_placement.h> @@ -205,4 +206,12 @@ struct drm_vram_mm *drm_vram_helper_alloc_mm( struct drm_device *dev, uint64_t vram_base, size_t vram_size); void drm_vram_helper_release_mm(struct drm_device *dev); +/* + * Mode-config helpers + */ + +enum drm_mode_status +drm_vram_helper_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode); + #endif diff --git a/include/drm/drm_legacy.h b/include/drm/drm_legacy.h index 5745710453c8..dcef3598f49e 100644 --- a/include/drm/drm_legacy.h +++ b/include/drm/drm_legacy.h @@ -194,17 +194,11 @@ void drm_legacy_idlelock_release(struct drm_lock_data *lock); #ifdef CONFIG_PCI -void __drm_legacy_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); int drm_legacy_pci_init(struct drm_driver *driver, struct pci_driver *pdriver); void drm_legacy_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver); #else -static inline void __drm_legacy_pci_free(struct drm_device *dev, - drm_dma_handle_t *dmah) -{ -} - static inline int drm_legacy_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) { diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h index 67c66f5ee591..33f325f5af2b 100644 --- a/include/drm/drm_mipi_dbi.h +++ b/include/drm/drm_mipi_dbi.h @@ -110,6 +110,18 @@ struct mipi_dbi_dev { unsigned int rotation; /** + * @left_offset: Horizontal offset of the display relative to the + * controller's driver array + */ + unsigned int left_offset; + + /** + * @top_offset: Vertical offset of the display relative to the + * controller's driver array + */ + unsigned int top_offset; + + /** * @backlight: backlight device (optional) */ struct backlight_device *backlight; diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 121f7aabccd1..6193cb555acc 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -198,7 +198,8 @@ static inline struct drm_panel *of_drm_find_panel(const struct device_node *np) } #endif -#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE) +#if IS_ENABLED(CONFIG_DRM_PANEL) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ + (IS_MODULE(CONFIG_DRM) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE))) int drm_panel_of_backlight(struct drm_panel *panel); #else static inline int drm_panel_of_backlight(struct drm_panel *panel) diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index 8f99d389792d..894a0b9437e2 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -553,4 +553,33 @@ void __drm_err(const char *format, ...); #define DRM_DEBUG_PRIME_RATELIMITED(fmt, ...) \ DRM_DEV_DEBUG_PRIME_RATELIMITED(NULL, fmt, ##__VA_ARGS__) +/* + * struct drm_device based WARNs + * + * drm_WARN*() acts like WARN*(), but with the key difference of + * using device specific information so that we know from which device + * warning is originating from. + * + * Prefer drm_device based drm_WARN* over regular WARN* + */ + +/* Helper for struct drm_device based WARNs */ +#define drm_WARN(drm, condition, format, arg...) \ + WARN(condition, "%s %s: " format, \ + dev_driver_string((drm)->dev), \ + dev_name((drm)->dev), ## arg) + +#define drm_WARN_ONCE(drm, condition, format, arg...) \ + WARN_ONCE(condition, "%s %s: " format, \ + dev_driver_string((drm)->dev), \ + dev_name((drm)->dev), ## arg) + +#define drm_WARN_ON(drm, x) \ + drm_WARN((drm), (x), "%s", \ + "drm_WARN_ON(" __stringify(x) ")") + +#define drm_WARN_ON_ONCE(drm, x) \ + drm_WARN_ONCE((drm), (x), "%s", \ + "drm_WARN_ON_ONCE(" __stringify(x) ")") + #endif /* DRM_PRINT_H_ */ diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h index 15afee9cf049..e253ba7bea9d 100644 --- a/include/drm/drm_simple_kms_helper.h +++ b/include/drm/drm_simple_kms_helper.h @@ -100,8 +100,11 @@ struct drm_simple_display_pipe_funcs { * This is the function drivers should submit the * &drm_pending_vblank_event from. Using either * drm_crtc_arm_vblank_event(), when the driver supports vblank - * interrupt handling, or drm_crtc_send_vblank_event() directly in case - * the hardware lacks vblank support entirely. + * interrupt handling, or drm_crtc_send_vblank_event() for more + * complex case. In case the hardware lacks vblank support entirely, + * drivers can set &struct drm_crtc_state.no_vblank in + * &struct drm_simple_display_pipe_funcs.check and let DRM's + * atomic helper fake a vblank event. */ void (*update)(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_plane_state); diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index c16c44052b3d..94275e93fd27 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -206,6 +206,7 @@ struct drm_vblank_crtc { }; int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs); +bool drm_dev_has_vblank(const struct drm_device *dev); u64 drm_crtc_vblank_count(struct drm_crtc *crtc); u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, ktime_t *vblanktime); diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index cac7a8a0825a..c9e0fd09f4b2 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -210,8 +210,6 @@ struct ttm_mem_type_manager { * struct ttm_bo_driver * * @create_ttm_backend_entry: Callback to create a struct ttm_backend. - * @invalidate_caches: Callback to invalidate read caches when a buffer object - * has been evicted. * @init_mem_type: Callback to initialize a struct ttm_mem_type_manager * structure. * @evict_flags: Callback to obtain placement flags when a buffer is evicted. @@ -256,19 +254,6 @@ struct ttm_bo_driver { */ void (*ttm_tt_unpopulate)(struct ttm_tt *ttm); - /** - * struct ttm_bo_driver member invalidate_caches - * - * @bdev: the buffer object device. - * @flags: new placement of the rebound buffer object. - * - * A previosly evicted buffer has been rebound in a - * potentially new location. Tell the driver that it might - * consider invalidating read (texture) caches on the next command - * submission as a consequence. - */ - - int (*invalidate_caches)(struct ttm_bo_device *bdev, uint32_t flags); int (*init_mem_type)(struct ttm_bo_device *bdev, uint32_t type, struct ttm_mem_type_manager *man); diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 868bf7996c0f..808b48a93330 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -948,6 +948,8 @@ extern "C" { #define DRM_IOCTL_SYNCOBJ_TRANSFER DRM_IOWR(0xCC, struct drm_syncobj_transfer) #define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCD, struct drm_syncobj_timeline_array) +#define DRM_IOCTL_MODE_GETFB2 DRM_IOWR(0xCE, struct drm_mode_fb_cmd2) + /** * Device specific ioctls should only be in their respective headers * The device specific ioctl range is from 0x40 to 0x9f. diff --git a/include/uapi/drm/lima_drm.h b/include/uapi/drm/lima_drm.h index 95a00fb867e6..1ec58d652a5a 100644 --- a/include/uapi/drm/lima_drm.h +++ b/include/uapi/drm/lima_drm.h @@ -32,12 +32,19 @@ struct drm_lima_get_param { __u64 value; /* out, parameter value */ }; +/* + * heap buffer dynamically increase backup memory size when GP task fail + * due to lack of heap memory. size field of heap buffer is an up bound of + * the backup memory which can be set to a fairly large value. + */ +#define LIMA_BO_FLAG_HEAP (1 << 0) + /** * create a buffer for used by GPU */ struct drm_lima_gem_create { __u32 size; /* in, buffer size */ - __u32 flags; /* in, currently no flags, must be zero */ + __u32 flags; /* in, buffer flags */ __u32 handle; /* out, GEM buffer handle */ __u32 pad; /* pad, must be zero */ }; diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index b6571c3cfa31..c4a93ce1de48 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -10,7 +10,7 @@ * * This is the register set for the fimd and new style framebuffer interface * found from the S3C2443 onwards into the S3C2416, S3C2450, the - * S3C64XX series such as the S3C6400 and S3C6410, and EXYNOS series. + * S3C64XX series such as the S3C6400 and S3C6410, and Exynos series. */ /* VIDCON0 */ |