diff options
148 files changed, 6562 insertions, 1892 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml index 304a1367faaa..1faae3e323a4 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml +++ b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml @@ -49,11 +49,26 @@ properties: properties: port@0: - $ref: /schemas/graph.yaml#/properties/port + $ref: /schemas/graph.yaml#/$defs/port-base description: | For LVDS encoders, port 0 is the parallel input For LVDS decoders, port 0 is the LVDS input + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-mapping: + enum: + - jeida-18 + - jeida-24 + - vesa-24 + description: | + The color signals mapping order. See details in + Documentation/devicetree/bindings/display/panel/lvds.yaml + port@1: $ref: /schemas/graph.yaml#/properties/port description: | @@ -71,6 +86,22 @@ properties: power-supply: true +if: + not: + properties: + compatible: + contains: + const: lvds-decoder +then: + properties: + ports: + properties: + port@0: + properties: + endpoint: + properties: + data-mapping: false + required: - compatible - ports diff --git a/Documentation/devicetree/bindings/display/bridge/nxp,ptn3460.yaml b/Documentation/devicetree/bindings/display/bridge/nxp,ptn3460.yaml new file mode 100644 index 000000000000..107dd138e6c6 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/nxp,ptn3460.yaml @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/nxp,ptn3460.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP PTN3460 eDP to LVDS bridge + +maintainers: + - Sean Paul <seanpaul@chromium.org> + +properties: + compatible: + const: nxp,ptn3460 + + reg: + description: I2C address of the bridge + maxItems: 1 + + edid-emulation: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: + The EDID emulation entry to use + Value Resolution Description + 0 1024x768 NXP Generic + 1 1920x1080 NXP Generic + 2 1920x1080 NXP Generic + 3 1600x900 Samsung LTM200KT + 4 1920x1080 Samsung LTM230HT + 5 1366x768 NXP Generic + 6 1600x900 ChiMei M215HGE + enum: [0, 1, 2, 3, 4, 5, 6] + + powerdown-gpios: + description: GPIO connected to the PD_N signal. + maxItems: 1 + + reset-gpios: + description: GPIO connected to the RST_N signal. + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: + Video port for LVDS output + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Video port for eDP input + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - edid-emulation + - powerdown-gpios + - reset-gpios + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + bridge@20 { + compatible = "nxp,ptn3460"; + reg = <0x20>; + edid-emulation = <5>; + powerdown-gpios = <&gpy2 5 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpx1 5 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + bridge_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + + port@1 { + reg = <1>; + bridge_in: endpoint { + remote-endpoint = <&dp_out>; + }; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/bridge/ps8640.yaml b/Documentation/devicetree/bindings/display/bridge/ps8640.yaml index fce82b605c8b..cdaf7a7a8f88 100644 --- a/Documentation/devicetree/bindings/display/bridge/ps8640.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ps8640.yaml @@ -40,6 +40,9 @@ properties: vdd33-supply: description: Regulator for 3.3V digital core power. + aux-bus: + $ref: /schemas/display/dp-aux-bus.yaml# + ports: $ref: /schemas/graph.yaml#/properties/ports @@ -98,7 +101,21 @@ examples: reg = <1>; ps8640_out: endpoint { remote-endpoint = <&panel_in>; - }; + }; + }; + }; + + aux-bus { + panel { + compatible = "boe,nv133fhm-n62"; + power-supply = <&pp3300_dx_edp>; + backlight = <&backlight>; + + port { + panel_in: endpoint { + remote-endpoint = <&ps8640_out>; + }; + }; }; }; }; diff --git a/Documentation/devicetree/bindings/display/bridge/ptn3460.txt b/Documentation/devicetree/bindings/display/bridge/ptn3460.txt deleted file mode 100644 index 361971ba104d..000000000000 --- a/Documentation/devicetree/bindings/display/bridge/ptn3460.txt +++ /dev/null @@ -1,39 +0,0 @@ -ptn3460 bridge bindings - -Required properties: - - compatible: "nxp,ptn3460" - - reg: i2c address of the bridge - - powerdown-gpio: OF device-tree gpio specification for PD_N pin. - - reset-gpio: OF device-tree gpio specification for RST_N pin. - - edid-emulation: The EDID emulation entry to use - +-------+------------+------------------+ - | Value | Resolution | Description | - | 0 | 1024x768 | NXP Generic | - | 1 | 1920x1080 | NXP Generic | - | 2 | 1920x1080 | NXP Generic | - | 3 | 1600x900 | Samsung LTM200KT | - | 4 | 1920x1080 | Samsung LTM230HT | - | 5 | 1366x768 | NXP Generic | - | 6 | 1600x900 | ChiMei M215HGE | - +-------+------------+------------------+ - - - video interfaces: Device node can contain video interface port - nodes for panel according to [1]. - -[1]: Documentation/devicetree/bindings/media/video-interfaces.txt - -Example: - lvds-bridge@20 { - compatible = "nxp,ptn3460"; - reg = <0x20>; - powerdown-gpio = <&gpy2 5 1 0 0>; - reset-gpio = <&gpx1 5 1 0 0>; - edid-emulation = <5>; - ports { - port@0 { - bridge_out: endpoint { - remote-endpoint = <&panel_in>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/bridge/sii9234.txt b/Documentation/devicetree/bindings/display/bridge/sii9234.txt deleted file mode 100644 index a55bf77bd960..000000000000 --- a/Documentation/devicetree/bindings/display/bridge/sii9234.txt +++ /dev/null @@ -1,49 +0,0 @@ -Silicon Image SiI9234 HDMI/MHL bridge bindings - -Required properties: - - compatible : "sil,sii9234". - - reg : I2C address for TPI interface, use 0x39 - - avcc33-supply : MHL/USB Switch Supply Voltage (3.3V) - - iovcc18-supply : I/O Supply Voltage (1.8V) - - avcc12-supply : TMDS Analog Supply Voltage (1.2V) - - cvcc12-supply : Digital Core Supply Voltage (1.2V) - - interrupts: interrupt specifier of INT pin - - reset-gpios: gpio specifier of RESET pin (active low) - - video interfaces: Device node can contain two video interface port - nodes for HDMI encoder and connector according to [1]. - - port@0 - MHL to HDMI - - port@1 - MHL to connector - -[1]: Documentation/devicetree/bindings/media/video-interfaces.txt - - -Example: - sii9234@39 { - compatible = "sil,sii9234"; - reg = <0x39>; - avcc33-supply = <&vcc33mhl>; - iovcc18-supply = <&vcc18mhl>; - avcc12-supply = <&vsil12>; - cvcc12-supply = <&vsil12>; - reset-gpios = <&gpf3 4 GPIO_ACTIVE_LOW>; - interrupt-parent = <&gpf3>; - interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - mhl_to_hdmi: endpoint { - remote-endpoint = <&hdmi_to_mhl>; - }; - }; - port@1 { - reg = <1>; - mhl_to_connector: endpoint { - remote-endpoint = <&connector_to_mhl>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/bridge/sil,sii9234.yaml b/Documentation/devicetree/bindings/display/bridge/sil,sii9234.yaml new file mode 100644 index 000000000000..f88ddfe4818b --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/sil,sii9234.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/sil,sii9234.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Silicon Image SiI9234 HDMI/MHL bridge + +maintainers: + - Maciej Purski <m.purski@samsung.com> + +properties: + compatible: + const: sil,sii9234 + + reg: + description: I2C address for TPI interface + maxItems: 1 + + avcc12-supply: + description: TMDS Analog Supply Voltage, 1.2V + + avcc33-supply: + description: MHL/USB Switch Supply Voltage, 3.3V + + cvcc12-supply: + description: Digital Core Supply Voltage, 1.2V + + iovcc18-supply: + description: I/O voltage supply, 1.8V + + interrupts: + maxItems: 1 + + reset-gpios: + description: GPIO connected to the reset pin. + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: + Video port for HDMI (encoder) input + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + MHL to connector port + + required: + - port@0 + +required: + - compatible + - reg + - avcc12-supply + - avcc33-supply + - cvcc12-supply + - iovcc18-supply + - interrupts + - reset-gpios + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + bridge@39 { + compatible = "sil,sii9234"; + reg = <0x39>; + avcc12-supply = <&vsil12>; + avcc33-supply = <&vcc33mhl>; + cvcc12-supply = <&vsil12>; + iovcc18-supply = <&vcc18mhl>; + interrupt-parent = <&gpf3>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + reset-gpios = <&gpf3 4 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + mhl_to_hdmi: endpoint { + remote-endpoint = <&hdmi_to_mhl>; + }; + }; + + port@1 { + reg = <1>; + mhl_to_connector: endpoint { + remote-endpoint = <&connector_to_mhl>; + }; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/boe,bf060y8m-aj0.yaml b/Documentation/devicetree/bindings/display/panel/boe,bf060y8m-aj0.yaml new file mode 100644 index 000000000000..a8f3afa922c8 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/boe,bf060y8m-aj0.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/boe,bf060y8m-aj0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: BOE BF060Y8M-AJ0 5.99" 1080x2160 AMOLED Panel + +maintainers: + - AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org> + +description: | + This is a 5.99" 1080x2160 16.7M Color active matrix AMOLED + video mode panel module on MIPI-DSI 4-Lane interface, GGRB + pixel arrangement, 63 micrometers pitch, with an active + area of 68.04 x 136.08 millimeters. + Each pixel is divided into red and green dots, or blue and + green dots, and two pixels share red or blue dots which are + arranged in vertical stripe. + The DriverIC for this panel module is SW43404. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: boe,bf060y8m-aj0 + + elvdd-supply: + description: EL Driving positive (VDD) supply (4.40-4.80V) + elvss-supply: + description: EL Driving negative (VSS) supply (-5.00V to -1.40V) + vcc-supply: + description: Core (TSP) voltage supply (2.70-3.60V) + vci-supply: + description: DriverIC Operation supply (2.60-3.60V) + vddio-supply: + description: I/O voltage supply (1.62-1.98V) + + port: true + reg: true + reset-gpios: true + +required: + - compatible + - elvdd-supply + - elvss-supply + - vcc-supply + - vci-supply + - vddio-supply + - reg + - reset-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "boe,bf060y8m-aj0"; + reg = <0>; + + reset-gpios = <&tlmm 94 GPIO_ACTIVE_HIGH>; + + vcc-supply = <&disp_vcc_vreg>; + vddio-supply = <&disp_vddio_vreg>; + vci-supply = <&disp_vci_vreg>; + elvdd-supply = <&disp_elvdd_vreg>; + elvss-supply = <&disp_elvss_vreg>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9163.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9163.yaml new file mode 100644 index 000000000000..7e7a8362b951 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9163.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/ilitek,ili9163.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ILI9163 display panels device tree bindings + +maintainers: + - Daniel Mack <daniel@zonque.org> + +description: + This binding is for display panels using an Ilitek ILI9163 controller in SPI + mode. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - newhaven,1.8-128160EF + - const: ilitek,ili9163 + + 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 = "newhaven,1.8-128160EF", "ilitek,ili9163"; + 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/ilitek,ili9881c.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml index b2fcec4f22fd..07789d554889 100644 --- a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml @@ -15,6 +15,7 @@ properties: - enum: - bananapi,lhr050h41 - feixin,k101-im2byl02 + - wanchanglong,w552946aba - const: ilitek,ili9881c backlight: true @@ -26,7 +27,6 @@ required: - compatible - power-supply - reg - - reset-gpios additionalProperties: false diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt35950.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt35950.yaml new file mode 100644 index 000000000000..377a05d48a02 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/novatek,nt35950.yaml @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/novatek,nt35950.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NT35950-based display panels + +maintainers: + - AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org> + +description: | + The nt35950 IC from Novatek is a Driver IC used to drive MIPI-DSI panels, + with Static RAM for content retention in command mode and also supports + video mode with VESA Frame Buffer Compression or Display Stream Compression + on single, or dual dsi port(s). + This DDIC is also capable of upscaling an input image to the panel's native + resolution, for example it can upscale a 1920x1080 input to 3840x2160 with + either bilinear interpolation or pixel duplication. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - sharp,ls055d1sx04 + - const: novatek,nt35950 + description: This indicates the panel manufacturer of the panel + that is in turn using the NT35950 panel driver. The compatible + string determines how the NT35950 panel driver shall be configured + to work with the indicated panel. The novatek,nt35950 compatible shall + always be provided as a fallback. + + reset-gpios: + maxItems: 1 + description: phandle of gpio for reset line - This should be 8mA, gpio + can be configured using mux, pinctrl, pinctrl-names (active high) + + avdd-supply: + description: positive boost supply regulator + avee-supply: + description: negative boost supply regulator + dvdd-supply: + description: regulator that supplies the digital voltage + vddio-supply: + description: regulator that supplies the I/O voltage + + backlight: true + ports: true + reg: true + +required: + - compatible + - reg + - reset-gpios + - avdd-supply + - avee-supply + - dvdd-supply + - vddio-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi0 { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "sharp,ls055d1sx04", "novatek,nt35950"; + reg = <0>; + + backlight = <&pmi8998_wled>; + reset-gpios = <&tlmm 94 GPIO_ACTIVE_HIGH>; + + avdd-supply = <&lab>; + avee-supply = <&ibb>; + dvdd-supply = <&disp_dvdd_vreg>; + vddio-supply = <&vreg_l14a_1p85>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + panel_in0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + panel_in1: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml index fbd71669248f..2c00813f5d20 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml @@ -35,6 +35,8 @@ properties: - boe,tv080wum-nl0 # Innolux P079ZCA 7.85" 768x1024 TFT LCD panel - innolux,p079zca + # JDI FHD_R63452 1080x1920 5.2" IPS LCD Panel + - jdi,fhd-r63452 # Khadas TS050 5" 1080x1920 LCD panel - khadas,ts050 # Kingdisplay KD097D04 9.7" 1536x2048 TFT LCD panel diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 335776c45474..f3c9395d23b6 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -166,6 +166,8 @@ properties: - innolux,at070tn92 # Innolux G070Y2-L01 7" WVGA (800x480) TFT LCD panel - innolux,g070y2-l01 + # Innolux G070Y2-T02 7" WVGA (800x480) TFT LCD TTL panel + - innolux,g070y2-t02 # Innolux Corporation 10.1" G101ICE-L01 WXGA (1280x800) LVDS panel - innolux,g101ice-l01 # Innolux Corporation 12.1" WXGA (1280x800) TFT LCD panel @@ -309,6 +311,8 @@ properties: - urt,umsh-8596md-11t - urt,umsh-8596md-19t - urt,umsh-8596md-20t + # Vivax TPC-9150 tablet 9.0" WSVGA TFT LCD panel + - vivax,tpc9150-panel # VXT 800x480 color TFT LCD panel - vxt,vl050-8048nt-c01 # Winstar Display Corporation 3.5" QVGA (320x240) TFT LCD panel @@ -317,6 +321,7 @@ properties: - yes-optoelectronics,ytc700tlag-05-201c backlight: true + ddc-i2c-bus: true enable-gpios: true port: true power-supply: true diff --git a/Documentation/devicetree/bindings/display/panel/sharp,ls060t1sx01.yaml b/Documentation/devicetree/bindings/display/panel/sharp,ls060t1sx01.yaml new file mode 100644 index 000000000000..271c097cc9a4 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/sharp,ls060t1sx01.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/sharp,ls060t1sx01.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sharp Microelectronics 6.0" FullHD TFT LCD panel + +maintainers: + - Dmitry Baryskov <dmitry.baryshkov@linaro.org> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: sharp,ls060t1sx01 + + reg: true + backlight: true + reset-gpios: true + port: true + + avdd-supply: + description: handle of the regulator that provides the positive supply voltage + avee-supply: + description: handle of the regulator that provides the negative supply voltage + vddi-supply: + description: handle of the regulator that provides the I/O supply voltage + vddh-supply: + description: handle of the regulator that provides the analog supply voltage + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "sharp,ls060t1sx01"; + reg = <0>; + avdd-supply = <&pm8941_l22>; + backlight = <&backlight>; + reset-gpios = <&pm8916_gpios 25 GPIO_ACTIVE_LOW>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/sony,tulip-truly-nt35521.yaml b/Documentation/devicetree/bindings/display/panel/sony,tulip-truly-nt35521.yaml new file mode 100644 index 000000000000..967972939598 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/sony,tulip-truly-nt35521.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/sony,tulip-truly-nt35521.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony Tulip Truly NT35521 5.24" 1280x720 MIPI-DSI Panel + +maintainers: + - Shawn Guo <shawn.guo@linaro.org> + +description: | + The Sony Tulip Truly NT35521 is a 5.24" 1280x720 MIPI-DSI panel, which + can be found no Sony Xperia M4 phone. The panel backlight is managed + through DSI link. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: sony,tulip-truly-nt35521 + + reg: true + + positive5-supply: + description: Positive 5V supply + + negative5-supply: + description: Negative 5V supply + + reset-gpios: true + + enable-gpios: true + + port: true + +required: + - compatible + - reg + - positive5-supply + - negative5-supply + - reset-gpios + - enable-gpios + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "sony,tulip-truly-nt35521"; + reg = <0>; + positive5-supply = <&positive5_reg>; + negative5-supply = <&negative5_reg>; + reset-gpios = <&msmgpio 25 GPIO_ACTIVE_LOW>; + enable-gpios = <&msmgpio 10 GPIO_ACTIVE_HIGH>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index a867f7102c35..2e848852f979 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1264,6 +1264,8 @@ patternProperties: description: Vitesse Semiconductor Corporation "^vivante,.*": description: Vivante Corporation + "^vivax,.*": + description: Vivax brand by M SAN Grupa d.o.o. "^vocore,.*": description: VoCore Studio "^voipac,.*": @@ -1304,6 +1306,8 @@ patternProperties: description: Wondermedia Technologies, Inc. "^wobo,.*": description: Wobo + "^wanchanglong,.*": + description: Wanchanglong Electronics Technology(SHENZHEN)Co.,Ltd. "^x-powers,.*": description: X-Powers "^xes,.*": diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index ec2f65b31930..5bb55ec1b9b5 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -435,3 +435,18 @@ Legacy CRTC/Modeset Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c :export: + +Privacy-screen class +==================== + +.. kernel-doc:: drivers/gpu/drm/drm_privacy_screen.c + :doc: overview + +.. kernel-doc:: include/drm/drm_privacy_screen_driver.h + :internal: + +.. kernel-doc:: include/drm/drm_privacy_screen_machine.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_privacy_screen.c + :export: diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 1ef7951ded5e..d14bf1c35d7e 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -506,6 +506,8 @@ Property Types and Blob Property Support .. kernel-doc:: drivers/gpu/drm/drm_property.c :export: +.. _standard_connector_properties: + Standard Connector Properties ----------------------------- diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 6613543955e9..60d1d7ee0719 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -314,16 +314,19 @@ Level: Advanced Garbage collect fbdev scrolling acceleration -------------------------------------------- -Scroll acceleration is disabled in fbcon by hard-wiring p->scrollmode = -SCROLL_REDRAW. There's a ton of code this will allow us to remove: +Scroll acceleration has been disabled in fbcon. Now it works as the old +SCROLL_REDRAW mode. A ton of code was removed in fbcon.c and the hook bmove was +removed from fbcon_ops. +Remaining tasks: -- lots of code in fbcon.c - -- a bunch of the hooks in fbcon_ops, maybe the remaining hooks could be called +- a bunch of the hooks in fbcon_ops could be removed or simplified by calling directly instead of the function table (with a switch on p->rotate) - fb_copyarea is unused after this, and can be deleted from all drivers +- after that, fb_copyarea can be deleted from fb_ops in include/linux/fb.h as + well as cfb_copyarea + Note that not all acceleration code can be deleted, since clearing and cursor support is still accelerated, which might be good candidates for further deletion projects. diff --git a/MAINTAINERS b/MAINTAINERS index fbfeca76f2af..00e372b9ffdb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6422,6 +6422,14 @@ F: drivers/gpu/drm/drm_panel.c F: drivers/gpu/drm/panel/ F: include/drm/drm_panel.h +DRM PRIVACY-SCREEN CLASS +M: Hans de Goede <hdegoede@redhat.com> +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc +F: drivers/gpu/drm/drm_privacy_screen* +F: include/drm/drm_privacy_screen* + DRM TTM SUBSYSTEM M: Christian Koenig <christian.koenig@amd.com> M: Huang Rui <ray.huang@amd.com> diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index 1ef021273a06..511805dbeb75 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o dmabuf_selftests-y := \ selftest.o \ st-dma-fence.o \ - st-dma-fence-chain.o + st-dma-fence-chain.o \ + st-dma-resv.o obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 61e20ae7b08b..7b619998f03a 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -209,19 +209,14 @@ static void dma_buf_poll_cb(struct dma_fence *fence, struct dma_fence_cb *cb) dma_fence_put(fence); } -static bool dma_buf_poll_shared(struct dma_resv *resv, +static bool dma_buf_poll_add_cb(struct dma_resv *resv, bool write, struct dma_buf_poll_cb_t *dcb) { - struct dma_resv_list *fobj = dma_resv_shared_list(resv); + struct dma_resv_iter cursor; struct dma_fence *fence; - int i, r; - - if (!fobj) - return false; + int r; - for (i = 0; i < fobj->shared_count; ++i) { - fence = rcu_dereference_protected(fobj->shared[i], - dma_resv_held(resv)); + dma_resv_for_each_fence(&cursor, resv, write, fence) { dma_fence_get(fence); r = dma_fence_add_callback(fence, &dcb->cb, dma_buf_poll_cb); if (!r) @@ -232,24 +227,6 @@ static bool dma_buf_poll_shared(struct dma_resv *resv, return false; } -static bool dma_buf_poll_excl(struct dma_resv *resv, - struct dma_buf_poll_cb_t *dcb) -{ - struct dma_fence *fence = dma_resv_excl_fence(resv); - int r; - - if (!fence) - return false; - - dma_fence_get(fence); - r = dma_fence_add_callback(fence, &dcb->cb, dma_buf_poll_cb); - if (!r) - return true; - dma_fence_put(fence); - - return false; -} - static __poll_t dma_buf_poll(struct file *file, poll_table *poll) { struct dma_buf *dmabuf; @@ -282,8 +259,7 @@ static __poll_t dma_buf_poll(struct file *file, poll_table *poll) spin_unlock_irq(&dmabuf->poll.lock); if (events & EPOLLOUT) { - if (!dma_buf_poll_shared(resv, dcb) && - !dma_buf_poll_excl(resv, dcb)) + if (!dma_buf_poll_add_cb(resv, true, dcb)) /* No callback queued, wake up any other waiters */ dma_buf_poll_cb(NULL, &dcb->cb); else @@ -303,7 +279,7 @@ static __poll_t dma_buf_poll(struct file *file, poll_table *poll) spin_unlock_irq(&dmabuf->poll.lock); if (events & EPOLLIN) { - if (!dma_buf_poll_excl(resv, dcb)) + if (!dma_buf_poll_add_cb(resv, false, dcb)) /* No callback queued, wake up any other waiters */ dma_buf_poll_cb(NULL, &dcb->cb); else @@ -1076,8 +1052,8 @@ EXPORT_SYMBOL_GPL(dma_buf_move_notify); * * Interfaces:: * - * void \*dma_buf_vmap(struct dma_buf \*dmabuf) - * void dma_buf_vunmap(struct dma_buf \*dmabuf, void \*vaddr) + * void \*dma_buf_vmap(struct dma_buf \*dmabuf, struct dma_buf_map \*map) + * void dma_buf_vunmap(struct dma_buf \*dmabuf, struct dma_buf_map \*map) * * The vmap call can fail if there is no vmap support in the exporter, or if * it runs out of vmalloc space. Note that the dma-buf layer keeps a reference @@ -1356,10 +1332,9 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) { struct dma_buf *buf_obj; struct dma_buf_attachment *attach_obj; - struct dma_resv *robj; - struct dma_resv_list *fobj; + struct dma_resv_iter cursor; struct dma_fence *fence; - int count = 0, attach_count, shared_count, i; + int count = 0, attach_count; size_t size = 0; int ret; @@ -1386,21 +1361,10 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) file_inode(buf_obj->file)->i_ino, buf_obj->name ?: ""); - robj = buf_obj->resv; - fence = dma_resv_excl_fence(robj); - if (fence) - seq_printf(s, "\tExclusive fence: %s %s %ssignalled\n", - fence->ops->get_driver_name(fence), - fence->ops->get_timeline_name(fence), - dma_fence_is_signaled(fence) ? "" : "un"); - - fobj = rcu_dereference_protected(robj->fence, - dma_resv_held(robj)); - shared_count = fobj ? fobj->shared_count : 0; - for (i = 0; i < shared_count; i++) { - fence = rcu_dereference_protected(fobj->shared[i], - dma_resv_held(robj)); - seq_printf(s, "\tShared fence: %s %s %ssignalled\n", + dma_resv_for_each_fence(&cursor, buf_obj->resv, true, fence) { + seq_printf(s, "\t%s fence: %s %s %ssignalled\n", + dma_resv_iter_is_exclusive(&cursor) ? + "Exclusive" : "Shared", fence->ops->get_driver_name(fence), fence->ops->get_timeline_name(fence), dma_fence_is_signaled(fence) ? "" : "un"); diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index a480af9581bd..9eb2baa387d4 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -333,10 +333,14 @@ static void dma_resv_iter_restart_unlocked(struct dma_resv_iter *cursor) { cursor->seq = read_seqcount_begin(&cursor->obj->seq); cursor->index = -1; - if (cursor->all_fences) + cursor->shared_count = 0; + if (cursor->all_fences) { cursor->fences = dma_resv_shared_list(cursor->obj); - else + if (cursor->fences) + cursor->shared_count = cursor->fences->shared_count; + } else { cursor->fences = NULL; + } cursor->is_restarted = true; } @@ -363,7 +367,7 @@ static void dma_resv_iter_walk_unlocked(struct dma_resv_iter *cursor) continue; } else if (!cursor->fences || - cursor->index >= cursor->fences->shared_count) { + cursor->index >= cursor->shared_count) { cursor->fence = NULL; break; @@ -424,6 +428,57 @@ struct dma_fence *dma_resv_iter_next_unlocked(struct dma_resv_iter *cursor) EXPORT_SYMBOL(dma_resv_iter_next_unlocked); /** + * dma_resv_iter_first - first fence from a locked dma_resv object + * @cursor: cursor to record the current position + * + * Return the first fence in the dma_resv object while holding the + * &dma_resv.lock. + */ +struct dma_fence *dma_resv_iter_first(struct dma_resv_iter *cursor) +{ + struct dma_fence *fence; + + dma_resv_assert_held(cursor->obj); + + cursor->index = 0; + if (cursor->all_fences) + cursor->fences = dma_resv_shared_list(cursor->obj); + else + cursor->fences = NULL; + + fence = dma_resv_excl_fence(cursor->obj); + if (!fence) + fence = dma_resv_iter_next(cursor); + + cursor->is_restarted = true; + return fence; +} +EXPORT_SYMBOL_GPL(dma_resv_iter_first); + +/** + * dma_resv_iter_next - next fence from a locked dma_resv object + * @cursor: cursor to record the current position + * + * Return the next fences from the dma_resv object while holding the + * &dma_resv.lock. + */ +struct dma_fence *dma_resv_iter_next(struct dma_resv_iter *cursor) +{ + unsigned int idx; + + dma_resv_assert_held(cursor->obj); + + cursor->is_restarted = false; + if (!cursor->fences || cursor->index >= cursor->fences->shared_count) + return NULL; + + idx = cursor->index++; + return rcu_dereference_protected(cursor->fences->shared[idx], + dma_resv_held(cursor->obj)); +} +EXPORT_SYMBOL_GPL(dma_resv_iter_next); + +/** * dma_resv_copy_fences - Copy all fences from src to dst. * @dst: the destination reservation object * @src: the source reservation object @@ -448,10 +503,8 @@ int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src) dma_resv_list_free(list); dma_fence_put(excl); - if (cursor.fences) { - unsigned int cnt = cursor.fences->shared_count; - - list = dma_resv_list_alloc(cnt); + if (cursor.shared_count) { + list = dma_resv_list_alloc(cursor.shared_count); if (!list) { dma_resv_iter_end(&cursor); return -ENOMEM; @@ -522,7 +575,7 @@ int dma_resv_get_fences(struct dma_resv *obj, struct dma_fence **fence_excl, if (fence_excl) dma_fence_put(*fence_excl); - count = cursor.fences ? cursor.fences->shared_count : 0; + count = cursor.shared_count; count += fence_excl ? 0 : 1; /* Eventually re-allocate the array */ diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h index bc8cea67bf1e..97d73aaa31da 100644 --- a/drivers/dma-buf/selftests.h +++ b/drivers/dma-buf/selftests.h @@ -12,3 +12,4 @@ selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */ selftest(dma_fence, dma_fence) selftest(dma_fence_chain, dma_fence_chain) +selftest(dma_resv, dma_resv) diff --git a/drivers/dma-buf/st-dma-resv.c b/drivers/dma-buf/st-dma-resv.c new file mode 100644 index 000000000000..6f3ba756da3e --- /dev/null +++ b/drivers/dma-buf/st-dma-resv.c @@ -0,0 +1,370 @@ +/* SPDX-License-Identifier: MIT */ + +/* +* Copyright © 2019 Intel Corporation +* Copyright © 2021 Advanced Micro Devices, Inc. +*/ + +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/dma-resv.h> + +#include "selftest.h" + +static struct spinlock fence_lock; + +static const char *fence_name(struct dma_fence *f) +{ + return "selftest"; +} + +static const struct dma_fence_ops fence_ops = { + .get_driver_name = fence_name, + .get_timeline_name = fence_name, +}; + +static struct dma_fence *alloc_fence(void) +{ + struct dma_fence *f; + + f = kmalloc(sizeof(*f), GFP_KERNEL); + if (!f) + return NULL; + + dma_fence_init(f, &fence_ops, &fence_lock, 0, 0); + return f; +} + +static int sanitycheck(void *arg) +{ + struct dma_resv resv; + struct dma_fence *f; + int r; + + f = alloc_fence(); + if (!f) + return -ENOMEM; + + dma_fence_signal(f); + dma_fence_put(f); + + dma_resv_init(&resv); + r = dma_resv_lock(&resv, NULL); + if (r) + pr_err("Resv locking failed\n"); + else + dma_resv_unlock(&resv); + dma_resv_fini(&resv); + return r; +} + +static int test_signaling(void *arg, bool shared) +{ + struct dma_resv resv; + struct dma_fence *f; + int r; + + f = alloc_fence(); + if (!f) + return -ENOMEM; + + dma_resv_init(&resv); + r = dma_resv_lock(&resv, NULL); + if (r) { + pr_err("Resv locking failed\n"); + goto err_free; + } + + if (shared) { + r = dma_resv_reserve_shared(&resv, 1); + if (r) { + pr_err("Resv shared slot allocation failed\n"); + goto err_unlock; + } + + dma_resv_add_shared_fence(&resv, f); + } else { + dma_resv_add_excl_fence(&resv, f); + } + + if (dma_resv_test_signaled(&resv, shared)) { + pr_err("Resv unexpectedly signaled\n"); + r = -EINVAL; + goto err_unlock; + } + dma_fence_signal(f); + if (!dma_resv_test_signaled(&resv, shared)) { + pr_err("Resv not reporting signaled\n"); + r = -EINVAL; + goto err_unlock; + } +err_unlock: + dma_resv_unlock(&resv); +err_free: + dma_resv_fini(&resv); + dma_fence_put(f); + return r; +} + +static int test_excl_signaling(void *arg) +{ + return test_signaling(arg, false); +} + +static int test_shared_signaling(void *arg) +{ + return test_signaling(arg, true); +} + +static int test_for_each(void *arg, bool shared) +{ + struct dma_resv_iter cursor; + struct dma_fence *f, *fence; + struct dma_resv resv; + int r; + + f = alloc_fence(); + if (!f) + return -ENOMEM; + + dma_resv_init(&resv); + r = dma_resv_lock(&resv, NULL); + if (r) { + pr_err("Resv locking failed\n"); + goto err_free; + } + + if (shared) { + r = dma_resv_reserve_shared(&resv, 1); + if (r) { + pr_err("Resv shared slot allocation failed\n"); + goto err_unlock; + } + + dma_resv_add_shared_fence(&resv, f); + } else { + dma_resv_add_excl_fence(&resv, f); + } + + r = -ENOENT; + dma_resv_for_each_fence(&cursor, &resv, shared, fence) { + if (!r) { + pr_err("More than one fence found\n"); + r = -EINVAL; + goto err_unlock; + } + if (f != fence) { + pr_err("Unexpected fence\n"); + r = -EINVAL; + goto err_unlock; + } + if (dma_resv_iter_is_exclusive(&cursor) != !shared) { + pr_err("Unexpected fence usage\n"); + r = -EINVAL; + goto err_unlock; + } + r = 0; + } + if (r) { + pr_err("No fence found\n"); + goto err_unlock; + } + dma_fence_signal(f); +err_unlock: + dma_resv_unlock(&resv); +err_free: + dma_resv_fini(&resv); + dma_fence_put(f); + return r; +} + +static int test_excl_for_each(void *arg) +{ + return test_for_each(arg, false); +} + +static int test_shared_for_each(void *arg) +{ + return test_for_each(arg, true); +} + +static int test_for_each_unlocked(void *arg, bool shared) +{ + struct dma_resv_iter cursor; + struct dma_fence *f, *fence; + struct dma_resv resv; + int r; + + f = alloc_fence(); + if (!f) + return -ENOMEM; + + dma_resv_init(&resv); + r = dma_resv_lock(&resv, NULL); + if (r) { + pr_err("Resv locking failed\n"); + goto err_free; + } + + if (shared) { + r = dma_resv_reserve_shared(&resv, 1); + if (r) { + pr_err("Resv shared slot allocation failed\n"); + dma_resv_unlock(&resv); + goto err_free; + } + + dma_resv_add_shared_fence(&resv, f); + } else { + dma_resv_add_excl_fence(&resv, f); + } + dma_resv_unlock(&resv); + + r = -ENOENT; + dma_resv_iter_begin(&cursor, &resv, shared); + dma_resv_for_each_fence_unlocked(&cursor, fence) { + if (!r) { + pr_err("More than one fence found\n"); + r = -EINVAL; + goto err_iter_end; + } + if (!dma_resv_iter_is_restarted(&cursor)) { + pr_err("No restart flag\n"); + goto err_iter_end; + } + if (f != fence) { + pr_err("Unexpected fence\n"); + r = -EINVAL; + goto err_iter_end; + } + if (dma_resv_iter_is_exclusive(&cursor) != !shared) { + pr_err("Unexpected fence usage\n"); + r = -EINVAL; + goto err_iter_end; + } + + /* We use r as state here */ + if (r == -ENOENT) { + r = -EINVAL; + /* That should trigger an restart */ + cursor.seq--; + } else if (r == -EINVAL) { + r = 0; + } + } + if (r) + pr_err("No fence found\n"); +err_iter_end: + dma_resv_iter_end(&cursor); + dma_fence_signal(f); +err_free: + dma_resv_fini(&resv); + dma_fence_put(f); + return r; +} + +static int test_excl_for_each_unlocked(void *arg) +{ + return test_for_each_unlocked(arg, false); +} + +static int test_shared_for_each_unlocked(void *arg) +{ + return test_for_each_unlocked(arg, true); +} + +static int test_get_fences(void *arg, bool shared) +{ + struct dma_fence *f, *excl = NULL, **fences = NULL; + struct dma_resv resv; + int r, i; + + f = alloc_fence(); + if (!f) + return -ENOMEM; + + dma_resv_init(&resv); + r = dma_resv_lock(&resv, NULL); + if (r) { + pr_err("Resv locking failed\n"); + goto err_free; + } + + if (shared) { + r = dma_resv_reserve_shared(&resv, 1); + if (r) { + pr_err("Resv shared slot allocation failed\n"); + dma_resv_unlock(&resv); + goto err_free; + } + + dma_resv_add_shared_fence(&resv, f); + } else { + dma_resv_add_excl_fence(&resv, f); + } + dma_resv_unlock(&resv); + + r = dma_resv_get_fences(&resv, &excl, &i, &fences); + if (r) { + pr_err("get_fences failed\n"); + goto err_free; + } + + if (shared) { + if (excl != NULL) { + pr_err("get_fences returned unexpected excl fence\n"); + goto err_free; + } + if (i != 1 || fences[0] != f) { + pr_err("get_fences returned unexpected shared fence\n"); + goto err_free; + } + } else { + if (excl != f) { + pr_err("get_fences returned unexpected excl fence\n"); + goto err_free; + } + if (i != 0) { + pr_err("get_fences returned unexpected shared fence\n"); + goto err_free; + } + } + + dma_fence_signal(f); +err_free: + dma_fence_put(excl); + while (i--) + dma_fence_put(fences[i]); + kfree(fences); + dma_resv_fini(&resv); + dma_fence_put(f); + return r; +} + +static int test_excl_get_fences(void *arg) +{ + return test_get_fences(arg, false); +} + +static int test_shared_get_fences(void *arg) +{ + return test_get_fences(arg, true); +} + +int dma_resv(void) +{ + static const struct subtest tests[] = { + SUBTEST(sanitycheck), + SUBTEST(test_excl_signaling), + SUBTEST(test_shared_signaling), + SUBTEST(test_excl_for_each), + SUBTEST(test_shared_for_each), + SUBTEST(test_excl_for_each_unlocked), + SUBTEST(test_shared_for_each_unlocked), + SUBTEST(test_excl_get_fences), + SUBTEST(test_shared_get_fences), + }; + + spin_lock_init(&fence_lock); + return subtests(tests, NULL); +} diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 2a926d0de423..fb144617055b 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -100,6 +100,21 @@ config DRM_DEBUG_DP_MST_TOPOLOGY_REFS This has the potential to use a lot of memory and print some very large kernel messages. If in doubt, say "N". +config DRM_DEBUG_MODESET_LOCK + bool "Enable backtrace history for lock contention" + depends on STACKTRACE_SUPPORT + depends on DEBUG_KERNEL + depends on EXPERT + select STACKDEPOT + default y if DEBUG_WW_MUTEX_SLOWPATH + help + Enable debug tracing of failures to gracefully handle drm modeset lock + contention. A history of each drm modeset lock path hitting -EDEADLK + will be saved until gracefully handled, and the backtrace will be + printed when attempting to lock a contended lock. + + If in doubt, say "N". + config DRM_FBDEV_EMULATION bool "Enable legacy fbdev support for your modesetting driver" depends on DRM @@ -197,7 +212,7 @@ config DRM_TTM_HELPER Helpers for ttm-based gem objects config DRM_GEM_CMA_HELPER - bool + tristate depends on DRM help Choose this if you need the GEM CMA helper functions @@ -210,7 +225,7 @@ config DRM_KMS_CMA_HELPER Choose this if you need the KMS CMA helper functions config DRM_GEM_SHMEM_HELPER - bool + tristate depends on DRM && MMU help Choose this if you need the GEM shmem helper functions @@ -481,3 +496,7 @@ config DRM_PANEL_ORIENTATION_QUIRKS config DRM_LIB_RANDOM bool default n + +config DRM_PRIVACY_SCREEN + bool + default n diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0dff40bb863c..7f6eb11b6aac 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -4,37 +4,42 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. drm-y := drm_aperture.o drm_auth.o drm_cache.o \ - drm_file.o drm_gem.o drm_ioctl.o drm_irq.o \ + drm_file.o drm_gem.o drm_ioctl.o \ drm_drv.o \ drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_fourcc.o drm_modes.o drm_edid.o drm_displayid.o \ - drm_encoder_slave.o \ drm_trace_points.o drm_prime.o \ - drm_rect.o drm_vma_manager.o drm_flip_work.o \ + drm_vma_manager.o \ drm_modeset_lock.o drm_atomic.o drm_bridge.o \ drm_framebuffer.o drm_connector.o drm_blend.o \ drm_encoder.o drm_mode_object.o drm_property.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ - drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \ + drm_client_modeset.o drm_atomic_uapi.o \ drm_managed.o drm_vblank_work.o drm-$(CONFIG_DRM_LEGACY) += drm_agpsupport.o drm_bufs.o drm_context.o drm_dma.o \ - drm_legacy_misc.o drm_lock.o drm_memory.o drm_scatter.o \ - drm_vm.o + drm_irq.o drm_legacy_misc.o drm_lock.o drm_memory.o \ + drm_scatter.o drm_vm.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o drm-$(CONFIG_COMPAT) += drm_ioc32.o -drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o -drm-$(CONFIG_DRM_GEM_SHMEM_HELPER) += drm_gem_shmem_helper.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o drm-$(CONFIG_PCI) += drm_pci.o drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o +drm-$(CONFIG_DRM_PRIVACY_SCREEN) += drm_privacy_screen.o drm_privacy_screen_x86.o obj-$(CONFIG_DRM_DP_AUX_BUS) += drm_dp_aux_bus.o +drm_cma_helper-y := drm_gem_cma_helper.o +drm_cma_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o +obj-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_cma_helper.o + +drm_shmem_helper-y := drm_gem_shmem_helper.o +obj-$(CONFIG_DRM_GEM_SHMEM_HELPER) += drm_shmem_helper.o + drm_vram_helper-y := drm_gem_vram_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o @@ -42,18 +47,18 @@ drm_ttm_helper-y := drm_gem_ttm_helper.o obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o drm_dp_helper.o \ - drm_dsc.o drm_probe_helper.o \ + drm_dsc.o drm_encoder_slave.o drm_flip_work.o drm_hdcp.o \ + drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ drm_simple_kms_helper.o drm_modeset_helper.o \ drm_scdc_helper.o drm_gem_atomic_helper.o \ drm_gem_framebuffer_helper.o \ drm_atomic_state_helper.o drm_damage_helper.o \ - drm_format_helper.o drm_self_refresh_helper.o + drm_format_helper.o drm_self_refresh_helper.o drm_rect.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o -drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o drm_kms_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c index 862eb3c1c4c5..f7d8487799b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c @@ -252,41 +252,25 @@ int amdgpu_sync_resv(struct amdgpu_device *adev, struct amdgpu_sync *sync, struct dma_resv *resv, enum amdgpu_sync_mode mode, void *owner) { - struct dma_resv_list *flist; + struct dma_resv_iter cursor; struct dma_fence *f; - unsigned i; - int r = 0; + int r; if (resv == NULL) return -EINVAL; - /* always sync to the exclusive fence */ - f = dma_resv_excl_fence(resv); - dma_fence_chain_for_each(f, f) { - struct dma_fence_chain *chain = to_dma_fence_chain(f); - - if (amdgpu_sync_test_fence(adev, mode, owner, chain ? - chain->fence : f)) { - r = amdgpu_sync_fence(sync, f); - dma_fence_put(f); - if (r) - return r; - break; - } - } - - flist = dma_resv_shared_list(resv); - if (!flist) - return 0; - - for (i = 0; i < flist->shared_count; ++i) { - f = rcu_dereference_protected(flist->shared[i], - dma_resv_held(resv)); - - if (amdgpu_sync_test_fence(adev, mode, owner, f)) { - r = amdgpu_sync_fence(sync, f); - if (r) - return r; + dma_resv_for_each_fence(&cursor, resv, true, f) { + dma_fence_chain_for_each(f, f) { + struct dma_fence_chain *chain = to_dma_fence_chain(f); + + if (amdgpu_sync_test_fence(adev, mode, owner, chain ? + chain->fence : f)) { + r = amdgpu_sync_fence(sync, f); + dma_fence_put(f); + if (r) + return r; + break; + } } } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 19d4d8b54490..30b7dde496fc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -1345,10 +1345,9 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, const struct ttm_place *place) { unsigned long num_pages = bo->resource->num_pages; + struct dma_resv_iter resv_cursor; struct amdgpu_res_cursor cursor; - struct dma_resv_list *flist; struct dma_fence *f; - int i; /* Swapout? */ if (bo->resource->mem_type == TTM_PL_SYSTEM) @@ -1362,14 +1361,9 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, * If true, then return false as any KFD process needs all its BOs to * be resident to run successfully */ - flist = dma_resv_shared_list(bo->base.resv); - if (flist) { - for (i = 0; i < flist->shared_count; ++i) { - f = rcu_dereference_protected(flist->shared[i], - dma_resv_held(bo->base.resv)); - if (amdkfd_fence_check_mm(f, current->mm)) - return false; - } + dma_resv_for_each_fence(&resv_cursor, bo->base.resv, true, f) { + if (amdkfd_fence_check_mm(f, current->mm)) + return false; } switch (bo->resource->mem_type) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 0e7dc23f78e7..a96ae4c0e040 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2102,30 +2102,14 @@ static void amdgpu_vm_free_mapping(struct amdgpu_device *adev, static void amdgpu_vm_prt_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm) { struct dma_resv *resv = vm->root.bo->tbo.base.resv; - struct dma_fence *excl, **shared; - unsigned i, shared_count; - int r; + struct dma_resv_iter cursor; + struct dma_fence *fence; - r = dma_resv_get_fences(resv, &excl, &shared_count, &shared); - if (r) { - /* Not enough memory to grab the fence list, as last resort - * block for all the fences to complete. - */ - dma_resv_wait_timeout(resv, true, false, - MAX_SCHEDULE_TIMEOUT); - return; - } - - /* Add a callback for each fence in the reservation object */ - amdgpu_vm_prt_get(adev); - amdgpu_vm_add_prt_cb(adev, excl); - - for (i = 0; i < shared_count; ++i) { + dma_resv_for_each_fence(&cursor, resv, true, fence) { + /* Add a callback for each fence in the reservation object */ amdgpu_vm_prt_get(adev); - amdgpu_vm_add_prt_cb(adev, shared[i]); + amdgpu_vm_add_prt_cb(adev, fence); } - - kfree(shared); } /** diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index 05eb759da6fc..847a0dce7f1d 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -107,7 +107,7 @@ static int display_connector_probe(struct platform_device *pdev) { struct display_connector *conn; unsigned int type; - const char *label; + const char *label = NULL; int ret; conn = devm_kzalloc(&pdev->dev, sizeof(*conn), GFP_KERNEL); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 3cac16db970f..010657ea7af7 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -167,9 +167,10 @@ static void lt9611uxc_hpd_work(struct work_struct *work) struct lt9611uxc *lt9611uxc = container_of(work, struct lt9611uxc, work); bool connected; - if (lt9611uxc->connector.dev) - drm_kms_helper_hotplug_event(lt9611uxc->connector.dev); - else { + if (lt9611uxc->connector.dev) { + if (lt9611uxc->connector.dev->mode_config.funcs) + drm_kms_helper_hotplug_event(lt9611uxc->connector.dev); + } else { mutex_lock(<9611uxc->ocm_lock); connected = lt9611uxc->hdmi_connected; @@ -339,6 +340,8 @@ static int lt9611uxc_connector_init(struct drm_bridge *bridge, struct lt9611uxc return -ENODEV; } + lt9611uxc->connector.polled = DRM_CONNECTOR_POLL_HPD; + drm_connector_helper_add(<9611uxc->connector, <9611uxc_bridge_connector_helper_funcs); ret = drm_connector_init(bridge->dev, <9611uxc->connector, diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index dcf579a4cf83..f991842a161f 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -12,7 +12,9 @@ #include <linux/platform_device.h> #include <linux/regulator/consumer.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> struct lvds_codec { @@ -22,6 +24,7 @@ struct lvds_codec { struct regulator *vcc; struct gpio_desc *powerdown_gpio; u32 connector_type; + unsigned int bus_format; }; static inline struct lvds_codec *to_lvds_codec(struct drm_bridge *bridge) @@ -74,12 +77,49 @@ static const struct drm_bridge_funcs funcs = { .disable = lvds_codec_disable, }; +#define MAX_INPUT_SEL_FORMATS 1 +static u32 * +lvds_codec_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 lvds_codec *lvds_codec = to_lvds_codec(bridge); + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + input_fmts[0] = lvds_codec->bus_format; + *num_input_fmts = MAX_INPUT_SEL_FORMATS; + + return input_fmts; +} + +static const struct drm_bridge_funcs funcs_decoder = { + .attach = lvds_codec_attach, + .enable = lvds_codec_enable, + .disable = lvds_codec_disable, + .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_get_input_bus_fmts = lvds_codec_atomic_get_input_bus_fmts, +}; + static int lvds_codec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *panel_node; + struct device_node *bus_node; struct drm_panel *panel; struct lvds_codec *lvds_codec; + int ret; lvds_codec = devm_kzalloc(dev, sizeof(*lvds_codec), GFP_KERNEL); if (!lvds_codec) @@ -119,13 +159,40 @@ static int lvds_codec_probe(struct platform_device *pdev) if (IS_ERR(lvds_codec->panel_bridge)) return PTR_ERR(lvds_codec->panel_bridge); + lvds_codec->bridge.funcs = &funcs; + + /* + * Decoder input LVDS format is a property of the decoder chip or even + * its strapping. Handle data-mapping the same way lvds-panel does. In + * case data-mapping is not present, do nothing, since there are still + * legacy bindings which do not specify this property. + */ + if (lvds_codec->connector_type != DRM_MODE_CONNECTOR_LVDS) { + bus_node = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); + if (!bus_node) { + dev_dbg(dev, "bus DT node not found\n"); + return -ENXIO; + } + + ret = drm_of_lvds_get_data_mapping(bus_node); + of_node_put(bus_node); + if (ret == -ENODEV) { + dev_warn(dev, "missing 'data-mapping' DT property\n"); + } else if (ret) { + dev_err(dev, "invalid 'data-mapping' DT property\n"); + return ret; + } else { + lvds_codec->bus_format = ret; + lvds_codec->bridge.funcs = &funcs_decoder; + } + } + /* * The panel_bridge bridge is attached to the panel's of_node, * but we need a bridge attached to our of_node for our user * to look up. */ lvds_codec->bridge.of_node = dev->of_node; - lvds_codec->bridge.funcs = &funcs; drm_bridge_add(&lvds_codec->bridge); platform_set_drvdata(pdev, lvds_codec); diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index ed8ac5059cd2..a7389a0facfb 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -939,6 +939,40 @@ static void nwl_dsi_bridge_detach(struct drm_bridge *bridge) drm_of_panel_bridge_remove(dsi->dev->of_node, 1, 0); } +static u32 *nwl_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) +{ + u32 *input_fmts, input_fmt; + + *num_input_fmts = 0; + + switch (output_fmt) { + /* If MEDIA_BUS_FMT_FIXED is tested, return default bus format */ + case MEDIA_BUS_FMT_FIXED: + input_fmt = MEDIA_BUS_FMT_RGB888_1X24; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RGB565_1X16: + input_fmt = output_fmt; + break; + default: + return NULL; + } + + input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + input_fmts[0] = input_fmt; + *num_input_fmts = 1; + + return input_fmts; +} + static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, @@ -946,6 +980,7 @@ static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = { .atomic_check = nwl_dsi_bridge_atomic_check, .atomic_enable = nwl_dsi_bridge_atomic_enable, .atomic_disable = nwl_dsi_bridge_atomic_disable, + .atomic_get_input_bus_fmts = nwl_bridge_atomic_get_input_bus_fmts, .mode_set = nwl_dsi_bridge_mode_set, .mode_valid = nwl_dsi_bridge_mode_valid, .attach = nwl_dsi_bridge_attach, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index f08d0fded61f..62ae63565d3a 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3413,6 +3413,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; + hdmi->bridge.interlace_allowed = true; #ifdef CONFIG_OF hdmi->bridge.of_node = pdev->dev.of_node; #endif diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index a3db532bbdd1..fd585bf925fe 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -237,6 +237,10 @@ static void tc358768_hw_enable(struct tc358768_priv *priv) if (priv->enabled) return; + ret = clk_prepare_enable(priv->refclk); + if (ret < 0) + dev_err(priv->dev, "error enabling refclk (%d)\n", ret); + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); if (ret < 0) dev_err(priv->dev, "error enabling regulators (%d)\n", ret); @@ -274,6 +278,8 @@ static void tc358768_hw_disable(struct tc358768_priv *priv) if (ret < 0) dev_err(priv->dev, "error disabling regulators (%d)\n", ret); + clk_disable_unprepare(priv->refclk); + priv->enabled = false; } @@ -625,12 +631,19 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) { struct tc358768_priv *priv = bridge_to_tc358768(bridge); struct mipi_dsi_device *dsi_dev = priv->output.dev; + unsigned long mode_flags = dsi_dev->mode_flags; u32 val, val2, lptxcnt, hact, data_type; const struct drm_display_mode *mode; u32 dsibclk_nsk, dsiclk_nsk, ui_nsk, phy_delay_nsk; - u32 dsiclk, dsibclk; + u32 dsiclk, dsibclk, video_start; + const u32 internal_delay = 40; int ret, i; + if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { + dev_warn_once(priv->dev, "Non-continuous mode unimplemented, falling back to continuous\n"); + mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS; + } + tc358768_hw_enable(priv); ret = tc358768_sw_reset(priv); @@ -657,23 +670,27 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) case MIPI_DSI_FMT_RGB888: val |= (0x3 << 4); hact = mode->hdisplay * 3; + video_start = (mode->htotal - mode->hsync_start) * 3; data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24; break; case MIPI_DSI_FMT_RGB666: val |= (0x4 << 4); hact = mode->hdisplay * 3; + video_start = (mode->htotal - mode->hsync_start) * 3; data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18; break; case MIPI_DSI_FMT_RGB666_PACKED: val |= (0x4 << 4) | BIT(3); hact = mode->hdisplay * 18 / 8; + video_start = (mode->htotal - mode->hsync_start) * 18 / 8; data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18; break; case MIPI_DSI_FMT_RGB565: val |= (0x5 << 4); hact = mode->hdisplay * 2; + video_start = (mode->htotal - mode->hsync_start) * 2; data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16; break; default: @@ -684,7 +701,8 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) } /* VSDly[9:0] */ - tc358768_write(priv, TC358768_VSDLY, 1); + video_start = max(video_start, internal_delay + 1) - internal_delay; + tc358768_write(priv, TC358768_VSDLY, video_start); tc358768_write(priv, TC358768_DATAFMT, val); tc358768_write(priv, TC358768_DSITX_DT, data_type); @@ -764,7 +782,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) val |= BIT(i + 1); tc358768_write(priv, TC358768_HSTXVREGEN, val); - if (!(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) + if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) tc358768_write(priv, TC358768_TXOPTIONCNTRL, 0x1); /* TXTAGOCNT[26:16] RXTASURECNT[10:0] */ @@ -772,31 +790,61 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1; val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk), dsibclk_nsk) - 2; - val |= val2 << 16; + val = val << 16 | val2; dev_dbg(priv->dev, "BTACNTRL1: 0x%x\n", val); tc358768_write(priv, TC358768_BTACNTRL1, val); /* START[0] */ tc358768_write(priv, TC358768_STARTCNTRL, 1); - /* Set event mode */ - tc358768_write(priv, TC358768_DSI_EVENT, 1); - - /* vsw (+ vbp) */ - tc358768_write(priv, TC358768_DSI_VSW, - mode->vtotal - mode->vsync_start); - /* vbp (not used in event mode) */ - tc358768_write(priv, TC358768_DSI_VBPR, 0); - /* vact */ - tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay); - - /* (hsw + hbp) * byteclk * ndl / pclk */ - val = (u32)div_u64((mode->htotal - mode->hsync_start) * - ((u64)priv->dsiclk / 4) * priv->dsi_lanes, - mode->clock * 1000); - tc358768_write(priv, TC358768_DSI_HSW, val); - /* hbp (not used in event mode) */ - tc358768_write(priv, TC358768_DSI_HBPR, 0); + if (dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { + /* Set pulse mode */ + tc358768_write(priv, TC358768_DSI_EVENT, 0); + + /* vact */ + tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay); + + /* vsw */ + tc358768_write(priv, TC358768_DSI_VSW, + mode->vsync_end - mode->vsync_start); + /* vbp */ + tc358768_write(priv, TC358768_DSI_VBPR, + mode->vtotal - mode->vsync_end); + + /* hsw * byteclk * ndl / pclk */ + val = (u32)div_u64((mode->hsync_end - mode->hsync_start) * + ((u64)priv->dsiclk / 4) * priv->dsi_lanes, + mode->clock * 1000); + tc358768_write(priv, TC358768_DSI_HSW, val); + + /* hbp * byteclk * ndl / pclk */ + val = (u32)div_u64((mode->htotal - mode->hsync_end) * + ((u64)priv->dsiclk / 4) * priv->dsi_lanes, + mode->clock * 1000); + tc358768_write(priv, TC358768_DSI_HBPR, val); + } else { + /* Set event mode */ + tc358768_write(priv, TC358768_DSI_EVENT, 1); + + /* vact */ + tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay); + + /* vsw (+ vbp) */ + tc358768_write(priv, TC358768_DSI_VSW, + mode->vtotal - mode->vsync_start); + /* vbp (not used in event mode) */ + tc358768_write(priv, TC358768_DSI_VBPR, 0); + + /* (hsw + hbp) * byteclk * ndl / pclk */ + val = (u32)div_u64((mode->htotal - mode->hsync_start) * + ((u64)priv->dsiclk / 4) * priv->dsi_lanes, + mode->clock * 1000); + tc358768_write(priv, TC358768_DSI_HSW, val); + + /* hbp (not used in event mode) */ + tc358768_write(priv, TC358768_DSI_HBPR, 0); + } + /* hact (bytes) */ tc358768_write(priv, TC358768_DSI_HACT, hact); @@ -822,7 +870,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) if (!(dsi_dev->mode_flags & MIPI_DSI_MODE_LPM)) val |= TC358768_DSI_CONTROL_TXMD; - if (!(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) + if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) val |= TC358768_DSI_CONTROL_HSCKMD; if (dsi_dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index a32f70bc68ea..52030a82f3e1 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -288,20 +288,17 @@ err_dsi_attach: return ret; } -static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) +static void sn65dsi83_detach(struct drm_bridge *bridge) { struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); - /* - * Reset the chip, pull EN line low for t_reset=10ms, - * then high for t_en=1ms. - */ - regcache_mark_dirty(ctx->regmap); - gpiod_set_value(ctx->enable_gpio, 0); - usleep_range(10000, 11000); - gpiod_set_value(ctx->enable_gpio, 1); - usleep_range(1000, 1100); + if (!ctx->dsi) + return; + + mipi_dsi_detach(ctx->dsi); + mipi_dsi_device_unregister(ctx->dsi); + drm_bridge_remove(&ctx->bridge); + ctx->dsi = NULL; } static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx, @@ -381,6 +378,10 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, u16 val; int ret; + /* Deassert reset */ + gpiod_set_value(ctx->enable_gpio, 1); + usleep_range(1000, 1100); + /* Get the LVDS format from the bridge state. */ bridge_state = drm_atomic_get_new_bridge_state(state, bridge); @@ -527,18 +528,11 @@ static void sn65dsi83_atomic_disable(struct drm_bridge *bridge, { struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); - /* Clear reset, disable PLL */ - regmap_write(ctx->regmap, REG_RC_RESET, 0x00); - regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00); -} - -static void sn65dsi83_atomic_post_disable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) -{ - struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); - - /* Put the chip in reset, pull EN line low. */ + /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */ gpiod_set_value(ctx->enable_gpio, 0); + usleep_range(10000, 11000); + + regcache_mark_dirty(ctx->regmap); } static enum drm_mode_status @@ -583,10 +577,9 @@ sn65dsi83_atomic_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs sn65dsi83_funcs = { .attach = sn65dsi83_attach, - .atomic_pre_enable = sn65dsi83_atomic_pre_enable, + .detach = sn65dsi83_detach, .atomic_enable = sn65dsi83_atomic_enable, .atomic_disable = sn65dsi83_atomic_disable, - .atomic_post_disable = sn65dsi83_atomic_post_disable, .mode_valid = sn65dsi83_mode_valid, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, @@ -671,10 +664,13 @@ static int sn65dsi83_probe(struct i2c_client *client, model = id->driver_data; } + /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */ ctx->enable_gpio = devm_gpiod_get(ctx->dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(ctx->enable_gpio)) return PTR_ERR(ctx->enable_gpio); + usleep_range(10000, 11000); + ret = sn65dsi83_parse_dt(ctx, model); if (ret) return ret; @@ -697,9 +693,6 @@ static int sn65dsi83_remove(struct i2c_client *client) { struct sn65dsi83 *ctx = i2c_get_clientdata(client); - mipi_dsi_detach(ctx->dsi); - mipi_dsi_device_unregister(ctx->dsi); - drm_bridge_remove(&ctx->bridge); of_node_put(ctx->host_node); return 0; diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 909f31833181..cdd31fc78bfc 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -797,6 +797,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, fence_ptr); } else if (property == connector->max_bpc_property) { state->max_requested_bpc = val; + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -874,6 +876,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = 0; } else if (property == connector->max_bpc_property) { *val = state->max_requested_bpc; + } else if (property == connector->privacy_screen_sw_state_property) { + *val = state->privacy_screen_sw_state; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3bc782b630b9..ec3973e8963c 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -28,6 +28,7 @@ #include <drm/drm_print.h> #include <drm/drm_drv.h> #include <drm/drm_file.h> +#include <drm/drm_privacy_screen_consumer.h> #include <drm/drm_sysfs.h> #include <linux/uaccess.h> @@ -462,6 +463,11 @@ void drm_connector_cleanup(struct drm_connector *connector) DRM_CONNECTOR_REGISTERED)) drm_connector_unregister(connector); + if (connector->privacy_screen) { + drm_privacy_screen_put(connector->privacy_screen); + connector->privacy_screen = NULL; + } + if (connector->tile_group) { drm_mode_put_tile_group(dev, connector->tile_group); connector->tile_group = NULL; @@ -543,6 +549,10 @@ int drm_connector_register(struct drm_connector *connector) /* Let userspace know we have a new connector */ drm_sysfs_hotplug_event(connector->dev); + if (connector->privacy_screen) + drm_privacy_screen_register_notifier(connector->privacy_screen, + &connector->privacy_screen_notifier); + mutex_lock(&connector_list_lock); list_add_tail(&connector->global_connector_list_entry, &connector_list); mutex_unlock(&connector_list_lock); @@ -578,6 +588,11 @@ void drm_connector_unregister(struct drm_connector *connector) list_del_init(&connector->global_connector_list_entry); mutex_unlock(&connector_list_lock); + if (connector->privacy_screen) + drm_privacy_screen_unregister_notifier( + connector->privacy_screen, + &connector->privacy_screen_notifier); + if (connector->funcs->early_unregister) connector->funcs->early_unregister(connector); @@ -625,6 +640,8 @@ int drm_connector_register_all(struct drm_device *dev) * * In contrast to the other drm_get_*_name functions this one here returns a * const pointer and hence is threadsafe. + * + * Returns: connector status string */ const char *drm_get_connector_status_name(enum drm_connector_status status) { @@ -707,7 +724,7 @@ __drm_connector_put_safe(struct drm_connector *conn) * drm_connector_list_iter_next - return next connector * @iter: connector_list iterator * - * Returns the next connector for @iter, or NULL when the list walk has + * Returns: the next connector for @iter, or NULL when the list walk has * completed. */ struct drm_connector * @@ -780,6 +797,8 @@ static const struct drm_prop_enum_list drm_subpixel_enum_list[] = { * * Note you could abuse this and return something out of bounds, but that * would be a caller error. No unscrubbed user data should make it here. + * + * Returns: string describing an enumerated subpixel property */ const char *drm_get_subpixel_order_name(enum subpixel_order order) { @@ -809,6 +828,9 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = { * Store the supported bus formats in display info structure. * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for * a full list of available formats. + * + * Returns: + * 0 on success or a negative error code on failure. */ int drm_display_info_set_bus_formats(struct drm_display_info *info, const u32 *formats, @@ -1264,6 +1286,46 @@ static const struct drm_prop_enum_list dp_colorspaces[] = { * For DVI-I and TVout there is also a matching property "select subconnector" * allowing to switch between signal types. * DP subconnector corresponds to a downstream port. + * + * privacy-screen sw-state, privacy-screen hw-state: + * These 2 optional properties can be used to query the state of the + * electronic privacy screen that is available on some displays; and in + * some cases also control the state. If a driver implements these + * properties then both properties must be present. + * + * "privacy-screen hw-state" is read-only and reflects the actual state + * of the privacy-screen, possible values: "Enabled", "Disabled, + * "Enabled-locked", "Disabled-locked". The locked states indicate + * that the state cannot be changed through the DRM API. E.g. there + * might be devices where the firmware-setup options, or a hardware + * slider-switch, offer always on / off modes. + * + * "privacy-screen sw-state" can be set to change the privacy-screen state + * when not locked. In this case the driver must update the hw-state + * property to reflect the new state on completion of the commit of the + * sw-state property. Setting the sw-state property when the hw-state is + * locked must be interpreted by the driver as a request to change the + * state to the set state when the hw-state becomes unlocked. E.g. if + * "privacy-screen hw-state" is "Enabled-locked" and the sw-state + * gets set to "Disabled" followed by the user unlocking the state by + * changing the slider-switch position, then the driver must set the + * state to "Disabled" upon receiving the unlock event. + * + * In some cases the privacy-screen's actual state might change outside of + * control of the DRM code. E.g. there might be a firmware handled hotkey + * which toggles the actual state, or the actual state might be changed + * through another userspace API such as writing /proc/acpi/ibm/lcdshadow. + * In this case the driver must update both the hw-state and the sw-state + * to reflect the new value, overwriting any pending state requests in the + * sw-state. Any pending sw-state requests are thus discarded. + * + * Note that the ability for the state to change outside of control of + * the DRM master process means that userspace must not cache the value + * of the sw-state. Caching the sw-state value and including it in later + * atomic commits may lead to overriding a state change done through e.g. + * a firmware handled hotkey. Therefor userspace must not include the + * privacy-screen sw-state in an atomic commit unless it wants to change + * its value. */ int drm_connector_create_standard_properties(struct drm_device *dev) @@ -1326,6 +1388,8 @@ int drm_connector_create_standard_properties(struct drm_device *dev) * @dev: DRM device * * Called by a driver the first time a DVI-I connector is made. + * + * Returns: %0 */ int drm_mode_create_dvi_i_properties(struct drm_device *dev) { @@ -1397,6 +1461,8 @@ EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property); * Game: * Content type is game * + * The meaning of each content type is defined in CTA-861-G table 15. + * * Drivers can set up this property by calling * drm_connector_attach_content_type_property(). Decoding to * infoframe values is done through drm_hdmi_avi_infoframe_content_type(). @@ -1407,6 +1473,8 @@ EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property); * @connector: connector to attach content type property on. * * Called by a driver the first time a HDMI connector is made. + * + * Returns: %0 */ int drm_connector_attach_content_type_property(struct drm_connector *connector) { @@ -1487,6 +1555,9 @@ EXPORT_SYMBOL(drm_connector_attach_tv_margin_properties); * creates the TV margin properties for a given device. No need to call this * function for an SDTV connector, it's already called from * drm_mode_create_tv_properties(). + * + * Returns: + * 0 on success or a negative error code on failure. */ int drm_mode_create_tv_margin_properties(struct drm_device *dev) { @@ -1527,6 +1598,9 @@ EXPORT_SYMBOL(drm_mode_create_tv_margin_properties); * the TV specific connector properties for a given device. Caller is * responsible for allocating a list of format names and passing them to * this routine. + * + * Returns: + * 0 on success or a negative error code on failure. */ int drm_mode_create_tv_properties(struct drm_device *dev, unsigned int num_modes, @@ -1622,6 +1696,8 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties); * Atomic drivers should use drm_connector_attach_scaling_mode_property() * instead to correctly assign &drm_connector_state.scaling_mode * in the atomic state. + * + * Returns: %0 */ int drm_mode_create_scaling_mode_property(struct drm_device *dev) { @@ -1939,6 +2015,9 @@ EXPORT_SYMBOL(drm_mode_create_content_type_property); * @dev: DRM device * * Create the suggested x/y offset property for connectors. + * + * Returns: + * 0 on success or a negative error code on failure. */ int drm_mode_create_suggested_offset_properties(struct drm_device *dev) { @@ -2312,8 +2391,8 @@ int drm_connector_set_panel_orientation( EXPORT_SYMBOL(drm_connector_set_panel_orientation); /** - * drm_connector_set_panel_orientation_with_quirk - - * set the connector's panel_orientation after checking for quirks + * drm_connector_set_panel_orientation_with_quirk - set the + * connector'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 @@ -2341,6 +2420,154 @@ int drm_connector_set_panel_orientation_with_quirk( } EXPORT_SYMBOL(drm_connector_set_panel_orientation_with_quirk); +static const struct drm_prop_enum_list privacy_screen_enum[] = { + { PRIVACY_SCREEN_DISABLED, "Disabled" }, + { PRIVACY_SCREEN_ENABLED, "Enabled" }, + { PRIVACY_SCREEN_DISABLED_LOCKED, "Disabled-locked" }, + { PRIVACY_SCREEN_ENABLED_LOCKED, "Enabled-locked" }, +}; + +/** + * drm_connector_create_privacy_screen_properties - create the drm connecter's + * privacy-screen properties. + * @connector: connector for which to create the privacy-screen properties + * + * This function creates the "privacy-screen sw-state" and "privacy-screen + * hw-state" properties for the connector. They are not attached. + */ +void +drm_connector_create_privacy_screen_properties(struct drm_connector *connector) +{ + if (connector->privacy_screen_sw_state_property) + return; + + /* Note sw-state only supports the first 2 values of the enum */ + connector->privacy_screen_sw_state_property = + drm_property_create_enum(connector->dev, DRM_MODE_PROP_ENUM, + "privacy-screen sw-state", + privacy_screen_enum, 2); + + connector->privacy_screen_hw_state_property = + drm_property_create_enum(connector->dev, + DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ENUM, + "privacy-screen hw-state", + privacy_screen_enum, + ARRAY_SIZE(privacy_screen_enum)); +} +EXPORT_SYMBOL(drm_connector_create_privacy_screen_properties); + +/** + * drm_connector_attach_privacy_screen_properties - attach the drm connecter's + * privacy-screen properties. + * @connector: connector on which to attach the privacy-screen properties + * + * This function attaches the "privacy-screen sw-state" and "privacy-screen + * hw-state" properties to the connector. The initial state of both is set + * to "Disabled". + */ +void +drm_connector_attach_privacy_screen_properties(struct drm_connector *connector) +{ + if (!connector->privacy_screen_sw_state_property) + return; + + drm_object_attach_property(&connector->base, + connector->privacy_screen_sw_state_property, + PRIVACY_SCREEN_DISABLED); + + drm_object_attach_property(&connector->base, + connector->privacy_screen_hw_state_property, + PRIVACY_SCREEN_DISABLED); +} +EXPORT_SYMBOL(drm_connector_attach_privacy_screen_properties); + +static void drm_connector_update_privacy_screen_properties( + struct drm_connector *connector, bool set_sw_state) +{ + enum drm_privacy_screen_status sw_state, hw_state; + + drm_privacy_screen_get_state(connector->privacy_screen, + &sw_state, &hw_state); + + if (set_sw_state) + connector->state->privacy_screen_sw_state = sw_state; + drm_object_property_set_value(&connector->base, + connector->privacy_screen_hw_state_property, hw_state); +} + +static int drm_connector_privacy_screen_notifier( + struct notifier_block *nb, unsigned long action, void *data) +{ + struct drm_connector *connector = + container_of(nb, struct drm_connector, privacy_screen_notifier); + struct drm_device *dev = connector->dev; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + drm_connector_update_privacy_screen_properties(connector, true); + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + drm_sysfs_connector_status_event(connector, + connector->privacy_screen_sw_state_property); + drm_sysfs_connector_status_event(connector, + connector->privacy_screen_hw_state_property); + + return NOTIFY_DONE; +} + +/** + * drm_connector_attach_privacy_screen_provider - attach a privacy-screen to + * the connector + * @connector: connector to attach the privacy-screen to + * @priv: drm_privacy_screen to attach + * + * Create and attach the standard privacy-screen properties and register + * a generic notifier for generating sysfs-connector-status-events + * on external changes to the privacy-screen status. + * This function takes ownership of the passed in drm_privacy_screen and will + * call drm_privacy_screen_put() on it when the connector is destroyed. + */ +void drm_connector_attach_privacy_screen_provider( + struct drm_connector *connector, struct drm_privacy_screen *priv) +{ + connector->privacy_screen = priv; + connector->privacy_screen_notifier.notifier_call = + drm_connector_privacy_screen_notifier; + + drm_connector_create_privacy_screen_properties(connector); + drm_connector_update_privacy_screen_properties(connector, true); + drm_connector_attach_privacy_screen_properties(connector); +} +EXPORT_SYMBOL(drm_connector_attach_privacy_screen_provider); + +/** + * drm_connector_update_privacy_screen - update connector's privacy-screen sw-state + * @connector_state: connector-state to update the privacy-screen for + * + * This function calls drm_privacy_screen_set_sw_state() on the connector's + * privacy-screen. + * + * If the connector has no privacy-screen, then this is a no-op. + */ +void drm_connector_update_privacy_screen(const struct drm_connector_state *connector_state) +{ + struct drm_connector *connector = connector_state->connector; + int ret; + + if (!connector->privacy_screen) + return; + + ret = drm_privacy_screen_set_sw_state(connector->privacy_screen, + connector_state->privacy_screen_sw_state); + if (ret) { + drm_err(connector->dev, "Error updating privacy-screen sw_state\n"); + return; + } + + /* The hw_state property value may have changed, update it. */ + drm_connector_update_privacy_screen_properties(connector, false); +} +EXPORT_SYMBOL(drm_connector_update_privacy_screen); + int drm_connector_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, uint64_t value) @@ -2597,7 +2824,7 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode) /** * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector - * @connector: connector to report the event on + * @connector_fwnode: fwnode_handle to report the event on * * On some hardware a hotplug event notification may come from outside the display * driver / device. An example of this is some USB Type-C setups where the hardware diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 4d0d1e8e51fa..ada0a1ff262d 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -154,38 +154,155 @@ u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZ } EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor); -void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux, - const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +static int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) { - unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & - DP_TRAINING_AUX_RD_MASK; - if (rd_interval > 4) - drm_dbg_kms(aux->drm_dev, "%s: AUX interval %lu, out of range (max 4)\n", + drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n", aux->name, rd_interval); - if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) - rd_interval = 100; - else - rd_interval *= 4 * USEC_PER_MSEC; + if (rd_interval == 0) + return 100; - usleep_range(rd_interval, rd_interval * 2); + return rd_interval * 4 * USEC_PER_MSEC; } -EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); -static void __drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux, - unsigned long rd_interval) +static int __8b10b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) { if (rd_interval > 4) - drm_dbg_kms(aux->drm_dev, "%s: AUX interval %lu, out of range (max 4)\n", + drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n", aux->name, rd_interval); if (rd_interval == 0) - rd_interval = 400; + return 400; + + return rd_interval * 4 * USEC_PER_MSEC; +} + +static int __128b132b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) +{ + switch (rd_interval) { + default: + drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x\n", + aux->name, rd_interval); + fallthrough; + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US: + return 400; + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS: + return 4000; + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS: + return 8000; + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS: + return 12000; + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS: + return 16000; + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS: + return 32000; + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS: + return 64000; + } +} + +/* + * The link training delays are different for: + * + * - Clock recovery vs. channel equalization + * - DPRX vs. LTTPR + * - 128b/132b vs. 8b/10b + * - DPCD rev 1.3 vs. later + * + * Get the correct delay in us, reading DPCD if necessary. + */ +static int __read_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], + enum drm_dp_phy dp_phy, bool uhbr, bool cr) +{ + int (*parse)(const struct drm_dp_aux *aux, u8 rd_interval); + unsigned int offset; + u8 rd_interval, mask; + + if (dp_phy == DP_PHY_DPRX) { + if (uhbr) { + if (cr) + return 100; + + offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL; + mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK; + parse = __128b132b_channel_eq_delay_us; + } else { + if (cr && dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) + return 100; + + offset = DP_TRAINING_AUX_RD_INTERVAL; + mask = DP_TRAINING_AUX_RD_MASK; + if (cr) + parse = __8b10b_clock_recovery_delay_us; + else + parse = __8b10b_channel_eq_delay_us; + } + } else { + if (uhbr) { + offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy); + mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK; + parse = __128b132b_channel_eq_delay_us; + } else { + if (cr) + return 100; + + offset = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy); + mask = DP_TRAINING_AUX_RD_MASK; + parse = __8b10b_channel_eq_delay_us; + } + } + + if (offset < DP_RECEIVER_CAP_SIZE) { + rd_interval = dpcd[offset]; + } else { + if (drm_dp_dpcd_readb(aux, offset, &rd_interval) != 1) { + drm_dbg_kms(aux->drm_dev, "%s: failed rd interval read\n", + aux->name); + /* arbitrary default delay */ + return 400; + } + } + + return parse(aux, rd_interval & mask); +} + +int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], + enum drm_dp_phy dp_phy, bool uhbr) +{ + return __read_delay(aux, dpcd, dp_phy, uhbr, true); +} +EXPORT_SYMBOL(drm_dp_read_clock_recovery_delay); + +int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], + enum drm_dp_phy dp_phy, bool uhbr) +{ + return __read_delay(aux, dpcd, dp_phy, uhbr, false); +} +EXPORT_SYMBOL(drm_dp_read_channel_eq_delay); + +void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux, + const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + u8 rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & + DP_TRAINING_AUX_RD_MASK; + int delay_us; + + if (dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) + delay_us = 100; else - rd_interval *= 4 * USEC_PER_MSEC; + delay_us = __8b10b_clock_recovery_delay_us(aux, rd_interval); + + usleep_range(delay_us, delay_us * 2); +} +EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); + +static void __drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux, + u8 rd_interval) +{ + int delay_us = __8b10b_channel_eq_delay_us(aux, rd_interval); - usleep_range(rd_interval, rd_interval * 2); + usleep_range(delay_us, delay_us * 2); } void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux, diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 7a5097467ba5..8214a0b1ab7f 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -43,6 +43,7 @@ #include <drm/drm_managed.h> #include <drm/drm_mode_object.h> #include <drm/drm_print.h> +#include <drm/drm_privacy_screen_machine.h> #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -581,6 +582,7 @@ static int drm_dev_init(struct drm_device *dev, const struct drm_driver *driver, struct device *parent) { + struct inode *inode; int ret; if (!drm_core_init_complete) { @@ -617,13 +619,15 @@ static int drm_dev_init(struct drm_device *dev, if (ret) return ret; - dev->anon_inode = drm_fs_inode_new(); - if (IS_ERR(dev->anon_inode)) { - ret = PTR_ERR(dev->anon_inode); + inode = drm_fs_inode_new(); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret); goto err; } + dev->anon_inode = inode; + if (drm_core_check_feature(dev, DRIVER_RENDER)) { ret = drm_minor_alloc(dev, DRM_MINOR_RENDER); if (ret) @@ -1029,6 +1033,7 @@ static const struct file_operations drm_stub_fops = { static void drm_core_exit(void) { + drm_privacy_screen_lookup_exit(); unregister_chrdev(DRM_MAJOR, "drm"); debugfs_remove(drm_debugfs_root); drm_sysfs_destroy(); @@ -1056,6 +1061,8 @@ static int __init drm_core_init(void) if (ret < 0) goto error; + drm_privacy_screen_lookup_init(); + drm_core_init_complete = true; DRM_DEBUG("Initialized\n"); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 09c820045859..4dcdec6487bb 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1340,31 +1340,15 @@ int drm_gem_fence_array_add_implicit(struct xarray *fence_array, struct drm_gem_object *obj, bool write) { - int ret; - struct dma_fence **fences; - unsigned int i, fence_count; - - if (!write) { - struct dma_fence *fence = - dma_resv_get_excl_unlocked(obj->resv); - - return drm_gem_fence_array_add(fence_array, fence); - } + struct dma_resv_iter cursor; + struct dma_fence *fence; + int ret = 0; - ret = dma_resv_get_fences(obj->resv, NULL, - &fence_count, &fences); - if (ret || !fence_count) - return ret; - - for (i = 0; i < fence_count; i++) { - ret = drm_gem_fence_array_add(fence_array, fences[i]); + dma_resv_for_each_fence(&cursor, obj->resv, write, fence) { + ret = drm_gem_fence_array_add(fence_array, fence); if (ret) break; } - - for (; i < fence_count; i++) - dma_fence_put(fences[i]); - kfree(fences); return ret; } EXPORT_SYMBOL(drm_gem_fence_array_add_implicit); diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c index e570398abd78..c3189afe10cb 100644 --- a/drivers/gpu/drm/drm_gem_atomic_helper.c +++ b/drivers/gpu/drm/drm_gem_atomic_helper.c @@ -143,6 +143,7 @@ */ int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state) { + struct dma_resv_iter cursor; struct drm_gem_object *obj; struct dma_fence *fence; @@ -150,9 +151,18 @@ int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_st return 0; obj = drm_gem_fb_get_obj(state->fb, 0); - fence = dma_resv_get_excl_unlocked(obj->resv); - drm_atomic_set_fence_for_plane(state, fence); + dma_resv_iter_begin(&cursor, obj->resv, false); + dma_resv_for_each_fence_unlocked(&cursor, fence) { + /* TODO: Currently there should be only one write fence, so this + * here works fine. But drm_atomic_set_fence_for_plane() should + * be changed to be able to handle more fences in general for + * multiple BOs per fb anyway. */ + dma_fence_get(fence); + break; + } + dma_resv_iter_end(&cursor); + drm_atomic_set_fence_for_plane(state, fence); return 0; } EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb); diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index d53388199f34..6f7b3f8ec04d 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -13,6 +13,7 @@ #include <linux/dma-mapping.h> #include <linux/export.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> @@ -578,3 +579,6 @@ drm_gem_cma_prime_import_sg_table_vmap(struct drm_device *dev, return obj; } EXPORT_SYMBOL(drm_gem_cma_prime_import_sg_table_vmap); + +MODULE_DESCRIPTION("DRM CMA memory-management helpers"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 0e0986dfbe0c..57ceecb3f4d8 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -5,6 +5,7 @@ #include <linux/dma-buf.h> #include <linux/export.h> +#include <linux/module.h> #include <linux/mutex.h> #include <linux/shmem_fs.h> #include <linux/slab.h> @@ -774,3 +775,6 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev, return &shmem->base; } EXPORT_SYMBOL_GPL(drm_gem_shmem_prime_import_sg_table); + +MODULE_DESCRIPTION("DRM SHMEM memory-management helpers"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 13e1d5c4ec82..d327638e15ee 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -66,7 +66,6 @@ #include "drm_internal.h" -#if IS_ENABLED(CONFIG_DRM_LEGACY) static int drm_legacy_irq_install(struct drm_device *dev, int irq) { int ret; @@ -203,4 +202,3 @@ int drm_legacy_irq_control(struct drm_device *dev, void *data, return -EINVAL; } } -#endif diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index bf8a6e823a15..c97323365675 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -25,6 +25,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_device.h> #include <drm/drm_modeset_lock.h> +#include <drm/drm_print.h> /** * DOC: kms locking @@ -77,6 +78,45 @@ static DEFINE_WW_CLASS(crtc_ww_class); +#if IS_ENABLED(CONFIG_DRM_DEBUG_MODESET_LOCK) +static noinline depot_stack_handle_t __drm_stack_depot_save(void) +{ + unsigned long entries[8]; + unsigned int n; + + n = stack_trace_save(entries, ARRAY_SIZE(entries), 1); + + return stack_depot_save(entries, n, GFP_NOWAIT | __GFP_NOWARN); +} + +static void __drm_stack_depot_print(depot_stack_handle_t stack_depot) +{ + struct drm_printer p = drm_debug_printer("drm_modeset_lock"); + unsigned long *entries; + unsigned int nr_entries; + char *buf; + + buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN); + if (!buf) + return; + + nr_entries = stack_depot_fetch(stack_depot, &entries); + stack_trace_snprint(buf, PAGE_SIZE, entries, nr_entries, 2); + + drm_printf(&p, "attempting to lock a contended lock without backoff:\n%s", buf); + + kfree(buf); +} +#else /* CONFIG_DRM_DEBUG_MODESET_LOCK */ +static depot_stack_handle_t __drm_stack_depot_save(void) +{ + return 0; +} +static void __drm_stack_depot_print(depot_stack_handle_t stack_depot) +{ +} +#endif /* CONFIG_DRM_DEBUG_MODESET_LOCK */ + /** * drm_modeset_lock_all - take all modeset locks * @dev: DRM device @@ -225,7 +265,9 @@ EXPORT_SYMBOL(drm_modeset_acquire_fini); */ void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) { - WARN_ON(ctx->contended); + if (WARN_ON(ctx->contended)) + __drm_stack_depot_print(ctx->stack_depot); + while (!list_empty(&ctx->locked)) { struct drm_modeset_lock *lock; @@ -243,7 +285,8 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, { int ret; - WARN_ON(ctx->contended); + if (WARN_ON(ctx->contended)) + __drm_stack_depot_print(ctx->stack_depot); if (ctx->trylock_only) { lockdep_assert_held(&ctx->ww_ctx); @@ -274,6 +317,7 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, ret = 0; } else if (ret == -EDEADLK) { ctx->contended = lock; + ctx->stack_depot = __drm_stack_depot_save(); } return ret; @@ -296,6 +340,7 @@ int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) struct drm_modeset_lock *contended = ctx->contended; ctx->contended = NULL; + ctx->stack_depot = 0; if (WARN_ON(!contended)) return 0; diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 37c34146eea8..59d368ea006b 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -402,3 +402,36 @@ int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; } EXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_pixel_order); + +/** + * drm_of_lvds_get_data_mapping - Get LVDS data mapping + * @port: DT port node of the LVDS source or sink + * + * Convert DT "data-mapping" property string value into media bus format value. + * + * Return: + * * MEDIA_BUS_FMT_RGB666_1X7X3_SPWG - data-mapping is "jeida-18" + * * MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA - data-mapping is "jeida-24" + * * MEDIA_BUS_FMT_RGB888_1X7X4_SPWG - data-mapping is "vesa-24" + * * -EINVAL - the "data-mapping" property is unsupported + * * -ENODEV - the "data-mapping" property is missing + */ +int drm_of_lvds_get_data_mapping(const struct device_node *port) +{ + const char *mapping; + int ret; + + ret = of_property_read_string(port, "data-mapping", &mapping); + if (ret < 0) + return -ENODEV; + + if (!strcmp(mapping, "jeida-18")) + return MEDIA_BUS_FMT_RGB666_1X7X3_SPWG; + if (!strcmp(mapping, "jeida-24")) + return MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA; + if (!strcmp(mapping, "vesa-24")) + return MEDIA_BUS_FMT_RGB888_1X7X4_SPWG; + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(drm_of_lvds_get_data_mapping); diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 5b2d0ca03705..838b32b70bce 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -123,7 +123,6 @@ static int drm_plane_helper_check_update(struct drm_plane *plane, .crtc_w = drm_rect_width(dst), .crtc_h = drm_rect_height(dst), .rotation = rotation, - .visible = *visible, }; struct drm_crtc_state crtc_state = { .crtc = crtc, diff --git a/drivers/gpu/drm/drm_privacy_screen.c b/drivers/gpu/drm/drm_privacy_screen.c new file mode 100644 index 000000000000..beaf99e9120a --- /dev/null +++ b/drivers/gpu/drm/drm_privacy_screen.c @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2020 - 2021 Red Hat, Inc. + * + * Authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <drm/drm_privacy_screen_machine.h> +#include <drm/drm_privacy_screen_consumer.h> +#include <drm/drm_privacy_screen_driver.h> +#include "drm_internal.h" + +/** + * DOC: overview + * + * This class allows non KMS drivers, from e.g. drivers/platform/x86 to + * register a privacy-screen device, which the KMS drivers can then use + * to implement the standard privacy-screen properties, see + * :ref:`Standard Connector Properties<standard_connector_properties>`. + * + * KMS drivers using a privacy-screen class device are advised to use the + * drm_connector_attach_privacy_screen_provider() and + * drm_connector_update_privacy_screen() helpers for dealing with this. + */ + +#define to_drm_privacy_screen(dev) \ + container_of(dev, struct drm_privacy_screen, dev) + +static DEFINE_MUTEX(drm_privacy_screen_lookup_lock); +static LIST_HEAD(drm_privacy_screen_lookup_list); + +static DEFINE_MUTEX(drm_privacy_screen_devs_lock); +static LIST_HEAD(drm_privacy_screen_devs); + +/*** drm_privacy_screen_machine.h functions ***/ + +/** + * drm_privacy_screen_lookup_add - add an entry to the static privacy-screen + * lookup list + * @lookup: lookup list entry to add + * + * Add an entry to the static privacy-screen lookup list. Note the + * &struct list_head which is part of the &struct drm_privacy_screen_lookup + * gets added to a list owned by the privacy-screen core. So the passed in + * &struct drm_privacy_screen_lookup must not be free-ed until it is removed + * from the lookup list by calling drm_privacy_screen_lookup_remove(). + */ +void drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup *lookup) +{ + mutex_lock(&drm_privacy_screen_lookup_lock); + list_add(&lookup->list, &drm_privacy_screen_lookup_list); + mutex_unlock(&drm_privacy_screen_lookup_lock); +} +EXPORT_SYMBOL(drm_privacy_screen_lookup_add); + +/** + * drm_privacy_screen_lookup_remove - remove an entry to the static + * privacy-screen lookup list + * @lookup: lookup list entry to remove + * + * Remove an entry previously added with drm_privacy_screen_lookup_add() + * from the static privacy-screen lookup list. + */ +void drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup *lookup) +{ + mutex_lock(&drm_privacy_screen_lookup_lock); + list_del(&lookup->list); + mutex_unlock(&drm_privacy_screen_lookup_lock); +} +EXPORT_SYMBOL(drm_privacy_screen_lookup_remove); + +/*** drm_privacy_screen_consumer.h functions ***/ + +static struct drm_privacy_screen *drm_privacy_screen_get_by_name( + const char *name) +{ + struct drm_privacy_screen *priv; + struct device *dev = NULL; + + mutex_lock(&drm_privacy_screen_devs_lock); + + list_for_each_entry(priv, &drm_privacy_screen_devs, list) { + if (strcmp(dev_name(&priv->dev), name) == 0) { + dev = get_device(&priv->dev); + break; + } + } + + mutex_unlock(&drm_privacy_screen_devs_lock); + + return dev ? to_drm_privacy_screen(dev) : NULL; +} + +/** + * drm_privacy_screen_get - get a privacy-screen provider + * @dev: consumer-device for which to get a privacy-screen provider + * @con_id: (video)connector name for which to get a privacy-screen provider + * + * Get a privacy-screen provider for a privacy-screen attached to the + * display described by the @dev and @con_id parameters. + * + * Return: + * * A pointer to a &struct drm_privacy_screen on success. + * * ERR_PTR(-ENODEV) if no matching privacy-screen is found + * * ERR_PTR(-EPROBE_DEFER) if there is a matching privacy-screen, + * but it has not been registered yet. + */ +struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev, + const char *con_id) +{ + const char *dev_id = dev ? dev_name(dev) : NULL; + struct drm_privacy_screen_lookup *l; + struct drm_privacy_screen *priv; + const char *provider = NULL; + int match, best = -1; + + /* + * For now we only support using a static lookup table, which is + * populated by the drm_privacy_screen_arch_init() call. This should + * be extended with device-tree / fw_node lookup when support is added + * for device-tree using hardware with a privacy-screen. + * + * The lookup algorithm was shamelessly taken from the clock + * framework: + * + * We do slightly fuzzy matching here: + * An entry with a NULL ID is assumed to be a wildcard. + * If an entry has a device ID, it must match + * If an entry has a connection ID, it must match + * Then we take the most specific entry - with the following order + * of precedence: dev+con > dev only > con only. + */ + mutex_lock(&drm_privacy_screen_lookup_lock); + + list_for_each_entry(l, &drm_privacy_screen_lookup_list, list) { + match = 0; + + if (l->dev_id) { + if (!dev_id || strcmp(l->dev_id, dev_id)) + continue; + + match += 2; + } + + if (l->con_id) { + if (!con_id || strcmp(l->con_id, con_id)) + continue; + + match += 1; + } + + if (match > best) { + provider = l->provider; + best = match; + } + } + + mutex_unlock(&drm_privacy_screen_lookup_lock); + + if (!provider) + return ERR_PTR(-ENODEV); + + priv = drm_privacy_screen_get_by_name(provider); + if (!priv) + return ERR_PTR(-EPROBE_DEFER); + + return priv; +} +EXPORT_SYMBOL(drm_privacy_screen_get); + +/** + * drm_privacy_screen_put - release a privacy-screen reference + * @priv: privacy screen reference to release + * + * Release a privacy-screen provider reference gotten through + * drm_privacy_screen_get(). May be called with a NULL or ERR_PTR, + * in which case it is a no-op. + */ +void drm_privacy_screen_put(struct drm_privacy_screen *priv) +{ + if (IS_ERR_OR_NULL(priv)) + return; + + put_device(&priv->dev); +} +EXPORT_SYMBOL(drm_privacy_screen_put); + +/** + * drm_privacy_screen_set_sw_state - set a privacy-screen's sw-state + * @priv: privacy screen to set the sw-state for + * @sw_state: new sw-state value to set + * + * Set the sw-state of a privacy screen. If the privacy-screen is not + * in a locked hw-state, then the actual and hw-state of the privacy-screen + * will be immediately updated to the new value. If the privacy-screen is + * in a locked hw-state, then the new sw-state will be remembered as the + * requested state to put the privacy-screen in when it becomes unlocked. + * + * Return: 0 on success, negative error code on failure. + */ +int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status sw_state) +{ + int ret = 0; + + mutex_lock(&priv->lock); + + if (!priv->ops) { + ret = -ENODEV; + goto out; + } + + /* + * As per the DRM connector properties documentation, setting the + * sw_state while the hw_state is locked is allowed. In this case + * it is a no-op other then storing the new sw_state so that it + * can be honored when the state gets unlocked. + * Also skip the set if the hw already is in the desired state. + */ + if (priv->hw_state >= PRIVACY_SCREEN_DISABLED_LOCKED || + priv->hw_state == sw_state) { + priv->sw_state = sw_state; + goto out; + } + + ret = priv->ops->set_sw_state(priv, sw_state); +out: + mutex_unlock(&priv->lock); + return ret; +} +EXPORT_SYMBOL(drm_privacy_screen_set_sw_state); + +/** + * drm_privacy_screen_get_state - get privacy-screen's current state + * @priv: privacy screen to get the state for + * @sw_state_ret: address where to store the privacy-screens current sw-state + * @hw_state_ret: address where to store the privacy-screens current hw-state + * + * Get the current state of a privacy-screen, both the sw-state and the + * hw-state. + */ +void drm_privacy_screen_get_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status *sw_state_ret, + enum drm_privacy_screen_status *hw_state_ret) +{ + mutex_lock(&priv->lock); + *sw_state_ret = priv->sw_state; + *hw_state_ret = priv->hw_state; + mutex_unlock(&priv->lock); +} +EXPORT_SYMBOL(drm_privacy_screen_get_state); + +/** + * drm_privacy_screen_register_notifier - register a notifier + * @priv: Privacy screen to register the notifier with + * @nb: Notifier-block for the notifier to register + * + * Register a notifier with the privacy-screen to be notified of changes made + * to the privacy-screen state from outside of the privacy-screen class. + * E.g. the state may be changed by the hardware itself in response to a + * hotkey press. + * + * The notifier is called with no locks held. The new hw_state and sw_state + * can be retrieved using the drm_privacy_screen_get_state() function. + * A pointer to the drm_privacy_screen's struct is passed as the void *data + * argument of the notifier_block's notifier_call. + * + * The notifier will NOT be called when changes are made through + * drm_privacy_screen_set_sw_state(). It is only called for external changes. + * + * Return: 0 on success, negative error code on failure. + */ +int drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&priv->notifier_head, nb); +} +EXPORT_SYMBOL(drm_privacy_screen_register_notifier); + +/** + * drm_privacy_screen_unregister_notifier - unregister a notifier + * @priv: Privacy screen to register the notifier with + * @nb: Notifier-block for the notifier to register + * + * Unregister a notifier registered with drm_privacy_screen_register_notifier(). + * + * Return: 0 on success, negative error code on failure. + */ +int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&priv->notifier_head, nb); +} +EXPORT_SYMBOL(drm_privacy_screen_unregister_notifier); + +/*** drm_privacy_screen_driver.h functions ***/ + +static ssize_t sw_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drm_privacy_screen *priv = to_drm_privacy_screen(dev); + const char * const sw_state_names[] = { + "Disabled", + "Enabled", + }; + ssize_t ret; + + mutex_lock(&priv->lock); + + if (!priv->ops) + ret = -ENODEV; + else if (WARN_ON(priv->sw_state >= ARRAY_SIZE(sw_state_names))) + ret = -ENXIO; + else + ret = sprintf(buf, "%s\n", sw_state_names[priv->sw_state]); + + mutex_unlock(&priv->lock); + return ret; +} +/* + * RO: Do not allow setting the sw_state through sysfs, this MUST be done + * through the drm_properties on the drm_connector. + */ +static DEVICE_ATTR_RO(sw_state); + +static ssize_t hw_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drm_privacy_screen *priv = to_drm_privacy_screen(dev); + const char * const hw_state_names[] = { + "Disabled", + "Enabled", + "Disabled, locked", + "Enabled, locked", + }; + ssize_t ret; + + mutex_lock(&priv->lock); + + if (!priv->ops) + ret = -ENODEV; + else if (WARN_ON(priv->hw_state >= ARRAY_SIZE(hw_state_names))) + ret = -ENXIO; + else + ret = sprintf(buf, "%s\n", hw_state_names[priv->hw_state]); + + mutex_unlock(&priv->lock); + return ret; +} +static DEVICE_ATTR_RO(hw_state); + +static struct attribute *drm_privacy_screen_attrs[] = { + &dev_attr_sw_state.attr, + &dev_attr_hw_state.attr, + NULL +}; +ATTRIBUTE_GROUPS(drm_privacy_screen); + +static struct device_type drm_privacy_screen_type = { + .name = "privacy_screen", + .groups = drm_privacy_screen_groups, +}; + +static void drm_privacy_screen_device_release(struct device *dev) +{ + struct drm_privacy_screen *priv = to_drm_privacy_screen(dev); + + kfree(priv); +} + +/** + * drm_privacy_screen_register - register a privacy-screen + * @parent: parent-device for the privacy-screen + * @ops: &struct drm_privacy_screen_ops pointer with ops for the privacy-screen + * + * Create and register a privacy-screen. + * + * Return: + * * A pointer to the created privacy-screen on success. + * * An ERR_PTR(errno) on failure. + */ +struct drm_privacy_screen *drm_privacy_screen_register( + struct device *parent, const struct drm_privacy_screen_ops *ops) +{ + struct drm_privacy_screen *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return ERR_PTR(-ENOMEM); + + mutex_init(&priv->lock); + BLOCKING_INIT_NOTIFIER_HEAD(&priv->notifier_head); + + priv->dev.class = drm_class; + priv->dev.type = &drm_privacy_screen_type; + priv->dev.parent = parent; + priv->dev.release = drm_privacy_screen_device_release; + dev_set_name(&priv->dev, "privacy_screen-%s", dev_name(parent)); + priv->ops = ops; + + priv->ops->get_hw_state(priv); + + ret = device_register(&priv->dev); + if (ret) { + put_device(&priv->dev); + return ERR_PTR(ret); + } + + mutex_lock(&drm_privacy_screen_devs_lock); + list_add(&priv->list, &drm_privacy_screen_devs); + mutex_unlock(&drm_privacy_screen_devs_lock); + + return priv; +} +EXPORT_SYMBOL(drm_privacy_screen_register); + +/** + * drm_privacy_screen_unregister - unregister privacy-screen + * @priv: privacy-screen to unregister + * + * Unregister a privacy-screen registered with drm_privacy_screen_register(). + * May be called with a NULL or ERR_PTR, in which case it is a no-op. + */ +void drm_privacy_screen_unregister(struct drm_privacy_screen *priv) +{ + if (IS_ERR_OR_NULL(priv)) + return; + + mutex_lock(&drm_privacy_screen_devs_lock); + list_del(&priv->list); + mutex_unlock(&drm_privacy_screen_devs_lock); + + mutex_lock(&priv->lock); + priv->ops = NULL; + mutex_unlock(&priv->lock); + + device_unregister(&priv->dev); +} +EXPORT_SYMBOL(drm_privacy_screen_unregister); + +/** + * drm_privacy_screen_call_notifier_chain - notify consumers of state change + * @priv: Privacy screen to register the notifier with + * + * A privacy-screen provider driver can call this functions upon external + * changes to the privacy-screen state. E.g. the state may be changed by the + * hardware itself in response to a hotkey press. + * This function must be called without holding the privacy-screen lock. + * the driver must update sw_state and hw_state to reflect the new state before + * calling this function. + * The expected behavior from the driver upon receiving an external state + * change event is: 1. Take the lock; 2. Update sw_state and hw_state; + * 3. Release the lock. 4. Call drm_privacy_screen_call_notifier_chain(). + */ +void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv) +{ + blocking_notifier_call_chain(&priv->notifier_head, 0, priv); +} +EXPORT_SYMBOL(drm_privacy_screen_call_notifier_chain); diff --git a/drivers/gpu/drm/drm_privacy_screen_x86.c b/drivers/gpu/drm/drm_privacy_screen_x86.c new file mode 100644 index 000000000000..a2cafb294ca6 --- /dev/null +++ b/drivers/gpu/drm/drm_privacy_screen_x86.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/acpi.h> +#include <drm/drm_privacy_screen_machine.h> + +#ifdef CONFIG_X86 +static struct drm_privacy_screen_lookup arch_lookup; + +struct arch_init_data { + struct drm_privacy_screen_lookup lookup; + bool (*detect)(void); +}; + +#if IS_ENABLED(CONFIG_THINKPAD_ACPI) +static acpi_status __init acpi_set_handle(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + *(acpi_handle *)return_value = handle; + return AE_CTRL_TERMINATE; +} + +static bool __init detect_thinkpad_privacy_screen(void) +{ + union acpi_object obj = { .type = ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { .count = 1, .pointer = &obj, }; + acpi_handle ec_handle = NULL; + unsigned long long output; + acpi_status status; + + /* Get embedded-controller handle */ + status = acpi_get_devices("PNP0C09", acpi_set_handle, NULL, &ec_handle); + if (ACPI_FAILURE(status) || !ec_handle) + return false; + + /* And call the privacy-screen get-status method */ + status = acpi_evaluate_integer(ec_handle, "HKEY.GSSS", &args, &output); + if (ACPI_FAILURE(status)) + return false; + + return (output & 0x10000) ? true : false; +} +#endif + +static const struct arch_init_data arch_init_data[] __initconst = { +#if IS_ENABLED(CONFIG_THINKPAD_ACPI) + { + .lookup = { + .dev_id = NULL, + .con_id = NULL, + .provider = "privacy_screen-thinkpad_acpi", + }, + .detect = detect_thinkpad_privacy_screen, + }, +#endif +}; + +void __init drm_privacy_screen_lookup_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arch_init_data); i++) { + if (!arch_init_data[i].detect()) + continue; + + pr_info("Found '%s' privacy-screen provider\n", + arch_init_data[i].lookup.provider); + + /* Make a copy because arch_init_data is __initconst */ + arch_lookup = arch_init_data[i].lookup; + drm_privacy_screen_lookup_add(&arch_lookup); + break; + } +} + +void drm_privacy_screen_lookup_exit(void) +{ + if (arch_lookup.provider) + drm_privacy_screen_lookup_remove(&arch_lookup); +} +#endif /* ifdef CONFIG_X86 */ diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 321e416489a9..45df9de22007 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -25,7 +25,6 @@ #include "framebuffer.h" #include "gem.h" -#include "gtt.h" #include "psb_drv.h" #include "psb_intel_drv.h" #include "psb_intel_reg.h" @@ -82,14 +81,13 @@ static vm_fault_t psbfb_vm_fault(struct vm_fault *vmf) struct drm_framebuffer *fb = vma->vm_private_data; struct drm_device *dev = fb->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct gtt_range *gtt = to_gtt_range(fb->obj[0]); + struct psb_gem_object *pobj = to_psb_gem_object(fb->obj[0]); int page_num; int i; unsigned long address; vm_fault_t ret = VM_FAULT_SIGBUS; unsigned long pfn; - unsigned long phys_addr = (unsigned long)dev_priv->stolen_base + - gtt->offset; + unsigned long phys_addr = (unsigned long)dev_priv->stolen_base + pobj->offset; page_num = vma_pages(vma); address = vmf->address - (vmf->pgoff << PAGE_SHIFT); @@ -226,31 +224,6 @@ static struct drm_framebuffer *psb_framebuffer_create } /** - * psbfb_alloc - allocate frame buffer memory - * @dev: the DRM device - * @aligned_size: space needed - * - * Allocate the frame buffer. In the usual case we get a GTT range that - * is stolen memory backed and life is simple. If there isn't sufficient - * we fail as we don't have the virtual mapping space to really vmap it - * and the kernel console code can't handle non linear framebuffers. - * - * Re-address this as and if the framebuffer layer grows this ability. - */ -static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size) -{ - struct gtt_range *backing; - /* Begin by trying to use stolen memory backing */ - backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1, PAGE_SIZE); - if (backing) { - backing->gem.funcs = &psb_gem_object_funcs; - drm_gem_private_object_init(dev, &backing->gem, aligned_size); - return backing; - } - return NULL; -} - -/** * psbfb_create - create a framebuffer * @fb_helper: the framebuffer helper * @sizes: specification of the layout @@ -268,7 +241,8 @@ static int psbfb_create(struct drm_fb_helper *fb_helper, struct drm_mode_fb_cmd2 mode_cmd; int size; int ret; - struct gtt_range *backing; + struct psb_gem_object *backing; + struct drm_gem_object *obj; u32 bpp, depth; mode_cmd.width = sizes->surface_width; @@ -286,24 +260,25 @@ static int psbfb_create(struct drm_fb_helper *fb_helper, size = ALIGN(size, PAGE_SIZE); /* Allocate the framebuffer in the GTT with stolen page backing */ - backing = psbfb_alloc(dev, size); - if (backing == NULL) - return -ENOMEM; + backing = psb_gem_create(dev, size, "fb", true, PAGE_SIZE); + if (IS_ERR(backing)) + return PTR_ERR(backing); + obj = &backing->base; memset(dev_priv->vram_addr + backing->offset, 0, size); info = drm_fb_helper_alloc_fbi(fb_helper); if (IS_ERR(info)) { ret = PTR_ERR(info); - goto out; + goto err_drm_gem_object_put; } mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth); - fb = psb_framebuffer_create(dev, &mode_cmd, &backing->gem); + fb = psb_framebuffer_create(dev, &mode_cmd, obj); if (IS_ERR(fb)) { ret = PTR_ERR(fb); - goto out; + goto err_drm_gem_object_put; } fb_helper->fb = fb; @@ -334,8 +309,9 @@ static int psbfb_create(struct drm_fb_helper *fb_helper, dev_dbg(dev->dev, "allocated %dx%d fb\n", fb->width, fb->height); return 0; -out: - psb_gtt_free_range(dev, backing); + +err_drm_gem_object_put: + drm_gem_object_put(obj); return ret; } diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index 5ae54c9d2819..8d65af80bb08 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -13,24 +13,105 @@ #include <linux/pagemap.h> +#include <asm/set_memory.h> + #include <drm/drm.h> #include <drm/drm_vma_manager.h> #include "gem.h" #include "psb_drv.h" +int psb_gem_pin(struct psb_gem_object *pobj) +{ + struct drm_gem_object *obj = &pobj->base; + struct drm_device *dev = obj->dev; + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + u32 gpu_base = dev_priv->gtt.gatt_start; + struct page **pages; + unsigned int npages; + int ret; + + mutex_lock(&dev_priv->gtt_mutex); + + if (pobj->in_gart || pobj->stolen) + goto out; /* already mapped */ + + pages = drm_gem_get_pages(obj); + if (IS_ERR(pages)) { + ret = PTR_ERR(pages); + goto err_mutex_unlock; + } + + npages = obj->size / PAGE_SIZE; + + set_pages_array_wc(pages, npages); + + psb_gtt_insert_pages(dev_priv, &pobj->resource, pages); + psb_mmu_insert_pages(psb_mmu_get_default_pd(dev_priv->mmu), pages, + (gpu_base + pobj->offset), npages, 0, 0, + PSB_MMU_CACHED_MEMORY); + + pobj->npage = npages; + pobj->pages = pages; + +out: + ++pobj->in_gart; + mutex_unlock(&dev_priv->gtt_mutex); + + return 0; + +err_mutex_unlock: + mutex_unlock(&dev_priv->gtt_mutex); + return ret; +} + +void psb_gem_unpin(struct psb_gem_object *pobj) +{ + struct drm_gem_object *obj = &pobj->base; + struct drm_device *dev = obj->dev; + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + u32 gpu_base = dev_priv->gtt.gatt_start; + + mutex_lock(&dev_priv->gtt_mutex); + + WARN_ON(!pobj->in_gart); + + --pobj->in_gart; + + if (pobj->in_gart || pobj->stolen) + goto out; + + psb_mmu_remove_pages(psb_mmu_get_default_pd(dev_priv->mmu), + (gpu_base + pobj->offset), pobj->npage, 0, 0); + psb_gtt_remove_pages(dev_priv, &pobj->resource); + + /* Reset caching flags */ + set_pages_array_wb(pobj->pages, pobj->npage); + + drm_gem_put_pages(obj, pobj->pages, true, false); + pobj->pages = NULL; + pobj->npage = 0; + +out: + mutex_unlock(&dev_priv->gtt_mutex); +} + static vm_fault_t psb_gem_fault(struct vm_fault *vmf); static void psb_gem_free_object(struct drm_gem_object *obj) { - struct gtt_range *gtt = container_of(obj, struct gtt_range, gem); + struct psb_gem_object *pobj = to_psb_gem_object(obj); - /* Remove the list map if one is present */ - drm_gem_free_mmap_offset(obj); drm_gem_object_release(obj); - /* This must occur last as it frees up the memory of the GEM object */ - psb_gtt_free_range(obj->dev, gtt); + /* Undo the mmap pin if we are destroying the object */ + if (pobj->mmapping) + psb_gem_unpin(pobj); + + WARN_ON(pobj->in_gart && !pobj->stolen); + + release_resource(&pobj->resource); + kfree(pobj); } static const struct vm_operations_struct psb_gem_vm_ops = { @@ -39,63 +120,60 @@ static const struct vm_operations_struct psb_gem_vm_ops = { .close = drm_gem_vm_close, }; -const struct drm_gem_object_funcs psb_gem_object_funcs = { +static const struct drm_gem_object_funcs psb_gem_object_funcs = { .free = psb_gem_free_object, .vm_ops = &psb_gem_vm_ops, }; -/** - * psb_gem_create - create a mappable object - * @file: the DRM file of the client - * @dev: our device - * @size: the size requested - * @handlep: returned handle (opaque number) - * @stolen: unused - * @align: unused - * - * Create a GEM object, fill in the boilerplate and attach a handle to - * it so that userspace can speak about it. This does the core work - * for the various methods that do/will create GEM objects for things - */ -int psb_gem_create(struct drm_file *file, struct drm_device *dev, u64 size, - u32 *handlep, int stolen, u32 align) +struct psb_gem_object * +psb_gem_create(struct drm_device *dev, u64 size, const char *name, bool stolen, u32 align) { - struct gtt_range *r; + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct psb_gem_object *pobj; + struct drm_gem_object *obj; int ret; - u32 handle; size = roundup(size, PAGE_SIZE); - /* Allocate our object - for now a direct gtt range which is not - stolen memory backed */ - r = psb_gtt_alloc_range(dev, size, "gem", 0, PAGE_SIZE); - if (r == NULL) { - dev_err(dev->dev, "no memory for %lld byte GEM object\n", size); - return -ENOSPC; - } - r->gem.funcs = &psb_gem_object_funcs; - /* Initialize the extra goodies GEM needs to do all the hard work */ - if (drm_gem_object_init(dev, &r->gem, size) != 0) { - psb_gtt_free_range(dev, r); - /* GEM doesn't give an error code so use -ENOMEM */ - dev_err(dev->dev, "GEM init failed for %lld\n", size); - return -ENOMEM; + pobj = kzalloc(sizeof(*pobj), GFP_KERNEL); + if (!pobj) + return ERR_PTR(-ENOMEM); + obj = &pobj->base; + + /* GTT resource */ + + ret = psb_gtt_allocate_resource(dev_priv, &pobj->resource, name, size, align, stolen, + &pobj->offset); + if (ret) + goto err_kfree; + + if (stolen) { + pobj->stolen = true; + pobj->in_gart = 1; } - /* Limit the object to 32bit mappings */ - mapping_set_gfp_mask(r->gem.filp->f_mapping, GFP_KERNEL | __GFP_DMA32); - /* Give the object a handle so we can carry it more easily */ - ret = drm_gem_handle_create(file, &r->gem, &handle); - if (ret) { - dev_err(dev->dev, "GEM handle failed for %p, %lld\n", - &r->gem, size); - drm_gem_object_release(&r->gem); - psb_gtt_free_range(dev, r); - return ret; + + /* GEM object */ + + obj->funcs = &psb_gem_object_funcs; + + if (stolen) { + drm_gem_private_object_init(dev, obj, size); + } else { + ret = drm_gem_object_init(dev, obj, size); + if (ret) + goto err_release_resource; + + /* Limit the object to 32-bit mappings */ + mapping_set_gfp_mask(obj->filp->f_mapping, GFP_KERNEL | __GFP_DMA32); } - /* We have the initial and handle reference but need only one now */ - drm_gem_object_put(&r->gem); - *handlep = handle; - return 0; + + return pobj; + +err_release_resource: + release_resource(&pobj->resource); +err_kfree: + kfree(pobj); + return ERR_PTR(ret); } /** @@ -111,10 +189,40 @@ int psb_gem_create(struct drm_file *file, struct drm_device *dev, u64 size, int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { - args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64); - args->size = args->pitch * args->height; - return psb_gem_create(file, dev, args->size, &args->handle, 0, - PAGE_SIZE); + size_t pitch, size; + struct psb_gem_object *pobj; + struct drm_gem_object *obj; + u32 handle; + int ret; + + pitch = args->width * DIV_ROUND_UP(args->bpp, 8); + pitch = ALIGN(pitch, 64); + + size = pitch * args->height; + size = roundup(size, PAGE_SIZE); + if (!size) + return -EINVAL; + + pobj = psb_gem_create(dev, size, "gem", false, PAGE_SIZE); + if (IS_ERR(pobj)) + return PTR_ERR(pobj); + obj = &pobj->base; + + ret = drm_gem_handle_create(file, obj, &handle); + if (ret) + goto err_drm_gem_object_put; + + drm_gem_object_put(obj); + + args->pitch = pitch; + args->size = size; + args->handle = handle; + + return 0; + +err_drm_gem_object_put: + drm_gem_object_put(obj); + return ret; } /** @@ -137,7 +245,7 @@ static vm_fault_t psb_gem_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj; - struct gtt_range *r; + struct psb_gem_object *pobj; int err; vm_fault_t ret; unsigned long pfn; @@ -149,7 +257,7 @@ static vm_fault_t psb_gem_fault(struct vm_fault *vmf) dev = obj->dev; dev_priv = to_drm_psb_private(dev); - r = container_of(obj, struct gtt_range, gem); /* Get the gtt range */ + pobj = to_psb_gem_object(obj); /* Make sure we don't parallel update on a fault, nor move or remove something from beneath our feet */ @@ -157,14 +265,14 @@ static vm_fault_t psb_gem_fault(struct vm_fault *vmf) /* For now the mmap pins the object and it stays pinned. As things stand that will do us no harm */ - if (r->mmapping == 0) { - err = psb_gtt_pin(r); + if (pobj->mmapping == 0) { + err = psb_gem_pin(pobj); if (err < 0) { dev_err(dev->dev, "gma500: pin failed: %d\n", err); ret = vmf_error(err); goto fail; } - r->mmapping = 1; + pobj->mmapping = 1; } /* Page relative to the VMA start - we must calculate this ourselves @@ -172,10 +280,10 @@ static vm_fault_t psb_gem_fault(struct vm_fault *vmf) page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; /* CPU view of the page, don't go via the GART for CPU writes */ - if (r->stolen) - pfn = (dev_priv->stolen_base + r->offset) >> PAGE_SHIFT; + if (pobj->stolen) + pfn = (dev_priv->stolen_base + pobj->offset) >> PAGE_SHIFT; else - pfn = page_to_pfn(r->pages[page_offset]); + pfn = page_to_pfn(pobj->pages[page_offset]); ret = vmf_insert_pfn(vma, vmf->address, pfn); fail: mutex_unlock(&dev_priv->mmap_mutex); diff --git a/drivers/gpu/drm/gma500/gem.h b/drivers/gpu/drm/gma500/gem.h index bae6454ead29..79cced40c87f 100644 --- a/drivers/gpu/drm/gma500/gem.h +++ b/drivers/gpu/drm/gma500/gem.h @@ -8,11 +8,33 @@ #ifndef _GEM_H #define _GEM_H +#include <linux/kernel.h> + +#include <drm/drm_gem.h> + struct drm_device; -extern const struct drm_gem_object_funcs psb_gem_object_funcs; +struct psb_gem_object { + struct drm_gem_object base; + + struct resource resource; /* GTT resource for our allocation */ + u32 offset; /* GTT offset of our object */ + int in_gart; /* Currently in the GART (ref ct) */ + bool stolen; /* Backed from stolen RAM */ + bool mmapping; /* Is mmappable */ + struct page **pages; /* Backing pages if present */ + int npage; /* Number of backing pages */ +}; + +static inline struct psb_gem_object *to_psb_gem_object(struct drm_gem_object *obj) +{ + return container_of(obj, struct psb_gem_object, base); +} + +struct psb_gem_object * +psb_gem_create(struct drm_device *dev, u64 size, const char *name, bool stolen, u32 align); -extern int psb_gem_create(struct drm_file *file, struct drm_device *dev, - u64 size, u32 *handlep, int stolen, u32 align); +int psb_gem_pin(struct psb_gem_object *pobj); +void psb_gem_unpin(struct psb_gem_object *pobj); #endif diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index cbcecbaa041b..99da3118131a 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -15,6 +15,7 @@ #include <drm/drm_vblank.h> #include "framebuffer.h" +#include "gem.h" #include "gma_display.h" #include "psb_drv.h" #include "psb_intel_drv.h" @@ -54,7 +55,7 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct gma_crtc *gma_crtc = to_gma_crtc(crtc); struct drm_framebuffer *fb = crtc->primary->fb; - struct gtt_range *gtt; + struct psb_gem_object *pobj; int pipe = gma_crtc->pipe; const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; @@ -70,14 +71,14 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, goto gma_pipe_cleaner; } - gtt = to_gtt_range(fb->obj[0]); + pobj = to_psb_gem_object(fb->obj[0]); /* We are displaying this buffer, make sure it is actually loaded into the GTT */ - ret = psb_gtt_pin(gtt); + ret = psb_gem_pin(pobj); if (ret < 0) goto gma_pipe_set_base_exit; - start = gtt->offset; + start = pobj->offset; offset = y * fb->pitches[0] + x * fb->format->cpp[0]; REG_WRITE(map->stride, fb->pitches[0]); @@ -125,7 +126,7 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, gma_pipe_cleaner: /* If there was a previous display we can now unpin it */ if (old_fb) - psb_gtt_unpin(to_gtt_range(old_fb->obj[0])); + psb_gem_unpin(to_psb_gem_object(old_fb->obj[0])); gma_pipe_set_base_exit: gma_power_end(dev); @@ -331,8 +332,8 @@ int gma_crtc_cursor_set(struct drm_crtc *crtc, uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; uint32_t temp; size_t addr = 0; - struct gtt_range *gt; - struct gtt_range *cursor_gt = gma_crtc->cursor_gt; + struct psb_gem_object *pobj; + struct psb_gem_object *cursor_pobj = gma_crtc->cursor_pobj; struct drm_gem_object *obj; void *tmp_dst, *tmp_src; int ret = 0, i, cursor_pages; @@ -348,9 +349,8 @@ int gma_crtc_cursor_set(struct drm_crtc *crtc, /* Unpin the old GEM object */ if (gma_crtc->cursor_obj) { - gt = container_of(gma_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); + pobj = to_psb_gem_object(gma_crtc->cursor_obj); + psb_gem_unpin(pobj); drm_gem_object_put(gma_crtc->cursor_obj); gma_crtc->cursor_obj = NULL; } @@ -375,40 +375,40 @@ int gma_crtc_cursor_set(struct drm_crtc *crtc, goto unref_cursor; } - gt = container_of(obj, struct gtt_range, gem); + pobj = to_psb_gem_object(obj); /* Pin the memory into the GTT */ - ret = psb_gtt_pin(gt); + ret = psb_gem_pin(pobj); if (ret) { dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); goto unref_cursor; } if (dev_priv->ops->cursor_needs_phys) { - if (cursor_gt == NULL) { + if (!cursor_pobj) { dev_err(dev->dev, "No hardware cursor mem available"); ret = -ENOMEM; goto unref_cursor; } /* Prevent overflow */ - if (gt->npage > 4) + if (pobj->npage > 4) cursor_pages = 4; else - cursor_pages = gt->npage; + cursor_pages = pobj->npage; /* Copy the cursor to cursor mem */ - tmp_dst = dev_priv->vram_addr + cursor_gt->offset; + tmp_dst = dev_priv->vram_addr + cursor_pobj->offset; for (i = 0; i < cursor_pages; i++) { - tmp_src = kmap(gt->pages[i]); + tmp_src = kmap(pobj->pages[i]); memcpy(tmp_dst, tmp_src, PAGE_SIZE); - kunmap(gt->pages[i]); + kunmap(pobj->pages[i]); tmp_dst += PAGE_SIZE; } addr = gma_crtc->cursor_addr; } else { - addr = gt->offset; + addr = pobj->offset; gma_crtc->cursor_addr = addr; } @@ -425,8 +425,8 @@ int gma_crtc_cursor_set(struct drm_crtc *crtc, /* unpin the old bo */ if (gma_crtc->cursor_obj) { - gt = container_of(gma_crtc->cursor_obj, struct gtt_range, gem); - psb_gtt_unpin(gt); + pobj = to_psb_gem_object(gma_crtc->cursor_obj); + psb_gem_unpin(pobj); drm_gem_object_put(gma_crtc->cursor_obj); } @@ -483,14 +483,14 @@ void gma_crtc_commit(struct drm_crtc *crtc) void gma_crtc_disable(struct drm_crtc *crtc) { - struct gtt_range *gt; + struct psb_gem_object *pobj; const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); if (crtc->primary->fb) { - gt = to_gtt_range(crtc->primary->fb->obj[0]); - psb_gtt_unpin(gt); + pobj = to_psb_gem_object(crtc->primary->fb->obj[0]); + psb_gem_unpin(pobj); } } @@ -498,6 +498,9 @@ void gma_crtc_destroy(struct drm_crtc *crtc) { struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + if (gma_crtc->cursor_pobj) + drm_gem_object_put(&gma_crtc->cursor_pobj->base); + kfree(gma_crtc->crtc_state); drm_crtc_cleanup(crtc); kfree(gma_crtc); diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 55a2a6919533..309ffe921bfd 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -7,10 +7,7 @@ * Alan Cox <alan@linux.intel.com> */ -#include <linux/shmem_fs.h> - -#include <asm/set_memory.h> - +#include "gem.h" /* TODO: for struct psb_gem_object, see psb_gtt_restore() */ #include "psb_drv.h" @@ -18,6 +15,33 @@ * GTT resource allocator - manage page mappings in GTT space */ +int psb_gtt_allocate_resource(struct drm_psb_private *pdev, struct resource *res, + const char *name, resource_size_t size, resource_size_t align, + bool stolen, u32 *offset) +{ + struct resource *root = pdev->gtt_mem; + resource_size_t start, end; + int ret; + + if (stolen) { + /* The start of the GTT is backed by stolen pages. */ + start = root->start; + end = root->start + pdev->gtt.stolen_size - 1; + } else { + /* The rest is backed by system pages. */ + start = root->start + pdev->gtt.stolen_size; + end = root->end; + } + + res->name = name; + ret = allocate_resource(root, res, size, start, end, align, NULL, NULL); + if (ret) + return ret; + *offset = res->start - root->start; + + return 0; +} + /** * psb_gtt_mask_pte - generate GTT pte entry * @pfn: page number to encode @@ -43,281 +67,62 @@ static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) return (pfn << PAGE_SHIFT) | mask; } -/** - * psb_gtt_entry - find the GTT entries for a gtt_range - * @dev: our DRM device - * @r: our GTT range - * - * Given a gtt_range object return the GTT offset of the page table - * entries for this gtt_range - */ -static u32 __iomem *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r) +static u32 __iomem *psb_gtt_entry(struct drm_psb_private *pdev, const struct resource *res) { - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - unsigned long offset; + unsigned long offset = res->start - pdev->gtt_mem->start; - offset = r->resource.start - dev_priv->gtt_mem->start; - - return dev_priv->gtt_map + (offset >> PAGE_SHIFT); + return pdev->gtt_map + (offset >> PAGE_SHIFT); } -/** - * psb_gtt_insert - put an object into the GTT - * @dev: our DRM device - * @r: our GTT range - * @resume: on resume - * - * Take our preallocated GTT range and insert the GEM object into - * the GTT. This is protected via the gtt mutex which the caller - * must hold. +/* + * Take our preallocated GTT range and insert the GEM object into + * the GTT. This is protected via the gtt mutex which the caller + * must hold. */ -static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r, - int resume) +void psb_gtt_insert_pages(struct drm_psb_private *pdev, const struct resource *res, + struct page **pages) { + resource_size_t npages, i; u32 __iomem *gtt_slot; u32 pte; - struct page **pages; - int i; - if (r->pages == NULL) { - WARN_ON(1); - return -EINVAL; - } - - WARN_ON(r->stolen); /* refcount these maybe ? */ - - gtt_slot = psb_gtt_entry(dev, r); - pages = r->pages; + /* Write our page entries into the GTT itself */ - if (!resume) { - /* Make sure changes are visible to the GPU */ - set_pages_array_wc(pages, r->npage); - } + npages = resource_size(res) >> PAGE_SHIFT; + gtt_slot = psb_gtt_entry(pdev, res); - /* Write our page entries into the GTT itself */ - for (i = 0; i < r->npage; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), - PSB_MMU_CACHED_MEMORY); - iowrite32(pte, gtt_slot++); + for (i = 0; i < npages; ++i, ++gtt_slot) { + pte = psb_gtt_mask_pte(page_to_pfn(pages[i]), PSB_MMU_CACHED_MEMORY); + iowrite32(pte, gtt_slot); } /* Make sure all the entries are set before we return */ ioread32(gtt_slot - 1); - - return 0; } -/** - * psb_gtt_remove - remove an object from the GTT - * @dev: our DRM device - * @r: our GTT range - * - * Remove a preallocated GTT range from the GTT. Overwrite all the - * page table entries with the dummy page. This is protected via the gtt - * mutex which the caller must hold. +/* + * Remove a preallocated GTT range from the GTT. Overwrite all the + * page table entries with the dummy page. This is protected via the gtt + * mutex which the caller must hold. */ -static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) +void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *res) { - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + resource_size_t npages, i; u32 __iomem *gtt_slot; u32 pte; - int i; - - WARN_ON(r->stolen); - - gtt_slot = psb_gtt_entry(dev, r); - pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), - PSB_MMU_CACHED_MEMORY); - - for (i = 0; i < r->npage; i++) - iowrite32(pte, gtt_slot++); - ioread32(gtt_slot - 1); - set_pages_array_wb(r->pages, r->npage); -} - -/** - * psb_gtt_attach_pages - attach and pin GEM pages - * @gt: the gtt range - * - * Pin and build an in kernel list of the pages that back our GEM object. - * While we hold this the pages cannot be swapped out. This is protected - * via the gtt mutex which the caller must hold. - */ -static int psb_gtt_attach_pages(struct gtt_range *gt) -{ - struct page **pages; - - WARN_ON(gt->pages); - - pages = drm_gem_get_pages(>->gem); - if (IS_ERR(pages)) - return PTR_ERR(pages); - - gt->npage = gt->gem.size / PAGE_SIZE; - gt->pages = pages; - - return 0; -} - -/** - * psb_gtt_detach_pages - attach and pin GEM pages - * @gt: the gtt range - * - * Undo the effect of psb_gtt_attach_pages. At this point the pages - * must have been removed from the GTT as they could now be paged out - * and move bus address. This is protected via the gtt mutex which the - * caller must hold. - */ -static void psb_gtt_detach_pages(struct gtt_range *gt) -{ - drm_gem_put_pages(>->gem, gt->pages, true, false); - gt->pages = NULL; -} -/** - * psb_gtt_pin - pin pages into the GTT - * @gt: range to pin - * - * Pin a set of pages into the GTT. The pins are refcounted so that - * multiple pins need multiple unpins to undo. - * - * Non GEM backed objects treat this as a no-op as they are always GTT - * backed objects. - */ -int psb_gtt_pin(struct gtt_range *gt) -{ - int ret = 0; - struct drm_device *dev = gt->gem.dev; - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - u32 gpu_base = dev_priv->gtt.gatt_start; + /* Install scratch page for the resource */ - mutex_lock(&dev_priv->gtt_mutex); + pte = psb_gtt_mask_pte(page_to_pfn(pdev->scratch_page), PSB_MMU_CACHED_MEMORY); - if (gt->in_gart == 0 && gt->stolen == 0) { - ret = psb_gtt_attach_pages(gt); - if (ret < 0) - goto out; - ret = psb_gtt_insert(dev, gt, 0); - if (ret < 0) { - psb_gtt_detach_pages(gt); - goto out; - } - psb_mmu_insert_pages(psb_mmu_get_default_pd(dev_priv->mmu), - gt->pages, (gpu_base + gt->offset), - gt->npage, 0, 0, PSB_MMU_CACHED_MEMORY); - } - gt->in_gart++; -out: - mutex_unlock(&dev_priv->gtt_mutex); - return ret; -} + npages = resource_size(res) >> PAGE_SHIFT; + gtt_slot = psb_gtt_entry(pdev, res); -/** - * psb_gtt_unpin - Drop a GTT pin requirement - * @gt: range to pin - * - * Undoes the effect of psb_gtt_pin. On the last drop the GEM object - * will be removed from the GTT which will also drop the page references - * and allow the VM to clean up or page stuff. - * - * Non GEM backed objects treat this as a no-op as they are always GTT - * backed objects. - */ -void psb_gtt_unpin(struct gtt_range *gt) -{ - struct drm_device *dev = gt->gem.dev; - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - u32 gpu_base = dev_priv->gtt.gatt_start; + for (i = 0; i < npages; ++i, ++gtt_slot) + iowrite32(pte, gtt_slot); - mutex_lock(&dev_priv->gtt_mutex); - - WARN_ON(!gt->in_gart); - - gt->in_gart--; - if (gt->in_gart == 0 && gt->stolen == 0) { - psb_mmu_remove_pages(psb_mmu_get_default_pd(dev_priv->mmu), - (gpu_base + gt->offset), gt->npage, 0, 0); - psb_gtt_remove(dev, gt); - psb_gtt_detach_pages(gt); - } - - mutex_unlock(&dev_priv->gtt_mutex); -} - -/* - * GTT resource allocator - allocate and manage GTT address space - */ - -/** - * psb_gtt_alloc_range - allocate GTT address space - * @dev: Our DRM device - * @len: length (bytes) of address space required - * @name: resource name - * @backed: resource should be backed by stolen pages - * @align: requested alignment - * - * Ask the kernel core to find us a suitable range of addresses - * to use for a GTT mapping. - * - * Returns a gtt_range structure describing the object, or NULL on - * error. On successful return the resource is both allocated and marked - * as in use. - */ -struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, - const char *name, int backed, u32 align) -{ - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct gtt_range *gt; - struct resource *r = dev_priv->gtt_mem; - int ret; - unsigned long start, end; - - if (backed) { - /* The start of the GTT is the stolen pages */ - start = r->start; - end = r->start + dev_priv->gtt.stolen_size - 1; - } else { - /* The rest we will use for GEM backed objects */ - start = r->start + dev_priv->gtt.stolen_size; - end = r->end; - } - - gt = kzalloc(sizeof(struct gtt_range), GFP_KERNEL); - if (gt == NULL) - return NULL; - gt->resource.name = name; - gt->stolen = backed; - gt->in_gart = backed; - /* Ensure this is set for non GEM objects */ - gt->gem.dev = dev; - ret = allocate_resource(dev_priv->gtt_mem, >->resource, - len, start, end, align, NULL, NULL); - if (ret == 0) { - gt->offset = gt->resource.start - r->start; - return gt; - } - kfree(gt); - return NULL; -} - -/** - * psb_gtt_free_range - release GTT address space - * @dev: our DRM device - * @gt: a mapping created with psb_gtt_alloc_range - * - * Release a resource that was allocated with psb_gtt_alloc_range. If the - * object has been pinned by mmap users we clean this up here currently. - */ -void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt) -{ - /* Undo the mmap pin if we are destroying the object */ - if (gt->mmapping) { - psb_gtt_unpin(gt); - gt->mmapping = 0; - } - WARN_ON(gt->in_gart && !gt->stolen); - release_resource(>->resource); - kfree(gt); + /* Make sure all the entries are set before we return */ + ioread32(gtt_slot - 1); } static void psb_gtt_alloc(struct drm_device *dev) @@ -498,7 +303,7 @@ int psb_gtt_restore(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct resource *r = dev_priv->gtt_mem->child; - struct gtt_range *range; + struct psb_gem_object *pobj; unsigned int restored = 0, total = 0, size = 0; /* On resume, the gtt_mutex is already initialized */ @@ -506,10 +311,15 @@ int psb_gtt_restore(struct drm_device *dev) psb_gtt_init(dev, 1); while (r != NULL) { - range = container_of(r, struct gtt_range, resource); - if (range->pages) { - psb_gtt_insert(dev, range, 1); - size += range->resource.end - range->resource.start; + /* + * TODO: GTT restoration needs a refactoring, so that we don't have to touch + * struct psb_gem_object here. The type represents a GEM object and is + * not related to the GTT itself. + */ + pobj = container_of(r, struct psb_gem_object, resource); + if (pobj->pages) { + psb_gtt_insert_pages(dev_priv, &pobj->resource, pobj->pages); + size += pobj->resource.end - pobj->resource.start; restored++; } r = r->sibling; diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index 2bf165849ebe..ff1dcdd1ff52 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -10,6 +10,8 @@ #include <drm/drm_gem.h> +struct drm_psb_private; + /* This wants cleaning up with respect to the psb_dev and un-needed stuff */ struct psb_gtt { uint32_t gatt_start; @@ -26,27 +28,14 @@ struct psb_gtt { /* Exported functions */ extern int psb_gtt_init(struct drm_device *dev, int resume); extern void psb_gtt_takedown(struct drm_device *dev); +extern int psb_gtt_restore(struct drm_device *dev); -/* Each gtt_range describes an allocation in the GTT area */ -struct gtt_range { - struct resource resource; /* Resource for our allocation */ - u32 offset; /* GTT offset of our object */ - struct drm_gem_object gem; /* GEM high level stuff */ - int in_gart; /* Currently in the GART (ref ct) */ - bool stolen; /* Backed from stolen RAM */ - bool mmapping; /* Is mmappable */ - struct page **pages; /* Backing pages if present */ - int npage; /* Number of backing pages */ -}; +int psb_gtt_allocate_resource(struct drm_psb_private *pdev, struct resource *res, + const char *name, resource_size_t size, resource_size_t align, + bool stolen, u32 *offset); -#define to_gtt_range(x) container_of(x, struct gtt_range, gem) +void psb_gtt_insert_pages(struct drm_psb_private *pdev, const struct resource *res, + struct page **pages); +void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *res); -extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, - const char *name, int backed, - u32 align); -extern void psb_gtt_kref_put(struct gtt_range *gt); -extern void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt); -extern int psb_gtt_pin(struct gtt_range *gt); -extern void psb_gtt_unpin(struct gtt_range *gt); -extern int psb_gtt_restore(struct drm_device *dev); #endif diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c index c6b115954b7d..36c7c2686c90 100644 --- a/drivers/gpu/drm/gma500/oaktrail_crtc.c +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c @@ -10,6 +10,7 @@ #include <drm/drm_fourcc.h> #include "framebuffer.h" +#include "gem.h" #include "gma_display.h" #include "power.h" #include "psb_drv.h" @@ -608,7 +609,7 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc, if (!gma_power_begin(dev, true)) return 0; - start = to_gtt_range(fb->obj[0])->offset; + start = to_psb_gem_object(fb->obj[0])->offset; offset = y * fb->pitches[0] + x * fb->format->cpp[0]; REG_WRITE(map->stride, fb->pitches[0]); diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 7a10bb39ef0b..65cf1c79dd7c 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -19,6 +19,7 @@ #include <acpi/video.h> #include <drm/drm.h> +#include <drm/drm_aperture.h> #include <drm/drm_drv.h> #include <drm/drm_fb_helper.h> #include <drm/drm_file.h> @@ -448,6 +449,17 @@ static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct drm_device *dev; int ret; + /* + * We cannot yet easily find the framebuffer's location in memory. So + * remove all framebuffers here. + * + * TODO: Refactor psb_driver_load() to map vdc_reg earlier. Then we + * might be able to read the framebuffer range from the device. + */ + ret = drm_aperture_remove_framebuffers(true, &driver); + if (ret) + return ret; + ret = pcim_enable_device(pdev); if (ret) return ret; diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index f5f259fde88e..d5f95212934e 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -12,6 +12,7 @@ #include <drm/drm_plane_helper.h> #include "framebuffer.h" +#include "gem.h" #include "gma_display.h" #include "power.h" #include "psb_drv.h" @@ -454,23 +455,21 @@ static void psb_intel_cursor_init(struct drm_device *dev, struct drm_psb_private *dev_priv = to_drm_psb_private(dev); u32 control[3] = { CURACNTR, CURBCNTR, CURCCNTR }; u32 base[3] = { CURABASE, CURBBASE, CURCBASE }; - struct gtt_range *cursor_gt; + struct psb_gem_object *cursor_pobj; if (dev_priv->ops->cursor_needs_phys) { /* Allocate 4 pages of stolen mem for a hardware cursor. That * is enough for the 64 x 64 ARGB cursors we support. */ - cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1, - PAGE_SIZE); - if (!cursor_gt) { - gma_crtc->cursor_gt = NULL; + cursor_pobj = psb_gem_create(dev, 4 * PAGE_SIZE, "cursor", true, PAGE_SIZE); + if (IS_ERR(cursor_pobj)) { + gma_crtc->cursor_pobj = NULL; goto out; } - gma_crtc->cursor_gt = cursor_gt; - gma_crtc->cursor_addr = dev_priv->stolen_base + - cursor_gt->offset; + gma_crtc->cursor_pobj = cursor_pobj; + gma_crtc->cursor_addr = dev_priv->stolen_base + cursor_pobj->offset; } else { - gma_crtc->cursor_gt = NULL; + gma_crtc->cursor_pobj = NULL; } out: diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 5340225d6997..db3e757328fe 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -140,7 +140,7 @@ struct gma_crtc { int pipe; int plane; uint32_t cursor_addr; - struct gtt_range *cursor_gt; + struct psb_gem_object *cursor_pobj; u8 lut_adj[256]; struct psb_intel_framebuffer *fbdev_fb; /* a mode_set for fbdev users on this crtc */ diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index 2c3cd6e635b5..820a1f38b271 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -1537,38 +1537,14 @@ i915_request_await_object(struct i915_request *to, struct drm_i915_gem_object *obj, bool write) { - struct dma_fence *excl; + struct dma_resv_iter cursor; + struct dma_fence *fence; int ret = 0; - if (write) { - struct dma_fence **shared; - unsigned int count, i; - - ret = dma_resv_get_fences(obj->base.resv, &excl, &count, - &shared); + dma_resv_for_each_fence(&cursor, obj->base.resv, write, fence) { + ret = i915_request_await_dma_fence(to, fence); if (ret) - return ret; - - for (i = 0; i < count; i++) { - ret = i915_request_await_dma_fence(to, shared[i]); - if (ret) - break; - - dma_fence_put(shared[i]); - } - - for (; i < count; i++) - dma_fence_put(shared[i]); - kfree(shared); - } else { - excl = dma_resv_get_excl_unlocked(obj->base.resv); - } - - if (excl) { - if (ret == 0) - ret = i915_request_await_dma_fence(to, excl); - - dma_fence_put(excl); + break; } return ret; diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 40a9863f5951..5bd511f07c07 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -880,7 +880,7 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m, { struct msm_gem_object *msm_obj = to_msm_bo(obj); struct dma_resv *robj = obj->resv; - struct dma_resv_list *fobj; + struct dma_resv_iter cursor; struct dma_fence *fence; struct msm_gem_vma *vma; uint64_t off = drm_vma_node_start(&obj->vma_node); @@ -955,22 +955,13 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m, seq_puts(m, "\n"); } - rcu_read_lock(); - fobj = dma_resv_shared_list(robj); - if (fobj) { - unsigned int i, shared_count = fobj->shared_count; - - for (i = 0; i < shared_count; i++) { - fence = rcu_dereference(fobj->shared[i]); + dma_resv_for_each_fence(&cursor, robj, true, fence) { + if (dma_resv_iter_is_exclusive(&cursor)) + describe_fence(fence, "Exclusive", m); + else describe_fence(fence, "Shared", m); - } } - fence = dma_resv_excl_fence(robj); - if (fence) - describe_fence(fence, "Exclusive", m); - rcu_read_unlock(); - msm_gem_unlock(obj); } diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 89dd618d78f3..0655582ae8ed 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -88,7 +88,7 @@ static void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb, ctrl |= CTRL_BUS_WIDTH_24; break; default: - dev_err(drm->dev, "Unknown media bus format %d\n", bus_format); + dev_err(drm->dev, "Unknown media bus format 0x%x\n", bus_format); break; } @@ -362,6 +362,12 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, drm_atomic_get_new_bridge_state(state, mxsfb->bridge); bus_format = bridge_state->input_bus_cfg.format; + if (bus_format == MEDIA_BUS_FMT_FIXED) { + dev_warn_once(drm->dev, + "Bridge does not provide bus format, assuming MEDIA_BUS_FMT_RGB888_1X24.\n" + "Please fix bridge driver by handling atomic_get_input_bus_fmts.\n"); + bus_format = MEDIA_BUS_FMT_RGB888_1X24; + } } /* If there is no bridge, use bus format from connector */ diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index b2c7e0802ac3..17a0a3ece485 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1248,7 +1248,6 @@ nouveau_ttm_tt_populate(struct ttm_device *bdev, { struct ttm_tt *ttm_dma = (void *)ttm; struct nouveau_drm *drm; - struct device *dev; bool slave = !!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL); if (ttm_tt_is_populated(ttm)) @@ -1261,7 +1260,6 @@ nouveau_ttm_tt_populate(struct ttm_device *bdev, } drm = nouveau_bdev(bdev); - dev = drm->dev->dev; return ttm_pool_alloc(&drm->ttm.bdev.pool, ttm, ctx); } @@ -1271,7 +1269,6 @@ nouveau_ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm) { struct nouveau_drm *drm; - struct device *dev; bool slave = !!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL); if (slave) @@ -1280,7 +1277,6 @@ nouveau_ttm_tt_unpopulate(struct ttm_device *bdev, nouveau_ttm_tt_unbind(bdev, ttm); drm = nouveau_bdev(bdev); - dev = drm->dev->dev; return ttm_pool_free(&drm->ttm.bdev.pool, ttm); } diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 05d0b3eb3690..26f9299df881 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -339,14 +339,15 @@ nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) } int -nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool exclusive, bool intr) +nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, + bool exclusive, bool intr) { struct nouveau_fence_chan *fctx = chan->fence; - struct dma_fence *fence; struct dma_resv *resv = nvbo->bo.base.resv; - struct dma_resv_list *fobj; + struct dma_resv_iter cursor; + struct dma_fence *fence; struct nouveau_fence *f; - int ret = 0, i; + int ret; if (!exclusive) { ret = dma_resv_reserve_shared(resv, 1); @@ -355,10 +356,7 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e return ret; } - fobj = dma_resv_shared_list(resv); - fence = dma_resv_excl_fence(resv); - - if (fence) { + dma_resv_for_each_fence(&cursor, resv, exclusive, fence) { struct nouveau_channel *prev = NULL; bool must_wait = true; @@ -366,41 +364,19 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e if (f) { rcu_read_lock(); prev = rcu_dereference(f->channel); - if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) + if (prev && (prev == chan || + fctx->sync(f, prev, chan) == 0)) must_wait = false; rcu_read_unlock(); } - if (must_wait) + if (must_wait) { ret = dma_fence_wait(fence, intr); - - return ret; - } - - if (!exclusive || !fobj) - return ret; - - for (i = 0; i < fobj->shared_count && !ret; ++i) { - struct nouveau_channel *prev = NULL; - bool must_wait = true; - - fence = rcu_dereference_protected(fobj->shared[i], - dma_resv_held(resv)); - - f = nouveau_local_fence(fence, chan->drm); - if (f) { - rcu_read_lock(); - prev = rcu_dereference(f->channel); - if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) - must_wait = false; - rcu_read_unlock(); + if (ret) + return ret; } - - if (must_wait) - ret = dma_fence_wait(fence, intr); } - - return ret; + return 0; } void diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 5b27845075a1..d476940ee97c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -339,7 +339,7 @@ nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains, struct ttm_buffer_object *bo = &nvbo->bo; uint32_t domains = valid_domains & nvbo->valid_domains & (write_domains ? write_domains : read_domains); - uint32_t pref_domains = 0;; + uint32_t pref_domains = 0; if (!domains) return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c index c39e797dc7c9..cf5dcfda7b25 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c @@ -21,7 +21,6 @@ */ #include "priv.h" -#include "priv.h" #include <core/firmware.h> static void * diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c index d6a1f8d04c09..186b4e63e559 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c @@ -299,7 +299,7 @@ nvkm_uvmm_mthd_page(struct nvkm_uvmm *uvmm, void *argv, u32 argc) page = uvmm->vmm->func->page; for (nr = 0; page[nr].shift; nr++); - if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { + if (!(nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) { if ((index = args->v0.index) >= nr) return -EINVAL; type = page[index].type; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c index b5e733783b5b..17899fc95b2d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c @@ -488,7 +488,7 @@ gp100_vmm_fault_cancel(struct nvkm_vmm *vmm, void *argv, u32 argc) struct gp100_vmm_fault_cancel_v0 v0; } *args = argv; int ret = -ENOSYS; - u32 inst, aper; + u32 aper; if ((ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) return ret; @@ -502,7 +502,7 @@ gp100_vmm_fault_cancel(struct nvkm_vmm *vmm, void *argv, u32 argc) args->v0.inst |= 0x80000000; if (!WARN_ON(nvkm_gr_ctxsw_pause(device))) { - if ((inst = nvkm_gr_ctxsw_inst(device)) == args->v0.inst) { + if (nvkm_gr_ctxsw_inst(device) == args->v0.inst) { gf100_vmm_invalidate(vmm, 0x0000001b /* CANCEL_TARGETED. */ | (args->v0.hub << 20) | diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 369cb76512fe..c64670707abf 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -37,6 +37,17 @@ config DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 NT35596 1080x1920 video mode panel as found in some Asus Zenfone 2 Laser Z00T devices. +config DRM_PANEL_BOE_BF060Y8M_AJ0 + tristate "Boe BF060Y8M-AJ0 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 Boe BF060Y8M-AJ0 + 5.99" AMOLED modules. The panel has a 1080x2160 resolution and + uses 24 bit RGB per pixel. It provides a MIPI DSI interface to + the host and backlight is controlled through DSI commands. + config DRM_PANEL_BOE_HIMAX8279D tristate "Boe Himax8279d panel" depends on OF @@ -189,6 +200,15 @@ config DRM_PANEL_JDI_LT070ME05000 The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses 24 bit per pixel. +config DRM_PANEL_JDI_R63452 + tristate "JDI R63452 Full HD DSI 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 JDI R63452 + DSI command mode panel as found in Xiaomi Mi 5 Devices. + config DRM_PANEL_KHADAS_TS050 tristate "Khadas TS050 panel" depends on OF @@ -272,6 +292,17 @@ config DRM_PANEL_NOVATEK_NT35510 around the Novatek NT35510 display controller, such as some Hydis panels. +config DRM_PANEL_NOVATEK_NT35950 + tristate "Novatek NT35950 DSI 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 panels built + around the Novatek NT35950 display controller, such as some + Sharp panels used in Sony Xperia Z5 Premium and XZ Premium + mobile phones. + config DRM_PANEL_NOVATEK_NT36672A tristate "Novatek NT36672A DSI panel" depends on OF @@ -519,6 +550,16 @@ config DRM_PANEL_SHARP_LS043T1LE01 Say Y here if you want to enable support for Sharp LS043T1LE01 qHD (540x960) DSI panel as found on the Qualcomm APQ8074 Dragonboard +config DRM_PANEL_SHARP_LS060T1SX01 + tristate "Sharp LS060T1SX01 FullHD video mode 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 Sharp LS060T1SX01 6.0" + FullHD (1080x1920) DSI panel as found in Dragonboard Display Adapter + Bundle. + config DRM_PANEL_SITRONIX_ST7701 tristate "Sitronix ST7701 panel driver" depends on OF @@ -569,6 +610,16 @@ config DRM_PANEL_SONY_ACX565AKM Say Y here if you want to enable support for the Sony ACX565AKM 800x600 3.5" panel (found on the Nokia N900). +config DRM_PANEL_SONY_TULIP_TRULY_NT35521 + tristate "Sony Tulip Truly NT35521 panel" + depends on GPIOLIB && OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for the Sony Tulip + NT35521 1280x720 video mode panel as found on Sony Xperia M4 + Aqua phone. + config DRM_PANEL_TDO_TL070WSH30 tristate "TDO TL070WSH30 DSI panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 6e30640b9099..d99fbbce49d1 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o +obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.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_DSI_CM) += panel-dsi-cm.o @@ -17,6 +18,7 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o +obj-$(CONFIG_DRM_PANEL_JDI_R63452) += panel-jdi-fhd-r63452.o obj-$(CONFIG_DRM_PANEL_KHADAS_TS050) += panel-khadas-ts050.o obj-$(CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04) += panel-kingdisplay-kd097d04.o obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W) += panel-leadtek-ltk050h3146w.o @@ -25,6 +27,7 @@ obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o @@ -53,11 +56,13 @@ obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o +obj-$(CONFIG_DRM_PANEL_SHARP_LS060T1SX01) += panel-sharp-ls060t1sx01.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o +obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o diff --git a/drivers/gpu/drm/panel/panel-abt-y030xx067a.c b/drivers/gpu/drm/panel/panel-abt-y030xx067a.c index 2d8794d495d0..1b5d8f755b12 100644 --- a/drivers/gpu/drm/panel/panel-abt-y030xx067a.c +++ b/drivers/gpu/drm/panel/panel-abt-y030xx067a.c @@ -272,16 +272,14 @@ static int y030xx067a_probe(struct spi_device *spi) return -EINVAL; priv->supply = devm_regulator_get(dev, "power"); - if (IS_ERR(priv->supply)) { - dev_err(dev, "Failed to get power supply\n"); - return PTR_ERR(priv->supply); - } + if (IS_ERR(priv->supply)) + return dev_err_probe(dev, PTR_ERR(priv->supply), + "Failed to get power supply\n"); priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(priv->reset_gpio)) { - dev_err(dev, "Failed to get reset GPIO\n"); - return PTR_ERR(priv->reset_gpio); - } + if (IS_ERR(priv->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), + "Failed to get reset GPIO\n"); drm_panel_init(&priv->panel, dev, &y030xx067a_funcs, DRM_MODE_CONNECTOR_DPI); diff --git a/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c b/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c new file mode 100644 index 000000000000..ef00cd67dc40 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BOE BF060Y8M-AJ0 5.99" MIPI-DSI OLED Panel on SW43404 DriverIC + * + * Copyright (c) 2020 AngeloGioacchino Del Regno + * <angelogioacchino.delregno@somainline.org> + */ + +#include <linux/backlight.h> +#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> + +#define DCS_ALLOW_HBM_RANGE 0x0c +#define DCS_DISALLOW_HBM_RANGE 0x08 + +enum boe_bf060y8m_aj0_supplies { + BF060Y8M_VREG_VCC, + BF060Y8M_VREG_VDDIO, + BF060Y8M_VREG_VCI, + BF060Y8M_VREG_EL_VDD, + BF060Y8M_VREG_EL_VSS, + BF060Y8M_VREG_MAX +}; + +struct boe_bf060y8m_aj0 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data vregs[BF060Y8M_VREG_MAX]; + struct gpio_desc *reset_gpio; + bool prepared; +}; + +static inline +struct boe_bf060y8m_aj0 *to_boe_bf060y8m_aj0(struct drm_panel *panel) +{ + return container_of(panel, struct boe_bf060y8m_aj0, 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 boe_bf060y8m_aj0_reset(struct boe_bf060y8m_aj0 *boe) +{ + gpiod_set_value_cansleep(boe->reset_gpio, 0); + usleep_range(2000, 3000); + gpiod_set_value_cansleep(boe->reset_gpio, 1); + usleep_range(15000, 16000); + gpiod_set_value_cansleep(boe->reset_gpio, 0); + usleep_range(5000, 6000); +} + +static int boe_bf060y8m_aj0_on(struct boe_bf060y8m_aj0 *boe) +{ + struct mipi_dsi_device *dsi = boe->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi_dcs_write_seq(dsi, 0xb0, 0xa5, 0x00); + dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0x4c); + dsi_dcs_write_seq(dsi, MIPI_DCS_SET_3D_CONTROL, 0x10); + dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, DCS_ALLOW_HBM_RANGE); + dsi_dcs_write_seq(dsi, 0xf8, + 0x00, 0x08, 0x10, 0x00, 0x22, 0x00, 0x00, 0x2d); + + 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(30); + + dsi_dcs_write_seq(dsi, 0xb0, 0xa5, 0x00); + dsi_dcs_write_seq(dsi, 0xc0, + 0x08, 0x48, 0x65, 0x33, 0x33, 0x33, + 0x2a, 0x31, 0x39, 0x20, 0x09); + dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x00, 0x00, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); + dsi_dcs_write_seq(dsi, 0xe2, 0x20, 0x04, 0x10, 0x12, 0x92, + 0x4f, 0x8f, 0x44, 0x84, 0x83, 0x83, 0x83, + 0x5c, 0x5c, 0x5c); + dsi_dcs_write_seq(dsi, 0xde, 0x01, 0x2c, 0x00, 0x77, 0x3e); + + msleep(30); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + msleep(50); + + return 0; +} + +static int boe_bf060y8m_aj0_off(struct boe_bf060y8m_aj0 *boe) +{ + struct mipi_dsi_device *dsi = boe->dsi; + struct device *dev = &dsi->dev; + int ret; + + /* OFF commands sent in HS mode */ + 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(20); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + return ret; + } + usleep_range(1000, 2000); + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return 0; +} + +static int boe_bf060y8m_aj0_prepare(struct drm_panel *panel) +{ + struct boe_bf060y8m_aj0 *boe = to_boe_bf060y8m_aj0(panel); + struct device *dev = &boe->dsi->dev; + int ret; + + if (boe->prepared) + return 0; + + /* + * Enable EL Driving Voltage first - doing that at the beginning + * or at the end of the power sequence doesn't matter, so enable + * it here to avoid yet another usleep at the end. + */ + ret = regulator_enable(boe->vregs[BF060Y8M_VREG_EL_VDD].consumer); + if (ret) + return ret; + ret = regulator_enable(boe->vregs[BF060Y8M_VREG_EL_VSS].consumer); + if (ret) + goto err_elvss; + + ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VCC].consumer); + if (ret) + goto err_vcc; + usleep_range(1000, 2000); + ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VDDIO].consumer); + if (ret) + goto err_vddio; + usleep_range(500, 1000); + ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VCI].consumer); + if (ret) + goto err_vci; + usleep_range(2000, 3000); + + boe_bf060y8m_aj0_reset(boe); + + ret = boe_bf060y8m_aj0_on(boe); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(boe->reset_gpio, 1); + return ret; + } + + boe->prepared = true; + return 0; + +err_vci: + regulator_disable(boe->vregs[BF060Y8M_VREG_VDDIO].consumer); +err_vddio: + regulator_disable(boe->vregs[BF060Y8M_VREG_VCC].consumer); +err_vcc: + regulator_disable(boe->vregs[BF060Y8M_VREG_EL_VSS].consumer); +err_elvss: + regulator_disable(boe->vregs[BF060Y8M_VREG_EL_VDD].consumer); + return ret; +} + +static int boe_bf060y8m_aj0_unprepare(struct drm_panel *panel) +{ + struct boe_bf060y8m_aj0 *boe = to_boe_bf060y8m_aj0(panel); + struct device *dev = &boe->dsi->dev; + int ret; + + if (!boe->prepared) + return 0; + + ret = boe_bf060y8m_aj0_off(boe); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(boe->reset_gpio, 1); + ret = regulator_bulk_disable(ARRAY_SIZE(boe->vregs), boe->vregs); + + boe->prepared = false; + return 0; +} + +static const struct drm_display_mode boe_bf060y8m_aj0_mode = { + .clock = 165268, + .hdisplay = 1080, + .hsync_start = 1080 + 36, + .hsync_end = 1080 + 36 + 24, + .htotal = 1080 + 36 + 24 + 96, + .vdisplay = 2160, + .vsync_start = 2160 + 16, + .vsync_end = 2160 + 16 + 1, + .vtotal = 2160 + 16 + 1 + 15, + .width_mm = 68, /* 68.04 mm */ + .height_mm = 136, /* 136.08 mm */ +}; + +static int boe_bf060y8m_aj0_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &boe_bf060y8m_aj0_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 boe_bf060y8m_aj0_panel_funcs = { + .prepare = boe_bf060y8m_aj0_prepare, + .unprepare = boe_bf060y8m_aj0_unprepare, + .get_modes = boe_bf060y8m_aj0_get_modes, +}; + +static int boe_bf060y8m_aj0_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness = backlight_get_brightness(bl); + int ret; + + ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness); + if (ret < 0) + return ret; + + return 0; +} + +static int boe_bf060y8m_aj0_bl_get_brightness(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness; + int ret; + + ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); + if (ret < 0) + return ret; + + return brightness & 0xff; +} + +static const struct backlight_ops boe_bf060y8m_aj0_bl_ops = { + .update_status = boe_bf060y8m_aj0_bl_update_status, + .get_brightness = boe_bf060y8m_aj0_bl_get_brightness, +}; + +static struct backlight_device * +boe_bf060y8m_aj0_create_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 127, + .max_brightness = 255, + .scale = BACKLIGHT_SCALE_NON_LINEAR, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &boe_bf060y8m_aj0_bl_ops, &props); +} + +static int boe_bf060y8m_aj0_init_vregs(struct boe_bf060y8m_aj0 *boe, + struct device *dev) +{ + struct regulator *vreg; + int ret; + + boe->vregs[BF060Y8M_VREG_VCC].supply = "vcc"; + boe->vregs[BF060Y8M_VREG_VDDIO].supply = "vddio"; + boe->vregs[BF060Y8M_VREG_VCI].supply = "vci"; + boe->vregs[BF060Y8M_VREG_EL_VDD].supply = "elvdd"; + boe->vregs[BF060Y8M_VREG_EL_VSS].supply = "elvss"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(boe->vregs), + boe->vregs); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + vreg = boe->vregs[BF060Y8M_VREG_VCC].consumer; + ret = regulator_is_supported_voltage(vreg, 2700000, 3600000); + if (!ret) + return ret; + + vreg = boe->vregs[BF060Y8M_VREG_VDDIO].consumer; + ret = regulator_is_supported_voltage(vreg, 1620000, 1980000); + if (!ret) + return ret; + + vreg = boe->vregs[BF060Y8M_VREG_VCI].consumer; + ret = regulator_is_supported_voltage(vreg, 2600000, 3600000); + if (!ret) + return ret; + + vreg = boe->vregs[BF060Y8M_VREG_EL_VDD].consumer; + ret = regulator_is_supported_voltage(vreg, 4400000, 4800000); + if (!ret) + return ret; + + /* ELVSS is negative: -5.00V to -1.40V */ + vreg = boe->vregs[BF060Y8M_VREG_EL_VSS].consumer; + ret = regulator_is_supported_voltage(vreg, 1400000, 5000000); + if (!ret) + return ret; + + /* + * Set min/max rated current, known only for VCI and VDDIO and, + * in case of failure, just go on gracefully, as this step is not + * guaranteed to succeed on all regulator HW but do a debug print + * to inform the developer during debugging. + * In any case, these two supplies are also optional, so they may + * be fixed-regulator which, at the time of writing, does not + * support fake current limiting. + */ + vreg = boe->vregs[BF060Y8M_VREG_VDDIO].consumer; + ret = regulator_set_current_limit(vreg, 1500, 2500); + if (ret) + dev_dbg(dev, "Current limit cannot be set on %s: %d\n", + boe->vregs[1].supply, ret); + + vreg = boe->vregs[BF060Y8M_VREG_VCI].consumer; + ret = regulator_set_current_limit(vreg, 20000, 40000); + if (ret) + dev_dbg(dev, "Current limit cannot be set on %s: %d\n", + boe->vregs[2].supply, ret); + + return 0; +} + +static int boe_bf060y8m_aj0_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct boe_bf060y8m_aj0 *boe; + int ret; + + boe = devm_kzalloc(dev, sizeof(*boe), GFP_KERNEL); + if (!boe) + return -ENOMEM; + + ret = boe_bf060y8m_aj0_init_vregs(boe, dev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize supplies.\n"); + + boe->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + if (IS_ERR(boe->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(boe->reset_gpio), + "Failed to get reset-gpios\n"); + + boe->dsi = dsi; + mipi_dsi_set_drvdata(dsi, boe); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_LPM; + + drm_panel_init(&boe->panel, dev, &boe_bf060y8m_aj0_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + boe->panel.backlight = boe_bf060y8m_aj0_create_backlight(dsi); + if (IS_ERR(boe->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(boe->panel.backlight), + "Failed to create backlight\n"); + + drm_panel_add(&boe->panel); + + 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 boe_bf060y8m_aj0_remove(struct mipi_dsi_device *dsi) +{ + struct boe_bf060y8m_aj0 *boe = 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(&boe->panel); + + return 0; +} + +static const struct of_device_id boe_bf060y8m_aj0_of_match[] = { + { .compatible = "boe,bf060y8m-aj0" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, boe_bf060y8m_aj0_of_match); + +static struct mipi_dsi_driver boe_bf060y8m_aj0_driver = { + .probe = boe_bf060y8m_aj0_probe, + .remove = boe_bf060y8m_aj0_remove, + .driver = { + .name = "panel-sw43404-boe-fhd-amoled", + .of_match_table = boe_bf060y8m_aj0_of_match, + }, +}; +module_mipi_dsi_driver(boe_bf060y8m_aj0_driver); + +MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>"); +MODULE_DESCRIPTION("BOE BF060Y8M-AJ0 MIPI-DSI OLED panel"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-dsi-cm.c b/drivers/gpu/drm/panel/panel-dsi-cm.c index da4a69067e18..b58cb064975f 100644 --- a/drivers/gpu/drm/panel/panel-dsi-cm.c +++ b/drivers/gpu/drm/panel/panel-dsi-cm.c @@ -248,7 +248,7 @@ static ssize_t num_dsi_errors_show(struct device *dev, if (r) return r; - return snprintf(buf, PAGE_SIZE, "%d\n", errors); + return sysfs_emit(buf, "%d\n", errors); } static ssize_t hw_revision_show(struct device *dev, @@ -268,7 +268,7 @@ static ssize_t hw_revision_show(struct device *dev, if (r) return r; - return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); + return sysfs_emit(buf, "%02x.%02x.%02x\n", id1, id2, id3); } static DEVICE_ATTR_RO(num_dsi_errors); diff --git a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c index 2a602aee61c3..cb0bb3076099 100644 --- a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c +++ b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c @@ -456,16 +456,13 @@ static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi) ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies), ctx->supplies); - if (ret < 0) { - dev_err(&dsi->dev, "Couldn't get regulators\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&dsi->dev, ret, "Couldn't get regulators\n"); ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset)) { - dev_err(&dsi->dev, "Couldn't get our reset GPIO\n"); - return PTR_ERR(ctx->reset); - } + if (IS_ERR(ctx->reset)) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), + "Couldn't get our reset GPIO\n"); drm_panel_init(&ctx->panel, &dsi->dev, &k101_im2ba02_funcs, DRM_MODE_CONNECTOR_DSI); diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c index 581661b506f8..a9cd7135cb51 100644 --- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c +++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c @@ -200,22 +200,19 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi) DRM_MODE_CONNECTOR_DSI); ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd"); - if (IS_ERR(ctx->dvdd)) { - dev_err(&dsi->dev, "Couldn't get dvdd regulator\n"); - return PTR_ERR(ctx->dvdd); - } + if (IS_ERR(ctx->dvdd)) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->dvdd), + "Couldn't get dvdd regulator\n"); ctx->avdd = devm_regulator_get(&dsi->dev, "avdd"); - if (IS_ERR(ctx->avdd)) { - dev_err(&dsi->dev, "Couldn't get avdd regulator\n"); - return PTR_ERR(ctx->avdd); - } + if (IS_ERR(ctx->avdd)) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->avdd), + "Couldn't get avdd regulator\n"); ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset)) { - dev_err(&dsi->dev, "Couldn't get our reset GPIO\n"); - return PTR_ERR(ctx->reset); - } + if (IS_ERR(ctx->reset)) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), + "Couldn't get our reset GPIO\n"); ret = drm_panel_of_backlight(&ctx->panel); if (ret) @@ -227,7 +224,13 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = 4; - return mipi_dsi_attach(dsi); + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + drm_panel_remove(&ctx->panel); + return ret; + } + + return 0; } static int feiyang_dsi_remove(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 0145129d7c66..c12faef76282 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -42,6 +42,7 @@ struct ili9881c_desc { const struct ili9881c_instr *init; const size_t init_length; const struct drm_display_mode *mode; + const unsigned long mode_flags; }; struct ili9881c { @@ -453,6 +454,213 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0xD3, 0x3F), /* VN0 */ }; +static const struct ili9881c_instr w552946ab_init[] = { + ILI9881C_SWITCH_PAGE_INSTR(3), + ILI9881C_COMMAND_INSTR(0x01, 0x00), + ILI9881C_COMMAND_INSTR(0x02, 0x00), + ILI9881C_COMMAND_INSTR(0x03, 0x53), + ILI9881C_COMMAND_INSTR(0x04, 0x53), + ILI9881C_COMMAND_INSTR(0x05, 0x13), + ILI9881C_COMMAND_INSTR(0x06, 0x04), + ILI9881C_COMMAND_INSTR(0x07, 0x02), + ILI9881C_COMMAND_INSTR(0x08, 0x02), + ILI9881C_COMMAND_INSTR(0x09, 0x00), + ILI9881C_COMMAND_INSTR(0x0A, 0x00), + ILI9881C_COMMAND_INSTR(0x0B, 0x00), + ILI9881C_COMMAND_INSTR(0x0C, 0x00), + ILI9881C_COMMAND_INSTR(0x0D, 0x00), + ILI9881C_COMMAND_INSTR(0x0E, 0x00), + ILI9881C_COMMAND_INSTR(0x0F, 0x00), + + ILI9881C_COMMAND_INSTR(0x10, 0x00), + ILI9881C_COMMAND_INSTR(0x11, 0x00), + ILI9881C_COMMAND_INSTR(0x12, 0x00), + ILI9881C_COMMAND_INSTR(0x13, 0x00), + ILI9881C_COMMAND_INSTR(0x14, 0x00), + ILI9881C_COMMAND_INSTR(0x15, 0x08), + ILI9881C_COMMAND_INSTR(0x16, 0x10), + ILI9881C_COMMAND_INSTR(0x17, 0x00), + ILI9881C_COMMAND_INSTR(0x18, 0x08), + ILI9881C_COMMAND_INSTR(0x19, 0x00), + ILI9881C_COMMAND_INSTR(0x1A, 0x00), + ILI9881C_COMMAND_INSTR(0x1B, 0x00), + ILI9881C_COMMAND_INSTR(0x1C, 0x00), + ILI9881C_COMMAND_INSTR(0x1D, 0x00), + ILI9881C_COMMAND_INSTR(0x1E, 0xC0), + ILI9881C_COMMAND_INSTR(0x1F, 0x80), + + ILI9881C_COMMAND_INSTR(0x20, 0x02), + ILI9881C_COMMAND_INSTR(0x21, 0x09), + ILI9881C_COMMAND_INSTR(0x22, 0x00), + ILI9881C_COMMAND_INSTR(0x23, 0x00), + ILI9881C_COMMAND_INSTR(0x24, 0x00), + ILI9881C_COMMAND_INSTR(0x25, 0x00), + ILI9881C_COMMAND_INSTR(0x26, 0x00), + ILI9881C_COMMAND_INSTR(0x27, 0x00), + ILI9881C_COMMAND_INSTR(0x28, 0x55), + ILI9881C_COMMAND_INSTR(0x29, 0x03), + ILI9881C_COMMAND_INSTR(0x2A, 0x00), + ILI9881C_COMMAND_INSTR(0x2B, 0x00), + ILI9881C_COMMAND_INSTR(0x2C, 0x00), + ILI9881C_COMMAND_INSTR(0x2D, 0x00), + ILI9881C_COMMAND_INSTR(0x2E, 0x00), + ILI9881C_COMMAND_INSTR(0x2F, 0x00), + + ILI9881C_COMMAND_INSTR(0x30, 0x00), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x32, 0x00), + ILI9881C_COMMAND_INSTR(0x33, 0x00), + ILI9881C_COMMAND_INSTR(0x34, 0x04), + ILI9881C_COMMAND_INSTR(0x35, 0x05), + ILI9881C_COMMAND_INSTR(0x36, 0x05), + ILI9881C_COMMAND_INSTR(0x37, 0x00), + ILI9881C_COMMAND_INSTR(0x38, 0x3C), + ILI9881C_COMMAND_INSTR(0x39, 0x35), + ILI9881C_COMMAND_INSTR(0x3A, 0x00), + ILI9881C_COMMAND_INSTR(0x3B, 0x40), + ILI9881C_COMMAND_INSTR(0x3C, 0x00), + ILI9881C_COMMAND_INSTR(0x3D, 0x00), + ILI9881C_COMMAND_INSTR(0x3E, 0x00), + ILI9881C_COMMAND_INSTR(0x3F, 0x00), + + ILI9881C_COMMAND_INSTR(0x40, 0x00), + ILI9881C_COMMAND_INSTR(0x41, 0x88), + ILI9881C_COMMAND_INSTR(0x42, 0x00), + ILI9881C_COMMAND_INSTR(0x43, 0x00), + ILI9881C_COMMAND_INSTR(0x44, 0x1F), + + ILI9881C_COMMAND_INSTR(0x50, 0x01), + ILI9881C_COMMAND_INSTR(0x51, 0x23), + ILI9881C_COMMAND_INSTR(0x52, 0x45), + ILI9881C_COMMAND_INSTR(0x53, 0x67), + ILI9881C_COMMAND_INSTR(0x54, 0x89), + ILI9881C_COMMAND_INSTR(0x55, 0xaB), + ILI9881C_COMMAND_INSTR(0x56, 0x01), + ILI9881C_COMMAND_INSTR(0x57, 0x23), + ILI9881C_COMMAND_INSTR(0x58, 0x45), + ILI9881C_COMMAND_INSTR(0x59, 0x67), + ILI9881C_COMMAND_INSTR(0x5A, 0x89), + ILI9881C_COMMAND_INSTR(0x5B, 0xAB), + ILI9881C_COMMAND_INSTR(0x5C, 0xCD), + ILI9881C_COMMAND_INSTR(0x5D, 0xEF), + ILI9881C_COMMAND_INSTR(0x5E, 0x03), + ILI9881C_COMMAND_INSTR(0x5F, 0x14), + + ILI9881C_COMMAND_INSTR(0x60, 0x15), + ILI9881C_COMMAND_INSTR(0x61, 0x0C), + ILI9881C_COMMAND_INSTR(0x62, 0x0D), + ILI9881C_COMMAND_INSTR(0x63, 0x0E), + ILI9881C_COMMAND_INSTR(0x64, 0x0F), + ILI9881C_COMMAND_INSTR(0x65, 0x10), + ILI9881C_COMMAND_INSTR(0x66, 0x11), + ILI9881C_COMMAND_INSTR(0x67, 0x08), + ILI9881C_COMMAND_INSTR(0x68, 0x02), + ILI9881C_COMMAND_INSTR(0x69, 0x0A), + ILI9881C_COMMAND_INSTR(0x6A, 0x02), + ILI9881C_COMMAND_INSTR(0x6B, 0x02), + ILI9881C_COMMAND_INSTR(0x6C, 0x02), + ILI9881C_COMMAND_INSTR(0x6D, 0x02), + ILI9881C_COMMAND_INSTR(0x6E, 0x02), + ILI9881C_COMMAND_INSTR(0x6F, 0x02), + + ILI9881C_COMMAND_INSTR(0x70, 0x02), + ILI9881C_COMMAND_INSTR(0x71, 0x02), + ILI9881C_COMMAND_INSTR(0x72, 0x06), + ILI9881C_COMMAND_INSTR(0x73, 0x02), + ILI9881C_COMMAND_INSTR(0x74, 0x02), + ILI9881C_COMMAND_INSTR(0x75, 0x14), + ILI9881C_COMMAND_INSTR(0x76, 0x15), + ILI9881C_COMMAND_INSTR(0x77, 0x0F), + ILI9881C_COMMAND_INSTR(0x78, 0x0E), + ILI9881C_COMMAND_INSTR(0x79, 0x0D), + ILI9881C_COMMAND_INSTR(0x7A, 0x0C), + ILI9881C_COMMAND_INSTR(0x7B, 0x11), + ILI9881C_COMMAND_INSTR(0x7C, 0x10), + ILI9881C_COMMAND_INSTR(0x7D, 0x06), + ILI9881C_COMMAND_INSTR(0x7E, 0x02), + ILI9881C_COMMAND_INSTR(0x7F, 0x0A), + + ILI9881C_COMMAND_INSTR(0x80, 0x02), + ILI9881C_COMMAND_INSTR(0x81, 0x02), + ILI9881C_COMMAND_INSTR(0x82, 0x02), + ILI9881C_COMMAND_INSTR(0x83, 0x02), + ILI9881C_COMMAND_INSTR(0x84, 0x02), + ILI9881C_COMMAND_INSTR(0x85, 0x02), + ILI9881C_COMMAND_INSTR(0x86, 0x02), + ILI9881C_COMMAND_INSTR(0x87, 0x02), + ILI9881C_COMMAND_INSTR(0x88, 0x08), + ILI9881C_COMMAND_INSTR(0x89, 0x02), + ILI9881C_COMMAND_INSTR(0x8A, 0x02), + + ILI9881C_SWITCH_PAGE_INSTR(4), + ILI9881C_COMMAND_INSTR(0x00, 0x80), + ILI9881C_COMMAND_INSTR(0x70, 0x00), + ILI9881C_COMMAND_INSTR(0x71, 0x00), + ILI9881C_COMMAND_INSTR(0x66, 0xFE), + ILI9881C_COMMAND_INSTR(0x82, 0x15), + ILI9881C_COMMAND_INSTR(0x84, 0x15), + ILI9881C_COMMAND_INSTR(0x85, 0x15), + ILI9881C_COMMAND_INSTR(0x3a, 0x24), + ILI9881C_COMMAND_INSTR(0x32, 0xAC), + ILI9881C_COMMAND_INSTR(0x8C, 0x80), + ILI9881C_COMMAND_INSTR(0x3C, 0xF5), + ILI9881C_COMMAND_INSTR(0x88, 0x33), + + ILI9881C_SWITCH_PAGE_INSTR(1), + ILI9881C_COMMAND_INSTR(0x22, 0x0A), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x53, 0x78), + ILI9881C_COMMAND_INSTR(0x50, 0x5B), + ILI9881C_COMMAND_INSTR(0x51, 0x5B), + ILI9881C_COMMAND_INSTR(0x60, 0x20), + ILI9881C_COMMAND_INSTR(0x61, 0x00), + ILI9881C_COMMAND_INSTR(0x62, 0x0D), + ILI9881C_COMMAND_INSTR(0x63, 0x00), + + ILI9881C_COMMAND_INSTR(0xA0, 0x00), + ILI9881C_COMMAND_INSTR(0xA1, 0x10), + ILI9881C_COMMAND_INSTR(0xA2, 0x1C), + ILI9881C_COMMAND_INSTR(0xA3, 0x13), + ILI9881C_COMMAND_INSTR(0xA4, 0x15), + ILI9881C_COMMAND_INSTR(0xA5, 0x26), + ILI9881C_COMMAND_INSTR(0xA6, 0x1A), + ILI9881C_COMMAND_INSTR(0xA7, 0x1D), + ILI9881C_COMMAND_INSTR(0xA8, 0x67), + ILI9881C_COMMAND_INSTR(0xA9, 0x1C), + ILI9881C_COMMAND_INSTR(0xAA, 0x29), + ILI9881C_COMMAND_INSTR(0xAB, 0x5B), + ILI9881C_COMMAND_INSTR(0xAC, 0x26), + ILI9881C_COMMAND_INSTR(0xAD, 0x28), + ILI9881C_COMMAND_INSTR(0xAE, 0x5C), + ILI9881C_COMMAND_INSTR(0xAF, 0x30), + ILI9881C_COMMAND_INSTR(0xB0, 0x31), + ILI9881C_COMMAND_INSTR(0xB1, 0x2E), + ILI9881C_COMMAND_INSTR(0xB2, 0x32), + ILI9881C_COMMAND_INSTR(0xB3, 0x00), + + ILI9881C_COMMAND_INSTR(0xC0, 0x00), + ILI9881C_COMMAND_INSTR(0xC1, 0x10), + ILI9881C_COMMAND_INSTR(0xC2, 0x1C), + ILI9881C_COMMAND_INSTR(0xC3, 0x13), + ILI9881C_COMMAND_INSTR(0xC4, 0x15), + ILI9881C_COMMAND_INSTR(0xC5, 0x26), + ILI9881C_COMMAND_INSTR(0xC6, 0x1A), + ILI9881C_COMMAND_INSTR(0xC7, 0x1D), + ILI9881C_COMMAND_INSTR(0xC8, 0x67), + ILI9881C_COMMAND_INSTR(0xC9, 0x1C), + ILI9881C_COMMAND_INSTR(0xCA, 0x29), + ILI9881C_COMMAND_INSTR(0xCB, 0x5B), + ILI9881C_COMMAND_INSTR(0xCC, 0x26), + ILI9881C_COMMAND_INSTR(0xCD, 0x28), + ILI9881C_COMMAND_INSTR(0xCE, 0x5C), + ILI9881C_COMMAND_INSTR(0xCF, 0x30), + ILI9881C_COMMAND_INSTR(0xD0, 0x31), + ILI9881C_COMMAND_INSTR(0xD1, 0x2E), + ILI9881C_COMMAND_INSTR(0xD2, 0x32), + ILI9881C_COMMAND_INSTR(0xD3, 0x00), + ILI9881C_SWITCH_PAGE_INSTR(0), +}; + static inline struct ili9881c *panel_to_ili9881c(struct drm_panel *panel) { return container_of(panel, struct ili9881c, panel); @@ -603,6 +811,23 @@ static const struct drm_display_mode k101_im2byl02_default_mode = { .height_mm = 217, }; +static const struct drm_display_mode w552946aba_default_mode = { + .clock = 64000, + + .hdisplay = 720, + .hsync_start = 720 + 40, + .hsync_end = 720 + 40 + 10, + .htotal = 720 + 40 + 10 + 40, + + .vdisplay = 1280, + .vsync_start = 1280 + 22, + .vsync_end = 1280 + 22 + 4, + .vtotal = 1280 + 22 + 4 + 11, + + .width_mm = 68, + .height_mm = 121, +}; + static int ili9881c_get_modes(struct drm_panel *panel, struct drm_connector *connector) { @@ -653,16 +878,14 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) DRM_MODE_CONNECTOR_DSI); ctx->power = devm_regulator_get(&dsi->dev, "power"); - if (IS_ERR(ctx->power)) { - dev_err(&dsi->dev, "Couldn't get our power regulator\n"); - return PTR_ERR(ctx->power); - } + if (IS_ERR(ctx->power)) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power), + "Couldn't get our power regulator\n"); - ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset)) { - dev_err(&dsi->dev, "Couldn't get our reset GPIO\n"); - return PTR_ERR(ctx->reset); - } + ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset)) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), + "Couldn't get our reset GPIO\n"); ret = drm_panel_of_backlight(&ctx->panel); if (ret) @@ -670,7 +893,7 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) drm_panel_add(&ctx->panel); - dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + dsi->mode_flags = ctx->desc->mode_flags; dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = 4; @@ -691,17 +914,28 @@ static const struct ili9881c_desc lhr050h41_desc = { .init = lhr050h41_init, .init_length = ARRAY_SIZE(lhr050h41_init), .mode = &lhr050h41_default_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE, }; static const struct ili9881c_desc k101_im2byl02_desc = { .init = k101_im2byl02_init, .init_length = ARRAY_SIZE(k101_im2byl02_init), .mode = &k101_im2byl02_default_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE, +}; + +static const struct ili9881c_desc w552946aba_desc = { + .init = w552946ab_init, + .init_length = ARRAY_SIZE(w552946ab_init), + .mode = &w552946aba_default_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET, }; static const struct of_device_id ili9881c_of_match[] = { { .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc }, { .compatible = "feixin,k101-im2byl02", .data = &k101_im2byl02_desc }, + { .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc }, { } }; MODULE_DEVICE_TABLE(of, ili9881c_of_match); diff --git a/drivers/gpu/drm/panel/panel-innolux-ej030na.c b/drivers/gpu/drm/panel/panel-innolux-ej030na.c index 34b98f70bd22..c558de3f99be 100644 --- a/drivers/gpu/drm/panel/panel-innolux-ej030na.c +++ b/drivers/gpu/drm/panel/panel-innolux-ej030na.c @@ -198,16 +198,14 @@ static int ej030na_probe(struct spi_device *spi) return -EINVAL; priv->supply = devm_regulator_get(dev, "power"); - if (IS_ERR(priv->supply)) { - dev_err(dev, "Failed to get power supply\n"); - return PTR_ERR(priv->supply); - } + if (IS_ERR(priv->supply)) + return dev_err_probe(dev, PTR_ERR(priv->supply), + "Failed to get power supply\n"); priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(priv->reset_gpio)) { - dev_err(dev, "Failed to get reset GPIO\n"); - return PTR_ERR(priv->reset_gpio); - } + if (IS_ERR(priv->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), + "Failed to get reset GPIO\n"); drm_panel_init(&priv->panel, dev, &ej030na_funcs, DRM_MODE_CONNECTOR_DPI); diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index aea316225391..f194b62e290c 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -484,6 +484,7 @@ static void innolux_panel_del(struct innolux_panel *innolux) static int innolux_panel_probe(struct mipi_dsi_device *dsi) { const struct panel_desc *desc; + struct innolux_panel *innolux; int err; desc = of_device_get_match_data(&dsi->dev); @@ -495,7 +496,14 @@ static int innolux_panel_probe(struct mipi_dsi_device *dsi) if (err < 0) return err; - return mipi_dsi_attach(dsi); + err = mipi_dsi_attach(dsi); + if (err < 0) { + innolux = mipi_dsi_get_drvdata(dsi); + innolux_panel_del(innolux); + return err; + } + + return 0; } static int innolux_panel_remove(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c new file mode 100644 index 000000000000..31eafbc38ec0 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Raffaele Tranquillini <raffaele.tranquillini@gmail.com> + * + * Generated using linux-mdss-dsi-panel-driver-generator from Lineage OS device tree: + * https://github.com/LineageOS/android_kernel_xiaomi_msm8996/blob/lineage-18.1/arch/arm/boot/dts/qcom/a1-msm8996-mtp.dtsi + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +struct jdi_fhd_r63452 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct gpio_desc *reset_gpio; + bool prepared; +}; + +static inline struct jdi_fhd_r63452 *to_jdi_fhd_r63452(struct drm_panel *panel) +{ + return container_of(panel, struct jdi_fhd_r63452, panel); +} + +#define dsi_generic_write_seq(dsi, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +#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 jdi_fhd_r63452_reset(struct jdi_fhd_r63452 *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 11000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 11000); +} + +static int jdi_fhd_r63452_on(struct jdi_fhd_r63452 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + dsi_generic_write_seq(dsi, 0xb0, 0x00); + dsi_generic_write_seq(dsi, 0xd6, 0x01); + dsi_generic_write_seq(dsi, 0xec, + 0x64, 0xdc, 0xec, 0x3b, 0x52, 0x00, 0x0b, 0x0b, + 0x13, 0x15, 0x68, 0x0b, 0xb5); + dsi_generic_write_seq(dsi, 0xb0, 0x03); + + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret < 0) { + dev_err(dev, "Failed to set tear on: %d\n", ret); + return ret; + } + + dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00); + + ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77); + if (ret < 0) { + dev_err(dev, "Failed to set pixel format: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x0437); + if (ret < 0) { + dev_err(dev, "Failed to set column address: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077f); + if (ret < 0) { + dev_err(dev, "Failed to set page address: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x0000); + if (ret < 0) { + dev_err(dev, "Failed to set tear scanline: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff); + if (ret < 0) { + dev_err(dev, "Failed to set display brightness: %d\n", ret); + return ret; + } + + dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); + dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00); + dsi_dcs_write_seq(dsi, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, 0x00); + dsi_dcs_write_seq(dsi, 0x84, 0x00); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + msleep(20); + + 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(80); + + dsi_generic_write_seq(dsi, 0xb0, 0x04); + dsi_dcs_write_seq(dsi, 0x84, 0x00); + dsi_generic_write_seq(dsi, 0xc8, 0x11); + dsi_generic_write_seq(dsi, 0xb0, 0x03); + + return 0; +} + +static int jdi_fhd_r63452_off(struct jdi_fhd_r63452 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + dsi_generic_write_seq(dsi, 0xb0, 0x00); + dsi_generic_write_seq(dsi, 0xd6, 0x01); + dsi_generic_write_seq(dsi, 0xec, + 0x64, 0xdc, 0xec, 0x3b, 0x52, 0x00, 0x0b, 0x0b, + 0x13, 0x15, 0x68, 0x0b, 0x95); + dsi_generic_write_seq(dsi, 0xb0, 0x03); + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + return ret; + } + usleep_range(2000, 3000); + + 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 jdi_fhd_r63452_prepare(struct drm_panel *panel) +{ + struct jdi_fhd_r63452 *ctx = to_jdi_fhd_r63452(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + jdi_fhd_r63452_reset(ctx); + + ret = jdi_fhd_r63452_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + return ret; + } + + ctx->prepared = true; + return 0; +} + +static int jdi_fhd_r63452_unprepare(struct drm_panel *panel) +{ + struct jdi_fhd_r63452 *ctx = to_jdi_fhd_r63452(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = jdi_fhd_r63452_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode jdi_fhd_r63452_mode = { + .clock = (1080 + 120 + 16 + 40) * (1920 + 4 + 2 + 4) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 120, + .hsync_end = 1080 + 120 + 16, + .htotal = 1080 + 120 + 16 + 40, + .vdisplay = 1920, + .vsync_start = 1920 + 4, + .vsync_end = 1920 + 4 + 2, + .vtotal = 1920 + 4 + 2 + 4, + .width_mm = 64, + .height_mm = 114, +}; + +static int jdi_fhd_r63452_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &jdi_fhd_r63452_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 jdi_fhd_r63452_panel_funcs = { + .prepare = jdi_fhd_r63452_prepare, + .unprepare = jdi_fhd_r63452_unprepare, + .get_modes = jdi_fhd_r63452_get_modes, +}; + +static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct jdi_fhd_r63452 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + + drm_panel_init(&ctx->panel, dev, &jdi_fhd_r63452_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&ctx->panel); + + 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 jdi_fhd_r63452_remove(struct mipi_dsi_device *dsi) +{ + struct jdi_fhd_r63452 *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 jdi_fhd_r63452_of_match[] = { + { .compatible = "jdi,fhd-r63452" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jdi_fhd_r63452_of_match); + +static struct mipi_dsi_driver jdi_fhd_r63452_driver = { + .probe = jdi_fhd_r63452_probe, + .remove = jdi_fhd_r63452_remove, + .driver = { + .name = "panel-jdi-fhd-r63452", + .of_match_table = jdi_fhd_r63452_of_match, + }, +}; +module_mipi_dsi_driver(jdi_fhd_r63452_driver); + +MODULE_AUTHOR("Raffaele Tranquillini <raffaele.tranquillini@gmail.com>"); +MODULE_DESCRIPTION("DRM driver for JDI FHD R63452 DSI panel, command mode"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c index 733010b5e4f5..3c86ad262d5e 100644 --- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c @@ -473,7 +473,13 @@ static int jdi_panel_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - return mipi_dsi_attach(dsi); + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + jdi_panel_del(jdi); + return ret; + } + + return 0; } static int jdi_panel_remove(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c index 86e4213e8bb1..daccb1fd5fda 100644 --- a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c +++ b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c @@ -406,7 +406,13 @@ static int kingdisplay_panel_probe(struct mipi_dsi_device *dsi) if (err < 0) return err; - return mipi_dsi_attach(dsi); + err = mipi_dsi_attach(dsi); + if (err < 0) { + kingdisplay_panel_del(kingdisplay); + return err; + } + + return 0; } static int kingdisplay_panel_remove(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c index 59a8d99e777d..27a1c9923b09 100644 --- a/drivers/gpu/drm/panel/panel-lvds.c +++ b/drivers/gpu/drm/panel/panel-lvds.c @@ -20,6 +20,7 @@ #include <video/videomode.h> #include <drm/drm_crtc.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> struct panel_lvds { @@ -116,7 +117,6 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds) { struct device_node *np = lvds->dev->of_node; struct display_timing timing; - const char *mapping; int ret; ret = of_drm_get_panel_orientation(np, &lvds->orientation); @@ -149,24 +149,14 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds) of_property_read_string(np, "label", &lvds->label); - ret = of_property_read_string(np, "data-mapping", &mapping); + ret = drm_of_lvds_get_data_mapping(np); if (ret < 0) { dev_err(lvds->dev, "%pOF: invalid or missing %s DT property\n", np, "data-mapping"); - return -ENODEV; + return ret; } - if (!strcmp(mapping, "jeida-18")) { - lvds->bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG; - } else if (!strcmp(mapping, "jeida-24")) { - lvds->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA; - } else if (!strcmp(mapping, "vesa-24")) { - lvds->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG; - } else { - dev_err(lvds->dev, "%pOF: invalid or missing %s DT property\n", - np, "data-mapping"); - return -EINVAL; - } + lvds->bus_format = ret; lvds->data_mirror = of_property_read_bool(np, "data-mirror"); diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 30f28ad4df6b..31daae1da9c9 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -8,6 +8,7 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> +#include <linux/media-bus-format.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/regulator/consumer.h> @@ -220,6 +221,10 @@ static const struct drm_display_mode default_mode_ys = { .height_mm = 130, }; +static const u32 mantix_bus_formats[] = { + MEDIA_BUS_FMT_RGB888_1X24, +}; + static int mantix_get_modes(struct drm_panel *panel, struct drm_connector *connector) { @@ -241,6 +246,10 @@ static int mantix_get_modes(struct drm_panel *panel, connector->display_info.height_mm = mode->height_mm; drm_mode_probed_add(connector, mode); + drm_display_info_set_bus_formats(&connector->display_info, + mantix_bus_formats, + ARRAY_SIZE(mantix_bus_formats)); + return 1; } diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35950.c b/drivers/gpu/drm/panel/panel-novatek-nt35950.c new file mode 100644 index 000000000000..d42c7af0d75c --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt35950.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Novatek NT35950 DriverIC panels driver + * + * Copyright (c) 2021 AngeloGioacchino Del Regno + * <angelogioacchino.delregno@somainline.org> + */ +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define MCS_CMD_MAUCCTR 0xf0 /* Manufacturer command enable */ +#define MCS_PARAM_SCALER_FUNCTION 0x58 /* Scale-up function */ +#define MCS_PARAM_SCALEUP_MODE 0xc9 + #define MCS_SCALEUP_SIMPLE 0x0 + #define MCS_SCALEUP_BILINEAR BIT(0) + #define MCS_SCALEUP_DUPLICATE (BIT(0) | BIT(4)) + +/* VESA Display Stream Compression param */ +#define MCS_PARAM_VESA_DSC_ON 0x03 + +/* Data Compression mode */ +#define MCS_PARAM_DATA_COMPRESSION 0x90 + #define MCS_DATA_COMPRESSION_NONE 0x00 + #define MCS_DATA_COMPRESSION_FBC 0x02 + #define MCS_DATA_COMPRESSION_DSC 0x03 + +/* Display Output control */ +#define MCS_PARAM_DISP_OUTPUT_CTRL 0xb4 + #define MCS_DISP_OUT_SRAM_EN BIT(0) + #define MCS_DISP_OUT_VIDEO_MODE BIT(4) + +/* VESA Display Stream Compression setting */ +#define MCS_PARAM_VESA_DSC_SETTING 0xc0 + +/* SubPixel Rendering (SPR) */ +#define MCS_PARAM_SPR_EN 0xe3 +#define MCS_PARAM_SPR_MODE 0xef + #define MCS_SPR_MODE_YYG_RAINBOW_RGB 0x01 + +#define NT35950_VREG_MAX 4 + +struct nt35950 { + struct drm_panel panel; + struct drm_connector *connector; + struct mipi_dsi_device *dsi[2]; + struct regulator_bulk_data vregs[NT35950_VREG_MAX]; + struct gpio_desc *reset_gpio; + const struct nt35950_panel_desc *desc; + + int cur_mode; + u8 last_page; + bool prepared; +}; + +struct nt35950_panel_mode { + const struct drm_display_mode mode; + + bool enable_sram; + bool is_video_mode; + u8 scaler_on; + u8 scaler_mode; + u8 compression; + u8 spr_en; + u8 spr_mode; +}; + +struct nt35950_panel_desc { + const char *model_name; + const struct mipi_dsi_device_info dsi_info; + const struct nt35950_panel_mode *mode_data; + + bool is_dual_dsi; + u8 num_lanes; + u8 num_modes; +}; + +static inline struct nt35950 *to_nt35950(struct drm_panel *panel) +{ + return container_of(panel, struct nt35950, 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 nt35950_reset(struct nt35950 *nt) +{ + gpiod_set_value_cansleep(nt->reset_gpio, 1); + usleep_range(12000, 13000); + gpiod_set_value_cansleep(nt->reset_gpio, 0); + usleep_range(300, 400); + gpiod_set_value_cansleep(nt->reset_gpio, 1); + usleep_range(12000, 13000); +} + +/* + * nt35950_set_cmd2_page - Select manufacturer control (CMD2) page + * @nt: Main driver structure + * @page: Page number (0-7) + * + * Return: Number of transferred bytes or negative number on error + */ +static int nt35950_set_cmd2_page(struct nt35950 *nt, u8 page) +{ + const u8 mauc_cmd2_page[] = { MCS_CMD_MAUCCTR, 0x55, 0xaa, 0x52, + 0x08, page }; + int ret; + + ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], mauc_cmd2_page, + ARRAY_SIZE(mauc_cmd2_page)); + if (ret < 0) + return ret; + + nt->last_page = page; + return 0; +} + +/* + * nt35950_set_data_compression - Set data compression mode + * @nt: Main driver structure + * @comp_mode: Compression mode + * + * Return: Number of transferred bytes or negative number on error + */ +static int nt35950_set_data_compression(struct nt35950 *nt, u8 comp_mode) +{ + u8 cmd_data_compression[] = { MCS_PARAM_DATA_COMPRESSION, comp_mode }; + u8 cmd_vesa_dsc_on[] = { MCS_PARAM_VESA_DSC_ON, !!comp_mode }; + u8 cmd_vesa_dsc_setting[] = { MCS_PARAM_VESA_DSC_SETTING, 0x03 }; + u8 last_page = nt->last_page; + int ret; + + /* Set CMD2 Page 0 if we're not there yet */ + if (last_page != 0) { + ret = nt35950_set_cmd2_page(nt, 0); + if (ret < 0) + return ret; + } + + ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_data_compression, + ARRAY_SIZE(cmd_data_compression)); + if (ret < 0) + return ret; + + ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_on, + ARRAY_SIZE(cmd_vesa_dsc_on)); + if (ret < 0) + return ret; + + /* Set the vesa dsc setting on Page 4 */ + ret = nt35950_set_cmd2_page(nt, 4); + if (ret < 0) + return ret; + + /* Display Stream Compression setting, always 0x03 */ + ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_vesa_dsc_setting, + ARRAY_SIZE(cmd_vesa_dsc_setting)); + if (ret < 0) + return ret; + + /* Get back to the previously set page */ + return nt35950_set_cmd2_page(nt, last_page); +} + +/* + * nt35950_set_scaler - Enable/disable resolution upscaling + * @nt: Main driver structure + * @scale_up: Scale up function control + * + * Return: Number of transferred bytes or negative number on error + */ +static int nt35950_set_scaler(struct nt35950 *nt, u8 scale_up) +{ + u8 cmd_scaler[] = { MCS_PARAM_SCALER_FUNCTION, scale_up }; + + return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler, + ARRAY_SIZE(cmd_scaler)); +} + +/* + * nt35950_set_scale_mode - Resolution upscaling mode + * @nt: Main driver structure + * @mode: Scaler mode (MCS_DATA_COMPRESSION_*) + * + * Return: Number of transferred bytes or negative number on error + */ +static int nt35950_set_scale_mode(struct nt35950 *nt, u8 mode) +{ + u8 cmd_scaler[] = { MCS_PARAM_SCALEUP_MODE, mode }; + + return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_scaler, + ARRAY_SIZE(cmd_scaler)); +} + +/* + * nt35950_inject_black_image - Display a completely black image + * @nt: Main driver structure + * + * After IC setup, the attached panel may show random data + * due to driveric behavior changes (resolution, compression, + * scaling, etc). This function, called after parameters setup, + * makes the driver ic to output a completely black image to + * the display. + * It makes sense to push a black image before sending the sleep-out + * and display-on commands. + * + * Return: Number of transferred bytes or negative number on error + */ +static int nt35950_inject_black_image(struct nt35950 *nt) +{ + const u8 cmd0_black_img[] = { 0x6f, 0x01 }; + const u8 cmd1_black_img[] = { 0xf3, 0x10 }; + u8 cmd_test[] = { 0xff, 0xaa, 0x55, 0xa5, 0x80 }; + int ret; + + /* Enable test command */ + ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test)); + if (ret < 0) + return ret; + + /* Send a black image */ + ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd0_black_img, + ARRAY_SIZE(cmd0_black_img)); + if (ret < 0) + return ret; + ret = mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd1_black_img, + ARRAY_SIZE(cmd1_black_img)); + if (ret < 0) + return ret; + + /* Disable test command */ + cmd_test[ARRAY_SIZE(cmd_test) - 1] = 0x00; + return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_test, ARRAY_SIZE(cmd_test)); +} + +/* + * nt35950_set_dispout - Set Display Output register parameters + * @nt: Main driver structure + * + * Return: Number of transferred bytes or negative number on error + */ +static int nt35950_set_dispout(struct nt35950 *nt) +{ + u8 cmd_dispout[] = { MCS_PARAM_DISP_OUTPUT_CTRL, 0x00 }; + const struct nt35950_panel_mode *mode_data = nt->desc->mode_data; + + if (mode_data[nt->cur_mode].is_video_mode) + cmd_dispout[1] |= MCS_DISP_OUT_VIDEO_MODE; + if (mode_data[nt->cur_mode].enable_sram) + cmd_dispout[1] |= MCS_DISP_OUT_SRAM_EN; + + return mipi_dsi_dcs_write_buffer(nt->dsi[0], cmd_dispout, + ARRAY_SIZE(cmd_dispout)); +} + +static int nt35950_get_current_mode(struct nt35950 *nt) +{ + struct drm_connector *connector = nt->connector; + struct drm_crtc_state *crtc_state; + int i; + + /* Return the default (first) mode if no info available yet */ + if (!connector->state || !connector->state->crtc) + return 0; + + crtc_state = connector->state->crtc->state; + + for (i = 0; i < nt->desc->num_modes; i++) { + if (drm_mode_match(&crtc_state->mode, + &nt->desc->mode_data[i].mode, + DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK)) + return i; + } + + return 0; +} + +static int nt35950_on(struct nt35950 *nt) +{ + const struct nt35950_panel_mode *mode_data = nt->desc->mode_data; + struct mipi_dsi_device *dsi = nt->dsi[0]; + struct device *dev = &dsi->dev; + int ret; + + nt->cur_mode = nt35950_get_current_mode(nt); + nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM; + nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM; + + ret = nt35950_set_cmd2_page(nt, 0); + if (ret < 0) + return ret; + + ret = nt35950_set_data_compression(nt, mode_data[nt->cur_mode].compression); + if (ret < 0) + return ret; + + ret = nt35950_set_scale_mode(nt, mode_data[nt->cur_mode].scaler_mode); + if (ret < 0) + return ret; + + ret = nt35950_set_scaler(nt, mode_data[nt->cur_mode].scaler_on); + if (ret < 0) + return ret; + + ret = nt35950_set_dispout(nt); + if (ret < 0) + return ret; + + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret < 0) { + dev_err(dev, "Failed to set tear on: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0); + if (ret < 0) { + dev_err(dev, "Failed to set tear scanline: %d\n", ret); + return ret; + } + + /* CMD2 Page 1 */ + ret = nt35950_set_cmd2_page(nt, 1); + if (ret < 0) + return ret; + + /* Unknown command */ + dsi_dcs_write_seq(dsi, 0xd4, 0x88, 0x88); + + /* CMD2 Page 7 */ + ret = nt35950_set_cmd2_page(nt, 7); + if (ret < 0) + return ret; + + /* Enable SubPixel Rendering */ + dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_EN, 0x01); + + /* SPR Mode: YYG Rainbow-RGB */ + dsi_dcs_write_seq(dsi, MCS_PARAM_SPR_MODE, MCS_SPR_MODE_YYG_RAINBOW_RGB); + + /* CMD3 */ + ret = nt35950_inject_black_image(nt); + if (ret < 0) + return ret; + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) + return ret; + msleep(120); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) + return ret; + msleep(120); + + nt->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM; + nt->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM; + + return 0; +} + +static int nt35950_off(struct nt35950 *nt) +{ + struct device *dev = &nt->dsi[0]->dev; + int ret; + + ret = mipi_dsi_dcs_set_display_off(nt->dsi[0]); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + goto set_lpm; + } + usleep_range(10000, 11000); + + ret = mipi_dsi_dcs_enter_sleep_mode(nt->dsi[0]); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + goto set_lpm; + } + msleep(150); + +set_lpm: + nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM; + nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM; + + return 0; +} + +static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev) +{ + int ret; + + nt->vregs[0].supply = "vddio"; + nt->vregs[1].supply = "avdd"; + nt->vregs[2].supply = "avee"; + nt->vregs[3].supply = "dvdd"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(nt->vregs), + nt->vregs); + if (ret < 0) + return ret; + + ret = regulator_is_supported_voltage(nt->vregs[0].consumer, + 1750000, 1950000); + if (!ret) + return -EINVAL; + ret = regulator_is_supported_voltage(nt->vregs[1].consumer, + 5200000, 5900000); + if (!ret) + return -EINVAL; + /* AVEE is negative: -5.90V to -5.20V */ + ret = regulator_is_supported_voltage(nt->vregs[2].consumer, + 5200000, 5900000); + if (!ret) + return -EINVAL; + + ret = regulator_is_supported_voltage(nt->vregs[3].consumer, + 1300000, 1400000); + if (!ret) + return -EINVAL; + + return 0; +} + +static int nt35950_prepare(struct drm_panel *panel) +{ + struct nt35950 *nt = to_nt35950(panel); + struct device *dev = &nt->dsi[0]->dev; + int ret; + + if (nt->prepared) + return 0; + + ret = regulator_enable(nt->vregs[0].consumer); + if (ret) + return ret; + usleep_range(2000, 5000); + + ret = regulator_enable(nt->vregs[3].consumer); + if (ret) + goto end; + usleep_range(15000, 18000); + + ret = regulator_enable(nt->vregs[1].consumer); + if (ret) + goto end; + + ret = regulator_enable(nt->vregs[2].consumer); + if (ret) + goto end; + usleep_range(12000, 13000); + + nt35950_reset(nt); + + ret = nt35950_on(nt); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + goto end; + } + nt->prepared = true; + +end: + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs); + return ret; + } + + return 0; +} + +static int nt35950_unprepare(struct drm_panel *panel) +{ + struct nt35950 *nt = to_nt35950(panel); + struct device *dev = &nt->dsi[0]->dev; + int ret; + + if (!nt->prepared) + return 0; + + ret = nt35950_off(nt); + if (ret < 0) + dev_err(dev, "Failed to deinitialize panel: %d\n", ret); + + gpiod_set_value_cansleep(nt->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs); + + nt->prepared = false; + return 0; +} + +static int nt35950_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct nt35950 *nt = to_nt35950(panel); + int i; + + for (i = 0; i < nt->desc->num_modes; i++) { + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, + &nt->desc->mode_data[i].mode); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type |= DRM_MODE_TYPE_DRIVER; + if (nt->desc->num_modes == 1) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + } + + connector->display_info.bpc = 8; + connector->display_info.height_mm = nt->desc->mode_data[0].mode.height_mm; + connector->display_info.width_mm = nt->desc->mode_data[0].mode.width_mm; + nt->connector = connector; + + return nt->desc->num_modes; +} + +static const struct drm_panel_funcs nt35950_panel_funcs = { + .prepare = nt35950_prepare, + .unprepare = nt35950_unprepare, + .get_modes = nt35950_get_modes, +}; + +static int nt35950_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct device_node *dsi_r; + struct mipi_dsi_host *dsi_r_host; + struct nt35950 *nt; + const struct mipi_dsi_device_info *info; + int i, num_dsis = 1, ret; + + nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL); + if (!nt) + return -ENOMEM; + + ret = nt35950_sharp_init_vregs(nt, dev); + if (ret) + return dev_err_probe(dev, ret, "Regulator init failure.\n"); + + nt->desc = of_device_get_match_data(dev); + if (!nt->desc) + return -ENODEV; + + nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(nt->reset_gpio)) { + return dev_err_probe(dev, PTR_ERR(nt->reset_gpio), + "Failed to get reset gpio\n"); + } + + /* If the panel is connected on two DSIs then DSI0 left, DSI1 right */ + if (nt->desc->is_dual_dsi) { + info = &nt->desc->dsi_info; + dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); + if (!dsi_r) { + dev_err(dev, "Cannot get secondary DSI node.\n"); + return -ENODEV; + } + dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r); + of_node_put(dsi_r); + if (!dsi_r_host) { + dev_err(dev, "Cannot get secondary DSI host\n"); + return -EPROBE_DEFER; + } + + nt->dsi[1] = mipi_dsi_device_register_full(dsi_r_host, info); + if (!nt->dsi[1]) { + dev_err(dev, "Cannot get secondary DSI node\n"); + return -ENODEV; + } + num_dsis++; + } + + nt->dsi[0] = dsi; + mipi_dsi_set_drvdata(dsi, nt); + + drm_panel_init(&nt->panel, dev, &nt35950_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&nt->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&nt->panel); + + for (i = 0; i < num_dsis; i++) { + nt->dsi[i]->lanes = nt->desc->num_lanes; + nt->dsi[i]->format = MIPI_DSI_FMT_RGB888; + + nt->dsi[i]->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_LPM; + + if (nt->desc->mode_data[0].is_video_mode) + nt->dsi[i]->mode_flags |= MIPI_DSI_MODE_VIDEO; + + ret = mipi_dsi_attach(nt->dsi[i]); + if (ret < 0) { + return dev_err_probe(dev, ret, + "Cannot attach to DSI%d host.\n", i); + } + } + + /* Make sure to set RESX LOW before starting the power-on sequence */ + gpiod_set_value_cansleep(nt->reset_gpio, 0); + return 0; +} + +static int nt35950_remove(struct mipi_dsi_device *dsi) +{ + struct nt35950 *nt = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(nt->dsi[0]); + if (ret < 0) + dev_err(&dsi->dev, + "Failed to detach from DSI0 host: %d\n", ret); + + if (nt->dsi[1]) { + ret = mipi_dsi_detach(nt->dsi[1]); + if (ret < 0) + dev_err(&dsi->dev, + "Failed to detach from DSI1 host: %d\n", ret); + mipi_dsi_device_unregister(nt->dsi[1]); + }; + + drm_panel_remove(&nt->panel); + + return 0; +} + +static const struct nt35950_panel_mode sharp_ls055d1sx04_modes[] = { + { + /* 1920x1080 60Hz no compression */ + .mode = { + .clock = 214537, + .hdisplay = 1080, + .hsync_start = 1080 + 400, + .hsync_end = 1080 + 400 + 40, + .htotal = 1080 + 400 + 40 + 300, + .vdisplay = 1920, + .vsync_start = 1920 + 12, + .vsync_end = 1920 + 12 + 2, + .vtotal = 1920 + 12 + 2 + 10, + .width_mm = 68, + .height_mm = 121, + }, + .compression = MCS_DATA_COMPRESSION_NONE, + .enable_sram = true, + .is_video_mode = false, + .scaler_on = 1, + .scaler_mode = MCS_SCALEUP_DUPLICATE, + }, + /* TODO: Add 2160x3840 60Hz when DSC is supported */ +}; + +const struct nt35950_panel_desc sharp_ls055d1sx04 = { + .model_name = "Sharp LS055D1SX04", + .dsi_info = { + .type = "LS055D1SX04", + .channel = 0, + .node = NULL, + }, + .mode_data = sharp_ls055d1sx04_modes, + .num_modes = ARRAY_SIZE(sharp_ls055d1sx04_modes), + .is_dual_dsi = true, + .num_lanes = 4, +}; + +static const struct of_device_id nt35950_of_match[] = { + { .compatible = "sharp,ls055d1sx04", .data = &sharp_ls055d1sx04 }, + { } +}; +MODULE_DEVICE_TABLE(of, nt35950_of_match); + +static struct mipi_dsi_driver nt35950_driver = { + .probe = nt35950_probe, + .remove = nt35950_remove, + .driver = { + .name = "panel-novatek-nt35950", + .of_match_table = nt35950_of_match, + }, +}; +module_mipi_dsi_driver(nt35950_driver); + +MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>"); +MODULE_DESCRIPTION("Novatek NT35950 DriverIC panels driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c index 533cd3934b8b..231f371901e8 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c @@ -618,7 +618,7 @@ static int nt36672a_panel_add(struct nt36672a_panel *pinfo) ret = regulator_set_load(pinfo->supplies[i].consumer, nt36672a_regulator_enable_loads[i]); if (ret) - return dev_err_probe(dev, ret, "failed to set regulator enable loads\n"); + return dev_err_probe(dev, ret, "failed to set regulator enable loads\n"); } pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); @@ -656,7 +656,13 @@ static int nt36672a_panel_probe(struct mipi_dsi_device *dsi) if (err < 0) return err; - return mipi_dsi_attach(dsi); + err = mipi_dsi_attach(dsi); + if (err < 0) { + drm_panel_remove(&pinfo->base); + return err; + } + + return 0; } static int nt36672a_panel_remove(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt39016.c b/drivers/gpu/drm/panel/panel-novatek-nt39016.c index f8151fe3ac9a..d036853db865 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt39016.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt39016.c @@ -258,16 +258,13 @@ static int nt39016_probe(struct spi_device *spi) return -EINVAL; panel->supply = devm_regulator_get(dev, "power"); - if (IS_ERR(panel->supply)) { - dev_err(dev, "Failed to get power supply\n"); - return PTR_ERR(panel->supply); - } + if (IS_ERR(panel->supply)) + return dev_err_probe(dev, PTR_ERR(panel->supply), + "Failed to get power supply\n"); panel->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(panel->reset_gpio)) { - dev_err(dev, "Failed to get reset GPIO\n"); - return PTR_ERR(panel->reset_gpio); - } + if (IS_ERR(panel->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(panel->reset_gpio), "Failed to get reset GPIO\n"); spi->bits_per_word = 8; spi->mode = SPI_MODE_3 | SPI_3WIRE; @@ -287,11 +284,8 @@ static int nt39016_probe(struct spi_device *spi) DRM_MODE_CONNECTOR_DPI); err = drm_panel_of_backlight(&panel->drm_panel); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get backlight handle\n"); - return err; - } + if (err) + return dev_err_probe(dev, err, "Failed to get backlight handle\n"); drm_panel_add(&panel->drm_panel); diff --git a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c index 3c20beeb1781..3991f5d950af 100644 --- a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c +++ b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c @@ -241,7 +241,13 @@ static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - return mipi_dsi_attach(dsi); + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + wuxga_nt_panel_del(wuxga_nt); + return ret; + } + + return 0; } static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c index a3782830ae3c..1fb579a574d9 100644 --- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c +++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c @@ -199,7 +199,13 @@ static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = 4; - return mipi_dsi_attach(dsi); + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + drm_panel_remove(&ctx->panel); + return ret; + } + + return 0; } static int rb070d30_panel_dsi_remove(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c index ccc8ed6fe3ae..e38262b67ff7 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c @@ -452,27 +452,22 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi) 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; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get regulators\n"); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset_gpio)) { - dev_err(dev, "cannot get reset-gpio: %ld\n", - PTR_ERR(ctx->reset_gpio)); - return PTR_ERR(ctx->reset_gpio); - } + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "cannot get reset-gpio\n"); drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs, DRM_MODE_CONNECTOR_DSI); ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx, &s6e63j0x03_bl_ops, NULL); - if (IS_ERR(ctx->bl_dev)) { - dev_err(dev, "failed to register backlight device\n"); - return PTR_ERR(ctx->bl_dev); - } + if (IS_ERR(ctx->bl_dev)) + return dev_err_probe(dev, PTR_ERR(ctx->bl_dev), + "failed to register backlight device\n"); ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS; ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS; diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c index e0b1a7e354f3..e0f773678168 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c @@ -116,7 +116,8 @@ static int s6e63m0_dsi_probe(struct mipi_dsi_device *dsi) static int s6e63m0_dsi_remove(struct mipi_dsi_device *dsi) { mipi_dsi_detach(dsi); - return s6e63m0_remove(&dsi->dev); + s6e63m0_remove(&dsi->dev); + return 0; } static const struct of_device_id s6e63m0_dsi_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c index 3669cc3719ce..c178d962b0d5 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c @@ -64,7 +64,8 @@ static int s6e63m0_spi_probe(struct spi_device *spi) static int s6e63m0_spi_remove(struct spi_device *spi) { - return s6e63m0_remove(&spi->dev); + s6e63m0_remove(&spi->dev); + return 0; } static const struct of_device_id s6e63m0_spi_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c index 35d72ac663d6..b34fa4d5de07 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c @@ -749,13 +749,11 @@ int s6e63m0_probe(struct device *dev, void *trsp, } EXPORT_SYMBOL_GPL(s6e63m0_probe); -int s6e63m0_remove(struct device *dev) +void s6e63m0_remove(struct device *dev) { struct s6e63m0 *ctx = dev_get_drvdata(dev); drm_panel_remove(&ctx->panel); - - return 0; } EXPORT_SYMBOL_GPL(s6e63m0_remove); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h index 306605ed1117..c926eca1c817 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h @@ -35,6 +35,6 @@ int s6e63m0_probe(struct device *dev, void *trsp, const u8 *data, size_t len), bool dsi_mode); -int s6e63m0_remove(struct device *dev); +void s6e63m0_remove(struct device *dev); #endif /* _PANEL_SAMSUNG_S6E63M0_H */ diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c index ea63799ff2a1..29fde3823212 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c @@ -247,6 +247,7 @@ static int s6e88a0_ams452ef01_probe(struct mipi_dsi_device *dsi) ret = mipi_dsi_attach(dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + drm_panel_remove(&ctx->panel); return ret; } diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c index 8cb1853574bb..1fb37fda4ba9 100644 --- a/drivers/gpu/drm/panel/panel-samsung-sofef00.c +++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c @@ -270,18 +270,14 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi) } ctx->supply = devm_regulator_get(dev, "vddio"); - if (IS_ERR(ctx->supply)) { - ret = PTR_ERR(ctx->supply); - dev_err(dev, "Failed to get vddio regulator: %d\n", ret); - return ret; - } + if (IS_ERR(ctx->supply)) + return dev_err_probe(dev, PTR_ERR(ctx->supply), + "Failed to get vddio regulator\n"); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(ctx->reset_gpio)) { - ret = PTR_ERR(ctx->reset_gpio); - dev_warn(dev, "Failed to get reset-gpios: %d\n", ret); - return ret; - } + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); ctx->dsi = dsi; mipi_dsi_set_drvdata(dsi, ctx); @@ -302,6 +298,7 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi) ret = mipi_dsi_attach(dsi); if (ret < 0) { dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + drm_panel_remove(&ctx->panel); return ret; } diff --git a/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c index 94992f45113a..a07d0f6c3e69 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c @@ -146,22 +146,19 @@ static int ls037v7dw01_probe(struct platform_device *pdev) lcd->pdev = pdev; lcd->vdd = devm_regulator_get(&pdev->dev, "envdd"); - if (IS_ERR(lcd->vdd)) { - dev_err(&pdev->dev, "failed to get regulator\n"); - return PTR_ERR(lcd->vdd); - } + if (IS_ERR(lcd->vdd)) + return dev_err_probe(&pdev->dev, PTR_ERR(lcd->vdd), + "failed to get regulator\n"); lcd->ini_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(lcd->ini_gpio)) { - dev_err(&pdev->dev, "failed to get enable gpio\n"); - return PTR_ERR(lcd->ini_gpio); - } + if (IS_ERR(lcd->ini_gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(lcd->ini_gpio), + "failed to get enable gpio\n"); lcd->resb_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(lcd->resb_gpio)) { - dev_err(&pdev->dev, "failed to get reset gpio\n"); - return PTR_ERR(lcd->resb_gpio); - } + if (IS_ERR(lcd->resb_gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(lcd->resb_gpio), + "failed to get reset gpio\n"); lcd->mo_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 0, GPIOD_OUT_LOW); diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c index b937e24dac8e..25829a0a8e80 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c @@ -296,7 +296,13 @@ static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - return mipi_dsi_attach(dsi); + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + sharp_nt_panel_del(sharp_nt); + return ret; + } + + return 0; } static int sharp_nt_panel_remove(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c new file mode 100644 index 000000000000..e12570561629 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2021 Linaro Ltd. + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + */ + +#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 sharp_ls060 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator *vddi_supply; + struct regulator *vddh_supply; + struct regulator *avdd_supply; + struct regulator *avee_supply; + struct gpio_desc *reset_gpio; + bool prepared; +}; + +static inline struct sharp_ls060 *to_sharp_ls060(struct drm_panel *panel) +{ + return container_of(panel, struct sharp_ls060, panel); +} + +#define dsi_dcs_write_seq(dsi, seq...) ({ \ + static const u8 d[] = { seq }; \ + \ + mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \ + }) + +static void sharp_ls060_reset(struct sharp_ls060 *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 11000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 11000); +} + +static int sharp_ls060_on(struct sharp_ls060 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + ret = dsi_dcs_write_seq(dsi, 0xbb, 0x13); + if (ret < 0) { + dev_err(dev, "Failed to send command: %d\n", ret); + return ret; + } + + ret = dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START); + if (ret < 0) { + dev_err(dev, "Failed to send command: %d\n", ret); + return ret; + } + + 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); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + msleep(50); + + return 0; +} + +static int sharp_ls060_off(struct sharp_ls060 *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; + } + usleep_range(2000, 3000); + + 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(121); + + return 0; +} + +static int sharp_ls060_prepare(struct drm_panel *panel) +{ + struct sharp_ls060 *ctx = to_sharp_ls060(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_enable(ctx->vddi_supply); + if (ret < 0) + return ret; + + ret = regulator_enable(ctx->avdd_supply); + if (ret < 0) + goto err_avdd; + + usleep_range(1000, 2000); + + ret = regulator_enable(ctx->avee_supply); + if (ret < 0) + goto err_avee; + + usleep_range(10000, 11000); + + ret = regulator_enable(ctx->vddh_supply); + if (ret < 0) + goto err_vddh; + + usleep_range(10000, 11000); + + sharp_ls060_reset(ctx); + + ret = sharp_ls060_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + goto err_on; + } + + ctx->prepared = true; + + return 0; + +err_on: + regulator_disable(ctx->vddh_supply); + + usleep_range(10000, 11000); + +err_vddh: + regulator_disable(ctx->avee_supply); + +err_avee: + regulator_disable(ctx->avdd_supply); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + +err_avdd: + regulator_disable(ctx->vddi_supply); + + return ret; +} + +static int sharp_ls060_unprepare(struct drm_panel *panel) +{ + struct sharp_ls060 *ctx = to_sharp_ls060(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = sharp_ls060_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + regulator_disable(ctx->vddh_supply); + + usleep_range(10000, 11000); + + regulator_disable(ctx->avee_supply); + regulator_disable(ctx->avdd_supply); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + + regulator_disable(ctx->vddi_supply); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode sharp_ls060_mode = { + .clock = (1080 + 96 + 16 + 64) * (1920 + 4 + 1 + 16) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 96, + .hsync_end = 1080 + 96 + 16, + .htotal = 1080 + 96 + 16 + 64, + .vdisplay = 1920, + .vsync_start = 1920 + 4, + .vsync_end = 1920 + 4 + 1, + .vtotal = 1920 + 4 + 1 + 16, + .width_mm = 75, + .height_mm = 132, +}; + +static int sharp_ls060_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &sharp_ls060_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 sharp_ls060_panel_funcs = { + .prepare = sharp_ls060_prepare, + .unprepare = sharp_ls060_unprepare, + .get_modes = sharp_ls060_get_modes, +}; + +static int sharp_ls060_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct sharp_ls060 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->vddi_supply = devm_regulator_get(dev, "vddi"); + if (IS_ERR(ctx->vddi_supply)) + return PTR_ERR(ctx->vddi_supply); + + ctx->vddh_supply = devm_regulator_get(dev, "vddh"); + if (IS_ERR(ctx->vddh_supply)) + return PTR_ERR(ctx->vddh_supply); + + ctx->avdd_supply = devm_regulator_get(dev, "avdd"); + if (IS_ERR(ctx->avdd_supply)) + return PTR_ERR(ctx->avdd_supply); + + ctx->avee_supply = devm_regulator_get(dev, "avee"); + if (IS_ERR(ctx->avee_supply)) + return PTR_ERR(ctx->avee_supply); + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + + drm_panel_init(&ctx->panel, dev, &sharp_ls060_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + drm_panel_remove(&ctx->panel); + return ret; + } + + return 0; +} + +static int sharp_ls060_remove(struct mipi_dsi_device *dsi) +{ + struct sharp_ls060 *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 sharp_ls060t1sx01_of_match[] = { + { .compatible = "sharp,ls060t1sx01" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sharp_ls060t1sx01_of_match); + +static struct mipi_dsi_driver sharp_ls060_driver = { + .probe = sharp_ls060_probe, + .remove = sharp_ls060_remove, + .driver = { + .name = "panel-sharp-ls060t1sx01", + .of_match_table = sharp_ls060t1sx01_of_match, + }, +}; +module_mipi_dsi_driver(sharp_ls060_driver); + +MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>"); +MODULE_DESCRIPTION("DRM driver for Sharp LS060T1SX01 1080p video mode dsi panel"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 7f3e1b84b5f5..dde033066f3d 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2031,6 +2031,31 @@ static const struct panel_desc innolux_g070y2_l01 = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct drm_display_mode innolux_g070y2_t02_mode = { + .clock = 33333, + .hdisplay = 800, + .hsync_start = 800 + 210, + .hsync_end = 800 + 210 + 20, + .htotal = 800 + 210 + 20 + 46, + .vdisplay = 480, + .vsync_start = 480 + 22, + .vsync_end = 480 + 22 + 10, + .vtotal = 480 + 22 + 23 + 10, +}; + +static const struct panel_desc innolux_g070y2_t02 = { + .modes = &innolux_g070y2_t02_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 152, + .height = 92, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct display_timing innolux_g101ice_l01_timing = { .pixelclock = { 60400000, 71100000, 74700000 }, .hactive = { 1280, 1280, 1280 }, @@ -2370,6 +2395,38 @@ static const struct panel_desc logictechno_lt170410_2whc = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct drm_display_mode logictechno_lttd800480070_l2rt_mode = { + .clock = 33000, + .hdisplay = 800, + .hsync_start = 800 + 112, + .hsync_end = 800 + 112 + 3, + .htotal = 800 + 112 + 3 + 85, + .vdisplay = 480, + .vsync_start = 480 + 38, + .vsync_end = 480 + 38 + 3, + .vtotal = 480 + 38 + 3 + 29, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc logictechno_lttd800480070_l2rt = { + .modes = &logictechno_lttd800480070_l2rt_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 154, + .height = 86, + }, + .delay = { + .prepare = 45, + .enable = 100, + .disable = 100, + .unprepare = 45 + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct drm_display_mode logictechno_lttd800480070_l6wh_rt_mode = { .clock = 33000, .hdisplay = 800, @@ -3441,6 +3498,31 @@ static const struct panel_desc urt_umsh_8596md_parallel = { .bus_format = MEDIA_BUS_FMT_RGB666_1X18, }; +static const struct drm_display_mode vivax_tpc9150_panel_mode = { + .clock = 60000, + .hdisplay = 1024, + .hsync_start = 1024 + 160, + .hsync_end = 1024 + 160 + 100, + .htotal = 1024 + 160 + 100 + 60, + .vdisplay = 600, + .vsync_start = 600 + 12, + .vsync_end = 600 + 12 + 10, + .vtotal = 600 + 12 + 10 + 13, +}; + +static const struct panel_desc vivax_tpc9150_panel = { + .modes = &vivax_tpc9150_panel_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 200, + .height = 115, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode vl050_8048nt_c01_mode = { .clock = 33333, .hdisplay = 800, @@ -3706,6 +3788,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "innolux,g070y2-l01", .data = &innolux_g070y2_l01, }, { + .compatible = "innolux,g070y2-t02", + .data = &innolux_g070y2_t02, + }, { .compatible = "innolux,g101ice-l01", .data = &innolux_g101ice_l01 }, { @@ -3751,6 +3836,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "logictechno,lt170410-2whc", .data = &logictechno_lt170410_2whc, }, { + .compatible = "logictechno,lttd800480070-l2rt", + .data = &logictechno_lttd800480070_l2rt, + }, { .compatible = "logictechno,lttd800480070-l6wh-rt", .data = &logictechno_lttd800480070_l6wh_rt, }, { @@ -3886,6 +3974,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "urt,umsh-8596md-20t", .data = &urt_umsh_8596md_parallel, }, { + .compatible = "vivax,tpc9150-panel", + .data = &vivax_tpc9150_panel, + }, { .compatible = "vxt,vl050-8048nt-c01", .data = &vl050_8048nt_c01, }, { diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c index a2c303e5732c..73f69c929a75 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c @@ -453,6 +453,10 @@ disable_vcc: return ret; } +static const u32 mantix_bus_formats[] = { + MEDIA_BUS_FMT_RGB888_1X24, +}; + static int st7703_get_modes(struct drm_panel *panel, struct drm_connector *connector) { @@ -474,6 +478,10 @@ static int st7703_get_modes(struct drm_panel *panel, connector->display_info.height_mm = mode->height_mm; drm_mode_probed_add(connector, mode); + drm_display_info_set_bus_formats(&connector->display_info, + mantix_bus_formats, + ARRAY_SIZE(mantix_bus_formats)); + return 1; } diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c new file mode 100644 index 000000000000..69f07b15fca4 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, Linaro Limited + * + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +struct truly_nt35521 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data supplies[2]; + struct gpio_desc *reset_gpio; + struct gpio_desc *blen_gpio; + bool prepared; + bool enabled; +}; + +static inline +struct truly_nt35521 *to_truly_nt35521(struct drm_panel *panel) +{ + return container_of(panel, struct truly_nt35521, panel); +} + +#define dsi_generic_write_seq(dsi, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static void truly_nt35521_reset(struct truly_nt35521 *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + msleep(150); +} + +static int truly_nt35521_on(struct truly_nt35521 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00); + dsi_generic_write_seq(dsi, 0xff, 0xaa, 0x55, 0xa5, 0x80); + dsi_generic_write_seq(dsi, 0x6f, 0x11, 0x00); + dsi_generic_write_seq(dsi, 0xf7, 0x20, 0x00); + dsi_generic_write_seq(dsi, 0x6f, 0x01); + dsi_generic_write_seq(dsi, 0xb1, 0x21); + dsi_generic_write_seq(dsi, 0xbd, 0x01, 0xa0, 0x10, 0x08, 0x01); + dsi_generic_write_seq(dsi, 0xb8, 0x01, 0x02, 0x0c, 0x02); + dsi_generic_write_seq(dsi, 0xbb, 0x11, 0x11); + dsi_generic_write_seq(dsi, 0xbc, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xb6, 0x02); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x01); + dsi_generic_write_seq(dsi, 0xb0, 0x09, 0x09); + dsi_generic_write_seq(dsi, 0xb1, 0x09, 0x09); + dsi_generic_write_seq(dsi, 0xbc, 0x8c, 0x00); + dsi_generic_write_seq(dsi, 0xbd, 0x8c, 0x00); + dsi_generic_write_seq(dsi, 0xca, 0x00); + dsi_generic_write_seq(dsi, 0xc0, 0x04); + dsi_generic_write_seq(dsi, 0xbe, 0xb5); + dsi_generic_write_seq(dsi, 0xb3, 0x35, 0x35); + dsi_generic_write_seq(dsi, 0xb4, 0x25, 0x25); + dsi_generic_write_seq(dsi, 0xb9, 0x43, 0x43); + dsi_generic_write_seq(dsi, 0xba, 0x24, 0x24); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x02); + dsi_generic_write_seq(dsi, 0xee, 0x03); + dsi_generic_write_seq(dsi, 0xb0, + 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xc3, + 0x00, 0xce, 0x00, 0xe1, 0x00, 0xf3, 0x01, 0x11); + dsi_generic_write_seq(dsi, 0xb1, + 0x01, 0x2e, 0x01, 0x5c, 0x01, 0x82, 0x01, 0xc3, + 0x01, 0xfe, 0x02, 0x00, 0x02, 0x37, 0x02, 0x77); + dsi_generic_write_seq(dsi, 0xb2, + 0x02, 0xa1, 0x02, 0xd7, 0x02, 0xfe, 0x03, 0x2c, + 0x03, 0x4b, 0x03, 0x63, 0x03, 0x8f, 0x03, 0x90); + dsi_generic_write_seq(dsi, 0xb3, 0x03, 0x96, 0x03, 0x98); + dsi_generic_write_seq(dsi, 0xb4, + 0x00, 0x81, 0x00, 0x8b, 0x00, 0x9c, 0x00, 0xa9, + 0x00, 0xb5, 0x00, 0xcb, 0x00, 0xdf, 0x01, 0x02); + dsi_generic_write_seq(dsi, 0xb5, + 0x01, 0x1f, 0x01, 0x51, 0x01, 0x7a, 0x01, 0xbf, + 0x01, 0xfa, 0x01, 0xfc, 0x02, 0x34, 0x02, 0x76); + dsi_generic_write_seq(dsi, 0xb6, + 0x02, 0x9f, 0x02, 0xd7, 0x02, 0xfc, 0x03, 0x2c, + 0x03, 0x4a, 0x03, 0x63, 0x03, 0x8f, 0x03, 0xa2); + dsi_generic_write_seq(dsi, 0xb7, 0x03, 0xb8, 0x03, 0xba); + dsi_generic_write_seq(dsi, 0xb8, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x2a, + 0x00, 0x41, 0x00, 0x67, 0x00, 0x87, 0x00, 0xb9); + dsi_generic_write_seq(dsi, 0xb9, + 0x00, 0xe2, 0x01, 0x22, 0x01, 0x54, 0x01, 0xa3, + 0x01, 0xe6, 0x01, 0xe7, 0x02, 0x24, 0x02, 0x67); + dsi_generic_write_seq(dsi, 0xba, + 0x02, 0x93, 0x02, 0xcd, 0x02, 0xf6, 0x03, 0x31, + 0x03, 0x6c, 0x03, 0xe9, 0x03, 0xef, 0x03, 0xf4); + dsi_generic_write_seq(dsi, 0xbb, 0x03, 0xf6, 0x03, 0xf7); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x03); + dsi_generic_write_seq(dsi, 0xb0, 0x22, 0x00); + dsi_generic_write_seq(dsi, 0xb1, 0x22, 0x00); + dsi_generic_write_seq(dsi, 0xb2, 0x05, 0x00, 0x60, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xb3, 0x05, 0x00, 0x60, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xb4, 0x05, 0x00, 0x60, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xb5, 0x05, 0x00, 0x60, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xba, 0x53, 0x00, 0x60, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xbb, 0x53, 0x00, 0x60, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xbc, 0x53, 0x00, 0x60, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xbd, 0x53, 0x00, 0x60, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xc0, 0x00, 0x34, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xc1, 0x00, 0x00, 0x34, 0x00); + dsi_generic_write_seq(dsi, 0xc2, 0x00, 0x00, 0x34, 0x00); + dsi_generic_write_seq(dsi, 0xc3, 0x00, 0x00, 0x34, 0x00); + dsi_generic_write_seq(dsi, 0xc4, 0x60); + dsi_generic_write_seq(dsi, 0xc5, 0xc0); + dsi_generic_write_seq(dsi, 0xc6, 0x00); + dsi_generic_write_seq(dsi, 0xc7, 0x00); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x05); + dsi_generic_write_seq(dsi, 0xb0, 0x17, 0x06); + dsi_generic_write_seq(dsi, 0xb1, 0x17, 0x06); + dsi_generic_write_seq(dsi, 0xb2, 0x17, 0x06); + dsi_generic_write_seq(dsi, 0xb3, 0x17, 0x06); + dsi_generic_write_seq(dsi, 0xb4, 0x17, 0x06); + dsi_generic_write_seq(dsi, 0xb5, 0x17, 0x06); + dsi_generic_write_seq(dsi, 0xb6, 0x17, 0x06); + dsi_generic_write_seq(dsi, 0xb7, 0x17, 0x06); + dsi_generic_write_seq(dsi, 0xb8, 0x00); + dsi_generic_write_seq(dsi, 0xb9, 0x00, 0x03); + dsi_generic_write_seq(dsi, 0xba, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xbb, 0x02, 0x03); + dsi_generic_write_seq(dsi, 0xbc, 0x02, 0x03); + dsi_generic_write_seq(dsi, 0xbd, 0x03, 0x03, 0x00, 0x03, 0x03); + dsi_generic_write_seq(dsi, 0xc0, 0x0b); + dsi_generic_write_seq(dsi, 0xc1, 0x09); + dsi_generic_write_seq(dsi, 0xc2, 0xa6); + dsi_generic_write_seq(dsi, 0xc3, 0x05); + dsi_generic_write_seq(dsi, 0xc4, 0x00); + dsi_generic_write_seq(dsi, 0xc5, 0x02); + dsi_generic_write_seq(dsi, 0xc6, 0x22); + dsi_generic_write_seq(dsi, 0xc7, 0x03); + dsi_generic_write_seq(dsi, 0xc8, 0x07, 0x20); + dsi_generic_write_seq(dsi, 0xc9, 0x03, 0x20); + dsi_generic_write_seq(dsi, 0xca, 0x01, 0x60); + dsi_generic_write_seq(dsi, 0xcb, 0x01, 0x60); + dsi_generic_write_seq(dsi, 0xcc, 0x00, 0x00, 0x02); + dsi_generic_write_seq(dsi, 0xcd, 0x00, 0x00, 0x02); + dsi_generic_write_seq(dsi, 0xce, 0x00, 0x00, 0x02); + dsi_generic_write_seq(dsi, 0xcf, 0x00, 0x00, 0x02); + dsi_generic_write_seq(dsi, 0xd1, 0x00, 0x05, 0x01, 0x07, 0x10); + dsi_generic_write_seq(dsi, 0xd2, 0x10, 0x05, 0x05, 0x03, 0x10); + dsi_generic_write_seq(dsi, 0xd3, 0x20, 0x00, 0x43, 0x07, 0x10); + dsi_generic_write_seq(dsi, 0xd4, 0x30, 0x00, 0x43, 0x07, 0x10); + dsi_generic_write_seq(dsi, 0xd0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xd5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xd6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xd7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xe5, 0x06); + dsi_generic_write_seq(dsi, 0xe6, 0x06); + dsi_generic_write_seq(dsi, 0xe7, 0x00); + dsi_generic_write_seq(dsi, 0xe8, 0x06); + dsi_generic_write_seq(dsi, 0xe9, 0x06); + dsi_generic_write_seq(dsi, 0xea, 0x06); + dsi_generic_write_seq(dsi, 0xeb, 0x00); + dsi_generic_write_seq(dsi, 0xec, 0x00); + dsi_generic_write_seq(dsi, 0xed, 0x30); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x06); + dsi_generic_write_seq(dsi, 0xb0, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xb1, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xb2, 0x2d, 0x2e); + dsi_generic_write_seq(dsi, 0xb3, 0x31, 0x34); + dsi_generic_write_seq(dsi, 0xb4, 0x29, 0x2a); + dsi_generic_write_seq(dsi, 0xb5, 0x12, 0x10); + dsi_generic_write_seq(dsi, 0xb6, 0x18, 0x16); + dsi_generic_write_seq(dsi, 0xb7, 0x00, 0x02); + dsi_generic_write_seq(dsi, 0xb8, 0x08, 0x31); + dsi_generic_write_seq(dsi, 0xb9, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xba, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xbb, 0x31, 0x08); + dsi_generic_write_seq(dsi, 0xbc, 0x03, 0x01); + dsi_generic_write_seq(dsi, 0xbd, 0x17, 0x19); + dsi_generic_write_seq(dsi, 0xbe, 0x11, 0x13); + dsi_generic_write_seq(dsi, 0xbf, 0x2a, 0x29); + dsi_generic_write_seq(dsi, 0xc0, 0x34, 0x31); + dsi_generic_write_seq(dsi, 0xc1, 0x2e, 0x2d); + dsi_generic_write_seq(dsi, 0xc2, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xc3, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xc4, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xc5, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xc6, 0x2e, 0x2d); + dsi_generic_write_seq(dsi, 0xc7, 0x31, 0x34); + dsi_generic_write_seq(dsi, 0xc8, 0x29, 0x2a); + dsi_generic_write_seq(dsi, 0xc9, 0x17, 0x19); + dsi_generic_write_seq(dsi, 0xca, 0x11, 0x13); + dsi_generic_write_seq(dsi, 0xcb, 0x03, 0x01); + dsi_generic_write_seq(dsi, 0xcc, 0x08, 0x31); + dsi_generic_write_seq(dsi, 0xcd, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xce, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xcf, 0x31, 0x08); + dsi_generic_write_seq(dsi, 0xd0, 0x00, 0x02); + dsi_generic_write_seq(dsi, 0xd1, 0x12, 0x10); + dsi_generic_write_seq(dsi, 0xd2, 0x18, 0x16); + dsi_generic_write_seq(dsi, 0xd3, 0x2a, 0x29); + dsi_generic_write_seq(dsi, 0xd4, 0x34, 0x31); + dsi_generic_write_seq(dsi, 0xd5, 0x2d, 0x2e); + dsi_generic_write_seq(dsi, 0xd6, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xd7, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xe5, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xe6, 0x31, 0x31); + dsi_generic_write_seq(dsi, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xd9, 0x00, 0x00, 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xe7, 0x00); + dsi_generic_write_seq(dsi, 0x6f, 0x02); + dsi_generic_write_seq(dsi, 0xf7, 0x47); + dsi_generic_write_seq(dsi, 0x6f, 0x0a); + dsi_generic_write_seq(dsi, 0xf7, 0x02); + dsi_generic_write_seq(dsi, 0x6f, 0x17); + dsi_generic_write_seq(dsi, 0xf4, 0x60); + dsi_generic_write_seq(dsi, 0x6f, 0x01); + dsi_generic_write_seq(dsi, 0xf9, 0x46); + dsi_generic_write_seq(dsi, 0x6f, 0x11); + dsi_generic_write_seq(dsi, 0xf3, 0x01); + dsi_generic_write_seq(dsi, 0x35, 0x00); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00); + dsi_generic_write_seq(dsi, 0xd9, 0x02, 0x03, 0x00); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00); + dsi_generic_write_seq(dsi, 0xb1, 0x6c, 0x21); + dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00); + dsi_generic_write_seq(dsi, 0x35, 0x00); + + 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); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + usleep_range(1000, 2000); + + dsi_generic_write_seq(dsi, 0x53, 0x24); + + return 0; +} + +static int truly_nt35521_off(struct truly_nt35521 *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(50); + + 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(150); + + return 0; +} + +static int truly_nt35521_prepare(struct drm_panel *panel) +{ + struct truly_nt35521 *ctx = to_truly_nt35521(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; + } + + truly_nt35521_reset(ctx); + + ret = truly_nt35521_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + return ret; + } + + ctx->prepared = true; + return 0; +} + +static int truly_nt35521_unprepare(struct drm_panel *panel) +{ + struct truly_nt35521 *ctx = to_truly_nt35521(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = truly_nt35521_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), + ctx->supplies); + + ctx->prepared = false; + return 0; +} + +static int truly_nt35521_enable(struct drm_panel *panel) +{ + struct truly_nt35521 *ctx = to_truly_nt35521(panel); + + if (ctx->enabled) + return 0; + + gpiod_set_value_cansleep(ctx->blen_gpio, 1); + + ctx->enabled = true; + return 0; +} + +static int truly_nt35521_disable(struct drm_panel *panel) +{ + struct truly_nt35521 *ctx = to_truly_nt35521(panel); + + if (!ctx->enabled) + return 0; + + gpiod_set_value_cansleep(ctx->blen_gpio, 0); + + ctx->enabled = false; + return 0; +} + +static const struct drm_display_mode truly_nt35521_mode = { + .clock = (720 + 232 + 20 + 112) * (1280 + 18 + 1 + 18) * 60 / 1000, + .hdisplay = 720, + .hsync_start = 720 + 232, + .hsync_end = 720 + 232 + 20, + .htotal = 720 + 232 + 20 + 112, + .vdisplay = 1280, + .vsync_start = 1280 + 18, + .vsync_end = 1280 + 18 + 1, + .vtotal = 1280 + 18 + 1 + 18, + .width_mm = 65, + .height_mm = 116, +}; + +static int truly_nt35521_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &truly_nt35521_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 truly_nt35521_panel_funcs = { + .prepare = truly_nt35521_prepare, + .unprepare = truly_nt35521_unprepare, + .enable = truly_nt35521_enable, + .disable = truly_nt35521_disable, + .get_modes = truly_nt35521_get_modes, +}; + +static int truly_nt35521_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness = backlight_get_brightness(bl); + int ret; + + ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness); + if (ret < 0) + return ret; + + return 0; +} + +static int truly_nt35521_bl_get_brightness(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness; + int ret; + + ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); + if (ret < 0) + return ret; + + return brightness & 0xff; +} + +static const struct backlight_ops truly_nt35521_bl_ops = { + .update_status = truly_nt35521_bl_update_status, + .get_brightness = truly_nt35521_bl_get_brightness, +}; + +static struct backlight_device * +truly_nt35521_create_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 255, + .max_brightness = 255, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &truly_nt35521_bl_ops, &props); +} + +static int truly_nt35521_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct truly_nt35521 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->supplies[0].supply = "positive5"; + ctx->supplies[1].supply = "negative5"; + 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_HIGH); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ctx->blen_gpio = devm_gpiod_get(dev, "backlight", GPIOD_OUT_LOW); + if (IS_ERR(ctx->blen_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->blen_gpio), + "Failed to get backlight-gpios\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + + drm_panel_init(&ctx->panel, dev, &truly_nt35521_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ctx->panel.backlight = truly_nt35521_create_backlight(dsi); + if (IS_ERR(ctx->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), + "Failed to create backlight\n"); + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + drm_panel_remove(&ctx->panel); + return ret; + } + + return 0; +} + +static int truly_nt35521_remove(struct mipi_dsi_device *dsi) +{ + struct truly_nt35521 *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 truly_nt35521_of_match[] = { + { .compatible = "sony,tulip-truly-nt35521" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, truly_nt35521_of_match); + +static struct mipi_dsi_driver truly_nt35521_driver = { + .probe = truly_nt35521_probe, + .remove = truly_nt35521_remove, + .driver = { + .name = "panel-truly-nt35521", + .of_match_table = truly_nt35521_of_match, + }, +}; +module_mipi_dsi_driver(truly_nt35521_driver); + +MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); +MODULE_DESCRIPTION("DRM driver for Sony Tulip Truly NT35521 panel"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c index bacaf1b7fb70..1866cdb8f9c1 100644 --- a/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c +++ b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c @@ -431,16 +431,14 @@ static int td043mtea1_probe(struct spi_device *spi) memcpy(lcd->gamma, td043mtea1_def_gamma, sizeof(lcd->gamma)); lcd->vcc_reg = devm_regulator_get(&spi->dev, "vcc"); - if (IS_ERR(lcd->vcc_reg)) { - dev_err(&spi->dev, "failed to get VCC regulator\n"); - return PTR_ERR(lcd->vcc_reg); - } + if (IS_ERR(lcd->vcc_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(lcd->vcc_reg), + "failed to get VCC regulator\n"); lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(lcd->reset_gpio)) { - dev_err(&spi->dev, "failed to get reset GPIO\n"); - return PTR_ERR(lcd->reset_gpio); - } + if (IS_ERR(lcd->reset_gpio)) + return dev_err_probe(&spi->dev, PTR_ERR(lcd->reset_gpio), + "failed to get reset GPIO\n"); spi->bits_per_word = 16; spi->mode = SPI_MODE_0; diff --git a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c index d17aae8b71d7..8177f5a360fb 100644 --- a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c +++ b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c @@ -283,26 +283,19 @@ static int xpp055c272_probe(struct mipi_dsi_device *dsi) return -ENOMEM; ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset_gpio)) { - dev_err(dev, "cannot get reset gpio\n"); - return PTR_ERR(ctx->reset_gpio); - } + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "cannot get reset gpio\n"); ctx->vci = devm_regulator_get(dev, "vci"); - if (IS_ERR(ctx->vci)) { - ret = PTR_ERR(ctx->vci); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to request vci regulator: %d\n", ret); - return ret; - } + if (IS_ERR(ctx->vci)) + return dev_err_probe(dev, PTR_ERR(ctx->vci), + "Failed to request vci regulator\n"); ctx->iovcc = devm_regulator_get(dev, "iovcc"); - if (IS_ERR(ctx->iovcc)) { - ret = PTR_ERR(ctx->iovcc); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to request iovcc regulator: %d\n", ret); - return ret; - } + if (IS_ERR(ctx->iovcc)) + return dev_err_probe(dev, PTR_ERR(ctx->iovcc), + "Failed to request iovcc regulator\n"); mipi_dsi_set_drvdata(dsi, ctx); diff --git a/drivers/gpu/drm/radeon/radeon_sync.c b/drivers/gpu/drm/radeon/radeon_sync.c index 9257b60144c4..b991ba1bcd51 100644 --- a/drivers/gpu/drm/radeon/radeon_sync.c +++ b/drivers/gpu/drm/radeon/radeon_sync.c @@ -91,33 +91,17 @@ int radeon_sync_resv(struct radeon_device *rdev, struct dma_resv *resv, bool shared) { - struct dma_resv_list *flist; - struct dma_fence *f; + struct dma_resv_iter cursor; struct radeon_fence *fence; - unsigned i; + struct dma_fence *f; int r = 0; - /* always sync to the exclusive fence */ - f = dma_resv_excl_fence(resv); - fence = f ? to_radeon_fence(f) : NULL; - if (fence && fence->rdev == rdev) - radeon_sync_fence(sync, fence); - else if (f) - r = dma_fence_wait(f, true); - - flist = dma_resv_shared_list(resv); - if (shared || !flist || r) - return r; - - for (i = 0; i < flist->shared_count; ++i) { - f = rcu_dereference_protected(flist->shared[i], - dma_resv_held(resv)); + dma_resv_for_each_fence(&cursor, resv, shared, f) { fence = to_radeon_fence(f); if (fence && fence->rdev == rdev) radeon_sync_fence(sync, fence); else r = dma_fence_wait(f, true); - if (r) break; } diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index a9acbcc420d0..4ed7a6868197 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -267,6 +267,8 @@ struct dw_mipi_dsi_rockchip { struct dw_mipi_dsi *dmd; const struct rockchip_dw_dsi_chip_data *cdata; struct dw_mipi_dsi_plat_data pdata; + + bool dsi_bound; }; struct dphy_pll_parameter_map { @@ -772,10 +774,6 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) if (mux < 0) return; - pm_runtime_get_sync(dsi->dev); - if (dsi->slave) - pm_runtime_get_sync(dsi->slave->dev); - /* * For the RK3399, the clk of grf must be enabled before writing grf * register. And for RK3288 or other soc, this grf_clk must be NULL, @@ -794,20 +792,10 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) clk_disable_unprepare(dsi->grf_clk); } -static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder) -{ - struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder); - - if (dsi->slave) - pm_runtime_put(dsi->slave->dev); - pm_runtime_put(dsi->dev); -} - static const struct drm_encoder_helper_funcs dw_mipi_dsi_encoder_helper_funcs = { .atomic_check = dw_mipi_dsi_encoder_atomic_check, .enable = dw_mipi_dsi_encoder_enable, - .disable = dw_mipi_dsi_encoder_disable, }; static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi, @@ -937,10 +925,14 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev, put_device(second); } + pm_runtime_get_sync(dsi->dev); + if (dsi->slave) + pm_runtime_get_sync(dsi->slave->dev); + ret = clk_prepare_enable(dsi->pllref_clk); if (ret) { DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret); - return ret; + goto out_pm_runtime; } /* @@ -952,7 +944,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev, ret = clk_prepare_enable(dsi->grf_clk); if (ret) { DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); - return ret; + goto out_pll_clk; } dw_mipi_dsi_rockchip_config(dsi); @@ -964,16 +956,27 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev, ret = rockchip_dsi_drm_create_encoder(dsi, drm_dev); if (ret) { DRM_DEV_ERROR(dev, "Failed to create drm encoder\n"); - return ret; + goto out_pll_clk; } ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder); if (ret) { DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret); - return ret; + goto out_pll_clk; } + dsi->dsi_bound = true; + return 0; + +out_pll_clk: + clk_disable_unprepare(dsi->pllref_clk); +out_pm_runtime: + pm_runtime_put(dsi->dev); + if (dsi->slave) + pm_runtime_put(dsi->slave->dev); + + return ret; } static void dw_mipi_dsi_rockchip_unbind(struct device *dev, @@ -985,9 +988,15 @@ static void dw_mipi_dsi_rockchip_unbind(struct device *dev, if (dsi->is_slave) return; + dsi->dsi_bound = false; + dw_mipi_dsi_unbind(dsi->dmd); clk_disable_unprepare(dsi->pllref_clk); + + pm_runtime_put(dsi->dev); + if (dsi->slave) + pm_runtime_put(dsi->slave->dev); } static const struct component_ops dw_mipi_dsi_rockchip_ops = { @@ -1275,6 +1284,36 @@ static const struct phy_ops dw_mipi_dsi_dphy_ops = { .exit = dw_mipi_dsi_dphy_exit, }; +static int __maybe_unused dw_mipi_dsi_rockchip_resume(struct device *dev) +{ + struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev); + int ret; + + /* + * Re-configure DSI state, if we were previously initialized. We need + * to do this before rockchip_drm_drv tries to re-enable() any panels. + */ + if (dsi->dsi_bound) { + ret = clk_prepare_enable(dsi->grf_clk); + if (ret) { + DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); + return ret; + } + + dw_mipi_dsi_rockchip_config(dsi); + if (dsi->slave) + dw_mipi_dsi_rockchip_config(dsi->slave); + + clk_disable_unprepare(dsi->grf_clk); + } + + return 0; +} + +static const struct dev_pm_ops dw_mipi_dsi_rockchip_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, dw_mipi_dsi_rockchip_resume) +}; + static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1396,14 +1435,10 @@ static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev) if (ret != -EPROBE_DEFER) DRM_DEV_ERROR(dev, "Failed to probe dw_mipi_dsi: %d\n", ret); - goto err_clkdisable; + return ret; } return 0; - -err_clkdisable: - clk_disable_unprepare(dsi->pllref_clk); - return ret; } static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev) @@ -1592,6 +1627,7 @@ struct platform_driver dw_mipi_dsi_rockchip_driver = { .remove = dw_mipi_dsi_rockchip_remove, .driver = { .of_match_table = dw_mipi_dsi_rockchip_dt_ids, + .pm = &dw_mipi_dsi_rockchip_pm_ops, .name = "dw-mipi-dsi-rockchip", }, }; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index e4ebe60b3cc1..69c699459dce 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -199,16 +199,7 @@ static void rockchip_drm_unbind(struct device *dev) drm_dev_put(drm_dev); } -static const struct file_operations rockchip_drm_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .mmap = rockchip_gem_mmap, - .poll = drm_poll, - .read = drm_read, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .release = drm_release, -}; +DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops); static const struct drm_driver rockchip_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, @@ -217,7 +208,7 @@ static const struct drm_driver rockchip_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, - .gem_prime_mmap = rockchip_gem_mmap_buf, + .gem_prime_mmap = drm_gem_prime_mmap, .fops = &rockchip_drm_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c index 2fdc455c4ad7..d8418dd39d0e 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -7,6 +7,7 @@ #include <drm/drm.h> #include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> +#include <drm/drm_prime.h> #include <drm/drm_probe_helper.h> #include "rockchip_drm_drv.h" @@ -24,7 +25,7 @@ static int rockchip_fbdev_mmap(struct fb_info *info, struct drm_fb_helper *helper = info->par; struct rockchip_drm_private *private = to_drm_private(helper); - return rockchip_gem_mmap_buf(private->fbdev_bo, vma); + return drm_gem_prime_mmap(private->fbdev_bo, vma); } static const struct fb_ops rockchip_drm_fbdev_ops = { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 7971f57436dd..63eb73b624aa 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -241,11 +241,21 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); /* + * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the + * whole buffer from the start. + */ + vma->vm_pgoff = 0; + + /* * We allocated a struct page table for rk_obj, so clear * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). */ + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_flags &= ~VM_PFNMAP; + vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); + vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); + if (rk_obj->pages) ret = rockchip_drm_gem_object_mmap_iommu(obj, vma); else @@ -257,39 +267,6 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, return ret; } -int rockchip_gem_mmap_buf(struct drm_gem_object *obj, - struct vm_area_struct *vma) -{ - int ret; - - ret = drm_gem_mmap_obj(obj, obj->size, vma); - if (ret) - return ret; - - return rockchip_drm_gem_object_mmap(obj, vma); -} - -/* drm driver mmap file operations */ -int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct drm_gem_object *obj; - int ret; - - ret = drm_gem_mmap(filp, vma); - if (ret) - return ret; - - /* - * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the - * whole buffer from the start. - */ - vma->vm_pgoff = 0; - - obj = vma->vm_private_data; - - return rockchip_drm_gem_object_mmap(obj, vma); -} - static void rockchip_gem_release_object(struct rockchip_gem_object *rk_obj) { drm_gem_object_release(&rk_obj->base); @@ -301,6 +278,7 @@ static const struct drm_gem_object_funcs rockchip_gem_object_funcs = { .get_sg_table = rockchip_gem_prime_get_sg_table, .vmap = rockchip_gem_prime_vmap, .vunmap = rockchip_gem_prime_vunmap, + .mmap = rockchip_drm_gem_object_mmap, .vm_ops = &drm_gem_cma_vm_ops, }; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h index 5a70a56cd406..47c1861eece0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h @@ -34,13 +34,6 @@ rockchip_gem_prime_import_sg_table(struct drm_device *dev, int rockchip_gem_prime_vmap(struct drm_gem_object *obj, struct dma_buf_map *map); void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, struct dma_buf_map *map); -/* drm driver mmap file operations */ -int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma); - -/* mmap a gem object to userspace. */ -int rockchip_gem_mmap_buf(struct drm_gem_object *obj, - struct vm_area_struct *vma); - struct rockchip_gem_object * rockchip_gem_create_object(struct drm_device *drm, unsigned int size, bool alloc_kmap); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index ba9e14da41b4..b28ea5d6a3e2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -726,7 +726,9 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc, spin_unlock(&vop->reg_lock); - wait_for_completion(&vop->dsp_hold_completion); + if (!wait_for_completion_timeout(&vop->dsp_hold_completion, + msecs_to_jiffies(200))) + WARN(1, "%s: timed out waiting for DSP hold", crtc->name); vop_dsp_hold_valid_irq_disable(vop); diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 042c16b5d54a..94fe51b3caa2 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -699,30 +699,19 @@ int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, struct drm_gem_object *obj, bool write) { + struct dma_resv_iter cursor; + struct dma_fence *fence; int ret; - struct dma_fence **fences; - unsigned int i, fence_count; - - if (!write) { - struct dma_fence *fence = dma_resv_get_excl_unlocked(obj->resv); - - return drm_sched_job_add_dependency(job, fence); - } - - ret = dma_resv_get_fences(obj->resv, NULL, &fence_count, &fences); - if (ret || !fence_count) - return ret; - for (i = 0; i < fence_count; i++) { - ret = drm_sched_job_add_dependency(job, fences[i]); + dma_resv_for_each_fence(&cursor, obj->resv, write, fence) { + ret = drm_sched_job_add_dependency(job, fence); if (ret) - break; - } + return ret; - for (; i < fence_count; i++) - dma_fence_put(fences[i]); - kfree(fences); - return ret; + /* Make sure to grab an additional ref on the added fence */ + dma_fence_get(fence); + } + return 0; } EXPORT_SYMBOL(drm_sched_job_add_implicit_dependencies); diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 54dd562e294c..b630614b3d72 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -53,7 +53,7 @@ static const struct drm_driver sun4i_drv_driver = { .minor = 0, /* GEM Operations */ - DRM_GEM_CMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(drm_sun4i_gem_dumb_create), + DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(drm_sun4i_gem_dumb_create), }; static int sun4i_drv_bind(struct device *dev) diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index 1ceb93fbdc50..4ca309f80dee 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -80,6 +80,19 @@ config TINYDRM_HX8357D If M is selected the module will be called hx8357d. +config TINYDRM_ILI9163 + tristate "DRM support for ILI9163 display panels" + depends on DRM && SPI + select BACKLIGHT_CLASS_DEVICE + select DRM_KMS_CMA_HELPER + select DRM_KMS_HELPER + select DRM_MIPI_DBI + help + DRM driver for the following Ilitek ILI9163 panels: + * NHD-1.8-128160EF 128x160 TFT + + If M is selected the module will be called ili9163. + config TINYDRM_ILI9225 tristate "DRM support for ILI9225 display panels" depends on DRM && SPI diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index e09942895c77..5d5505d40e7b 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o obj-$(CONFIG_DRM_GM12U320) += gm12u320.o obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm.o obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o +obj-$(CONFIG_TINYDRM_ILI9163) += ili9163.o obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o obj-$(CONFIG_TINYDRM_ILI9341) += ili9341.o obj-$(CONFIG_TINYDRM_ILI9486) += ili9486.o diff --git a/drivers/gpu/drm/tiny/ili9163.c b/drivers/gpu/drm/tiny/ili9163.c new file mode 100644 index 000000000000..bcc181351236 --- /dev/null +++ b/drivers/gpu/drm/tiny/ili9163.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/spi/spi.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_mipi_dbi.h> +#include <drm/drm_modeset_helper.h> + +#include <video/mipi_display.h> + +#define ILI9163_FRMCTR1 0xb1 + +#define ILI9163_PWCTRL1 0xc0 +#define ILI9163_PWCTRL2 0xc1 +#define ILI9163_VMCTRL1 0xc5 +#define ILI9163_VMCTRL2 0xc7 +#define ILI9163_PWCTRLA 0xcb +#define ILI9163_PWCTRLB 0xcf + +#define ILI9163_EN3GAM 0xf2 + +#define ILI9163_MADCTL_BGR BIT(3) +#define ILI9163_MADCTL_MV BIT(5) +#define ILI9163_MADCTL_MX BIT(6) +#define ILI9163_MADCTL_MY BIT(7) + +static void yx240qv29_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; + + /* Gamma */ + mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x04); + mipi_dbi_command(dbi, ILI9163_EN3GAM, 0x00); + + /* Frame Rate */ + mipi_dbi_command(dbi, ILI9163_FRMCTR1, 0x0a, 0x14); + + /* Power Control */ + mipi_dbi_command(dbi, ILI9163_PWCTRL1, 0x0a, 0x00); + mipi_dbi_command(dbi, ILI9163_PWCTRL2, 0x02); + + /* VCOM */ + mipi_dbi_command(dbi, ILI9163_VMCTRL1, 0x2f, 0x3e); + mipi_dbi_command(dbi, ILI9163_VMCTRL2, 0x40); + + /* Memory Access Control */ + mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); + + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(100); + + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); + msleep(100); + +out_enable: + switch (dbidev->rotation) { + default: + addr_mode = ILI9163_MADCTL_MX | ILI9163_MADCTL_MY; + break; + case 90: + addr_mode = ILI9163_MADCTL_MX | ILI9163_MADCTL_MV; + break; + case 180: + addr_mode = 0; + break; + case 270: + addr_mode = ILI9163_MADCTL_MY | ILI9163_MADCTL_MV; + break; + } + addr_mode |= ILI9163_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 ili9163_pipe_funcs = { + .enable = yx240qv29_enable, + .disable = mipi_dbi_pipe_disable, + .update = mipi_dbi_pipe_update, + .prepare_fb = drm_gem_simple_display_pipe_prepare_fb, +}; + +static const struct drm_display_mode yx240qv29_mode = { + DRM_SIMPLE_MODE(128, 160, 28, 35), +}; + +DEFINE_DRM_GEM_CMA_FOPS(ili9163_fops); + +static struct drm_driver ili9163_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &ili9163_fops, + DRM_GEM_CMA_DRIVER_OPS_VMAP, + .debugfs_init = mipi_dbi_debugfs_init, + .name = "ili9163", + .desc = "Ilitek ILI9163", + .date = "20210208", + .major = 1, + .minor = 0, +}; + +static const struct of_device_id ili9163_of_match[] = { + { .compatible = "newhaven,1.8-128160EF" }, + { } +}; +MODULE_DEVICE_TABLE(of, ili9163_of_match); + +static const struct spi_device_id ili9163_id[] = { + { "nhd-1.8-128160EF", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ili9163_id); + +static int ili9163_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 = devm_drm_dev_alloc(dev, &ili9163_driver, + struct mipi_dbi_dev, drm); + if (IS_ERR(dbidev)) + return PTR_ERR(dbidev); + + dbi = &dbidev->dbi; + drm = &dbidev->drm; + + spi_set_drvdata(spi, drm); + + dbi->reset = devm_gpiod_get_optional(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_optional(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; + + ret = mipi_dbi_dev_init(dbidev, &ili9163_pipe_funcs, &yx240qv29_mode, rotation); + if (ret) + return ret; + + drm_mode_config_reset(drm); + + ret = drm_dev_register(drm, 0); + if (ret) + return ret; + + drm_fbdev_generic_setup(drm, 0); + + return 0; +} + +static int ili9163_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 ili9163_shutdown(struct spi_device *spi) +{ + drm_atomic_helper_shutdown(spi_get_drvdata(spi)); +} + +static struct spi_driver ili9163_spi_driver = { + .driver = { + .name = "ili9163", + .of_match_table = ili9163_of_match, + }, + .id_table = ili9163_id, + .probe = ili9163_probe, + .remove = ili9163_remove, + .shutdown = ili9163_shutdown, +}; +module_spi_driver(ili9163_spi_driver); + +MODULE_DESCRIPTION("Ilitek ILI9163 DRM driver"); +MODULE_AUTHOR("Daniel Mack <daniel@zonque.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index d62b2013c367..3934ee225c78 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -269,23 +269,15 @@ static int ttm_bo_individualize_resv(struct ttm_buffer_object *bo) static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo) { struct dma_resv *resv = &bo->base._resv; - struct dma_resv_list *fobj; + struct dma_resv_iter cursor; struct dma_fence *fence; - int i; - - rcu_read_lock(); - fobj = dma_resv_shared_list(resv); - fence = dma_resv_excl_fence(resv); - if (fence && !fence->ops->signaled) - dma_fence_enable_sw_signaling(fence); - - for (i = 0; fobj && i < fobj->shared_count; ++i) { - fence = rcu_dereference(fobj->shared[i]); + dma_resv_iter_begin(&cursor, resv, true); + dma_resv_for_each_fence_unlocked(&cursor, fence) { if (!fence->ops->signaled) dma_fence_enable_sw_signaling(fence); } - rcu_read_unlock(); + dma_resv_iter_end(&cursor); } /** diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 6a000d77c568..c7ed2e1cbab6 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -487,8 +487,8 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, for (i = 0; i < se->in_sync_count; i++) { struct drm_v3d_sem in; - ret = copy_from_user(&in, handle++, sizeof(in)); - if (ret) { + if (copy_from_user(&in, handle++, sizeof(in))) { + ret = -EFAULT; DRM_DEBUG("Failed to copy wait dep handle.\n"); goto fail_deps; } @@ -609,8 +609,8 @@ v3d_get_multisync_post_deps(struct drm_file *file_priv, for (i = 0; i < count; i++) { struct drm_v3d_sem out; - ret = copy_from_user(&out, post_deps++, sizeof(out)); - if (ret) { + if (copy_from_user(&out, post_deps++, sizeof(out))) { + ret = -EFAULT; DRM_DEBUG("Failed to copy post dep handles\n"); goto fail; } @@ -646,9 +646,8 @@ v3d_get_multisync_submit_deps(struct drm_file *file_priv, struct v3d_submit_ext *se = data; int ret; - ret = copy_from_user(&multisync, ext, sizeof(multisync)); - if (ret) - return ret; + if (copy_from_user(&multisync, ext, sizeof(multisync))) + return -EFAULT; if (multisync.pad) return -EINVAL; @@ -775,7 +774,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) { ret = v3d_job_init(v3d, file_priv, (void *)&clean_job, sizeof(*clean_job), - v3d_job_free, 0, 0, V3D_CACHE_CLEAN); + v3d_job_free, 0, NULL, V3D_CACHE_CLEAN); if (ret) goto fail; @@ -1008,7 +1007,7 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data, goto fail; ret = v3d_job_init(v3d, file_priv, (void *)&clean_job, sizeof(*clean_job), - v3d_job_free, 0, 0, V3D_CACHE_CLEAN); + v3d_job_free, 0, NULL, V3D_CACHE_CLEAN); if (ret) goto fail; diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index a6caebd4a0dd..5b00310ac4cd 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -308,8 +308,10 @@ virtio_gpu_user_framebuffer_create(struct drm_device *dev, return ERR_PTR(-EINVAL); virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL); - if (virtio_gpu_fb == NULL) + if (virtio_gpu_fb == NULL) { + drm_gem_object_put(obj); return ERR_PTR(-ENOMEM); + } ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj); if (ret) { diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index e21ea3d23e6f..20208207e366 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -501,7 +501,9 @@ config THINKPAD_ACPI depends on ACPI_VIDEO || ACPI_VIDEO = n depends on BACKLIGHT_CLASS_DEVICE depends on I2C + depends on DRM select ACPI_PLATFORM_PROFILE + select DRM_PRIVACY_SCREEN select HWMON select NVRAM select NEW_LEDS diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 50ff04c84650..291cd18c9c8f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -73,6 +73,7 @@ #include <linux/uaccess.h> #include <acpi/battery.h> #include <acpi/video.h> +#include <drm/drm_privacy_screen_driver.h> #include "dual_accel_detect.h" /* ThinkPad CMOS commands */ @@ -157,6 +158,7 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ + TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */ /* Reasons for waking up from S3/S4 */ TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ @@ -3885,6 +3887,30 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) } } +static bool hotkey_notify_extended_hotkey(const u32 hkey) +{ + unsigned int scancode; + + switch (hkey) { + case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: + tpacpi_driver_event(hkey); + return true; + } + + /* Extended keycodes start at 0x300 and our offset into the map + * TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode + * will be positive, but might not be in the correct range. + */ + scancode = (hkey & 0xfff) - (0x300 - TP_ACPI_HOTKEYSCAN_EXTENDED_START); + if (scancode >= TP_ACPI_HOTKEYSCAN_EXTENDED_START && + scancode < TPACPI_HOTKEY_MAP_LEN) { + tpacpi_input_send_key(scancode); + return true; + } + + return false; +} + static bool hotkey_notify_hotkey(const u32 hkey, bool *send_acpi_ev, bool *ignore_acpi_ev) @@ -3919,17 +3945,7 @@ static bool hotkey_notify_hotkey(const u32 hkey, return adaptive_keyboard_hotkey_notify_hotkey(scancode); case 3: - /* Extended keycodes start at 0x300 and our offset into the map - * TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode - * will be positive, but might not be in the correct range. - */ - scancode -= (0x300 - TP_ACPI_HOTKEYSCAN_EXTENDED_START); - if (scancode >= TP_ACPI_HOTKEYSCAN_EXTENDED_START && - scancode < TPACPI_HOTKEY_MAP_LEN) { - tpacpi_input_send_key(scancode); - return true; - } - break; + return hotkey_notify_extended_hotkey(hkey); } return false; @@ -9811,69 +9827,85 @@ static struct ibm_struct battery_driver_data = { * LCD Shadow subdriver, for the Lenovo PrivacyGuard feature */ -static int lcdshadow_state; +static struct drm_privacy_screen *lcdshadow_dev; +static acpi_handle lcdshadow_get_handle; +static acpi_handle lcdshadow_set_handle; -static int lcdshadow_on_off(bool state) +static int lcdshadow_set_sw_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status state) { - acpi_handle set_shadow_handle; int output; - if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "SSSS", &set_shadow_handle))) { - pr_warn("Thinkpad ACPI has no %s interface.\n", "SSSS"); + if (WARN_ON(!mutex_is_locked(&priv->lock))) return -EIO; - } - if (!acpi_evalf(set_shadow_handle, &output, NULL, "dd", (int)state)) + if (!acpi_evalf(lcdshadow_set_handle, &output, NULL, "dd", (int)state)) return -EIO; - lcdshadow_state = state; + priv->hw_state = priv->sw_state = state; return 0; } -static int lcdshadow_set(bool on) +static void lcdshadow_get_hw_state(struct drm_privacy_screen *priv) { - if (lcdshadow_state < 0) - return lcdshadow_state; - if (lcdshadow_state == on) - return 0; - return lcdshadow_on_off(on); + int output; + + if (!acpi_evalf(lcdshadow_get_handle, &output, NULL, "dd", 0)) + return; + + priv->hw_state = priv->sw_state = output & 0x1; } +static const struct drm_privacy_screen_ops lcdshadow_ops = { + .set_sw_state = lcdshadow_set_sw_state, + .get_hw_state = lcdshadow_get_hw_state, +}; + static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm) { - acpi_handle get_shadow_handle; + acpi_status status1, status2; int output; - if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GSSS", &get_shadow_handle))) { - lcdshadow_state = -ENODEV; + status1 = acpi_get_handle(hkey_handle, "GSSS", &lcdshadow_get_handle); + status2 = acpi_get_handle(hkey_handle, "SSSS", &lcdshadow_set_handle); + if (ACPI_FAILURE(status1) || ACPI_FAILURE(status2)) return 0; - } - if (!acpi_evalf(get_shadow_handle, &output, NULL, "dd", 0)) { - lcdshadow_state = -EIO; + if (!acpi_evalf(lcdshadow_get_handle, &output, NULL, "dd", 0)) return -EIO; - } - if (!(output & 0x10000)) { - lcdshadow_state = -ENODEV; + + if (!(output & 0x10000)) return 0; - } - lcdshadow_state = output & 0x1; + + lcdshadow_dev = drm_privacy_screen_register(&tpacpi_pdev->dev, + &lcdshadow_ops); + if (IS_ERR(lcdshadow_dev)) + return PTR_ERR(lcdshadow_dev); return 0; } +static void lcdshadow_exit(void) +{ + drm_privacy_screen_unregister(lcdshadow_dev); +} + static void lcdshadow_resume(void) { - if (lcdshadow_state >= 0) - lcdshadow_on_off(lcdshadow_state); + if (!lcdshadow_dev) + return; + + mutex_lock(&lcdshadow_dev->lock); + lcdshadow_set_sw_state(lcdshadow_dev, lcdshadow_dev->sw_state); + mutex_unlock(&lcdshadow_dev->lock); } static int lcdshadow_read(struct seq_file *m) { - if (lcdshadow_state < 0) { + if (!lcdshadow_dev) { seq_puts(m, "status:\t\tnot supported\n"); } else { - seq_printf(m, "status:\t\t%d\n", lcdshadow_state); + seq_printf(m, "status:\t\t%d\n", lcdshadow_dev->hw_state); seq_puts(m, "commands:\t0, 1\n"); } @@ -9885,7 +9917,7 @@ static int lcdshadow_write(char *buf) char *cmd; int res, state = -EINVAL; - if (lcdshadow_state < 0) + if (!lcdshadow_dev) return -ENODEV; while ((cmd = strsep(&buf, ","))) { @@ -9897,11 +9929,18 @@ static int lcdshadow_write(char *buf) if (state >= 2 || state < 0) return -EINVAL; - return lcdshadow_set(state); + mutex_lock(&lcdshadow_dev->lock); + res = lcdshadow_set_sw_state(lcdshadow_dev, state); + mutex_unlock(&lcdshadow_dev->lock); + + drm_privacy_screen_call_notifier_chain(lcdshadow_dev); + + return res; } static struct ibm_struct lcdshadow_driver_data = { .name = "lcdshadow", + .exit = lcdshadow_exit, .resume = lcdshadow_resume, .read = lcdshadow_read, .write = lcdshadow_write, @@ -10711,6 +10750,20 @@ static void tpacpi_driver_event(const unsigned int hkey_event) if (!atomic_add_unless(&dytc_ignore_event, -1, 0)) dytc_profile_refresh(); } + + if (lcdshadow_dev && hkey_event == TP_HKEY_EV_PRIVACYGUARD_TOGGLE) { + enum drm_privacy_screen_status old_hw_state; + bool changed; + + mutex_lock(&lcdshadow_dev->lock); + old_hw_state = lcdshadow_dev->hw_state; + lcdshadow_get_hw_state(lcdshadow_dev); + changed = lcdshadow_dev->hw_state != old_hw_state; + mutex_unlock(&lcdshadow_dev->lock); + + if (changed) + drm_privacy_screen_call_notifier_chain(lcdshadow_dev); + } } static void hotkey_driver_event(const unsigned int scancode) diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c index f98e8f298bc1..01fae2c96965 100644 --- a/drivers/video/fbdev/core/bitblit.c +++ b/drivers/video/fbdev/core/bitblit.c @@ -43,21 +43,6 @@ static void update_attr(u8 *dst, u8 *src, int attribute, } } -static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int dy, int dx, int height, int width) -{ - struct fb_copyarea area; - - area.sx = sx * vc->vc_font.width; - area.sy = sy * vc->vc_font.height; - area.dx = dx * vc->vc_font.width; - area.dy = dy * vc->vc_font.height; - area.height = height * vc->vc_font.height; - area.width = width * vc->vc_font.width; - - info->fbops->fb_copyarea(info, &area); -} - static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) { @@ -393,7 +378,6 @@ static int bit_update_start(struct fb_info *info) void fbcon_set_bitops(struct fbcon_ops *ops) { - ops->bmove = bit_bmove; ops->clear = bit_clear; ops->putcs = bit_putcs; ops->clear_margins = bit_clear_margins; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 22bb3892f6bd..99ecd9a6d844 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -173,8 +173,6 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos); static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); static void fbcon_cursor(struct vc_data *vc, int mode); -static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, - int height, int width); static int fbcon_switch(struct vc_data *vc); static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch); static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table); @@ -182,16 +180,8 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table); /* * Internal routines */ -static __inline__ void ywrap_up(struct vc_data *vc, int count); -static __inline__ void ywrap_down(struct vc_data *vc, int count); -static __inline__ void ypan_up(struct vc_data *vc, int count); -static __inline__ void ypan_down(struct vc_data *vc, int count); -static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, - int dy, int dx, int height, int width, u_int y_break); static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, int unit); -static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, - int line, int count, int dy); static void fbcon_modechanged(struct fb_info *info); static void fbcon_set_all_vcs(struct fb_info *info); static void fbcon_start(void); @@ -1136,14 +1126,6 @@ static void fbcon_init(struct vc_data *vc, int init) ops->graphics = 0; /* - * No more hw acceleration for fbcon. - * - * FIXME: Garbage collect all the now dead code after sufficient time - * has passed. - */ - p->scrollmode = SCROLL_REDRAW; - - /* * ++guenther: console.c:vc_allocate() relies on initializing * vc_{cols,rows}, but we must not set those if we are only * resizing the console. @@ -1229,14 +1211,13 @@ finished: * This system is now divided into two levels because of complications * caused by hardware scrolling. Top level functions: * - * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins() + * fbcon_clear(), fbcon_putc(), fbcon_clear_margins() * * handles y values in range [0, scr_height-1] that correspond to real * screen positions. y_wrap shift means that first line of bitmap may be * anywhere on this display. These functions convert lineoffsets to * bitmap offsets and deal with the wrap-around case by splitting blits. * - * fbcon_bmove_physical_8() -- These functions fast implementations * fbcon_clear_physical_8() -- of original fbcon_XXX fns. * fbcon_putc_physical_8() -- (font width != 8) may be added later * @@ -1409,224 +1390,6 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, } } -static __inline__ void ywrap_up(struct vc_data *vc, int count) -{ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - struct fbcon_display *p = &fb_display[vc->vc_num]; - - p->yscroll += count; - if (p->yscroll >= p->vrows) /* Deal with wrap */ - p->yscroll -= p->vrows; - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode |= FB_VMODE_YWRAP; - ops->update_start(info); - scrollback_max += count; - if (scrollback_max > scrollback_phys_max) - scrollback_max = scrollback_phys_max; - scrollback_current = 0; -} - -static __inline__ void ywrap_down(struct vc_data *vc, int count) -{ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - struct fbcon_display *p = &fb_display[vc->vc_num]; - - p->yscroll -= count; - if (p->yscroll < 0) /* Deal with wrap */ - p->yscroll += p->vrows; - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode |= FB_VMODE_YWRAP; - ops->update_start(info); - scrollback_max -= count; - if (scrollback_max < 0) - scrollback_max = 0; - scrollback_current = 0; -} - -static __inline__ void ypan_up(struct vc_data *vc, int count) -{ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_display *p = &fb_display[vc->vc_num]; - struct fbcon_ops *ops = info->fbcon_par; - - p->yscroll += count; - if (p->yscroll > p->vrows - vc->vc_rows) { - ops->bmove(vc, info, p->vrows - vc->vc_rows, - 0, 0, 0, vc->vc_rows, vc->vc_cols); - p->yscroll -= p->vrows - vc->vc_rows; - } - - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode &= ~FB_VMODE_YWRAP; - ops->update_start(info); - fbcon_clear_margins(vc, 1); - scrollback_max += count; - if (scrollback_max > scrollback_phys_max) - scrollback_max = scrollback_phys_max; - scrollback_current = 0; -} - -static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) -{ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - struct fbcon_display *p = &fb_display[vc->vc_num]; - - p->yscroll += count; - - if (p->yscroll > p->vrows - vc->vc_rows) { - p->yscroll -= p->vrows - vc->vc_rows; - fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); - } - - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode &= ~FB_VMODE_YWRAP; - ops->update_start(info); - fbcon_clear_margins(vc, 1); - scrollback_max += count; - if (scrollback_max > scrollback_phys_max) - scrollback_max = scrollback_phys_max; - scrollback_current = 0; -} - -static __inline__ void ypan_down(struct vc_data *vc, int count) -{ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_display *p = &fb_display[vc->vc_num]; - struct fbcon_ops *ops = info->fbcon_par; - - p->yscroll -= count; - if (p->yscroll < 0) { - ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, - 0, vc->vc_rows, vc->vc_cols); - p->yscroll += p->vrows - vc->vc_rows; - } - - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode &= ~FB_VMODE_YWRAP; - ops->update_start(info); - fbcon_clear_margins(vc, 1); - scrollback_max -= count; - if (scrollback_max < 0) - scrollback_max = 0; - scrollback_current = 0; -} - -static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) -{ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - struct fbcon_display *p = &fb_display[vc->vc_num]; - - p->yscroll -= count; - - if (p->yscroll < 0) { - p->yscroll += p->vrows - vc->vc_rows; - fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); - } - - ops->var.xoffset = 0; - ops->var.yoffset = p->yscroll * vc->vc_font.height; - ops->var.vmode &= ~FB_VMODE_YWRAP; - ops->update_start(info); - fbcon_clear_margins(vc, 1); - scrollback_max -= count; - if (scrollback_max < 0) - scrollback_max = 0; - scrollback_current = 0; -} - -static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, - int line, int count, int dy) -{ - unsigned short *s = (unsigned short *) - (vc->vc_origin + vc->vc_size_row * line); - - while (count--) { - unsigned short *start = s; - unsigned short *le = advance_row(s, 1); - unsigned short c; - int x = 0; - unsigned short attr = 1; - - do { - c = scr_readw(s); - if (attr != (c & 0xff00)) { - attr = c & 0xff00; - if (s > start) { - fbcon_putcs(vc, start, s - start, - dy, x); - x += s - start; - start = s; - } - } - console_conditional_schedule(); - s++; - } while (s < le); - if (s > start) - fbcon_putcs(vc, start, s - start, dy, x); - console_conditional_schedule(); - dy++; - } -} - -static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, - struct fbcon_display *p, int line, int count, int ycount) -{ - int offset = ycount * vc->vc_cols; - unsigned short *d = (unsigned short *) - (vc->vc_origin + vc->vc_size_row * line); - unsigned short *s = d + offset; - struct fbcon_ops *ops = info->fbcon_par; - - while (count--) { - unsigned short *start = s; - unsigned short *le = advance_row(s, 1); - unsigned short c; - int x = 0; - - do { - c = scr_readw(s); - - if (c == scr_readw(d)) { - if (s > start) { - ops->bmove(vc, info, line + ycount, x, - line, x, 1, s-start); - x += s - start + 1; - start = s + 1; - } else { - x++; - start++; - } - } - - scr_writew(c, d); - console_conditional_schedule(); - s++; - d++; - } while (s < le); - if (s > start) - ops->bmove(vc, info, line + ycount, x, line, x, 1, - s-start); - console_conditional_schedule(); - if (ycount > 0) - line++; - else { - line--; - /* NOTE: We subtract two lines from these pointers */ - s -= vc->vc_size_row; - d -= vc->vc_size_row; - } - } -} - static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p, int line, int count, int offset) { @@ -1687,7 +1450,6 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct fbcon_display *p = &fb_display[vc->vc_num]; - int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; if (fbcon_is_inactive(vc, info)) return true; @@ -1704,249 +1466,32 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, case SM_UP: if (count > vc->vc_rows) /* Maximum realistic size */ count = vc->vc_rows; - if (logo_shown >= 0) - goto redraw_up; - switch (p->scrollmode) { - case SCROLL_MOVE: - fbcon_redraw_blit(vc, info, p, t, b - t - count, - count); - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); - scr_memsetw((unsigned short *) (vc->vc_origin + - vc->vc_size_row * - (b - count)), - vc->vc_video_erase_char, - vc->vc_size_row * count); - return true; - - case SCROLL_WRAP_MOVE: - if (b - t - count > 3 * vc->vc_rows >> 2) { - if (t > 0) - fbcon_bmove(vc, 0, 0, count, 0, t, - vc->vc_cols); - ywrap_up(vc, count); - if (vc->vc_rows - b > 0) - fbcon_bmove(vc, b - count, 0, b, 0, - vc->vc_rows - b, - vc->vc_cols); - } else if (info->flags & FBINFO_READS_FAST) - fbcon_bmove(vc, t + count, 0, t, 0, - b - t - count, vc->vc_cols); - else - goto redraw_up; - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); - break; - - case SCROLL_PAN_REDRAW: - if ((p->yscroll + count <= - 2 * (p->vrows - vc->vc_rows)) - && ((!scroll_partial && (b - t == vc->vc_rows)) - || (scroll_partial - && (b - t - count > - 3 * vc->vc_rows >> 2)))) { - if (t > 0) - fbcon_redraw_move(vc, p, 0, t, count); - ypan_up_redraw(vc, t, count); - if (vc->vc_rows - b > 0) - fbcon_redraw_move(vc, p, b, - vc->vc_rows - b, b); - } else - fbcon_redraw_move(vc, p, t + count, b - t - count, t); - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); - break; - - case SCROLL_PAN_MOVE: - if ((p->yscroll + count <= - 2 * (p->vrows - vc->vc_rows)) - && ((!scroll_partial && (b - t == vc->vc_rows)) - || (scroll_partial - && (b - t - count > - 3 * vc->vc_rows >> 2)))) { - if (t > 0) - fbcon_bmove(vc, 0, 0, count, 0, t, - vc->vc_cols); - ypan_up(vc, count); - if (vc->vc_rows - b > 0) - fbcon_bmove(vc, b - count, 0, b, 0, - vc->vc_rows - b, - vc->vc_cols); - } else if (info->flags & FBINFO_READS_FAST) - fbcon_bmove(vc, t + count, 0, t, 0, - b - t - count, vc->vc_cols); - else - goto redraw_up; - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); - break; - - case SCROLL_REDRAW: - redraw_up: - fbcon_redraw(vc, p, t, b - t - count, - count * vc->vc_cols); - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); - scr_memsetw((unsigned short *) (vc->vc_origin + - vc->vc_size_row * - (b - count)), - vc->vc_video_erase_char, - vc->vc_size_row * count); - return true; - } - break; + fbcon_redraw(vc, p, t, b - t - count, + count * vc->vc_cols); + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + scr_memsetw((unsigned short *) (vc->vc_origin + + vc->vc_size_row * + (b - count)), + vc->vc_video_erase_char, + vc->vc_size_row * count); + return true; case SM_DOWN: if (count > vc->vc_rows) /* Maximum realistic size */ count = vc->vc_rows; - if (logo_shown >= 0) - goto redraw_down; - switch (p->scrollmode) { - case SCROLL_MOVE: - fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, - -count); - fbcon_clear(vc, t, 0, count, vc->vc_cols); - scr_memsetw((unsigned short *) (vc->vc_origin + - vc->vc_size_row * - t), - vc->vc_video_erase_char, - vc->vc_size_row * count); - return true; - - case SCROLL_WRAP_MOVE: - if (b - t - count > 3 * vc->vc_rows >> 2) { - if (vc->vc_rows - b > 0) - fbcon_bmove(vc, b, 0, b - count, 0, - vc->vc_rows - b, - vc->vc_cols); - ywrap_down(vc, count); - if (t > 0) - fbcon_bmove(vc, count, 0, 0, 0, t, - vc->vc_cols); - } else if (info->flags & FBINFO_READS_FAST) - fbcon_bmove(vc, t, 0, t + count, 0, - b - t - count, vc->vc_cols); - else - goto redraw_down; - fbcon_clear(vc, t, 0, count, vc->vc_cols); - break; - - case SCROLL_PAN_MOVE: - if ((count - p->yscroll <= p->vrows - vc->vc_rows) - && ((!scroll_partial && (b - t == vc->vc_rows)) - || (scroll_partial - && (b - t - count > - 3 * vc->vc_rows >> 2)))) { - if (vc->vc_rows - b > 0) - fbcon_bmove(vc, b, 0, b - count, 0, - vc->vc_rows - b, - vc->vc_cols); - ypan_down(vc, count); - if (t > 0) - fbcon_bmove(vc, count, 0, 0, 0, t, - vc->vc_cols); - } else if (info->flags & FBINFO_READS_FAST) - fbcon_bmove(vc, t, 0, t + count, 0, - b - t - count, vc->vc_cols); - else - goto redraw_down; - fbcon_clear(vc, t, 0, count, vc->vc_cols); - break; - - case SCROLL_PAN_REDRAW: - if ((count - p->yscroll <= p->vrows - vc->vc_rows) - && ((!scroll_partial && (b - t == vc->vc_rows)) - || (scroll_partial - && (b - t - count > - 3 * vc->vc_rows >> 2)))) { - if (vc->vc_rows - b > 0) - fbcon_redraw_move(vc, p, b, vc->vc_rows - b, - b - count); - ypan_down_redraw(vc, t, count); - if (t > 0) - fbcon_redraw_move(vc, p, count, t, 0); - } else - fbcon_redraw_move(vc, p, t, b - t - count, t + count); - fbcon_clear(vc, t, 0, count, vc->vc_cols); - break; - - case SCROLL_REDRAW: - redraw_down: - fbcon_redraw(vc, p, b - 1, b - t - count, - -count * vc->vc_cols); - fbcon_clear(vc, t, 0, count, vc->vc_cols); - scr_memsetw((unsigned short *) (vc->vc_origin + - vc->vc_size_row * - t), - vc->vc_video_erase_char, - vc->vc_size_row * count); - return true; - } + fbcon_redraw(vc, p, b - 1, b - t - count, + -count * vc->vc_cols); + fbcon_clear(vc, t, 0, count, vc->vc_cols); + scr_memsetw((unsigned short *) (vc->vc_origin + + vc->vc_size_row * + t), + vc->vc_video_erase_char, + vc->vc_size_row * count); + return true; } return false; } - -static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, - int height, int width) -{ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_display *p = &fb_display[vc->vc_num]; - - if (fbcon_is_inactive(vc, info)) - return; - - if (!width || !height) - return; - - /* Split blits that cross physical y_wrap case. - * Pathological case involves 4 blits, better to use recursive - * code rather than unrolled case - * - * Recursive invocations don't need to erase the cursor over and - * over again, so we use fbcon_bmove_rec() - */ - fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width, - p->vrows - p->yscroll); -} - -static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, - int dy, int dx, int height, int width, u_int y_break) -{ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - u_int b; - - if (sy < y_break && sy + height > y_break) { - b = y_break - sy; - if (dy < sy) { /* Avoid trashing self */ - fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, - y_break); - fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, - height - b, width, y_break); - } else { - fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, - height - b, width, y_break); - fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, - y_break); - } - return; - } - - if (dy < y_break && dy + height > y_break) { - b = y_break - dy; - if (dy < sy) { /* Avoid trashing self */ - fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, - y_break); - fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, - height - b, width, y_break); - } else { - fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, - height - b, width, y_break); - fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, - y_break); - } - return; - } - ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, - height, width); -} - static void updatescrollmode(struct fbcon_display *p, struct fb_info *info, struct vc_data *vc) @@ -2119,21 +1664,7 @@ static int fbcon_switch(struct vc_data *vc) updatescrollmode(p, info, vc); - switch (p->scrollmode) { - case SCROLL_WRAP_MOVE: - scrollback_phys_max = p->vrows - vc->vc_rows; - break; - case SCROLL_PAN_MOVE: - case SCROLL_PAN_REDRAW: - scrollback_phys_max = p->vrows - 2 * vc->vc_rows; - if (scrollback_phys_max < 0) - scrollback_phys_max = 0; - break; - default: - scrollback_phys_max = 0; - break; - } - + scrollback_phys_max = 0; scrollback_max = 0; scrollback_current = 0; diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h index 9315b360c898..a00603b4451a 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -29,7 +29,6 @@ struct fbcon_display { /* Filled in by the low-level console driver */ const u_char *fontdata; int userfont; /* != 0 if fontdata kmalloc()ed */ - u_short scrollmode; /* Scroll Method */ u_short inverse; /* != 0 text black on white as default */ short yscroll; /* Hardware scrolling */ int vrows; /* number of virtual rows */ @@ -52,8 +51,6 @@ struct fbcon_display { }; struct fbcon_ops { - void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int dy, int dx, int height, int width); void (*clear)(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width); void (*putcs)(struct vc_data *vc, struct fb_info *info, @@ -152,62 +149,6 @@ static inline int attr_col_ec(int shift, struct vc_data *vc, #define attr_bgcol_ec(bgshift, vc, info) attr_col_ec(bgshift, vc, info, 0) #define attr_fgcol_ec(fgshift, vc, info) attr_col_ec(fgshift, vc, info, 1) - /* - * Scroll Method - */ - -/* There are several methods fbcon can use to move text around the screen: - * - * Operation Pan Wrap - *--------------------------------------------- - * SCROLL_MOVE copyarea No No - * SCROLL_PAN_MOVE copyarea Yes No - * SCROLL_WRAP_MOVE copyarea No Yes - * SCROLL_REDRAW imageblit No No - * SCROLL_PAN_REDRAW imageblit Yes No - * SCROLL_WRAP_REDRAW imageblit No Yes - * - * (SCROLL_WRAP_REDRAW is not implemented yet) - * - * In general, fbcon will choose the best scrolling - * method based on the rule below: - * - * Pan/Wrap > accel imageblit > accel copyarea > - * soft imageblit > (soft copyarea) - * - * Exception to the rule: Pan + accel copyarea is - * preferred over Pan + accel imageblit. - * - * The above is typical for PCI/AGP cards. Unless - * overridden, fbcon will never use soft copyarea. - * - * If you need to override the above rule, set the - * appropriate flags in fb_info->flags. For example, - * to prefer copyarea over imageblit, set - * FBINFO_READS_FAST. - * - * Other notes: - * + use the hardware engine to move the text - * (hw-accelerated copyarea() and fillrect()) - * + use hardware-supported panning on a large virtual screen - * + amifb can not only pan, but also wrap the display by N lines - * (i.e. visible line i = physical line (i+N) % yres). - * + read what's already rendered on the screen and - * write it in a different place (this is cfb_copyarea()) - * + re-render the text to the screen - * - * Whether to use wrapping or panning can only be figured out at - * runtime (when we know whether our font height is a multiple - * of the pan/wrap step) - * - */ - -#define SCROLL_MOVE 0x001 -#define SCROLL_PAN_MOVE 0x002 -#define SCROLL_WRAP_MOVE 0x003 -#define SCROLL_REDRAW 0x004 -#define SCROLL_PAN_REDRAW 0x005 - #ifdef CONFIG_FB_TILEBLITTING extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info); #endif diff --git a/drivers/video/fbdev/core/fbcon_ccw.c b/drivers/video/fbdev/core/fbcon_ccw.c index 9cd2c4b05c32..ffa78936eaab 100644 --- a/drivers/video/fbdev/core/fbcon_ccw.c +++ b/drivers/video/fbdev/core/fbcon_ccw.c @@ -59,31 +59,12 @@ static void ccw_update_attr(u8 *dst, u8 *src, int attribute, } } - -static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int dy, int dx, int height, int width) -{ - struct fbcon_ops *ops = info->fbcon_par; - struct fb_copyarea area; - u32 vyres = GETVYRES(ops->p->scrollmode, info); - - area.sx = sy * vc->vc_font.height; - area.sy = vyres - ((sx + width) * vc->vc_font.width); - area.dx = dy * vc->vc_font.height; - area.dy = vyres - ((dx + width) * vc->vc_font.width); - area.width = height * vc->vc_font.height; - area.height = width * vc->vc_font.width; - - info->fbops->fb_copyarea(info, &area); -} - static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) { - struct fbcon_ops *ops = info->fbcon_par; struct fb_fillrect region; int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - u32 vyres = GETVYRES(ops->p->scrollmode, info); + u32 vyres = info->var.yres; region.color = attr_bgcol_ec(bgshift,vc,info); region.dx = sy * vc->vc_font.height; @@ -140,7 +121,7 @@ static void ccw_putcs(struct vc_data *vc, struct fb_info *info, u32 cnt, pitch, size; u32 attribute = get_attribute(info, scr_readw(s)); u8 *dst, *buf = NULL; - u32 vyres = GETVYRES(ops->p->scrollmode, info); + u32 vyres = info->var.yres; if (!ops->fontbuffer) return; @@ -229,7 +210,7 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, int attribute, use_sw = vc->vc_cursor_type & CUR_SW; int err = 1, dx, dy; char *src; - u32 vyres = GETVYRES(ops->p->scrollmode, info); + u32 vyres = info->var.yres; if (!ops->fontbuffer) return; @@ -387,7 +368,7 @@ static int ccw_update_start(struct fb_info *info) { struct fbcon_ops *ops = info->fbcon_par; u32 yoffset; - u32 vyres = GETVYRES(ops->p->scrollmode, info); + u32 vyres = info->var.yres; int err; yoffset = (vyres - info->var.yres) - ops->var.xoffset; @@ -402,7 +383,6 @@ static int ccw_update_start(struct fb_info *info) void fbcon_rotate_ccw(struct fbcon_ops *ops) { - ops->bmove = ccw_bmove; ops->clear = ccw_clear; ops->putcs = ccw_putcs; ops->clear_margins = ccw_clear_margins; diff --git a/drivers/video/fbdev/core/fbcon_cw.c b/drivers/video/fbdev/core/fbcon_cw.c index 88d89fad3f05..92e5b7fb51ee 100644 --- a/drivers/video/fbdev/core/fbcon_cw.c +++ b/drivers/video/fbdev/core/fbcon_cw.c @@ -44,31 +44,12 @@ static void cw_update_attr(u8 *dst, u8 *src, int attribute, } } - -static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int dy, int dx, int height, int width) -{ - struct fbcon_ops *ops = info->fbcon_par; - struct fb_copyarea area; - u32 vxres = GETVXRES(ops->p->scrollmode, info); - - area.sx = vxres - ((sy + height) * vc->vc_font.height); - area.sy = sx * vc->vc_font.width; - area.dx = vxres - ((dy + height) * vc->vc_font.height); - area.dy = dx * vc->vc_font.width; - area.width = height * vc->vc_font.height; - area.height = width * vc->vc_font.width; - - info->fbops->fb_copyarea(info, &area); -} - static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) { - struct fbcon_ops *ops = info->fbcon_par; struct fb_fillrect region; int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - u32 vxres = GETVXRES(ops->p->scrollmode, info); + u32 vxres = info->var.xres; region.color = attr_bgcol_ec(bgshift,vc,info); region.dx = vxres - ((sy + height) * vc->vc_font.height); @@ -125,7 +106,7 @@ static void cw_putcs(struct vc_data *vc, struct fb_info *info, u32 cnt, pitch, size; u32 attribute = get_attribute(info, scr_readw(s)); u8 *dst, *buf = NULL; - u32 vxres = GETVXRES(ops->p->scrollmode, info); + u32 vxres = info->var.xres; if (!ops->fontbuffer) return; @@ -212,7 +193,7 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, int attribute, use_sw = vc->vc_cursor_type & CUR_SW; int err = 1, dx, dy; char *src; - u32 vxres = GETVXRES(ops->p->scrollmode, info); + u32 vxres = info->var.xres; if (!ops->fontbuffer) return; @@ -369,7 +350,7 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, static int cw_update_start(struct fb_info *info) { struct fbcon_ops *ops = info->fbcon_par; - u32 vxres = GETVXRES(ops->p->scrollmode, info); + u32 vxres = info->var.xres; u32 xoffset; int err; @@ -385,7 +366,6 @@ static int cw_update_start(struct fb_info *info) void fbcon_rotate_cw(struct fbcon_ops *ops) { - ops->bmove = cw_bmove; ops->clear = cw_clear; ops->putcs = cw_putcs; ops->clear_margins = cw_clear_margins; diff --git a/drivers/video/fbdev/core/fbcon_rotate.h b/drivers/video/fbdev/core/fbcon_rotate.h index e233444cda66..b528b2e54283 100644 --- a/drivers/video/fbdev/core/fbcon_rotate.h +++ b/drivers/video/fbdev/core/fbcon_rotate.h @@ -11,15 +11,6 @@ #ifndef _FBCON_ROTATE_H #define _FBCON_ROTATE_H -#define GETVYRES(s,i) ({ \ - (s == SCROLL_REDRAW || s == SCROLL_MOVE) ? \ - (i)->var.yres : (i)->var.yres_virtual; }) - -#define GETVXRES(s,i) ({ \ - (s == SCROLL_REDRAW || s == SCROLL_MOVE || !(i)->fix.xpanstep) ? \ - (i)->var.xres : (i)->var.xres_virtual; }) - - static inline int pattern_test_bit(u32 x, u32 y, u32 pitch, const char *pat) { u32 tmp = (y * pitch) + x, index = tmp / 8, bit = tmp % 8; diff --git a/drivers/video/fbdev/core/fbcon_ud.c b/drivers/video/fbdev/core/fbcon_ud.c index 8d5e66b1bdfb..09619bd8e021 100644 --- a/drivers/video/fbdev/core/fbcon_ud.c +++ b/drivers/video/fbdev/core/fbcon_ud.c @@ -44,33 +44,13 @@ static void ud_update_attr(u8 *dst, u8 *src, int attribute, } } - -static void ud_bmove(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int dy, int dx, int height, int width) -{ - struct fbcon_ops *ops = info->fbcon_par; - struct fb_copyarea area; - u32 vyres = GETVYRES(ops->p->scrollmode, info); - u32 vxres = GETVXRES(ops->p->scrollmode, info); - - area.sy = vyres - ((sy + height) * vc->vc_font.height); - area.sx = vxres - ((sx + width) * vc->vc_font.width); - area.dy = vyres - ((dy + height) * vc->vc_font.height); - area.dx = vxres - ((dx + width) * vc->vc_font.width); - area.height = height * vc->vc_font.height; - area.width = width * vc->vc_font.width; - - info->fbops->fb_copyarea(info, &area); -} - static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) { - struct fbcon_ops *ops = info->fbcon_par; struct fb_fillrect region; int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - u32 vyres = GETVYRES(ops->p->scrollmode, info); - u32 vxres = GETVXRES(ops->p->scrollmode, info); + u32 vyres = info->var.yres; + u32 vxres = info->var.xres; region.color = attr_bgcol_ec(bgshift,vc,info); region.dy = vyres - ((sy + height) * vc->vc_font.height); @@ -162,8 +142,8 @@ static void ud_putcs(struct vc_data *vc, struct fb_info *info, u32 mod = vc->vc_font.width % 8, cnt, pitch, size; u32 attribute = get_attribute(info, scr_readw(s)); u8 *dst, *buf = NULL; - u32 vyres = GETVYRES(ops->p->scrollmode, info); - u32 vxres = GETVXRES(ops->p->scrollmode, info); + u32 vyres = info->var.yres; + u32 vxres = info->var.xres; if (!ops->fontbuffer) return; @@ -259,8 +239,8 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, int attribute, use_sw = vc->vc_cursor_type & CUR_SW; int err = 1, dx, dy; char *src; - u32 vyres = GETVYRES(ops->p->scrollmode, info); - u32 vxres = GETVXRES(ops->p->scrollmode, info); + u32 vyres = info->var.yres; + u32 vxres = info->var.xres; if (!ops->fontbuffer) return; @@ -410,8 +390,8 @@ static int ud_update_start(struct fb_info *info) { struct fbcon_ops *ops = info->fbcon_par; int xoffset, yoffset; - u32 vyres = GETVYRES(ops->p->scrollmode, info); - u32 vxres = GETVXRES(ops->p->scrollmode, info); + u32 vyres = info->var.yres; + u32 vxres = info->var.xres; int err; xoffset = vxres - info->var.xres - ops->var.xoffset; @@ -429,7 +409,6 @@ static int ud_update_start(struct fb_info *info) void fbcon_rotate_ud(struct fbcon_ops *ops) { - ops->bmove = ud_bmove; ops->clear = ud_clear; ops->putcs = ud_putcs; ops->clear_margins = ud_clear_margins; diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 7420d2c16e47..826175ad88a2 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -1702,8 +1702,11 @@ static void do_unregister_framebuffer(struct fb_info *fb_info) { unlink_framebuffer(fb_info); if (fb_info->pixmap.addr && - (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) + (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) { kfree(fb_info->pixmap.addr); + fb_info->pixmap.addr = NULL; + } + fb_destroy_modelist(&fb_info->modelist); registered_fb[fb_info->node] = NULL; num_registered_fb--; diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index 65dae05fff8e..26892940c213 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -230,7 +230,7 @@ static ssize_t show_bpp(struct device *device, struct device_attribute *attr, char *buf) { struct fb_info *fb_info = dev_get_drvdata(device); - return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel); + return sysfs_emit(buf, "%d\n", fb_info->var.bits_per_pixel); } static ssize_t store_rotate(struct device *device, @@ -257,7 +257,7 @@ static ssize_t show_rotate(struct device *device, { struct fb_info *fb_info = dev_get_drvdata(device); - return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate); + return sysfs_emit(buf, "%d\n", fb_info->var.rotate); } static ssize_t store_virtual(struct device *device, @@ -285,7 +285,7 @@ static ssize_t show_virtual(struct device *device, struct device_attribute *attr, char *buf) { struct fb_info *fb_info = dev_get_drvdata(device); - return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual, + return sysfs_emit(buf, "%d,%d\n", fb_info->var.xres_virtual, fb_info->var.yres_virtual); } @@ -293,7 +293,7 @@ static ssize_t show_stride(struct device *device, struct device_attribute *attr, char *buf) { struct fb_info *fb_info = dev_get_drvdata(device); - return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length); + return sysfs_emit(buf, "%d\n", fb_info->fix.line_length); } static ssize_t store_blank(struct device *device, @@ -381,7 +381,7 @@ static ssize_t show_pan(struct device *device, struct device_attribute *attr, char *buf) { struct fb_info *fb_info = dev_get_drvdata(device); - return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset, + return sysfs_emit(buf, "%d,%d\n", fb_info->var.xoffset, fb_info->var.yoffset); } @@ -390,7 +390,7 @@ static ssize_t show_name(struct device *device, { struct fb_info *fb_info = dev_get_drvdata(device); - return snprintf(buf, PAGE_SIZE, "%s\n", fb_info->fix.id); + return sysfs_emit(buf, "%s\n", fb_info->fix.id); } static ssize_t store_fbstate(struct device *device, @@ -418,7 +418,7 @@ static ssize_t show_fbstate(struct device *device, struct device_attribute *attr, char *buf) { struct fb_info *fb_info = dev_get_drvdata(device); - return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state); + return sysfs_emit(buf, "%d\n", fb_info->state); } #if IS_ENABLED(CONFIG_FB_BACKLIGHT) diff --git a/drivers/video/fbdev/core/tileblit.c b/drivers/video/fbdev/core/tileblit.c index 2768eff247ba..72af95053bcb 100644 --- a/drivers/video/fbdev/core/tileblit.c +++ b/drivers/video/fbdev/core/tileblit.c @@ -16,21 +16,6 @@ #include <asm/types.h> #include "fbcon.h" -static void tile_bmove(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int dy, int dx, int height, int width) -{ - struct fb_tilearea area; - - area.sx = sx; - area.sy = sy; - area.dx = dx; - area.dy = dy; - area.height = height; - area.width = width; - - info->tileops->fb_tilecopy(info, &area); -} - static void tile_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) { @@ -133,7 +118,6 @@ void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info) struct fb_tilemap map; struct fbcon_ops *ops = info->fbcon_par; - ops->bmove = tile_bmove; ops->clear = tile_clear; ops->putcs = tile_putcs; ops->clear_margins = tile_clear_margins; diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c index 3d090d2d9ed9..b495c09e6102 100644 --- a/drivers/video/fbdev/omap/omapfb_main.c +++ b/drivers/video/fbdev/omap/omapfb_main.c @@ -1555,6 +1555,7 @@ static void omapfb_free_resources(struct omapfb_device *fbdev, int state) case 1: dev_set_drvdata(fbdev->dev, NULL); kfree(fbdev); + break; case 0: /* nothing to free */ break; diff --git a/drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c b/drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c index 6dbe265b312d..8f355d1caf86 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c @@ -19,14 +19,14 @@ static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s\n", + return sysfs_emit(buf, "%s\n", dssdev->name ? dssdev->name : ""); } static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", omapdss_device_is_enabled(dssdev)); } @@ -59,7 +59,7 @@ static ssize_t display_enabled_store(struct omap_dss_device *dssdev, static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", dssdev->driver->get_te ? dssdev->driver->get_te(dssdev) : 0); } @@ -93,7 +93,7 @@ static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf) dssdev->driver->get_timings(dssdev, &t); - return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", + return sysfs_emit(buf, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", t.pixelclock, t.x_res, t.hfp, t.hbp, t.hsw, t.y_res, t.vfp, t.vbp, t.vsw); @@ -143,7 +143,7 @@ static ssize_t display_rotate_show(struct omap_dss_device *dssdev, char *buf) if (!dssdev->driver->get_rotate) return -ENOENT; rotate = dssdev->driver->get_rotate(dssdev); - return snprintf(buf, PAGE_SIZE, "%u\n", rotate); + return sysfs_emit(buf, "%u\n", rotate); } static ssize_t display_rotate_store(struct omap_dss_device *dssdev, @@ -171,7 +171,7 @@ static ssize_t display_mirror_show(struct omap_dss_device *dssdev, char *buf) if (!dssdev->driver->get_mirror) return -ENOENT; mirror = dssdev->driver->get_mirror(dssdev); - return snprintf(buf, PAGE_SIZE, "%u\n", mirror); + return sysfs_emit(buf, "%u\n", mirror); } static ssize_t display_mirror_store(struct omap_dss_device *dssdev, @@ -203,7 +203,7 @@ static ssize_t display_wss_show(struct omap_dss_device *dssdev, char *buf) wss = dssdev->driver->get_wss(dssdev); - return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); + return sysfs_emit(buf, "0x%05x\n", wss); } static ssize_t display_wss_store(struct omap_dss_device *dssdev, diff --git a/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c b/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c index b52cc1af0959..3ffb1fe4a38a 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c @@ -22,14 +22,14 @@ static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); + return sysfs_emit(buf, "%s\n", mgr->name); } static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) { struct omap_dss_device *dssdev = mgr->get_device(mgr); - return snprintf(buf, PAGE_SIZE, "%s\n", dssdev ? + return sysfs_emit(buf, "%s\n", dssdev ? dssdev->name : "<none>"); } @@ -120,7 +120,7 @@ static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, mgr->get_manager_info(mgr, &info); - return snprintf(buf, PAGE_SIZE, "%#x\n", info.default_color); + return sysfs_emit(buf, "%#x\n", info.default_color); } static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, @@ -165,7 +165,7 @@ static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, key_type = info.trans_key_type; BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); - return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); + return sysfs_emit(buf, "%s\n", trans_key_type_str[key_type]); } static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, @@ -200,7 +200,7 @@ static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, mgr->get_manager_info(mgr, &info); - return snprintf(buf, PAGE_SIZE, "%#x\n", info.trans_key); + return sysfs_emit(buf, "%#x\n", info.trans_key); } static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, @@ -236,7 +236,7 @@ static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, mgr->get_manager_info(mgr, &info); - return snprintf(buf, PAGE_SIZE, "%d\n", info.trans_enabled); + return sysfs_emit(buf, "%d\n", info.trans_enabled); } static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, @@ -275,7 +275,7 @@ static ssize_t manager_alpha_blending_enabled_show( mgr->get_manager_info(mgr, &info); - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", info.partial_alpha_enabled); } @@ -316,7 +316,7 @@ static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr, mgr->get_manager_info(mgr, &info); - return snprintf(buf, PAGE_SIZE, "%d\n", info.cpr_enable); + return sysfs_emit(buf, "%d\n", info.cpr_enable); } static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr, @@ -358,7 +358,7 @@ static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr, mgr->get_manager_info(mgr, &info); - return snprintf(buf, PAGE_SIZE, + return sysfs_emit(buf, "%d %d %d %d %d %d %d %d %d\n", info.cpr_coefs.rr, info.cpr_coefs.rg, diff --git a/drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c b/drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c index 36acf366213a..421dcb7564ad 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c @@ -22,12 +22,12 @@ static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); + return sysfs_emit(buf, "%s\n", ovl->name); } static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s\n", + return sysfs_emit(buf, "%s\n", ovl->manager ? ovl->manager->name : "<none>"); } @@ -108,7 +108,7 @@ static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) ovl->get_overlay_info(ovl, &info); - return snprintf(buf, PAGE_SIZE, "%d,%d\n", + return sysfs_emit(buf, "%d,%d\n", info.width, info.height); } @@ -118,7 +118,7 @@ static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) ovl->get_overlay_info(ovl, &info); - return snprintf(buf, PAGE_SIZE, "%d\n", info.screen_width); + return sysfs_emit(buf, "%d\n", info.screen_width); } static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) @@ -127,7 +127,7 @@ static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) ovl->get_overlay_info(ovl, &info); - return snprintf(buf, PAGE_SIZE, "%d,%d\n", + return sysfs_emit(buf, "%d,%d\n", info.pos_x, info.pos_y); } @@ -166,7 +166,7 @@ static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) ovl->get_overlay_info(ovl, &info); - return snprintf(buf, PAGE_SIZE, "%d,%d\n", + return sysfs_emit(buf, "%d,%d\n", info.out_width, info.out_height); } @@ -201,7 +201,7 @@ static ssize_t overlay_output_size_store(struct omap_overlay *ovl, static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", ovl->is_enabled(ovl)); + return sysfs_emit(buf, "%d\n", ovl->is_enabled(ovl)); } static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, @@ -231,7 +231,7 @@ static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) ovl->get_overlay_info(ovl, &info); - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", info.global_alpha); } @@ -273,7 +273,7 @@ static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl, ovl->get_overlay_info(ovl, &info); - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", info.pre_mult_alpha); } @@ -314,7 +314,7 @@ static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf) ovl->get_overlay_info(ovl, &info); - return snprintf(buf, PAGE_SIZE, "%d\n", info.zorder); + return sysfs_emit(buf, "%d\n", info.zorder); } static ssize_t overlay_zorder_store(struct omap_overlay *ovl, diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c b/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c index 2d39dbfa742e..06dc41aa0354 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c @@ -29,7 +29,7 @@ static ssize_t show_rotate_type(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type); + return sysfs_emit(buf, "%d\n", ofbi->rotation_type); } static ssize_t store_rotate_type(struct device *dev, @@ -83,7 +83,7 @@ static ssize_t show_mirror(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror); + return sysfs_emit(buf, "%d\n", ofbi->mirror); } static ssize_t store_mirror(struct device *dev, @@ -415,7 +415,7 @@ static ssize_t show_size(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region->size); + return sysfs_emit(buf, "%lu\n", ofbi->region->size); } static ssize_t store_size(struct device *dev, struct device_attribute *attr, @@ -492,7 +492,7 @@ static ssize_t show_phys(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region->paddr); + return sysfs_emit(buf, "%0x\n", ofbi->region->paddr); } static ssize_t show_virt(struct device *dev, @@ -501,7 +501,7 @@ static ssize_t show_virt(struct device *dev, struct fb_info *fbi = dev_get_drvdata(dev); struct omapfb_info *ofbi = FB2OFB(fbi); - return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr); + return sysfs_emit(buf, "%p\n", ofbi->region->vaddr); } static ssize_t show_upd_mode(struct device *dev, @@ -516,7 +516,7 @@ static ssize_t show_upd_mode(struct device *dev, if (r) return r; - return snprintf(buf, PAGE_SIZE, "%u\n", (unsigned)mode); + return sysfs_emit(buf, "%u\n", (unsigned int)mode); } static ssize_t store_upd_mode(struct device *dev, struct device_attribute *attr, diff --git a/drivers/video/fbdev/skeletonfb.c b/drivers/video/fbdev/skeletonfb.c index bcacfb6934fa..0fe922f726e9 100644 --- a/drivers/video/fbdev/skeletonfb.c +++ b/drivers/video/fbdev/skeletonfb.c @@ -505,15 +505,15 @@ void xxxfb_fillrect(struct fb_info *p, const struct fb_fillrect *region) } /** - * xxxfb_copyarea - REQUIRED function. Can use generic routines if - * non acclerated hardware and packed pixel based. + * xxxfb_copyarea - OBSOLETE function. * Copies one area of the screen to another area. + * Will be deleted in a future version * * @info: frame buffer structure that represents a single frame buffer * @area: Structure providing the data to copy the framebuffer contents * from one region to another. * - * This drawing operation copies a rectangular area from one area of the + * This drawing operation copied a rectangular area from one area of the * screen to another area. */ void xxxfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) @@ -645,9 +645,9 @@ static const struct fb_ops xxxfb_ops = { .fb_setcolreg = xxxfb_setcolreg, .fb_blank = xxxfb_blank, .fb_pan_display = xxxfb_pan_display, - .fb_fillrect = xxxfb_fillrect, /* Needed !!! */ - .fb_copyarea = xxxfb_copyarea, /* Needed !!! */ - .fb_imageblit = xxxfb_imageblit, /* Needed !!! */ + .fb_fillrect = xxxfb_fillrect, /* Needed !!! */ + .fb_copyarea = xxxfb_copyarea, /* Obsolete */ + .fb_imageblit = xxxfb_imageblit, /* Needed !!! */ .fb_cursor = xxxfb_cursor, /* Optional !!! */ .fb_sync = xxxfb_sync, .fb_ioctl = xxxfb_ioctl, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 379746d3266f..b501d0badaea 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -27,6 +27,7 @@ #include <linux/llist.h> #include <linux/ctype.h> #include <linux/hdmi.h> +#include <linux/notifier.h> #include <drm/drm_mode_object.h> #include <drm/drm_util.h> @@ -40,6 +41,7 @@ struct drm_encoder; struct drm_property; struct drm_property_blob; struct drm_printer; +struct drm_privacy_screen; struct edid; struct i2c_adapter; @@ -320,6 +322,30 @@ struct drm_monitor_range_info { u8 max_vfreq; }; +/** + * enum drm_privacy_screen_status - privacy screen status + * + * This enum is used to track and control the state of the integrated privacy + * screen present on some display panels, via the "privacy-screen sw-state" + * and "privacy-screen hw-state" properties. Note the _LOCKED enum values + * are only valid for the "privacy-screen hw-state" property. + * + * @PRIVACY_SCREEN_DISABLED: + * The privacy-screen on the panel is disabled + * @PRIVACY_SCREEN_ENABLED: + * The privacy-screen on the panel is enabled + * @PRIVACY_SCREEN_DISABLED_LOCKED: + * The privacy-screen on the panel is disabled and locked (cannot be changed) + * @PRIVACY_SCREEN_ENABLED_LOCKED: + * The privacy-screen on the panel is enabled and locked (cannot be changed) + */ +enum drm_privacy_screen_status { + PRIVACY_SCREEN_DISABLED = 0, + PRIVACY_SCREEN_ENABLED, + PRIVACY_SCREEN_DISABLED_LOCKED, + PRIVACY_SCREEN_ENABLED_LOCKED, +}; + /* * This is a consolidated colorimetry list supported by HDMI and * DP protocol standard. The respective connectors will register @@ -794,6 +820,12 @@ struct drm_connector_state { u8 max_bpc; /** + * @privacy_screen_sw_state: See :ref:`Standard Connector + * Properties<standard_connector_properties>` + */ + enum drm_privacy_screen_status privacy_screen_sw_state; + + /** * @hdr_output_metadata: * DRM blob property for HDR output metadata */ @@ -1421,6 +1453,24 @@ struct drm_connector { */ struct drm_property *max_bpc_property; + /** @privacy_screen: drm_privacy_screen for this connector, or NULL. */ + struct drm_privacy_screen *privacy_screen; + + /** @privacy_screen_notifier: privacy-screen notifier_block */ + struct notifier_block privacy_screen_notifier; + + /** + * @privacy_screen_sw_state_property: Optional atomic property for the + * connector to control the integrated privacy screen. + */ + struct drm_property *privacy_screen_sw_state_property; + + /** + * @privacy_screen_hw_state_property: Optional atomic property for the + * connector to report the actual integrated privacy screen state. + */ + struct drm_property *privacy_screen_hw_state_property; + #define DRM_CONNECTOR_POLL_HPD (1 << 0) #define DRM_CONNECTOR_POLL_CONNECT (1 << 1) #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) @@ -1744,6 +1794,11 @@ int drm_connector_set_panel_orientation_with_quirk( int width, int height); int drm_connector_attach_max_bpc_property(struct drm_connector *connector, int min, int max); +void drm_connector_create_privacy_screen_properties(struct drm_connector *conn); +void drm_connector_attach_privacy_screen_properties(struct drm_connector *conn); +void drm_connector_attach_privacy_screen_provider( + struct drm_connector *connector, struct drm_privacy_screen *priv); +void drm_connector_update_privacy_screen(const struct drm_connector_state *connector_state); /** * struct drm_tile_group - Tile group metadata diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index b52df4db3e8f..afdf7f4183f9 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1114,8 +1114,15 @@ struct drm_panel; # define DP_UHBR20 (1 << 1) # define DP_UHBR13_5 (1 << 2) -#define DP_128B132B_TRAINING_AUX_RD_INTERVAL 0x2216 /* 2.0 */ -# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK 0x7f +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL 0x2216 /* 2.0 */ +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK 0x7f +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US 0x00 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS 0x01 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS 0x02 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS 0x03 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS 0x04 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS 0x05 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS 0x06 #define DP_TEST_264BIT_CUSTOM_PATTERN_7_0 0x2230 #define DP_TEST_264BIT_CUSTOM_PATTERN_263_256 0x2250 @@ -1389,6 +1396,11 @@ enum drm_dp_phy { # define DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED BIT(0) # define DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED BIT(1) +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 0xf0022 /* 2.0 */ +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy) \ + DP_LTTPR_REG(dp_phy, DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) +/* see DP_128B132B_TRAINING_AUX_RD_INTERVAL for values */ + #define DP_LANE0_1_STATUS_PHY_REPEATER1 0xf0030 /* 1.3 */ #define DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy) \ DP_LTTPR_REG(dp_phy, DP_LANE0_1_STATUS_PHY_REPEATER1) @@ -1527,6 +1539,11 @@ u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZ #define DP_LTTPR_COMMON_CAP_SIZE 8 #define DP_LTTPR_PHY_CAP_SIZE 3 +int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], + enum drm_dp_phy dp_phy, bool uhbr); +int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], + enum drm_dp_phy dp_phy, bool uhbr); + void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]); void drm_dp_lttpr_link_train_clock_recovery_delay(void); diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index aafd07388eb7..b84693fbd2b5 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -24,6 +24,8 @@ #ifndef DRM_MODESET_LOCK_H_ #define DRM_MODESET_LOCK_H_ +#include <linux/types.h> /* stackdepot.h is not self-contained */ +#include <linux/stackdepot.h> #include <linux/ww_mutex.h> struct drm_modeset_lock; @@ -52,6 +54,12 @@ struct drm_modeset_acquire_ctx { struct drm_modeset_lock *contended; /* + * Stack depot for debugging when a contended lock was not backed off + * from. + */ + depot_stack_handle_t stack_depot; + + /* * list of held locks (drm_modeset_lock) */ struct list_head locked; diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index b9b093add92e..99f79ac8b4cd 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -49,6 +49,7 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, struct drm_bridge **bridge); int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, const struct device_node *port2); +int drm_of_lvds_get_data_mapping(const struct device_node *port); #else static inline uint32_t drm_of_crtc_port_mask(struct drm_device *dev, struct device_node *port) @@ -98,6 +99,12 @@ drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, { return -EINVAL; } + +static inline int +drm_of_lvds_get_data_mapping(const struct device_node *port) +{ + return -EINVAL; +} #endif /* diff --git a/include/drm/drm_privacy_screen_consumer.h b/include/drm/drm_privacy_screen_consumer.h new file mode 100644 index 000000000000..7f66a90d15b7 --- /dev/null +++ b/include/drm/drm_privacy_screen_consumer.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#ifndef __DRM_PRIVACY_SCREEN_CONSUMER_H__ +#define __DRM_PRIVACY_SCREEN_CONSUMER_H__ + +#include <linux/device.h> +#include <drm/drm_connector.h> + +struct drm_privacy_screen; + +#if IS_ENABLED(CONFIG_DRM_PRIVACY_SCREEN) +struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev, + const char *con_id); +void drm_privacy_screen_put(struct drm_privacy_screen *priv); + +int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status sw_state); +void drm_privacy_screen_get_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status *sw_state_ret, + enum drm_privacy_screen_status *hw_state_ret); + +int drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb); +int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb); +#else +static inline struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev, + const char *con_id) +{ + return ERR_PTR(-ENODEV); +} +static inline void drm_privacy_screen_put(struct drm_privacy_screen *priv) +{ +} +static inline int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status sw_state) +{ + return -ENODEV; +} +static inline void drm_privacy_screen_get_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status *sw_state_ret, + enum drm_privacy_screen_status *hw_state_ret) +{ + *sw_state_ret = PRIVACY_SCREEN_DISABLED; + *hw_state_ret = PRIVACY_SCREEN_DISABLED; +} +static inline int drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb) +{ + return -ENODEV; +} +static inline int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb) +{ + return -ENODEV; +} +#endif + +#endif diff --git a/include/drm/drm_privacy_screen_driver.h b/include/drm/drm_privacy_screen_driver.h new file mode 100644 index 000000000000..24591b607675 --- /dev/null +++ b/include/drm/drm_privacy_screen_driver.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#ifndef __DRM_PRIVACY_SCREEN_DRIVER_H__ +#define __DRM_PRIVACY_SCREEN_DRIVER_H__ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <drm/drm_connector.h> + +struct drm_privacy_screen; + +/** + * struct drm_privacy_screen_ops - drm_privacy_screen operations + * + * Defines the operations which the privacy-screen class code may call. + * These functions should be implemented by the privacy-screen driver. + */ +struct drm_privacy_screen_ops { + /** + * @set_sw_state: Called to request a change of the privacy-screen + * state. The privacy-screen class code contains a check to avoid this + * getting called when the hw_state reports the state is locked. + * It is the driver's responsibility to update sw_state and hw_state. + * This is always called with the drm_privacy_screen's lock held. + */ + int (*set_sw_state)(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status sw_state); + /** + * @get_hw_state: Called to request that the driver gets the current + * privacy-screen state from the hardware and then updates sw_state and + * hw_state accordingly. This will be called by the core just before + * the privacy-screen is registered in sysfs. + */ + void (*get_hw_state)(struct drm_privacy_screen *priv); +}; + +/** + * struct drm_privacy_screen - central privacy-screen structure + * + * Central privacy-screen structure, this contains the struct device used + * to register the screen in sysfs, the screen's state, ops, etc. + */ +struct drm_privacy_screen { + /** @dev: device used to register the privacy-screen in sysfs. */ + struct device dev; + /** @lock: mutex protection all fields in this struct. */ + struct mutex lock; + /** @list: privacy-screen devices list list-entry. */ + struct list_head list; + /** @notifier_head: privacy-screen notifier head. */ + struct blocking_notifier_head notifier_head; + /** + * @ops: &struct drm_privacy_screen_ops for this privacy-screen. + * This is NULL if the driver has unregistered the privacy-screen. + */ + const struct drm_privacy_screen_ops *ops; + /** + * @sw_state: The privacy-screen's software state, see + * :ref:`Standard Connector Properties<standard_connector_properties>` + * for more info. + */ + enum drm_privacy_screen_status sw_state; + /** + * @hw_state: The privacy-screen's hardware state, see + * :ref:`Standard Connector Properties<standard_connector_properties>` + * for more info. + */ + enum drm_privacy_screen_status hw_state; +}; + +struct drm_privacy_screen *drm_privacy_screen_register( + struct device *parent, const struct drm_privacy_screen_ops *ops); +void drm_privacy_screen_unregister(struct drm_privacy_screen *priv); + +void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv); + +#endif diff --git a/include/drm/drm_privacy_screen_machine.h b/include/drm/drm_privacy_screen_machine.h new file mode 100644 index 000000000000..02e5371904d3 --- /dev/null +++ b/include/drm/drm_privacy_screen_machine.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#ifndef __DRM_PRIVACY_SCREEN_MACHINE_H__ +#define __DRM_PRIVACY_SCREEN_MACHINE_H__ + +#include <linux/list.h> + +/** + * struct drm_privacy_screen_lookup - static privacy-screen lookup list entry + * + * Used for the static lookup-list for mapping privacy-screen consumer + * dev-connector pairs to a privacy-screen provider. + */ +struct drm_privacy_screen_lookup { + /** @list: Lookup list list-entry. */ + struct list_head list; + /** @dev_id: Consumer device name or NULL to match all devices. */ + const char *dev_id; + /** @con_id: Consumer connector name or NULL to match all connectors. */ + const char *con_id; + /** @provider: dev_name() of the privacy_screen provider. */ + const char *provider; +}; + +void drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup *lookup); +void drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup *lookup); + +#if IS_ENABLED(CONFIG_DRM_PRIVACY_SCREEN) && IS_ENABLED(CONFIG_X86) +void drm_privacy_screen_lookup_init(void); +void drm_privacy_screen_lookup_exit(void); +#else +static inline void drm_privacy_screen_lookup_init(void) +{ +} +static inline void drm_privacy_screen_lookup_exit(void) +{ +} +#endif + +#endif diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 76d7c33884da..0551e2587f14 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -351,9 +351,10 @@ bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, * @bo: Pointer to a ttm_buffer_object to be initialized. * @size: Requested size of buffer object. * @type: Requested type of buffer object. - * @flags: Initial placement flags. + * @placement: Initial placement for buffer object. * @page_alignment: Data alignment in pages. * @ctx: TTM operation context for memory allocation. + * @sg: Scatter-gather table. * @resv: Pointer to a dma_resv, or NULL to let ttm allocate one. * @destroy: Destroy function. Use NULL for kfree(). * @@ -394,7 +395,7 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, * @bo: Pointer to a ttm_buffer_object to be initialized. * @size: Requested size of buffer object. * @type: Requested type of buffer object. - * @flags: Initial placement flags. + * @placement: Initial placement for buffer object. * @page_alignment: Data alignment in pages. * @interruptible: If needing to sleep to wait for GPU resources, * sleep interruptible. @@ -402,6 +403,7 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, * holds a pointer to a persistent shmem object. Typically, this would * point to the shmem object backing a GEM object if TTM is used to back a * GEM user interface. + * @sg: Scatter-gather table. * @resv: Pointer to a dma_resv, or NULL to let ttm allocate one. * @destroy: Destroy function. Use NULL for kfree(). * diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 02c2eb874da6..9807aef33685 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -433,8 +433,8 @@ struct dma_buf { /** @poll: for userspace poll support */ wait_queue_head_t poll; - /** @cb_excl: for userspace poll support */ - /** @cb_shared: for userspace poll support */ + /** @cb_in: for userspace poll support */ + /** @cb_out: for userspace poll support */ struct dma_buf_poll_cb_t { struct dma_fence_cb cb; wait_queue_head_t *poll; diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h index 8b6c20636a79..dbd235ab447f 100644 --- a/include/linux/dma-resv.h +++ b/include/linux/dma-resv.h @@ -170,15 +170,20 @@ struct dma_resv_iter { /** @index: index into the shared fences */ unsigned int index; - /** @fences: the shared fences */ + /** @fences: the shared fences; private, *MUST* not dereference */ struct dma_resv_list *fences; + /** @shared_count: number of shared fences */ + unsigned int shared_count; + /** @is_restarted: true if this is the first returned fence */ bool is_restarted; }; struct dma_fence *dma_resv_iter_first_unlocked(struct dma_resv_iter *cursor); struct dma_fence *dma_resv_iter_next_unlocked(struct dma_resv_iter *cursor); +struct dma_fence *dma_resv_iter_first(struct dma_resv_iter *cursor); +struct dma_fence *dma_resv_iter_next(struct dma_resv_iter *cursor); /** * dma_resv_iter_begin - initialize a dma_resv_iter object @@ -244,6 +249,24 @@ static inline bool dma_resv_iter_is_restarted(struct dma_resv_iter *cursor) for (fence = dma_resv_iter_first_unlocked(cursor); \ fence; fence = dma_resv_iter_next_unlocked(cursor)) +/** + * dma_resv_for_each_fence - fence iterator + * @cursor: a struct dma_resv_iter pointer + * @obj: a dma_resv object pointer + * @all_fences: true if all fences should be returned + * @fence: the current fence + * + * Iterate over the fences in a struct dma_resv object while holding the + * &dma_resv.lock. @all_fences controls if the shared fences are returned as + * well. The cursor initialisation is part of the iterator and the fence stays + * valid as long as the lock is held and so no extra reference to the fence is + * taken. + */ +#define dma_resv_for_each_fence(cursor, obj, all_fences, fence) \ + for (dma_resv_iter_begin(cursor, obj, all_fences), \ + fence = dma_resv_iter_first(cursor); fence; \ + fence = dma_resv_iter_next(cursor)) + #define dma_resv_held(obj) lockdep_is_held(&(obj)->lock.base) #define dma_resv_assert_held(obj) lockdep_assert_held(&(obj)->lock.base) diff --git a/include/linux/fb.h b/include/linux/fb.h index 5950f8f5dc74..6f3db99ab990 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -262,7 +262,7 @@ struct fb_ops { /* Draws a rectangle */ void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); - /* Copy data from area to another */ + /* Copy data from area to another. Obsolete. */ void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); /* Draws a image to the display */ void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); |