drm-misc-next for 5.10:
UAPI Changes: Cross-subsystem Changes: Core Changes: - ttm: various cleanups and reworks of the API Driver Changes: - ast: various cleanups - gma500: A few fixes, conversion to GPIOd API - hisilicon: Change of maintainer, various reworks - ingenic: Clock handling and formats support improvements - mcde: improvements to the DSI support - mgag200: Support G200 desktop cards - mxsfb: Support the i.MX7 and i.MX8M and the alpha plane - panfrost: support devfreq - ps8640: Retrieve the EDID from eDP control, misc improvements - tidss: Add a workaround for AM65xx YUV formats handling - virtio: a few cleanups, support for virtio-gpu exported resources - bridges: Support the chained bridges on more drivers, new bridges: Toshiba TC358762, Toshiba TC358775, Lontium LT9611 - panels: Convert to dev_ based logging, read orientation from the DT, various fixes, new panels: Mantix MLAF057WE51-X, Chefree CH101OLHLWH-002, Powertip PH800480T013, KingDisplay KD116N21-30NV-A010 -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCX0fXGwAKCRDj7w1vZxhR xTmMAQDPmfSsBLLNnDxu4++zFrQ7OKmNSHCkVr4nAQ/yg3GVPQEAuRw6qPwPWuV3 +jEPxaQSSmHOhx/jXfolV1tJaE/FHgA= =WYoO -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2020-08-27' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for 5.10: UAPI Changes: Cross-subsystem Changes: Core Changes: - ttm: various cleanups and reworks of the API Driver Changes: - ast: various cleanups - gma500: A few fixes, conversion to GPIOd API - hisilicon: Change of maintainer, various reworks - ingenic: Clock handling and formats support improvements - mcde: improvements to the DSI support - mgag200: Support G200 desktop cards - mxsfb: Support the i.MX7 and i.MX8M and the alpha plane - panfrost: support devfreq - ps8640: Retrieve the EDID from eDP control, misc improvements - tidss: Add a workaround for AM65xx YUV formats handling - virtio: a few cleanups, support for virtio-gpu exported resources - bridges: Support the chained bridges on more drivers, new bridges: Toshiba TC358762, Toshiba TC358775, Lontium LT9611 - panels: Convert to dev_ based logging, read orientation from the DT, various fixes, new panels: Mantix MLAF057WE51-X, Chefree CH101OLHLWH-002, Powertip PH800480T013, KingDisplay KD116N21-30NV-A010 Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maxime Ripard <maxime@cerno.tech> Link: https://patchwork.freedesktop.org/patch/msgid/20200827155517.do6emeacetpturli@gilmour.lan
This commit is contained in:
Коммит
cbc2e82932
|
@ -0,0 +1,176 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/bridge/lontium,lt9611.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Lontium LT9611 2 Port MIPI to HDMI Bridge
|
||||
|
||||
maintainers:
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
|
||||
description: |
|
||||
The LT9611 is a bridge device which converts DSI to HDMI
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- lontium,lt9611
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#sound-dai-cells":
|
||||
const: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
description: GPIO connected to active high RESET pin.
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator for 1.8V MIPI phy power.
|
||||
|
||||
vcc-supply:
|
||||
description: Regulator for 3.3V IO power.
|
||||
|
||||
ports:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
port@0:
|
||||
type: object
|
||||
description: |
|
||||
Primary MIPI port-1 for MIPI input
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^endpoint(@[0-9])$":
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
remote-endpoint:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
port@1:
|
||||
type: object
|
||||
description: |
|
||||
Additional MIPI port-2 for MIPI input, used in combination
|
||||
with primary MIPI port-1 to drive higher resolution displays
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 1
|
||||
|
||||
patternProperties:
|
||||
"^endpoint(@[0-9])$":
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
remote-endpoint:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
port@2:
|
||||
type: object
|
||||
description: |
|
||||
HDMI port for HDMI output
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 2
|
||||
|
||||
patternProperties:
|
||||
"^endpoint(@[0-9])$":
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
remote-endpoint:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- port@0
|
||||
- port@2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- vdd-supply
|
||||
- vcc-supply
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c10 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hdmi-bridge@3b {
|
||||
compatible = "lontium,lt9611";
|
||||
reg = <0x3b>;
|
||||
|
||||
reset-gpios = <&tlmm 128 GPIO_ACTIVE_HIGH>;
|
||||
interrupts-extended = <&tlmm 84 IRQ_TYPE_EDGE_FALLING>;
|
||||
|
||||
vdd-supply = <<9611_1v8>;
|
||||
vcc-supply = <<9611_3v3>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
lt9611_a: endpoint {
|
||||
remote-endpoint = <&dsi0_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
lt9611_b: endpoint {
|
||||
remote-endpoint = <&dsi1_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
lt9611_out: endpoint {
|
||||
remote-endpoint = <&hdmi_con>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,127 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/bridge/toshiba,tc358762.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Toshiba TC358762 MIPI DSI to MIPI DPI bridge
|
||||
|
||||
maintainers:
|
||||
- Marek Vasut <marex@denx.de>
|
||||
|
||||
description: |
|
||||
The TC358762 is bridge device which converts MIPI DSI to MIPI DPI.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- toshiba,tc358762
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: virtual channel number of a DSI peripheral
|
||||
|
||||
vddc-supply:
|
||||
description: Regulator for 1.2V internal core power.
|
||||
|
||||
ports:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
port@0:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
description: |
|
||||
Video port for MIPI DSI input
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
endpoint:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
remote-endpoint: true
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
port@1:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
description: |
|
||||
Video port for MIPI DPI output (panel or connector).
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 1
|
||||
|
||||
patternProperties:
|
||||
endpoint:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
remote-endpoint: true
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- port@0
|
||||
- port@1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vddc-supply
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
bridge@0 {
|
||||
reg = <0>;
|
||||
compatible = "toshiba,tc358762";
|
||||
vddc-supply = <&vcc_1v2_reg>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
bridge_in: endpoint {
|
||||
remote-endpoint = <&dsi_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
bridge_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,215 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/bridge/toshiba,tc358775.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Toshiba TC358775 DSI to LVDS bridge bindings
|
||||
|
||||
maintainers:
|
||||
- Vinay Simha BN <simhavcs@gmail.com>
|
||||
|
||||
description: |
|
||||
This binding supports DSI to LVDS bridge TC358775
|
||||
|
||||
MIPI DSI-RX Data 4-lane, CLK 1-lane with data rates up to 800 Mbps/lane.
|
||||
Video frame size:
|
||||
Up to 1600x1200 24-bit/pixel resolution for single-link LVDS display panel
|
||||
limited by 135 MHz LVDS speed
|
||||
Up to WUXGA (1920x1200 24-bit pixels) resolution for dual-link LVDS display
|
||||
panel, limited by 270 MHz LVDS speed.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: toshiba,tc358775
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: i2c address of the bridge, 0x0f
|
||||
|
||||
vdd-supply:
|
||||
maxItems: 1
|
||||
description: 1.2V LVDS Power Supply
|
||||
|
||||
vddio-supply:
|
||||
maxItems: 1
|
||||
description: 1.8V IO Power Supply
|
||||
|
||||
stby-gpios:
|
||||
maxItems: 1
|
||||
description: Standby pin, Low active
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
description: Hardware reset, Low active
|
||||
|
||||
ports:
|
||||
type: object
|
||||
description:
|
||||
A node containing input and output port nodes with endpoint definitions
|
||||
as documented in
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt
|
||||
properties:
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
port@0:
|
||||
type: object
|
||||
description: |
|
||||
DSI Input. The remote endpoint phandle should be a
|
||||
reference to a valid mipi_dsi_host device node.
|
||||
|
||||
port@1:
|
||||
type: object
|
||||
description: |
|
||||
Video port for LVDS output (panel or connector).
|
||||
|
||||
port@2:
|
||||
type: object
|
||||
description: |
|
||||
Video port for Dual link LVDS output (panel or connector).
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
- vddio-supply
|
||||
- stby-gpios
|
||||
- reset-gpios
|
||||
- ports
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
/* For single-link LVDS display panel */
|
||||
|
||||
i2c@78b8000 {
|
||||
/* On High speed expansion */
|
||||
label = "HS-I2C2";
|
||||
reg = <0x078b8000 0x500>;
|
||||
clock-frequency = <400000>; /* fastmode operation */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
tc_bridge: bridge@f {
|
||||
compatible = "toshiba,tc358775";
|
||||
reg = <0x0f>;
|
||||
|
||||
vdd-supply = <&pm8916_l2>;
|
||||
vddio-supply = <&pm8916_l6>;
|
||||
|
||||
stby-gpios = <&msmgpio 99 GPIO_ACTIVE_LOW>;
|
||||
reset-gpios = <&msmgpio 72 GPIO_ACTIVE_LOW>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
d2l_in_test: endpoint {
|
||||
remote-endpoint = <&dsi0_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
lvds_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
dsi@1a98000 {
|
||||
reg = <0x1a98000 0x25c>;
|
||||
reg-names = "dsi_ctrl";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
dsi0_out: endpoint {
|
||||
remote-endpoint = <&d2l_in_test>;
|
||||
data-lanes = <0 1 2 3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
/* For dual-link LVDS display panel */
|
||||
|
||||
i2c@78b8000 {
|
||||
/* On High speed expansion */
|
||||
label = "HS-I2C2";
|
||||
reg = <0x078b8000 0x500>;
|
||||
clock-frequency = <400000>; /* fastmode operation */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
tc_bridge_dual: bridge@f {
|
||||
compatible = "toshiba,tc358775";
|
||||
reg = <0x0f>;
|
||||
|
||||
vdd-supply = <&pm8916_l2>;
|
||||
vddio-supply = <&pm8916_l6>;
|
||||
|
||||
stby-gpios = <&msmgpio 99 GPIO_ACTIVE_LOW>;
|
||||
reset-gpios = <&msmgpio 72 GPIO_ACTIVE_LOW>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
d2l_in_dual: endpoint {
|
||||
remote-endpoint = <&dsi0_out_dual>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
lvds0_out: endpoint {
|
||||
remote-endpoint = <&panel_in0>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
lvds1_out: endpoint {
|
||||
remote-endpoint = <&panel_in1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
dsi@1a98000 {
|
||||
reg = <0x1a98000 0x25c>;
|
||||
reg-names = "dsi_ctrl";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
dsi0_out_dual: endpoint {
|
||||
remote-endpoint = <&d2l_in_dual>;
|
||||
data-lanes = <0 1 2 3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
|
@ -13,7 +13,9 @@ properties:
|
|||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- bananapi,lhr050h41
|
||||
- bananapi,lhr050h41
|
||||
- feixin,k101-im2byl02
|
||||
|
||||
- const: ilitek,ili9881c
|
||||
|
||||
backlight: true
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/mantix,mlaf057we51-x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Mantix MLAF057WE51-X 5.7" 720x1440 TFT LCD panel
|
||||
|
||||
maintainers:
|
||||
- Guido Günther <agx@sigxcpu.org>
|
||||
|
||||
description:
|
||||
Mantix MLAF057WE51 X is a 720x1440 TFT LCD panel connected using
|
||||
a MIPI-DSI video interface.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mantix,mlaf057we51-x
|
||||
|
||||
port: true
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: DSI virtual channel
|
||||
|
||||
avdd-supply:
|
||||
description: Positive analog power supply
|
||||
|
||||
avee-supply:
|
||||
description: Negative analog power supply
|
||||
|
||||
vddi-supply:
|
||||
description: 1.8V I/O voltage supply
|
||||
|
||||
reset-gpios: true
|
||||
|
||||
backlight: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- avdd-supply
|
||||
- avee-supply
|
||||
- vddi-supply
|
||||
- reset-gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
panel@0 {
|
||||
compatible = "mantix,mlaf057we51-x";
|
||||
reg = <0>;
|
||||
avdd-supply = <®_avdd>;
|
||||
avee-supply = <®_avee>;
|
||||
vddi-supply = <®_1v8_p>;
|
||||
reset-gpios = <&gpio1 29 GPIO_ACTIVE_LOW>;
|
||||
backlight = <&backlight>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -87,6 +87,8 @@ properties:
|
|||
- cdtech,s070swv29hg-dc44
|
||||
# CDTech(H.K.) Electronics Limited 7" 800x480 color TFT-LCD panel
|
||||
- cdtech,s070wv95-ct16
|
||||
# Chefree CH101OLHLWH-002 10.1" (1280x800) color TFT LCD panel
|
||||
- chefree,ch101olhlwh-002
|
||||
# Chunghwa Picture Tubes Ltd. 7" WXGA TFT LCD panel
|
||||
- chunghwa,claa070wp03xg
|
||||
# Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel
|
||||
|
@ -159,6 +161,8 @@ properties:
|
|||
- innolux,n156bge-l21
|
||||
# Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel
|
||||
- innolux,zj070na-01p
|
||||
# King & Display KD116N21-30NV-A010 eDP TFT LCD panel
|
||||
- kingdisplay,kd116n21-30nv-a010
|
||||
# Kaohsiung Opto-Electronics Inc. 5.7" QVGA (320 x 240) TFT LCD panel
|
||||
- koe,tx14d24vm1bpa
|
||||
# Kaohsiung Opto-Electronics Inc. 10.1" WUXGA (1920 x 1200) LVDS TFT LCD panel
|
||||
|
@ -219,6 +223,8 @@ properties:
|
|||
- osddisplays,osd070t1718-19ts
|
||||
# One Stop Displays OSD101T2045-53TS 10.1" 1920x1200 panel
|
||||
- osddisplays,osd101t2045-53ts
|
||||
# POWERTIP PH800480T013-IDF2 7.0" WVGA TFT LCD panel
|
||||
- powertip,ph800480t013-idf02
|
||||
# QiaoDian XianShi Corporation 4"3 TFT LCD panel
|
||||
- qiaodian,qd43003c0-40
|
||||
# Rocktech Displays Ltd. RK101II01D-CT 10.1" TFT 1280x800
|
||||
|
|
|
@ -8,10 +8,11 @@ title: Rocktech JH057N00900 5.5" 720x1440 TFT LCD panel
|
|||
|
||||
maintainers:
|
||||
- Ondrej Jirman <megi@xff.cz>
|
||||
- Guido Gŭnther <agx@sigxcpu.org>
|
||||
|
||||
description: |
|
||||
Rocktech JH057N00900 is a 720x1440 TFT LCD panel
|
||||
connected using a MIPI-DSI video interface.
|
||||
description:
|
||||
Rocktech JH057N00900 is a 720x1440 TFT LCD panel
|
||||
connected using a MIPI-DSI video interface.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
@ -19,9 +20,9 @@ allOf:
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
# Rocktech JH057N00900 5.5" 720x1440 TFT LCD panel
|
||||
# Rocktech JH057N00900 5.5" 720x1440 TFT LCD panel
|
||||
- rocktech,jh057n00900
|
||||
# Xingbangda XBD599 5.99" 720x1440 TFT LCD panel
|
||||
# Xingbangda XBD599 5.99" 720x1440 TFT LCD panel
|
||||
- xingbangda,xbd599
|
||||
|
||||
port: true
|
||||
|
@ -35,13 +36,9 @@ properties:
|
|||
iovcc-supply:
|
||||
description: I/O voltage supply
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO used for the reset pin
|
||||
maxItems: 1
|
||||
reset-gpios: true
|
||||
|
||||
backlight:
|
||||
description: Backlight used by the panel
|
||||
$ref: "/schemas/types.yaml#/definitions/phandle"
|
||||
backlight: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
@ -57,15 +54,16 @@ examples:
|
|||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
panel@0 {
|
||||
compatible = "rocktech,jh057n00900";
|
||||
reg = <0>;
|
||||
vcc-supply = <®_2v8_p>;
|
||||
iovcc-supply = <®_1v8_p>;
|
||||
reset-gpios = <&gpio3 13 GPIO_ACTIVE_LOW>;
|
||||
backlight = <&backlight>;
|
||||
};
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
panel@0 {
|
||||
compatible = "rocktech,jh057n00900";
|
||||
reg = <0>;
|
||||
vcc-supply = <®_2v8_p>;
|
||||
iovcc-supply = <®_1v8_p>;
|
||||
reset-gpios = <&gpio3 13 GPIO_ACTIVE_LOW>;
|
||||
backlight = <&backlight>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -197,6 +197,8 @@ patternProperties:
|
|||
description: Ceva, Inc.
|
||||
"^checkpoint,.*":
|
||||
description: Check Point Software Technologies Ltd.
|
||||
"^chefree,.*":
|
||||
description: Chefree Technology Corp.
|
||||
"^chipidea,.*":
|
||||
description: Chipidea, Inc
|
||||
"^chipone,.*":
|
||||
|
@ -601,6 +603,8 @@ patternProperties:
|
|||
description: Logic Technologies Limited
|
||||
"^longcheer,.*":
|
||||
description: Longcheer Technology (Shanghai) Co., Ltd.
|
||||
"^lontium,.*":
|
||||
description: Lontium Semiconductor Corporation
|
||||
"^loongson,.*":
|
||||
description: Loongson Technology Corporation Limited
|
||||
"^lsi,.*":
|
||||
|
@ -611,6 +615,8 @@ patternProperties:
|
|||
description: Linux Automation GmbH
|
||||
"^macnica,.*":
|
||||
description: Macnica Americas
|
||||
"^mantix,.*":
|
||||
description: Mantix Display Technology Co.,Ltd.
|
||||
"^mapleboard,.*":
|
||||
description: Mapleboard.org
|
||||
"^marvell,.*":
|
||||
|
@ -830,6 +836,8 @@ patternProperties:
|
|||
description: Poslab Technology Co., Ltd.
|
||||
"^pov,.*":
|
||||
description: Point of View International B.V.
|
||||
"^powertip,.*":
|
||||
description: Powertip Tech. Corp.
|
||||
"^powervr,.*":
|
||||
description: PowerVR (deprecated, use img)
|
||||
"^primux,.*":
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. Copyright 2020 DisplayLink (UK) Ltd.
|
||||
|
||||
===================
|
||||
Userland interfaces
|
||||
===================
|
||||
|
@ -162,6 +164,116 @@ other hand, a driver requires shared state between clients which is
|
|||
visible to user-space and accessible beyond open-file boundaries, they
|
||||
cannot support render nodes.
|
||||
|
||||
Device Hot-Unplug
|
||||
=================
|
||||
|
||||
.. note::
|
||||
The following is the plan. Implementation is not there yet
|
||||
(2020 May).
|
||||
|
||||
Graphics devices (display and/or render) may be connected via USB (e.g.
|
||||
display adapters or docking stations) or Thunderbolt (e.g. eGPU). An end
|
||||
user is able to hot-unplug this kind of devices while they are being
|
||||
used, and expects that the very least the machine does not crash. Any
|
||||
damage from hot-unplugging a DRM device needs to be limited as much as
|
||||
possible and userspace must be given the chance to handle it if it wants
|
||||
to. Ideally, unplugging a DRM device still lets a desktop continue to
|
||||
run, but that is going to need explicit support throughout the whole
|
||||
graphics stack: from kernel and userspace drivers, through display
|
||||
servers, via window system protocols, and in applications and libraries.
|
||||
|
||||
Other scenarios that should lead to the same are: unrecoverable GPU
|
||||
crash, PCI device disappearing off the bus, or forced unbind of a driver
|
||||
from the physical device.
|
||||
|
||||
In other words, from userspace perspective everything needs to keep on
|
||||
working more or less, until userspace stops using the disappeared DRM
|
||||
device and closes it completely. Userspace will learn of the device
|
||||
disappearance from the device removed uevent, ioctls returning ENODEV
|
||||
(or driver-specific ioctls returning driver-specific things), or open()
|
||||
returning ENXIO.
|
||||
|
||||
Only after userspace has closed all relevant DRM device and dmabuf file
|
||||
descriptors and removed all mmaps, the DRM driver can tear down its
|
||||
instance for the device that no longer exists. If the same physical
|
||||
device somehow comes back in the mean time, it shall be a new DRM
|
||||
device.
|
||||
|
||||
Similar to PIDs, chardev minor numbers are not recycled immediately. A
|
||||
new DRM device always picks the next free minor number compared to the
|
||||
previous one allocated, and wraps around when minor numbers are
|
||||
exhausted.
|
||||
|
||||
The goal raises at least the following requirements for the kernel and
|
||||
drivers.
|
||||
|
||||
Requirements for KMS UAPI
|
||||
-------------------------
|
||||
|
||||
- KMS connectors must change their status to disconnected.
|
||||
|
||||
- Legacy modesets and pageflips, and atomic commits, both real and
|
||||
TEST_ONLY, and any other ioctls either fail with ENODEV or fake
|
||||
success.
|
||||
|
||||
- Pending non-blocking KMS operations deliver the DRM events userspace
|
||||
is expecting. This applies also to ioctls that faked success.
|
||||
|
||||
- open() on a device node whose underlying device has disappeared will
|
||||
fail with ENXIO.
|
||||
|
||||
- Attempting to create a DRM lease on a disappeared DRM device will
|
||||
fail with ENODEV. Existing DRM leases remain and work as listed
|
||||
above.
|
||||
|
||||
Requirements for Render and Cross-Device UAPI
|
||||
---------------------------------------------
|
||||
|
||||
- All GPU jobs that can no longer run must have their fences
|
||||
force-signalled to avoid inflicting hangs on userspace.
|
||||
The associated error code is ENODEV.
|
||||
|
||||
- Some userspace APIs already define what should happen when the device
|
||||
disappears (OpenGL, GL ES: `GL_KHR_robustness`_; `Vulkan`_:
|
||||
VK_ERROR_DEVICE_LOST; etc.). DRM drivers are free to implement this
|
||||
behaviour the way they see best, e.g. returning failures in
|
||||
driver-specific ioctls and handling those in userspace drivers, or
|
||||
rely on uevents, and so on.
|
||||
|
||||
- dmabuf which point to memory that has disappeared will either fail to
|
||||
import with ENODEV or continue to be successfully imported if it would
|
||||
have succeeded before the disappearance. See also about memory maps
|
||||
below for already imported dmabufs.
|
||||
|
||||
- Attempting to import a dmabuf to a disappeared device will either fail
|
||||
with ENODEV or succeed if it would have succeeded without the
|
||||
disappearance.
|
||||
|
||||
- open() on a device node whose underlying device has disappeared will
|
||||
fail with ENXIO.
|
||||
|
||||
.. _GL_KHR_robustness: https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_robustness.txt
|
||||
.. _Vulkan: https://www.khronos.org/vulkan/
|
||||
|
||||
Requirements for Memory Maps
|
||||
----------------------------
|
||||
|
||||
Memory maps have further requirements that apply to both existing maps
|
||||
and maps created after the device has disappeared. If the underlying
|
||||
memory disappears, the map is created or modified such that reads and
|
||||
writes will still complete successfully but the result is undefined.
|
||||
This applies to both userspace mmap()'d memory and memory pointed to by
|
||||
dmabuf which might be mapped to other devices (cross-device dmabuf
|
||||
imports).
|
||||
|
||||
Raising SIGBUS is not an option, because userspace cannot realistically
|
||||
handle it. Signal handlers are global, which makes them extremely
|
||||
difficult to use correctly from libraries like those that Mesa produces.
|
||||
Signal handlers are not composable, you can't have different handlers
|
||||
for GPU1 and GPU2 from different vendors, and a third handler for
|
||||
mmapped regular files. Threads cause additional pain with signal
|
||||
handling as well.
|
||||
|
||||
.. _drm_driver_ioctl:
|
||||
|
||||
IOCTL Support on Device Nodes
|
||||
|
@ -199,7 +311,7 @@ EPERM/EACCES:
|
|||
difference between EACCES and EPERM.
|
||||
|
||||
ENODEV:
|
||||
The device is not (yet) present or fully initialized.
|
||||
The device is not present anymore or is not yet fully initialized.
|
||||
|
||||
EOPNOTSUPP:
|
||||
Feature (like PRIME, modesetting, GEM) is not supported by the driver.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
==========================================
|
||||
drm/pl111 ARM PrimeCell PL111 CLCD Driver
|
||||
==========================================
|
||||
====================================================
|
||||
drm/pl111 ARM PrimeCell PL110 and PL111 CLCD Driver
|
||||
====================================================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
|
||||
:doc: ARM PrimeCell PL111 CLCD Driver
|
||||
:doc: ARM PrimeCell PL110 and PL111 CLCD Driver
|
||||
|
|
|
@ -403,6 +403,52 @@ Contact: Emil Velikov, respective driver maintainers
|
|||
|
||||
Level: Intermediate
|
||||
|
||||
Plumb drm_atomic_state all over
|
||||
-------------------------------
|
||||
|
||||
Currently various atomic functions take just a single or a handful of
|
||||
object states (eg. plane state). While that single object state can
|
||||
suffice for some simple cases, we often have to dig out additional
|
||||
object states for dealing with various dependencies between the individual
|
||||
objects or the hardware they represent. The process of digging out the
|
||||
additional states is rather non-intuitive and error prone.
|
||||
|
||||
To fix that most functions should rather take the overall
|
||||
drm_atomic_state as one of their parameters. The other parameters
|
||||
would generally be the object(s) we mainly want to interact with.
|
||||
|
||||
For example, instead of
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int (*atomic_check)(struct drm_plane *plane, struct drm_plane_state *state);
|
||||
|
||||
we would have something like
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int (*atomic_check)(struct drm_plane *plane, struct drm_atomic_state *state);
|
||||
|
||||
The implementation can then trivially gain access to any required object
|
||||
state(s) via drm_atomic_get_plane_state(), drm_atomic_get_new_plane_state(),
|
||||
drm_atomic_get_old_plane_state(), and their equivalents for
|
||||
other object types.
|
||||
|
||||
Additionally many drivers currently access the object->state pointer
|
||||
directly in their commit functions. That is not going to work if we
|
||||
eg. want to allow deeper commit pipelines as those pointers could
|
||||
then point to the states corresponding to a future commit instead of
|
||||
the current commit we're trying to process. Also non-blocking commits
|
||||
execute locklessly so there are serious concerns with dereferencing
|
||||
the object->state pointers without holding the locks that protect them.
|
||||
Use of drm_atomic_get_new_plane_state(), drm_atomic_get_old_plane_state(),
|
||||
etc. avoids these problems as well since they relate to a specific
|
||||
commit via the passed in drm_atomic_state.
|
||||
|
||||
Contact: Ville Syrjälä, Daniel Vetter
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
|
||||
Core refactorings
|
||||
=================
|
||||
|
|
11
MAINTAINERS
11
MAINTAINERS
|
@ -5474,12 +5474,19 @@ S: Maintained
|
|||
F: drivers/gpu/drm/panel/panel-lvds.c
|
||||
F: Documentation/devicetree/bindings/display/panel/lvds.yaml
|
||||
|
||||
DRM DRIVER FOR MANTIX MLAF057WE51 PANELS
|
||||
M: Guido Günther <agx@sigxcpu.org>
|
||||
R: Purism Kernel Team <kernel@puri.sm>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/panel/mantix,mlaf057we51-x.yaml
|
||||
F: drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c
|
||||
|
||||
DRM DRIVER FOR MATROX G200/G400 GRAPHICS CARDS
|
||||
S: Orphan / Obsolete
|
||||
F: drivers/gpu/drm/mga/
|
||||
F: include/uapi/drm/mga_drm.h
|
||||
|
||||
DRM DRIVER FOR MGA G200 SERVER GRAPHICS CHIPS
|
||||
DRM DRIVER FOR MGA G200 GRAPHICS CHIPS
|
||||
M: Dave Airlie <airlied@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/gpu/drm/mgag200/
|
||||
|
@ -5767,7 +5774,7 @@ F: drivers/gpu/drm/gma500/
|
|||
|
||||
DRM DRIVERS FOR HISILICON
|
||||
M: Xinliang Liu <xinliang.liu@linaro.org>
|
||||
M: Rongrong Zou <zourongrong@gmail.com>
|
||||
M: Tian Tao <tiantao6@hisilicon.com>
|
||||
R: John Stultz <john.stultz@linaro.org>
|
||||
R: Xinwei Kong <kong.kongxinwei@hisilicon.com>
|
||||
R: Chen Feng <puck.chen@hisilicon.com>
|
||||
|
|
|
@ -517,8 +517,9 @@ out_put:
|
|||
uint64_t amdgpu_amdkfd_get_vram_usage(struct kgd_dev *kgd)
|
||||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)kgd;
|
||||
struct ttm_resource_manager *vram_man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
|
||||
|
||||
return amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
|
||||
return amdgpu_vram_mgr_usage(vram_man);
|
||||
}
|
||||
|
||||
uint64_t amdgpu_amdkfd_get_hive_id(struct kgd_dev *kgd)
|
||||
|
|
|
@ -562,7 +562,7 @@ static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr)
|
|||
|
||||
mutex_lock(&process_info->lock);
|
||||
|
||||
ret = amdgpu_ttm_tt_set_userptr(bo->tbo.ttm, user_addr, 0);
|
||||
ret = amdgpu_ttm_tt_set_userptr(&bo->tbo, user_addr, 0);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to set userptr: %d\n", __func__, ret);
|
||||
goto out;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/amdgpu_drm.h>
|
||||
#include "amdgpu.h"
|
||||
|
@ -1413,6 +1414,10 @@ out:
|
|||
pm_runtime_put_autosuspend(connector->dev->dev);
|
||||
}
|
||||
|
||||
drm_dp_set_subconnector_property(&amdgpu_connector->base,
|
||||
ret,
|
||||
amdgpu_dig_connector->dpcd,
|
||||
amdgpu_dig_connector->downstream_ports);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1959,6 +1964,11 @@ amdgpu_connector_add(struct amdgpu_device *adev,
|
|||
if (has_aux)
|
||||
amdgpu_atombios_dp_aux_init(amdgpu_connector);
|
||||
|
||||
if (connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
|
||||
connector_type == DRM_MODE_CONNECTOR_eDP) {
|
||||
drm_connector_attach_dp_subconnector_property(&amdgpu_connector->base);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
failed:
|
||||
|
|
|
@ -299,7 +299,7 @@ static void amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev,
|
|||
{
|
||||
s64 time_us, increment_us;
|
||||
u64 free_vram, total_vram, used_vram;
|
||||
|
||||
struct ttm_resource_manager *vram_man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
|
||||
/* Allow a maximum of 200 accumulated ms. This is basically per-IB
|
||||
* throttling.
|
||||
*
|
||||
|
@ -316,7 +316,7 @@ static void amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev,
|
|||
}
|
||||
|
||||
total_vram = adev->gmc.real_vram_size - atomic64_read(&adev->vram_pin_size);
|
||||
used_vram = amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
|
||||
used_vram = amdgpu_vram_mgr_usage(vram_man);
|
||||
free_vram = used_vram >= total_vram ? 0 : total_vram - used_vram;
|
||||
|
||||
spin_lock(&adev->mm_stats.lock);
|
||||
|
@ -363,7 +363,7 @@ static void amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev,
|
|||
if (!amdgpu_gmc_vram_full_visible(&adev->gmc)) {
|
||||
u64 total_vis_vram = adev->gmc.visible_vram_size;
|
||||
u64 used_vis_vram =
|
||||
amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
|
||||
amdgpu_vram_mgr_vis_usage(vram_man);
|
||||
|
||||
if (used_vis_vram < total_vis_vram) {
|
||||
u64 free_vis_vram = total_vis_vram - used_vis_vram;
|
||||
|
|
|
@ -3882,7 +3882,7 @@ static int amdgpu_device_reset_sriov(struct amdgpu_device *adev,
|
|||
|
||||
amdgpu_virt_init_data_exchange(adev);
|
||||
/* we need recover gart prior to run SMC/CP/SDMA resume */
|
||||
amdgpu_gtt_mgr_recover(&adev->mman.bdev.man[TTM_PL_TT]);
|
||||
amdgpu_gtt_mgr_recover(ttm_manager_type(&adev->mman.bdev, TTM_PL_TT));
|
||||
|
||||
r = amdgpu_device_fw_loading(adev);
|
||||
if (r)
|
||||
|
@ -4081,8 +4081,7 @@ static int amdgpu_do_asic_reset(struct amdgpu_hive_info *hive,
|
|||
amdgpu_inc_vram_lost(tmp_adev);
|
||||
}
|
||||
|
||||
r = amdgpu_gtt_mgr_recover(
|
||||
&tmp_adev->mman.bdev.man[TTM_PL_TT]);
|
||||
r = amdgpu_gtt_mgr_recover(ttm_manager_type(&tmp_adev->mman.bdev, TTM_PL_TT));
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -393,12 +393,12 @@ MODULE_PARM_DESC(sched_hw_submission, "the max number of HW submissions (default
|
|||
module_param_named(sched_hw_submission, amdgpu_sched_hw_submission, int, 0444);
|
||||
|
||||
/**
|
||||
* DOC: ppfeaturemask (uint)
|
||||
* DOC: ppfeaturemask (hexint)
|
||||
* Override power features enabled. See enum PP_FEATURE_MASK in drivers/gpu/drm/amd/include/amd_shared.h.
|
||||
* The default is the current set of stable power features.
|
||||
*/
|
||||
MODULE_PARM_DESC(ppfeaturemask, "all power features enabled (default))");
|
||||
module_param_named(ppfeaturemask, amdgpu_pp_feature_mask, uint, 0444);
|
||||
module_param_named(ppfeaturemask, amdgpu_pp_feature_mask, hexint, 0444);
|
||||
|
||||
/**
|
||||
* DOC: forcelongtraining (uint)
|
||||
|
|
|
@ -332,7 +332,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data,
|
|||
bo = gem_to_amdgpu_bo(gobj);
|
||||
bo->preferred_domains = AMDGPU_GEM_DOMAIN_GTT;
|
||||
bo->allowed_domains = AMDGPU_GEM_DOMAIN_GTT;
|
||||
r = amdgpu_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags);
|
||||
r = amdgpu_ttm_tt_set_userptr(&bo->tbo, args->addr, args->flags);
|
||||
if (r)
|
||||
goto release_object;
|
||||
|
||||
|
|
|
@ -24,11 +24,10 @@
|
|||
|
||||
#include "amdgpu.h"
|
||||
|
||||
struct amdgpu_gtt_mgr {
|
||||
struct drm_mm mm;
|
||||
spinlock_t lock;
|
||||
atomic64_t available;
|
||||
};
|
||||
static inline struct amdgpu_gtt_mgr *to_gtt_mgr(struct ttm_resource_manager *man)
|
||||
{
|
||||
return container_of(man, struct amdgpu_gtt_mgr, manager);
|
||||
}
|
||||
|
||||
struct amdgpu_gtt_node {
|
||||
struct drm_mm_node node;
|
||||
|
@ -48,9 +47,9 @@ static ssize_t amdgpu_mem_info_gtt_total_show(struct device *dev,
|
|||
{
|
||||
struct drm_device *ddev = dev_get_drvdata(dev);
|
||||
struct amdgpu_device *adev = ddev->dev_private;
|
||||
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
(adev->mman.bdev.man[TTM_PL_TT].size) * PAGE_SIZE);
|
||||
man->size * PAGE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,9 +65,9 @@ static ssize_t amdgpu_mem_info_gtt_used_show(struct device *dev,
|
|||
{
|
||||
struct drm_device *ddev = dev_get_drvdata(dev);
|
||||
struct amdgpu_device *adev = ddev->dev_private;
|
||||
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
amdgpu_gtt_mgr_usage(&adev->mman.bdev.man[TTM_PL_TT]));
|
||||
amdgpu_gtt_mgr_usage(man));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(mem_info_gtt_total, S_IRUGO,
|
||||
|
@ -76,6 +75,7 @@ static DEVICE_ATTR(mem_info_gtt_total, S_IRUGO,
|
|||
static DEVICE_ATTR(mem_info_gtt_used, S_IRUGO,
|
||||
amdgpu_mem_info_gtt_used_show, NULL);
|
||||
|
||||
static const struct ttm_resource_manager_func amdgpu_gtt_mgr_func;
|
||||
/**
|
||||
* amdgpu_gtt_mgr_init - init GTT manager and DRM MM
|
||||
*
|
||||
|
@ -84,24 +84,25 @@ static DEVICE_ATTR(mem_info_gtt_used, S_IRUGO,
|
|||
*
|
||||
* Allocate and initialize the GTT manager.
|
||||
*/
|
||||
static int amdgpu_gtt_mgr_init(struct ttm_mem_type_manager *man,
|
||||
unsigned long p_size)
|
||||
int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
|
||||
struct amdgpu_gtt_mgr *mgr;
|
||||
struct amdgpu_gtt_mgr *mgr = &adev->mman.gtt_mgr;
|
||||
struct ttm_resource_manager *man = &mgr->manager;
|
||||
uint64_t start, size;
|
||||
int ret;
|
||||
|
||||
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
|
||||
if (!mgr)
|
||||
return -ENOMEM;
|
||||
man->use_tt = true;
|
||||
man->func = &amdgpu_gtt_mgr_func;
|
||||
man->available_caching = TTM_PL_MASK_CACHING;
|
||||
man->default_caching = TTM_PL_FLAG_CACHED;
|
||||
|
||||
ttm_resource_manager_init(man, gtt_size >> PAGE_SHIFT);
|
||||
|
||||
start = AMDGPU_GTT_MAX_TRANSFER_SIZE * AMDGPU_GTT_NUM_TRANSFER_WINDOWS;
|
||||
size = (adev->gmc.gart_size >> PAGE_SHIFT) - start;
|
||||
drm_mm_init(&mgr->mm, start, size);
|
||||
spin_lock_init(&mgr->lock);
|
||||
atomic64_set(&mgr->available, p_size);
|
||||
man->priv = mgr;
|
||||
atomic64_set(&mgr->available, gtt_size >> PAGE_SHIFT);
|
||||
|
||||
ret = device_create_file(adev->dev, &dev_attr_mem_info_gtt_total);
|
||||
if (ret) {
|
||||
|
@ -114,6 +115,8 @@ static int amdgpu_gtt_mgr_init(struct ttm_mem_type_manager *man,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_TT, &mgr->manager);
|
||||
ttm_resource_manager_set_used(man, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -125,20 +128,27 @@ static int amdgpu_gtt_mgr_init(struct ttm_mem_type_manager *man,
|
|||
* Destroy and free the GTT manager, returns -EBUSY if ranges are still
|
||||
* allocated inside it.
|
||||
*/
|
||||
static int amdgpu_gtt_mgr_fini(struct ttm_mem_type_manager *man)
|
||||
void amdgpu_gtt_mgr_fini(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
|
||||
struct amdgpu_gtt_mgr *mgr = man->priv;
|
||||
struct amdgpu_gtt_mgr *mgr = &adev->mman.gtt_mgr;
|
||||
struct ttm_resource_manager *man = &mgr->manager;
|
||||
int ret;
|
||||
|
||||
ttm_resource_manager_set_used(man, false);
|
||||
|
||||
ret = ttm_resource_manager_force_list_clean(&adev->mman.bdev, man);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
spin_lock(&mgr->lock);
|
||||
drm_mm_takedown(&mgr->mm);
|
||||
spin_unlock(&mgr->lock);
|
||||
kfree(mgr);
|
||||
man->priv = NULL;
|
||||
|
||||
device_remove_file(adev->dev, &dev_attr_mem_info_gtt_total);
|
||||
device_remove_file(adev->dev, &dev_attr_mem_info_gtt_used);
|
||||
|
||||
return 0;
|
||||
ttm_resource_manager_cleanup(man);
|
||||
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_TT, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,7 +158,7 @@ static int amdgpu_gtt_mgr_fini(struct ttm_mem_type_manager *man)
|
|||
*
|
||||
* Check if a mem object has already address space allocated.
|
||||
*/
|
||||
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem)
|
||||
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_resource *mem)
|
||||
{
|
||||
return mem->mm_node != NULL;
|
||||
}
|
||||
|
@ -163,12 +173,12 @@ bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem)
|
|||
*
|
||||
* Dummy, allocate the node but no space for it yet.
|
||||
*/
|
||||
static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man,
|
||||
static int amdgpu_gtt_mgr_new(struct ttm_resource_manager *man,
|
||||
struct ttm_buffer_object *tbo,
|
||||
const struct ttm_place *place,
|
||||
struct ttm_mem_reg *mem)
|
||||
struct ttm_resource *mem)
|
||||
{
|
||||
struct amdgpu_gtt_mgr *mgr = man->priv;
|
||||
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
|
||||
struct amdgpu_gtt_node *node;
|
||||
int r;
|
||||
|
||||
|
@ -226,10 +236,10 @@ err_out:
|
|||
*
|
||||
* Free the allocated GTT again.
|
||||
*/
|
||||
static void amdgpu_gtt_mgr_del(struct ttm_mem_type_manager *man,
|
||||
struct ttm_mem_reg *mem)
|
||||
static void amdgpu_gtt_mgr_del(struct ttm_resource_manager *man,
|
||||
struct ttm_resource *mem)
|
||||
{
|
||||
struct amdgpu_gtt_mgr *mgr = man->priv;
|
||||
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
|
||||
struct amdgpu_gtt_node *node = mem->mm_node;
|
||||
|
||||
if (node) {
|
||||
|
@ -249,17 +259,17 @@ static void amdgpu_gtt_mgr_del(struct ttm_mem_type_manager *man,
|
|||
*
|
||||
* Return how many bytes are used in the GTT domain
|
||||
*/
|
||||
uint64_t amdgpu_gtt_mgr_usage(struct ttm_mem_type_manager *man)
|
||||
uint64_t amdgpu_gtt_mgr_usage(struct ttm_resource_manager *man)
|
||||
{
|
||||
struct amdgpu_gtt_mgr *mgr = man->priv;
|
||||
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
|
||||
s64 result = man->size - atomic64_read(&mgr->available);
|
||||
|
||||
return (result > 0 ? result : 0) * PAGE_SIZE;
|
||||
}
|
||||
|
||||
int amdgpu_gtt_mgr_recover(struct ttm_mem_type_manager *man)
|
||||
int amdgpu_gtt_mgr_recover(struct ttm_resource_manager *man)
|
||||
{
|
||||
struct amdgpu_gtt_mgr *mgr = man->priv;
|
||||
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
|
||||
struct amdgpu_gtt_node *node;
|
||||
struct drm_mm_node *mm_node;
|
||||
int r = 0;
|
||||
|
@ -284,10 +294,10 @@ int amdgpu_gtt_mgr_recover(struct ttm_mem_type_manager *man)
|
|||
*
|
||||
* Dump the table content using printk.
|
||||
*/
|
||||
static void amdgpu_gtt_mgr_debug(struct ttm_mem_type_manager *man,
|
||||
static void amdgpu_gtt_mgr_debug(struct ttm_resource_manager *man,
|
||||
struct drm_printer *printer)
|
||||
{
|
||||
struct amdgpu_gtt_mgr *mgr = man->priv;
|
||||
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
|
||||
|
||||
spin_lock(&mgr->lock);
|
||||
drm_mm_print(&mgr->mm, printer);
|
||||
|
@ -298,10 +308,8 @@ static void amdgpu_gtt_mgr_debug(struct ttm_mem_type_manager *man,
|
|||
amdgpu_gtt_mgr_usage(man) >> 20);
|
||||
}
|
||||
|
||||
const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func = {
|
||||
.init = amdgpu_gtt_mgr_init,
|
||||
.takedown = amdgpu_gtt_mgr_fini,
|
||||
.get_node = amdgpu_gtt_mgr_new,
|
||||
.put_node = amdgpu_gtt_mgr_del,
|
||||
static const struct ttm_resource_manager_func amdgpu_gtt_mgr_func = {
|
||||
.alloc = amdgpu_gtt_mgr_new,
|
||||
.free = amdgpu_gtt_mgr_del,
|
||||
.debug = amdgpu_gtt_mgr_debug
|
||||
};
|
||||
|
|
|
@ -594,13 +594,13 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
|
|||
ui64 = atomic64_read(&adev->num_vram_cpu_page_faults);
|
||||
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
|
||||
case AMDGPU_INFO_VRAM_USAGE:
|
||||
ui64 = amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
|
||||
ui64 = amdgpu_vram_mgr_usage(ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM));
|
||||
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
|
||||
case AMDGPU_INFO_VIS_VRAM_USAGE:
|
||||
ui64 = amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
|
||||
ui64 = amdgpu_vram_mgr_vis_usage(ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM));
|
||||
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
|
||||
case AMDGPU_INFO_GTT_USAGE:
|
||||
ui64 = amdgpu_gtt_mgr_usage(&adev->mman.bdev.man[TTM_PL_TT]);
|
||||
ui64 = amdgpu_gtt_mgr_usage(ttm_manager_type(&adev->mman.bdev, TTM_PL_TT));
|
||||
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
|
||||
case AMDGPU_INFO_GDS_CONFIG: {
|
||||
struct drm_amdgpu_info_gds gds_info;
|
||||
|
@ -623,7 +623,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
|
|||
min(adev->gmc.visible_vram_size -
|
||||
atomic64_read(&adev->visible_pin_size),
|
||||
vram_gtt.vram_size);
|
||||
vram_gtt.gtt_size = adev->mman.bdev.man[TTM_PL_TT].size;
|
||||
vram_gtt.gtt_size = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT)->size;
|
||||
vram_gtt.gtt_size *= PAGE_SIZE;
|
||||
vram_gtt.gtt_size -= atomic64_read(&adev->gart_pin_size);
|
||||
return copy_to_user(out, &vram_gtt,
|
||||
|
@ -631,14 +631,17 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
|
|||
}
|
||||
case AMDGPU_INFO_MEMORY: {
|
||||
struct drm_amdgpu_memory_info mem;
|
||||
|
||||
struct ttm_resource_manager *vram_man =
|
||||
ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
|
||||
struct ttm_resource_manager *gtt_man =
|
||||
ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
|
||||
memset(&mem, 0, sizeof(mem));
|
||||
mem.vram.total_heap_size = adev->gmc.real_vram_size;
|
||||
mem.vram.usable_heap_size = adev->gmc.real_vram_size -
|
||||
atomic64_read(&adev->vram_pin_size) -
|
||||
AMDGPU_VM_RESERVED_VRAM;
|
||||
mem.vram.heap_usage =
|
||||
amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
|
||||
amdgpu_vram_mgr_usage(vram_man);
|
||||
mem.vram.max_allocation = mem.vram.usable_heap_size * 3 / 4;
|
||||
|
||||
mem.cpu_accessible_vram.total_heap_size =
|
||||
|
@ -648,16 +651,16 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
|
|||
atomic64_read(&adev->visible_pin_size),
|
||||
mem.vram.usable_heap_size);
|
||||
mem.cpu_accessible_vram.heap_usage =
|
||||
amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]);
|
||||
amdgpu_vram_mgr_vis_usage(vram_man);
|
||||
mem.cpu_accessible_vram.max_allocation =
|
||||
mem.cpu_accessible_vram.usable_heap_size * 3 / 4;
|
||||
|
||||
mem.gtt.total_heap_size = adev->mman.bdev.man[TTM_PL_TT].size;
|
||||
mem.gtt.total_heap_size = gtt_man->size;
|
||||
mem.gtt.total_heap_size *= PAGE_SIZE;
|
||||
mem.gtt.usable_heap_size = mem.gtt.total_heap_size -
|
||||
atomic64_read(&adev->gart_pin_size);
|
||||
mem.gtt.heap_usage =
|
||||
amdgpu_gtt_mgr_usage(&adev->mman.bdev.man[TTM_PL_TT]);
|
||||
amdgpu_gtt_mgr_usage(gtt_man);
|
||||
mem.gtt.max_allocation = mem.gtt.usable_heap_size * 3 / 4;
|
||||
|
||||
return copy_to_user(out, &mem,
|
||||
|
|
|
@ -469,6 +469,7 @@ struct amdgpu_encoder {
|
|||
struct amdgpu_connector_atom_dig {
|
||||
/* displayport */
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
|
||||
u8 dp_sink_type;
|
||||
int dp_clock;
|
||||
int dp_lane_count;
|
||||
|
|
|
@ -381,7 +381,7 @@ int amdgpu_bo_create_kernel_at(struct amdgpu_device *adev,
|
|||
if (cpu_addr)
|
||||
amdgpu_bo_kunmap(*bo_ptr);
|
||||
|
||||
ttm_bo_mem_put(&(*bo_ptr)->tbo, &(*bo_ptr)->tbo.mem);
|
||||
ttm_resource_free(&(*bo_ptr)->tbo, &(*bo_ptr)->tbo.mem);
|
||||
|
||||
for (i = 0; i < (*bo_ptr)->placement.num_placement; ++i) {
|
||||
(*bo_ptr)->placements[i].fpfn = offset >> PAGE_SHIFT;
|
||||
|
@ -442,14 +442,14 @@ void amdgpu_bo_free_kernel(struct amdgpu_bo **bo, u64 *gpu_addr,
|
|||
static bool amdgpu_bo_validate_size(struct amdgpu_device *adev,
|
||||
unsigned long size, u32 domain)
|
||||
{
|
||||
struct ttm_mem_type_manager *man = NULL;
|
||||
struct ttm_resource_manager *man = NULL;
|
||||
|
||||
/*
|
||||
* If GTT is part of requested domains the check must succeed to
|
||||
* allow fall back to GTT
|
||||
*/
|
||||
if (domain & AMDGPU_GEM_DOMAIN_GTT) {
|
||||
man = &adev->mman.bdev.man[TTM_PL_TT];
|
||||
man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT);
|
||||
|
||||
if (size < (man->size << PAGE_SHIFT))
|
||||
return true;
|
||||
|
@ -458,7 +458,7 @@ static bool amdgpu_bo_validate_size(struct amdgpu_device *adev,
|
|||
}
|
||||
|
||||
if (domain & AMDGPU_GEM_DOMAIN_VRAM) {
|
||||
man = &adev->mman.bdev.man[TTM_PL_VRAM];
|
||||
man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
|
||||
|
||||
if (size < (man->size << PAGE_SHIFT))
|
||||
return true;
|
||||
|
@ -1268,11 +1268,11 @@ int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer,
|
|||
*/
|
||||
void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
|
||||
bool evict,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
struct ttm_resource *new_mem)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
|
||||
struct amdgpu_bo *abo;
|
||||
struct ttm_mem_reg *old_mem = &bo->mem;
|
||||
struct ttm_resource *old_mem = &bo->mem;
|
||||
|
||||
if (!amdgpu_bo_is_amdgpu_bo(bo))
|
||||
return;
|
||||
|
|
|
@ -160,7 +160,7 @@ static inline int amdgpu_bo_reserve(struct amdgpu_bo *bo, bool no_intr)
|
|||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
|
||||
int r;
|
||||
|
||||
r = __ttm_bo_reserve(&bo->tbo, !no_intr, false, NULL);
|
||||
r = ttm_bo_reserve(&bo->tbo, !no_intr, false, NULL);
|
||||
if (unlikely(r != 0)) {
|
||||
if (r != -ERESTARTSYS)
|
||||
dev_err(adev->dev, "%p reserve failed\n", bo);
|
||||
|
@ -283,7 +283,7 @@ int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer,
|
|||
uint64_t *flags);
|
||||
void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
|
||||
bool evict,
|
||||
struct ttm_mem_reg *new_mem);
|
||||
struct ttm_resource *new_mem);
|
||||
void amdgpu_bo_release_notify(struct ttm_buffer_object *bo);
|
||||
int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
|
||||
void amdgpu_bo_fence(struct amdgpu_bo *bo, struct dma_fence *fence,
|
||||
|
|
|
@ -63,61 +63,13 @@
|
|||
|
||||
#define AMDGPU_TTM_VRAM_MAX_DW_READ (size_t)128
|
||||
|
||||
|
||||
/**
|
||||
* amdgpu_init_mem_type - Initialize a memory manager for a specific type of
|
||||
* memory request.
|
||||
*
|
||||
* @bdev: The TTM BO device object (contains a reference to amdgpu_device)
|
||||
* @type: The type of memory requested
|
||||
* @man: The memory type manager for each domain
|
||||
*
|
||||
* This is called by ttm_bo_init_mm() when a buffer object is being
|
||||
* initialized.
|
||||
*/
|
||||
static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
||||
struct ttm_mem_type_manager *man)
|
||||
static int amdgpu_ttm_init_on_chip(struct amdgpu_device *adev,
|
||||
unsigned int type,
|
||||
uint64_t size)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
|
||||
adev = amdgpu_ttm_adev(bdev);
|
||||
|
||||
switch (type) {
|
||||
case TTM_PL_SYSTEM:
|
||||
/* System memory */
|
||||
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
|
||||
man->available_caching = TTM_PL_MASK_CACHING;
|
||||
man->default_caching = TTM_PL_FLAG_CACHED;
|
||||
break;
|
||||
case TTM_PL_TT:
|
||||
/* GTT memory */
|
||||
man->func = &amdgpu_gtt_mgr_func;
|
||||
man->available_caching = TTM_PL_MASK_CACHING;
|
||||
man->default_caching = TTM_PL_FLAG_CACHED;
|
||||
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
|
||||
break;
|
||||
case TTM_PL_VRAM:
|
||||
/* "On-card" video ram */
|
||||
man->func = &amdgpu_vram_mgr_func;
|
||||
man->flags = TTM_MEMTYPE_FLAG_FIXED |
|
||||
TTM_MEMTYPE_FLAG_MAPPABLE;
|
||||
man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC;
|
||||
man->default_caching = TTM_PL_FLAG_WC;
|
||||
break;
|
||||
case AMDGPU_PL_GDS:
|
||||
case AMDGPU_PL_GWS:
|
||||
case AMDGPU_PL_OA:
|
||||
/* On-chip GDS memory*/
|
||||
man->func = &ttm_bo_manager_func;
|
||||
man->flags = TTM_MEMTYPE_FLAG_FIXED;
|
||||
man->available_caching = TTM_PL_FLAG_UNCACHED;
|
||||
man->default_caching = TTM_PL_FLAG_UNCACHED;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
return ttm_range_man_init(&adev->mman.bdev, type,
|
||||
TTM_PL_FLAG_UNCACHED, TTM_PL_FLAG_UNCACHED,
|
||||
false, size >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -231,9 +183,9 @@ static int amdgpu_verify_access(struct ttm_buffer_object *bo, struct file *filp)
|
|||
* Assign the memory from new_mem to the memory of the buffer object bo.
|
||||
*/
|
||||
static void amdgpu_move_null(struct ttm_buffer_object *bo,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
struct ttm_resource *new_mem)
|
||||
{
|
||||
struct ttm_mem_reg *old_mem = &bo->mem;
|
||||
struct ttm_resource *old_mem = &bo->mem;
|
||||
|
||||
BUG_ON(old_mem->mm_node != NULL);
|
||||
*old_mem = *new_mem;
|
||||
|
@ -250,7 +202,7 @@ static void amdgpu_move_null(struct ttm_buffer_object *bo,
|
|||
*/
|
||||
static uint64_t amdgpu_mm_node_addr(struct ttm_buffer_object *bo,
|
||||
struct drm_mm_node *mm_node,
|
||||
struct ttm_mem_reg *mem)
|
||||
struct ttm_resource *mem)
|
||||
{
|
||||
uint64_t addr = 0;
|
||||
|
||||
|
@ -270,7 +222,7 @@ static uint64_t amdgpu_mm_node_addr(struct ttm_buffer_object *bo,
|
|||
* @offset: The offset that drm_mm_node is used for finding.
|
||||
*
|
||||
*/
|
||||
static struct drm_mm_node *amdgpu_find_mm_node(struct ttm_mem_reg *mem,
|
||||
static struct drm_mm_node *amdgpu_find_mm_node(struct ttm_resource *mem,
|
||||
uint64_t *offset)
|
||||
{
|
||||
struct drm_mm_node *mm_node = mem->mm_node;
|
||||
|
@ -298,7 +250,7 @@ static struct drm_mm_node *amdgpu_find_mm_node(struct ttm_mem_reg *mem,
|
|||
* the physical address for local memory.
|
||||
*/
|
||||
static int amdgpu_ttm_map_buffer(struct ttm_buffer_object *bo,
|
||||
struct ttm_mem_reg *mem,
|
||||
struct ttm_resource *mem,
|
||||
struct drm_mm_node *mm_node,
|
||||
unsigned num_pages, uint64_t offset,
|
||||
unsigned window, struct amdgpu_ring *ring,
|
||||
|
@ -522,8 +474,8 @@ error:
|
|||
*/
|
||||
static int amdgpu_move_blit(struct ttm_buffer_object *bo,
|
||||
bool evict, bool no_wait_gpu,
|
||||
struct ttm_mem_reg *new_mem,
|
||||
struct ttm_mem_reg *old_mem)
|
||||
struct ttm_resource *new_mem,
|
||||
struct ttm_resource *old_mem)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
|
||||
struct amdgpu_bo *abo = ttm_to_amdgpu_bo(bo);
|
||||
|
@ -582,10 +534,10 @@ error:
|
|||
*/
|
||||
static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
|
||||
struct ttm_operation_ctx *ctx,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
struct ttm_resource *new_mem)
|
||||
{
|
||||
struct ttm_mem_reg *old_mem = &bo->mem;
|
||||
struct ttm_mem_reg tmp_mem;
|
||||
struct ttm_resource *old_mem = &bo->mem;
|
||||
struct ttm_resource tmp_mem;
|
||||
struct ttm_place placements;
|
||||
struct ttm_placement placement;
|
||||
int r;
|
||||
|
@ -627,7 +579,7 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
|
|||
/* move BO (in tmp_mem) to new_mem */
|
||||
r = ttm_bo_move_ttm(bo, ctx, new_mem);
|
||||
out_cleanup:
|
||||
ttm_bo_mem_put(bo, &tmp_mem);
|
||||
ttm_resource_free(bo, &tmp_mem);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -638,10 +590,10 @@ out_cleanup:
|
|||
*/
|
||||
static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict,
|
||||
struct ttm_operation_ctx *ctx,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
struct ttm_resource *new_mem)
|
||||
{
|
||||
struct ttm_mem_reg *old_mem = &bo->mem;
|
||||
struct ttm_mem_reg tmp_mem;
|
||||
struct ttm_resource *old_mem = &bo->mem;
|
||||
struct ttm_resource tmp_mem;
|
||||
struct ttm_placement placement;
|
||||
struct ttm_place placements;
|
||||
int r;
|
||||
|
@ -674,7 +626,7 @@ static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict,
|
|||
goto out_cleanup;
|
||||
}
|
||||
out_cleanup:
|
||||
ttm_bo_mem_put(bo, &tmp_mem);
|
||||
ttm_resource_free(bo, &tmp_mem);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -684,7 +636,7 @@ out_cleanup:
|
|||
* Called by amdgpu_bo_move()
|
||||
*/
|
||||
static bool amdgpu_mem_visible(struct amdgpu_device *adev,
|
||||
struct ttm_mem_reg *mem)
|
||||
struct ttm_resource *mem)
|
||||
{
|
||||
struct drm_mm_node *nodes = mem->mm_node;
|
||||
|
||||
|
@ -694,7 +646,7 @@ static bool amdgpu_mem_visible(struct amdgpu_device *adev,
|
|||
if (mem->mem_type != TTM_PL_VRAM)
|
||||
return false;
|
||||
|
||||
/* ttm_mem_reg_ioremap only supports contiguous memory */
|
||||
/* ttm_resource_ioremap only supports contiguous memory */
|
||||
if (nodes->size != mem->num_pages)
|
||||
return false;
|
||||
|
||||
|
@ -709,11 +661,11 @@ static bool amdgpu_mem_visible(struct amdgpu_device *adev,
|
|||
*/
|
||||
static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
|
||||
struct ttm_operation_ctx *ctx,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
struct ttm_resource *new_mem)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_bo *abo;
|
||||
struct ttm_mem_reg *old_mem = &bo->mem;
|
||||
struct ttm_resource *old_mem = &bo->mem;
|
||||
int r;
|
||||
|
||||
/* Can't move a pinned BO */
|
||||
|
@ -795,19 +747,12 @@ memcpy:
|
|||
*
|
||||
* Called by ttm_mem_io_reserve() ultimately via ttm_bo_vm_fault()
|
||||
*/
|
||||
static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
|
||||
static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_resource *mem)
|
||||
{
|
||||
struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
|
||||
struct drm_mm_node *mm_node = mem->mm_node;
|
||||
size_t bus_size = (size_t)mem->num_pages << PAGE_SHIFT;
|
||||
|
||||
mem->bus.addr = NULL;
|
||||
mem->bus.offset = 0;
|
||||
mem->bus.size = mem->num_pages << PAGE_SHIFT;
|
||||
mem->bus.base = 0;
|
||||
mem->bus.is_iomem = false;
|
||||
if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
|
||||
return -EINVAL;
|
||||
switch (mem->mem_type) {
|
||||
case TTM_PL_SYSTEM:
|
||||
/* system memory */
|
||||
|
@ -817,11 +762,11 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_
|
|||
case TTM_PL_VRAM:
|
||||
mem->bus.offset = mem->start << PAGE_SHIFT;
|
||||
/* check if it's visible */
|
||||
if ((mem->bus.offset + mem->bus.size) > adev->gmc.visible_vram_size)
|
||||
if ((mem->bus.offset + bus_size) > adev->gmc.visible_vram_size)
|
||||
return -EINVAL;
|
||||
/* Only physically contiguous buffers apply. In a contiguous
|
||||
* buffer, size of the first mm_node would match the number of
|
||||
* pages in ttm_mem_reg.
|
||||
* pages in ttm_resource.
|
||||
*/
|
||||
if (adev->mman.aper_base_kaddr &&
|
||||
(mm_node->size == mem->num_pages))
|
||||
|
@ -1166,7 +1111,7 @@ gart_bind_fail:
|
|||
* This handles binding GTT memory to the device address space.
|
||||
*/
|
||||
static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm,
|
||||
struct ttm_mem_reg *bo_mem)
|
||||
struct ttm_resource *bo_mem)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(ttm->bdev);
|
||||
struct amdgpu_ttm_tt *gtt = (void*)ttm;
|
||||
|
@ -1217,7 +1162,7 @@ int amdgpu_ttm_alloc_gart(struct ttm_buffer_object *bo)
|
|||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
|
||||
struct ttm_operation_ctx ctx = { false, false };
|
||||
struct amdgpu_ttm_tt *gtt = (void*)bo->ttm;
|
||||
struct ttm_mem_reg tmp;
|
||||
struct ttm_resource tmp;
|
||||
struct ttm_placement placement;
|
||||
struct ttm_place placements;
|
||||
uint64_t addr, flags;
|
||||
|
@ -1254,11 +1199,11 @@ int amdgpu_ttm_alloc_gart(struct ttm_buffer_object *bo)
|
|||
gtt->offset = (u64)tmp.start << PAGE_SHIFT;
|
||||
r = amdgpu_ttm_gart_bind(adev, bo, flags);
|
||||
if (unlikely(r)) {
|
||||
ttm_bo_mem_put(bo, &tmp);
|
||||
ttm_resource_free(bo, &tmp);
|
||||
return r;
|
||||
}
|
||||
|
||||
ttm_bo_mem_put(bo, &bo->mem);
|
||||
ttm_resource_free(bo, &bo->mem);
|
||||
bo->mem = tmp;
|
||||
}
|
||||
|
||||
|
@ -1457,21 +1402,26 @@ static void amdgpu_ttm_tt_unpopulate(struct ttm_tt *ttm)
|
|||
* amdgpu_ttm_tt_set_userptr - Initialize userptr GTT ttm_tt for the current
|
||||
* task
|
||||
*
|
||||
* @ttm: The ttm_tt object to bind this userptr object to
|
||||
* @bo: The ttm_buffer_object to bind this userptr to
|
||||
* @addr: The address in the current tasks VM space to use
|
||||
* @flags: Requirements of userptr object.
|
||||
*
|
||||
* Called by amdgpu_gem_userptr_ioctl() to bind userptr pages
|
||||
* to current task
|
||||
*/
|
||||
int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
|
||||
uint32_t flags)
|
||||
int amdgpu_ttm_tt_set_userptr(struct ttm_buffer_object *bo,
|
||||
uint64_t addr, uint32_t flags)
|
||||
{
|
||||
struct amdgpu_ttm_tt *gtt = (void *)ttm;
|
||||
struct amdgpu_ttm_tt *gtt;
|
||||
|
||||
if (gtt == NULL)
|
||||
return -EINVAL;
|
||||
if (!bo->ttm) {
|
||||
/* TODO: We want a separate TTM object type for userptrs */
|
||||
bo->ttm = amdgpu_ttm_tt_create(bo, 0);
|
||||
if (bo->ttm == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
gtt = (void*)bo->ttm;
|
||||
gtt->userptr = addr;
|
||||
gtt->userflags = flags;
|
||||
|
||||
|
@ -1557,7 +1507,7 @@ bool amdgpu_ttm_tt_is_readonly(struct ttm_tt *ttm)
|
|||
*
|
||||
* Figure out the flags to use for a VM PDE (Page Directory Entry).
|
||||
*/
|
||||
uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_mem_reg *mem)
|
||||
uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_resource *mem)
|
||||
{
|
||||
uint64_t flags = 0;
|
||||
|
||||
|
@ -1583,7 +1533,7 @@ uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_mem_reg *mem)
|
|||
* Figure out the flags to use for a VM PTE (Page Table Entry).
|
||||
*/
|
||||
uint64_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
|
||||
struct ttm_mem_reg *mem)
|
||||
struct ttm_resource *mem)
|
||||
{
|
||||
uint64_t flags = amdgpu_ttm_tt_pde_flags(ttm, mem);
|
||||
|
||||
|
@ -1741,7 +1691,6 @@ static struct ttm_bo_driver amdgpu_bo_driver = {
|
|||
.ttm_tt_create = &amdgpu_ttm_tt_create,
|
||||
.ttm_tt_populate = &amdgpu_ttm_tt_populate,
|
||||
.ttm_tt_unpopulate = &amdgpu_ttm_tt_unpopulate,
|
||||
.init_mem_type = &amdgpu_init_mem_type,
|
||||
.eviction_valuable = amdgpu_ttm_bo_eviction_valuable,
|
||||
.evict_flags = &amdgpu_evict_flags,
|
||||
.move = &amdgpu_bo_move,
|
||||
|
@ -1936,8 +1885,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
|
|||
adev->mman.bdev.no_retry = true;
|
||||
|
||||
/* Initialize VRAM pool with all of VRAM divided into pages */
|
||||
r = ttm_bo_init_mm(&adev->mman.bdev, TTM_PL_VRAM,
|
||||
adev->gmc.real_vram_size >> PAGE_SHIFT);
|
||||
r = amdgpu_vram_mgr_init(adev);
|
||||
if (r) {
|
||||
DRM_ERROR("Failed initializing VRAM heap.\n");
|
||||
return r;
|
||||
|
@ -2004,7 +1952,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
|
|||
gtt_size = (uint64_t)amdgpu_gtt_size << 20;
|
||||
|
||||
/* Initialize GTT memory pool */
|
||||
r = ttm_bo_init_mm(&adev->mman.bdev, TTM_PL_TT, gtt_size >> PAGE_SHIFT);
|
||||
r = amdgpu_gtt_mgr_init(adev, gtt_size);
|
||||
if (r) {
|
||||
DRM_ERROR("Failed initializing GTT heap.\n");
|
||||
return r;
|
||||
|
@ -2013,22 +1961,19 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
|
|||
(unsigned)(gtt_size / (1024 * 1024)));
|
||||
|
||||
/* Initialize various on-chip memory pools */
|
||||
r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_GDS,
|
||||
adev->gds.gds_size);
|
||||
r = amdgpu_ttm_init_on_chip(adev, AMDGPU_PL_GDS, adev->gds.gds_size);
|
||||
if (r) {
|
||||
DRM_ERROR("Failed initializing GDS heap.\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_GWS,
|
||||
adev->gds.gws_size);
|
||||
r = amdgpu_ttm_init_on_chip(adev, AMDGPU_PL_GWS, adev->gds.gws_size);
|
||||
if (r) {
|
||||
DRM_ERROR("Failed initializing gws heap.\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_OA,
|
||||
adev->gds.oa_size);
|
||||
r = amdgpu_ttm_init_on_chip(adev, AMDGPU_PL_OA, adev->gds.oa_size);
|
||||
if (r) {
|
||||
DRM_ERROR("Failed initializing oa heap.\n");
|
||||
return r;
|
||||
|
@ -2064,11 +2009,11 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
|
|||
iounmap(adev->mman.aper_base_kaddr);
|
||||
adev->mman.aper_base_kaddr = NULL;
|
||||
|
||||
ttm_bo_clean_mm(&adev->mman.bdev, TTM_PL_VRAM);
|
||||
ttm_bo_clean_mm(&adev->mman.bdev, TTM_PL_TT);
|
||||
ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_GDS);
|
||||
ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_GWS);
|
||||
ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_OA);
|
||||
amdgpu_vram_mgr_fini(adev);
|
||||
amdgpu_gtt_mgr_fini(adev);
|
||||
ttm_range_man_fini(&adev->mman.bdev, AMDGPU_PL_GDS);
|
||||
ttm_range_man_fini(&adev->mman.bdev, AMDGPU_PL_GWS);
|
||||
ttm_range_man_fini(&adev->mman.bdev, AMDGPU_PL_OA);
|
||||
ttm_bo_device_release(&adev->mman.bdev);
|
||||
adev->mman.initialized = false;
|
||||
DRM_INFO("amdgpu: ttm finalized\n");
|
||||
|
@ -2085,7 +2030,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
|
|||
*/
|
||||
void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable)
|
||||
{
|
||||
struct ttm_mem_type_manager *man = &adev->mman.bdev.man[TTM_PL_VRAM];
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
|
||||
uint64_t size;
|
||||
int r;
|
||||
|
||||
|
@ -2307,7 +2252,7 @@ static int amdgpu_mm_dump_table(struct seq_file *m, void *data)
|
|||
unsigned ttm_pl = (uintptr_t)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct amdgpu_device *adev = dev->dev_private;
|
||||
struct ttm_mem_type_manager *man = &adev->mman.bdev.man[ttm_pl];
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, ttm_pl);
|
||||
struct drm_printer p = drm_seq_file_printer(m);
|
||||
|
||||
man->func->debug(man, &p);
|
||||
|
|
|
@ -41,6 +41,21 @@
|
|||
|
||||
#define AMDGPU_POISON 0xd0bed0be
|
||||
|
||||
struct amdgpu_vram_mgr {
|
||||
struct ttm_resource_manager manager;
|
||||
struct drm_mm mm;
|
||||
spinlock_t lock;
|
||||
atomic64_t usage;
|
||||
atomic64_t vis_usage;
|
||||
};
|
||||
|
||||
struct amdgpu_gtt_mgr {
|
||||
struct ttm_resource_manager manager;
|
||||
struct drm_mm mm;
|
||||
spinlock_t lock;
|
||||
atomic64_t available;
|
||||
};
|
||||
|
||||
struct amdgpu_mman {
|
||||
struct ttm_bo_device bdev;
|
||||
bool mem_global_referenced;
|
||||
|
@ -59,24 +74,29 @@ struct amdgpu_mman {
|
|||
struct mutex gtt_window_lock;
|
||||
/* Scheduler entity for buffer moves */
|
||||
struct drm_sched_entity entity;
|
||||
|
||||
struct amdgpu_vram_mgr vram_mgr;
|
||||
struct amdgpu_gtt_mgr gtt_mgr;
|
||||
};
|
||||
|
||||
struct amdgpu_copy_mem {
|
||||
struct ttm_buffer_object *bo;
|
||||
struct ttm_mem_reg *mem;
|
||||
struct ttm_resource *mem;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
extern const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func;
|
||||
extern const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func;
|
||||
int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size);
|
||||
void amdgpu_gtt_mgr_fini(struct amdgpu_device *adev);
|
||||
int amdgpu_vram_mgr_init(struct amdgpu_device *adev);
|
||||
void amdgpu_vram_mgr_fini(struct amdgpu_device *adev);
|
||||
|
||||
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem);
|
||||
uint64_t amdgpu_gtt_mgr_usage(struct ttm_mem_type_manager *man);
|
||||
int amdgpu_gtt_mgr_recover(struct ttm_mem_type_manager *man);
|
||||
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_resource *mem);
|
||||
uint64_t amdgpu_gtt_mgr_usage(struct ttm_resource_manager *man);
|
||||
int amdgpu_gtt_mgr_recover(struct ttm_resource_manager *man);
|
||||
|
||||
u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo);
|
||||
int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,
|
||||
struct ttm_mem_reg *mem,
|
||||
struct ttm_resource *mem,
|
||||
struct device *dev,
|
||||
enum dma_data_direction dir,
|
||||
struct sg_table **sgt);
|
||||
|
@ -84,8 +104,8 @@ void amdgpu_vram_mgr_free_sgt(struct amdgpu_device *adev,
|
|||
struct device *dev,
|
||||
enum dma_data_direction dir,
|
||||
struct sg_table *sgt);
|
||||
uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man);
|
||||
uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man);
|
||||
uint64_t amdgpu_vram_mgr_usage(struct ttm_resource_manager *man);
|
||||
uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_resource_manager *man);
|
||||
|
||||
int amdgpu_ttm_init(struct amdgpu_device *adev);
|
||||
void amdgpu_ttm_late_init(struct amdgpu_device *adev);
|
||||
|
@ -130,8 +150,8 @@ static inline bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm)
|
|||
#endif
|
||||
|
||||
void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct page **pages);
|
||||
int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
|
||||
uint32_t flags);
|
||||
int amdgpu_ttm_tt_set_userptr(struct ttm_buffer_object *bo,
|
||||
uint64_t addr, uint32_t flags);
|
||||
bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm);
|
||||
struct mm_struct *amdgpu_ttm_tt_get_usermm(struct ttm_tt *ttm);
|
||||
bool amdgpu_ttm_tt_affect_userptr(struct ttm_tt *ttm, unsigned long start,
|
||||
|
@ -140,9 +160,9 @@ bool amdgpu_ttm_tt_userptr_invalidated(struct ttm_tt *ttm,
|
|||
int *last_invalidated);
|
||||
bool amdgpu_ttm_tt_is_userptr(struct ttm_tt *ttm);
|
||||
bool amdgpu_ttm_tt_is_readonly(struct ttm_tt *ttm);
|
||||
uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_mem_reg *mem);
|
||||
uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_resource *mem);
|
||||
uint64_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
|
||||
struct ttm_mem_reg *mem);
|
||||
struct ttm_resource *mem);
|
||||
|
||||
int amdgpu_ttm_debugfs_init(struct amdgpu_device *adev);
|
||||
|
||||
|
|
|
@ -1765,7 +1765,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va,
|
|||
struct amdgpu_vm *vm = bo_va->base.vm;
|
||||
struct amdgpu_bo_va_mapping *mapping;
|
||||
dma_addr_t *pages_addr = NULL;
|
||||
struct ttm_mem_reg *mem;
|
||||
struct ttm_resource *mem;
|
||||
struct drm_mm_node *nodes;
|
||||
struct dma_fence **last_update;
|
||||
struct dma_resv *resv;
|
||||
|
|
|
@ -28,12 +28,15 @@
|
|||
#include "amdgpu_atomfirmware.h"
|
||||
#include "atom.h"
|
||||
|
||||
struct amdgpu_vram_mgr {
|
||||
struct drm_mm mm;
|
||||
spinlock_t lock;
|
||||
atomic64_t usage;
|
||||
atomic64_t vis_usage;
|
||||
};
|
||||
static inline struct amdgpu_vram_mgr *to_vram_mgr(struct ttm_resource_manager *man)
|
||||
{
|
||||
return container_of(man, struct amdgpu_vram_mgr, manager);
|
||||
}
|
||||
|
||||
static inline struct amdgpu_device *to_amdgpu_device(struct amdgpu_vram_mgr *mgr)
|
||||
{
|
||||
return container_of(mgr, struct amdgpu_device, mman.vram_mgr);
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: mem_info_vram_total
|
||||
|
@ -82,9 +85,9 @@ static ssize_t amdgpu_mem_info_vram_used_show(struct device *dev,
|
|||
{
|
||||
struct drm_device *ddev = dev_get_drvdata(dev);
|
||||
struct amdgpu_device *adev = ddev->dev_private;
|
||||
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
amdgpu_vram_mgr_usage(&adev->mman.bdev.man[TTM_PL_VRAM]));
|
||||
amdgpu_vram_mgr_usage(man));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,9 +103,9 @@ static ssize_t amdgpu_mem_info_vis_vram_used_show(struct device *dev,
|
|||
{
|
||||
struct drm_device *ddev = dev_get_drvdata(dev);
|
||||
struct amdgpu_device *adev = ddev->dev_private;
|
||||
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
amdgpu_vram_mgr_vis_usage(&adev->mman.bdev.man[TTM_PL_VRAM]));
|
||||
amdgpu_vram_mgr_vis_usage(man));
|
||||
}
|
||||
|
||||
static ssize_t amdgpu_mem_info_vram_vendor(struct device *dev,
|
||||
|
@ -158,6 +161,8 @@ static const struct attribute *amdgpu_vram_mgr_attributes[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func;
|
||||
|
||||
/**
|
||||
* amdgpu_vram_mgr_init - init VRAM manager and DRM MM
|
||||
*
|
||||
|
@ -166,26 +171,29 @@ static const struct attribute *amdgpu_vram_mgr_attributes[] = {
|
|||
*
|
||||
* Allocate and initialize the VRAM manager.
|
||||
*/
|
||||
static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man,
|
||||
unsigned long p_size)
|
||||
int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
|
||||
struct amdgpu_vram_mgr *mgr;
|
||||
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
|
||||
struct ttm_resource_manager *man = &mgr->manager;
|
||||
int ret;
|
||||
|
||||
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
|
||||
if (!mgr)
|
||||
return -ENOMEM;
|
||||
man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC;
|
||||
man->default_caching = TTM_PL_FLAG_WC;
|
||||
|
||||
drm_mm_init(&mgr->mm, 0, p_size);
|
||||
ttm_resource_manager_init(man, adev->gmc.real_vram_size >> PAGE_SHIFT);
|
||||
|
||||
man->func = &amdgpu_vram_mgr_func;
|
||||
|
||||
drm_mm_init(&mgr->mm, 0, man->size);
|
||||
spin_lock_init(&mgr->lock);
|
||||
man->priv = mgr;
|
||||
|
||||
/* Add the two VRAM-related sysfs files */
|
||||
ret = sysfs_create_files(&adev->dev->kobj, amdgpu_vram_mgr_attributes);
|
||||
if (ret)
|
||||
DRM_ERROR("Failed to register sysfs\n");
|
||||
|
||||
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
|
||||
ttm_resource_manager_set_used(man, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -197,18 +205,26 @@ static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man,
|
|||
* Destroy and free the VRAM manager, returns -EBUSY if ranges are still
|
||||
* allocated inside it.
|
||||
*/
|
||||
static int amdgpu_vram_mgr_fini(struct ttm_mem_type_manager *man)
|
||||
void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
|
||||
struct amdgpu_vram_mgr *mgr = man->priv;
|
||||
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
|
||||
struct ttm_resource_manager *man = &mgr->manager;
|
||||
int ret;
|
||||
|
||||
ttm_resource_manager_set_used(man, false);
|
||||
|
||||
ret = ttm_resource_manager_force_list_clean(&adev->mman.bdev, man);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
spin_lock(&mgr->lock);
|
||||
drm_mm_takedown(&mgr->mm);
|
||||
spin_unlock(&mgr->lock);
|
||||
kfree(mgr);
|
||||
man->priv = NULL;
|
||||
|
||||
sysfs_remove_files(&adev->dev->kobj, amdgpu_vram_mgr_attributes);
|
||||
return 0;
|
||||
|
||||
ttm_resource_manager_cleanup(man);
|
||||
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,7 +259,7 @@ static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev,
|
|||
u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
|
||||
struct ttm_mem_reg *mem = &bo->tbo.mem;
|
||||
struct ttm_resource *mem = &bo->tbo.mem;
|
||||
struct drm_mm_node *nodes = mem->mm_node;
|
||||
unsigned pages = mem->num_pages;
|
||||
u64 usage;
|
||||
|
@ -263,13 +279,13 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
|
|||
/**
|
||||
* amdgpu_vram_mgr_virt_start - update virtual start address
|
||||
*
|
||||
* @mem: ttm_mem_reg to update
|
||||
* @mem: ttm_resource to update
|
||||
* @node: just allocated node
|
||||
*
|
||||
* Calculate a virtual BO start address to easily check if everything is CPU
|
||||
* accessible.
|
||||
*/
|
||||
static void amdgpu_vram_mgr_virt_start(struct ttm_mem_reg *mem,
|
||||
static void amdgpu_vram_mgr_virt_start(struct ttm_resource *mem,
|
||||
struct drm_mm_node *node)
|
||||
{
|
||||
unsigned long start;
|
||||
|
@ -292,13 +308,13 @@ static void amdgpu_vram_mgr_virt_start(struct ttm_mem_reg *mem,
|
|||
*
|
||||
* Allocate VRAM for the given BO.
|
||||
*/
|
||||
static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man,
|
||||
static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
|
||||
struct ttm_buffer_object *tbo,
|
||||
const struct ttm_place *place,
|
||||
struct ttm_mem_reg *mem)
|
||||
struct ttm_resource *mem)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
|
||||
struct amdgpu_vram_mgr *mgr = man->priv;
|
||||
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
|
||||
struct amdgpu_device *adev = to_amdgpu_device(mgr);
|
||||
struct drm_mm *mm = &mgr->mm;
|
||||
struct drm_mm_node *nodes;
|
||||
enum drm_mm_insert_mode mode;
|
||||
|
@ -410,11 +426,11 @@ error:
|
|||
*
|
||||
* Free the allocated VRAM again.
|
||||
*/
|
||||
static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man,
|
||||
struct ttm_mem_reg *mem)
|
||||
static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
|
||||
struct ttm_resource *mem)
|
||||
{
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
|
||||
struct amdgpu_vram_mgr *mgr = man->priv;
|
||||
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
|
||||
struct amdgpu_device *adev = to_amdgpu_device(mgr);
|
||||
struct drm_mm_node *nodes = mem->mm_node;
|
||||
uint64_t usage = 0, vis_usage = 0;
|
||||
unsigned pages = mem->num_pages;
|
||||
|
@ -451,7 +467,7 @@ static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man,
|
|||
* Allocate and fill a sg table from a VRAM allocation.
|
||||
*/
|
||||
int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,
|
||||
struct ttm_mem_reg *mem,
|
||||
struct ttm_resource *mem,
|
||||
struct device *dev,
|
||||
enum dma_data_direction dir,
|
||||
struct sg_table **sgt)
|
||||
|
@ -544,9 +560,9 @@ void amdgpu_vram_mgr_free_sgt(struct amdgpu_device *adev,
|
|||
*
|
||||
* Returns how many bytes are used in this domain.
|
||||
*/
|
||||
uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man)
|
||||
uint64_t amdgpu_vram_mgr_usage(struct ttm_resource_manager *man)
|
||||
{
|
||||
struct amdgpu_vram_mgr *mgr = man->priv;
|
||||
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
|
||||
|
||||
return atomic64_read(&mgr->usage);
|
||||
}
|
||||
|
@ -558,9 +574,9 @@ uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man)
|
|||
*
|
||||
* Returns how many bytes are used in the visible part of VRAM
|
||||
*/
|
||||
uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man)
|
||||
uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_resource_manager *man)
|
||||
{
|
||||
struct amdgpu_vram_mgr *mgr = man->priv;
|
||||
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
|
||||
|
||||
return atomic64_read(&mgr->vis_usage);
|
||||
}
|
||||
|
@ -573,10 +589,10 @@ uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man)
|
|||
*
|
||||
* Dump the table content using printk.
|
||||
*/
|
||||
static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
|
||||
static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man,
|
||||
struct drm_printer *printer)
|
||||
{
|
||||
struct amdgpu_vram_mgr *mgr = man->priv;
|
||||
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
|
||||
|
||||
spin_lock(&mgr->lock);
|
||||
drm_mm_print(&mgr->mm, printer);
|
||||
|
@ -587,10 +603,8 @@ static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
|
|||
amdgpu_vram_mgr_vis_usage(man) >> 20);
|
||||
}
|
||||
|
||||
const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
|
||||
.init = amdgpu_vram_mgr_init,
|
||||
.takedown = amdgpu_vram_mgr_fini,
|
||||
.get_node = amdgpu_vram_mgr_new,
|
||||
.put_node = amdgpu_vram_mgr_del,
|
||||
.debug = amdgpu_vram_mgr_debug
|
||||
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
|
||||
.alloc = amdgpu_vram_mgr_new,
|
||||
.free = amdgpu_vram_mgr_del,
|
||||
.debug = amdgpu_vram_mgr_debug
|
||||
};
|
||||
|
|
|
@ -328,6 +328,22 @@ static void amdgpu_atombios_dp_probe_oui(struct amdgpu_connector *amdgpu_connect
|
|||
buf[0], buf[1], buf[2]);
|
||||
}
|
||||
|
||||
static void amdgpu_atombios_dp_ds_ports(struct amdgpu_connector *amdgpu_connector)
|
||||
{
|
||||
struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv;
|
||||
int ret;
|
||||
|
||||
if (dig_connector->dpcd[DP_DPCD_REV] > 0x10) {
|
||||
ret = drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux,
|
||||
DP_DOWNSTREAM_PORT_0,
|
||||
dig_connector->downstream_ports,
|
||||
DP_MAX_DOWNSTREAM_PORTS);
|
||||
if (ret)
|
||||
memset(dig_connector->downstream_ports, 0,
|
||||
DP_MAX_DOWNSTREAM_PORTS);
|
||||
}
|
||||
}
|
||||
|
||||
int amdgpu_atombios_dp_get_dpcd(struct amdgpu_connector *amdgpu_connector)
|
||||
{
|
||||
struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv;
|
||||
|
@ -343,7 +359,7 @@ int amdgpu_atombios_dp_get_dpcd(struct amdgpu_connector *amdgpu_connector)
|
|||
dig_connector->dpcd);
|
||||
|
||||
amdgpu_atombios_dp_probe_oui(amdgpu_connector);
|
||||
|
||||
amdgpu_atombios_dp_ds_ports(amdgpu_connector);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,42 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
|
|||
static int amdgpu_dm_init(struct amdgpu_device *adev);
|
||||
static void amdgpu_dm_fini(struct amdgpu_device *adev);
|
||||
|
||||
static enum drm_mode_subconnector get_subconnector_type(struct dc_link *link)
|
||||
{
|
||||
switch (link->dpcd_caps.dongle_type) {
|
||||
case DISPLAY_DONGLE_NONE:
|
||||
return DRM_MODE_SUBCONNECTOR_Native;
|
||||
case DISPLAY_DONGLE_DP_VGA_CONVERTER:
|
||||
return DRM_MODE_SUBCONNECTOR_VGA;
|
||||
case DISPLAY_DONGLE_DP_DVI_CONVERTER:
|
||||
case DISPLAY_DONGLE_DP_DVI_DONGLE:
|
||||
return DRM_MODE_SUBCONNECTOR_DVID;
|
||||
case DISPLAY_DONGLE_DP_HDMI_CONVERTER:
|
||||
case DISPLAY_DONGLE_DP_HDMI_DONGLE:
|
||||
return DRM_MODE_SUBCONNECTOR_HDMIA;
|
||||
case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE:
|
||||
default:
|
||||
return DRM_MODE_SUBCONNECTOR_Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct dc_link *link = aconnector->dc_link;
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
|
||||
|
||||
if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
|
||||
return;
|
||||
|
||||
if (aconnector->dc_sink)
|
||||
subconnector = get_subconnector_type(link);
|
||||
|
||||
drm_object_property_set_value(&connector->base,
|
||||
connector->dev->mode_config.dp_subconnector_property,
|
||||
subconnector);
|
||||
}
|
||||
|
||||
/*
|
||||
* initializes drm_device display related structures, based on the information
|
||||
* provided by DAL. The drm strcutures are: drm_crtc, drm_connector,
|
||||
|
@ -2095,7 +2131,6 @@ void amdgpu_dm_update_connector_after_detect(
|
|||
if (aconnector->mst_mgr.mst_state == true)
|
||||
return;
|
||||
|
||||
|
||||
sink = aconnector->dc_link->local_sink;
|
||||
if (sink)
|
||||
dc_sink_retain(sink);
|
||||
|
@ -2222,6 +2257,8 @@ void amdgpu_dm_update_connector_after_detect(
|
|||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
update_subconnector_property(aconnector);
|
||||
|
||||
if (sink)
|
||||
dc_sink_release(sink);
|
||||
}
|
||||
|
@ -4757,6 +4794,8 @@ amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
|
|||
else
|
||||
connected = (aconnector->base.force == DRM_FORCE_ON);
|
||||
|
||||
update_subconnector_property(aconnector);
|
||||
|
||||
return (connected ? connector_status_connected :
|
||||
connector_status_disconnected);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/version.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_dp_mst_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include "dm_services.h"
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_dm.h"
|
||||
|
@ -431,6 +432,8 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
|
|||
16,
|
||||
4,
|
||||
aconnector->connector_id);
|
||||
|
||||
drm_connector_attach_dp_subconnector_property(&aconnector->base);
|
||||
}
|
||||
|
||||
int dm_mst_get_pbn_divider(struct dc_link *link)
|
||||
|
|
|
@ -346,7 +346,7 @@ static bool malidp_check_pages_threshold(struct malidp_plane_state *ms,
|
|||
if (cma_obj->sgt)
|
||||
sgt = cma_obj->sgt;
|
||||
else
|
||||
sgt = obj->dev->driver->gem_prime_get_sg_table(obj);
|
||||
sgt = obj->funcs->get_sg_table(obj);
|
||||
|
||||
if (!sgt)
|
||||
return false;
|
||||
|
|
|
@ -47,7 +47,7 @@ static void ast_cursor_fini(struct ast_private *ast)
|
|||
|
||||
static void ast_cursor_release(struct drm_device *dev, void *ptr)
|
||||
{
|
||||
struct ast_private *ast = dev->dev_private;
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
|
||||
ast_cursor_fini(ast);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ static void ast_cursor_release(struct drm_device *dev, void *ptr)
|
|||
*/
|
||||
int ast_cursor_init(struct ast_private *ast)
|
||||
{
|
||||
struct drm_device *dev = ast->dev;
|
||||
struct drm_device *dev = &ast->base;
|
||||
size_t size, i;
|
||||
struct drm_gem_vram_object *gbo;
|
||||
void __iomem *vaddr;
|
||||
|
@ -168,7 +168,7 @@ static void update_cursor_image(u8 __iomem *dst, const u8 *src, int width, int h
|
|||
|
||||
int ast_cursor_blit(struct ast_private *ast, struct drm_framebuffer *fb)
|
||||
{
|
||||
struct drm_device *dev = ast->dev;
|
||||
struct drm_device *dev = &ast->base;
|
||||
struct drm_gem_vram_object *gbo;
|
||||
int ret;
|
||||
void *src;
|
||||
|
@ -217,7 +217,7 @@ static void ast_cursor_set_base(struct ast_private *ast, u64 address)
|
|||
|
||||
void ast_cursor_page_flip(struct ast_private *ast)
|
||||
{
|
||||
struct drm_device *dev = ast->dev;
|
||||
struct drm_device *dev = &ast->base;
|
||||
struct drm_gem_vram_object *gbo;
|
||||
s64 off;
|
||||
|
||||
|
@ -253,7 +253,8 @@ void ast_cursor_show(struct ast_private *ast, int x, int y,
|
|||
unsigned int offset_x, unsigned int offset_y)
|
||||
{
|
||||
u8 x_offset, y_offset;
|
||||
u8 __iomem *dst, __iomem *sig;
|
||||
u8 __iomem *dst;
|
||||
u8 __iomem *sig;
|
||||
u8 jreg;
|
||||
|
||||
dst = ast->cursor.vaddr[ast->cursor.next_index];
|
||||
|
|
|
@ -8,11 +8,24 @@
|
|||
|
||||
MODULE_FIRMWARE("ast_dp501_fw.bin");
|
||||
|
||||
static void ast_release_firmware(void *data)
|
||||
{
|
||||
struct ast_private *ast = data;
|
||||
|
||||
release_firmware(ast->dp501_fw);
|
||||
ast->dp501_fw = NULL;
|
||||
}
|
||||
|
||||
static int ast_load_dp501_microcode(struct drm_device *dev)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
int ret;
|
||||
|
||||
return request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev);
|
||||
ret = request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev->dev, ast_release_firmware, ast);
|
||||
}
|
||||
|
||||
static void send_ack(struct ast_private *ast)
|
||||
|
@ -435,11 +448,3 @@ void ast_init_3rdtx(struct drm_device *dev)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ast_release_firmware(struct drm_device *dev)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
|
||||
release_firmware(ast->dp501_fw);
|
||||
ast->dp501_fw = NULL;
|
||||
}
|
||||
|
|
|
@ -43,9 +43,33 @@ int ast_modeset = -1;
|
|||
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
|
||||
module_param_named(modeset, ast_modeset, int, 0400);
|
||||
|
||||
#define PCI_VENDOR_ASPEED 0x1a03
|
||||
/*
|
||||
* DRM driver
|
||||
*/
|
||||
|
||||
static struct drm_driver driver;
|
||||
DEFINE_DRM_GEM_FOPS(ast_fops);
|
||||
|
||||
static struct drm_driver ast_driver = {
|
||||
.driver_features = DRIVER_ATOMIC |
|
||||
DRIVER_GEM |
|
||||
DRIVER_MODESET,
|
||||
|
||||
.fops = &ast_fops,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = DRIVER_DATE,
|
||||
.major = DRIVER_MAJOR,
|
||||
.minor = DRIVER_MINOR,
|
||||
.patchlevel = DRIVER_PATCHLEVEL,
|
||||
|
||||
DRM_GEM_VRAM_DRIVER
|
||||
};
|
||||
|
||||
/*
|
||||
* PCI driver
|
||||
*/
|
||||
|
||||
#define PCI_VENDOR_ASPEED 0x1a03
|
||||
|
||||
#define AST_VGA_DEVICE(id, info) { \
|
||||
.class = PCI_BASE_CLASS_DISPLAY << 16, \
|
||||
|
@ -56,13 +80,13 @@ static struct drm_driver driver;
|
|||
.subdevice = PCI_ANY_ID, \
|
||||
.driver_data = (unsigned long) info }
|
||||
|
||||
static const struct pci_device_id pciidlist[] = {
|
||||
static const struct pci_device_id ast_pciidlist[] = {
|
||||
AST_VGA_DEVICE(PCI_CHIP_AST2000, NULL),
|
||||
AST_VGA_DEVICE(PCI_CHIP_AST2100, NULL),
|
||||
{0, 0, 0},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, pciidlist);
|
||||
MODULE_DEVICE_TABLE(pci, ast_pciidlist);
|
||||
|
||||
static void ast_kick_out_firmware_fb(struct pci_dev *pdev)
|
||||
{
|
||||
|
@ -85,6 +109,7 @@ static void ast_kick_out_firmware_fb(struct pci_dev *pdev)
|
|||
|
||||
static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
struct ast_private *ast;
|
||||
struct drm_device *dev;
|
||||
int ret;
|
||||
|
||||
|
@ -94,41 +119,25 @@ static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev = drm_dev_alloc(&driver, &pdev->dev);
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
dev->pdev = pdev;
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
ret = ast_driver_load(dev, ent->driver_data);
|
||||
if (ret)
|
||||
goto err_drm_dev_put;
|
||||
ast = ast_device_create(&ast_driver, pdev, ent->driver_data);
|
||||
if (IS_ERR(ast))
|
||||
return PTR_ERR(ast);
|
||||
dev = &ast->base;
|
||||
|
||||
ret = drm_dev_register(dev, ent->driver_data);
|
||||
if (ret)
|
||||
goto err_ast_driver_unload;
|
||||
return ret;
|
||||
|
||||
drm_fbdev_generic_setup(dev, 32);
|
||||
|
||||
return 0;
|
||||
|
||||
err_ast_driver_unload:
|
||||
ast_driver_unload(dev);
|
||||
err_drm_dev_put:
|
||||
drm_dev_put(dev);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
ast_pci_remove(struct pci_dev *pdev)
|
||||
static void ast_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct drm_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
drm_dev_unregister(dev);
|
||||
ast_driver_unload(dev);
|
||||
drm_dev_put(dev);
|
||||
}
|
||||
|
||||
static int ast_drm_freeze(struct drm_device *dev)
|
||||
|
@ -217,30 +226,12 @@ static const struct dev_pm_ops ast_pm_ops = {
|
|||
|
||||
static struct pci_driver ast_pci_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = pciidlist,
|
||||
.id_table = ast_pciidlist,
|
||||
.probe = ast_pci_probe,
|
||||
.remove = ast_pci_remove,
|
||||
.driver.pm = &ast_pm_ops,
|
||||
};
|
||||
|
||||
DEFINE_DRM_GEM_FOPS(ast_fops);
|
||||
|
||||
static struct drm_driver driver = {
|
||||
.driver_features = DRIVER_ATOMIC |
|
||||
DRIVER_GEM |
|
||||
DRIVER_MODESET,
|
||||
|
||||
.fops = &ast_fops,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = DRIVER_DATE,
|
||||
.major = DRIVER_MAJOR,
|
||||
.minor = DRIVER_MINOR,
|
||||
.patchlevel = DRIVER_PATCHLEVEL,
|
||||
|
||||
DRM_GEM_VRAM_DRIVER
|
||||
};
|
||||
|
||||
static int __init ast_init(void)
|
||||
{
|
||||
if (vgacon_text_force() && ast_modeset == -1)
|
||||
|
@ -261,4 +252,3 @@ module_exit(ast_exit);
|
|||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
||||
|
|
|
@ -98,9 +98,25 @@ enum ast_tx_chip {
|
|||
#define AST_HWC_SIGNATURE_HOTSPOTX 0x14
|
||||
#define AST_HWC_SIGNATURE_HOTSPOTY 0x18
|
||||
|
||||
struct ast_i2c_chan {
|
||||
struct i2c_adapter adapter;
|
||||
struct drm_device *dev;
|
||||
struct i2c_algo_bit_data bit;
|
||||
};
|
||||
|
||||
struct ast_connector {
|
||||
struct drm_connector base;
|
||||
struct ast_i2c_chan *i2c;
|
||||
};
|
||||
|
||||
static inline struct ast_connector *
|
||||
to_ast_connector(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct ast_connector, base);
|
||||
}
|
||||
|
||||
struct ast_private {
|
||||
struct drm_device *dev;
|
||||
struct drm_device base;
|
||||
|
||||
void __iomem *regs;
|
||||
void __iomem *ioregs;
|
||||
|
@ -119,9 +135,11 @@ struct ast_private {
|
|||
unsigned int next_index;
|
||||
} cursor;
|
||||
|
||||
struct drm_encoder encoder;
|
||||
struct drm_plane primary_plane;
|
||||
struct drm_plane cursor_plane;
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder encoder;
|
||||
struct ast_connector connector;
|
||||
|
||||
bool support_wide_screen;
|
||||
enum {
|
||||
|
@ -138,11 +156,12 @@ struct ast_private {
|
|||
|
||||
static inline struct ast_private *to_ast_private(struct drm_device *dev)
|
||||
{
|
||||
return dev->dev_private;
|
||||
return container_of(dev, struct ast_private, base);
|
||||
}
|
||||
|
||||
int ast_driver_load(struct drm_device *dev, unsigned long flags);
|
||||
void ast_driver_unload(struct drm_device *dev);
|
||||
struct ast_private *ast_device_create(struct drm_driver *drv,
|
||||
struct pci_dev *pdev,
|
||||
unsigned long flags);
|
||||
|
||||
#define AST_IO_AR_PORT_WRITE (0x40)
|
||||
#define AST_IO_MISC_PORT_WRITE (0x42)
|
||||
|
@ -226,19 +245,6 @@ static inline void ast_open_key(struct ast_private *ast)
|
|||
|
||||
#define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M
|
||||
|
||||
struct ast_i2c_chan {
|
||||
struct i2c_adapter adapter;
|
||||
struct drm_device *dev;
|
||||
struct i2c_algo_bit_data bit;
|
||||
};
|
||||
|
||||
struct ast_connector {
|
||||
struct drm_connector base;
|
||||
struct ast_i2c_chan *i2c;
|
||||
};
|
||||
|
||||
#define to_ast_connector(x) container_of(x, struct ast_connector, base)
|
||||
|
||||
struct ast_vbios_stdtable {
|
||||
u8 misc;
|
||||
u8 seq[4];
|
||||
|
@ -305,7 +311,6 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size);
|
|||
bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata);
|
||||
u8 ast_get_dp501_max_clk(struct drm_device *dev);
|
||||
void ast_init_3rdtx(struct drm_device *dev);
|
||||
void ast_release_firmware(struct drm_device *dev);
|
||||
|
||||
/* ast_cursor.c */
|
||||
int ast_cursor_init(struct ast_private *ast);
|
||||
|
|
|
@ -30,8 +30,10 @@
|
|||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_gem_vram_helper.h>
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
#include "ast_drv.h"
|
||||
|
||||
|
@ -230,11 +232,11 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
|
|||
ast->tx_chip_type = AST_TX_SIL164;
|
||||
break;
|
||||
case 0x08:
|
||||
ast->dp501_fw_addr = kzalloc(32*1024, GFP_KERNEL);
|
||||
ast->dp501_fw_addr = drmm_kzalloc(dev, 32*1024, GFP_KERNEL);
|
||||
if (ast->dp501_fw_addr) {
|
||||
/* backup firmware */
|
||||
if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) {
|
||||
kfree(ast->dp501_fw_addr);
|
||||
drmm_kfree(dev, ast->dp501_fw_addr);
|
||||
ast->dp501_fw_addr = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -378,24 +380,38 @@ static int ast_get_dram_info(struct drm_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ast_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
/*
|
||||
* Run this function as part of the HW device cleanup; not
|
||||
* when the DRM device gets released.
|
||||
*/
|
||||
static void ast_device_release(void *data)
|
||||
{
|
||||
struct ast_private *ast = data;
|
||||
|
||||
/* enable standard VGA decode */
|
||||
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04);
|
||||
}
|
||||
|
||||
struct ast_private *ast_device_create(struct drm_driver *drv,
|
||||
struct pci_dev *pdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
struct ast_private *ast;
|
||||
bool need_post;
|
||||
int ret = 0;
|
||||
|
||||
ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL);
|
||||
if (!ast)
|
||||
return -ENOMEM;
|
||||
ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_private, base);
|
||||
if (IS_ERR(ast))
|
||||
return ast;
|
||||
dev = &ast->base;
|
||||
|
||||
dev->dev_private = ast;
|
||||
ast->dev = dev;
|
||||
dev->pdev = pdev;
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
ast->regs = pci_iomap(dev->pdev, 1, 0);
|
||||
if (!ast->regs) {
|
||||
ret = -EIO;
|
||||
goto out_free;
|
||||
}
|
||||
if (!ast->regs)
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
/*
|
||||
* If we don't have IO space at all, use MMIO now and
|
||||
|
@ -410,17 +426,16 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
/* "map" IO regs if the above hasn't done so already */
|
||||
if (!ast->ioregs) {
|
||||
ast->ioregs = pci_iomap(dev->pdev, 2, 0);
|
||||
if (!ast->ioregs) {
|
||||
ret = -EIO;
|
||||
goto out_free;
|
||||
}
|
||||
if (!ast->ioregs)
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
ast_detect_chip(dev, &need_post);
|
||||
|
||||
ret = ast_get_dram_info(dev);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
return ERR_PTR(ret);
|
||||
|
||||
drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n",
|
||||
ast->mclk, ast->dram_type, ast->dram_bus_width);
|
||||
|
||||
|
@ -429,28 +444,15 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
|
||||
ret = ast_mm_init(ast);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = ast_mode_config_init(ast);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return 0;
|
||||
out_free:
|
||||
kfree(ast);
|
||||
dev->dev_private = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ast_driver_unload(struct drm_device *dev)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
|
||||
/* enable standard VGA decode */
|
||||
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04);
|
||||
|
||||
ast_release_firmware(dev);
|
||||
kfree(ast->dp501_fw_addr);
|
||||
|
||||
kfree(ast);
|
||||
ret = devm_add_action_or_reset(dev->dev, ast_device_release, ast);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
|
|
@ -85,9 +85,9 @@ static void ast_mm_release(struct drm_device *dev, void *ptr)
|
|||
|
||||
int ast_mm_init(struct ast_private *ast)
|
||||
{
|
||||
struct drm_device *dev = &ast->base;
|
||||
u32 vram_size;
|
||||
int ret;
|
||||
struct drm_device *dev = ast->dev;
|
||||
|
||||
vram_size = ast_get_vram_size(ast);
|
||||
|
||||
|
|
|
@ -663,7 +663,7 @@ ast_cursor_plane_helper_atomic_update(struct drm_plane *plane,
|
|||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct ast_private *ast = plane->dev->dev_private;
|
||||
struct ast_private *ast = to_ast_private(plane->dev);
|
||||
unsigned int offset_x, offset_y;
|
||||
|
||||
offset_x = AST_MAX_HWC_WIDTH - fb->width;
|
||||
|
@ -831,12 +831,6 @@ static void ast_crtc_reset(struct drm_crtc *crtc)
|
|||
__drm_atomic_helper_crtc_reset(crtc, &ast_state->base);
|
||||
}
|
||||
|
||||
static void ast_crtc_destroy(struct drm_crtc *crtc)
|
||||
{
|
||||
drm_crtc_cleanup(crtc);
|
||||
kfree(crtc);
|
||||
}
|
||||
|
||||
static struct drm_crtc_state *
|
||||
ast_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
|
||||
{
|
||||
|
@ -872,7 +866,7 @@ static void ast_crtc_atomic_destroy_state(struct drm_crtc *crtc,
|
|||
static const struct drm_crtc_funcs ast_crtc_funcs = {
|
||||
.reset = ast_crtc_reset,
|
||||
.gamma_set = drm_atomic_helper_legacy_gamma_set,
|
||||
.destroy = ast_crtc_destroy,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.atomic_duplicate_state = ast_crtc_atomic_duplicate_state,
|
||||
|
@ -882,27 +876,19 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
|
|||
static int ast_crtc_init(struct drm_device *dev)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc *crtc = &ast->crtc;
|
||||
int ret;
|
||||
|
||||
crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
|
||||
if (!crtc)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = drm_crtc_init_with_planes(dev, crtc, &ast->primary_plane,
|
||||
&ast->cursor_plane, &ast_crtc_funcs,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto err_kfree;
|
||||
return ret;
|
||||
|
||||
drm_mode_crtc_set_gamma_size(crtc, 256);
|
||||
drm_crtc_helper_add(crtc, &ast_crtc_helper_funcs);
|
||||
|
||||
return 0;
|
||||
|
||||
err_kfree:
|
||||
kfree(crtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1021,7 +1007,6 @@ static void ast_connector_destroy(struct drm_connector *connector)
|
|||
struct ast_connector *ast_connector = to_ast_connector(connector);
|
||||
ast_i2c_destroy(ast_connector->i2c);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs ast_connector_helper_funcs = {
|
||||
|
@ -1039,15 +1024,11 @@ static const struct drm_connector_funcs ast_connector_funcs = {
|
|||
|
||||
static int ast_connector_init(struct drm_device *dev)
|
||||
{
|
||||
struct ast_connector *ast_connector;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
struct ast_connector *ast_connector = &ast->connector;
|
||||
struct drm_connector *connector = &ast_connector->base;
|
||||
struct drm_encoder *encoder = &ast->encoder;
|
||||
|
||||
ast_connector = kzalloc(sizeof(struct ast_connector), GFP_KERNEL);
|
||||
if (!ast_connector)
|
||||
return -ENOMEM;
|
||||
|
||||
connector = &ast_connector->base;
|
||||
ast_connector->i2c = ast_i2c_create(dev);
|
||||
if (!ast_connector->i2c)
|
||||
drm_err(dev, "failed to add ddc bus for connector\n");
|
||||
|
@ -1064,7 +1045,6 @@ static int ast_connector_init(struct drm_device *dev)
|
|||
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
|
||||
encoder = list_first_entry(&dev->mode_config.encoder_list, struct drm_encoder, head);
|
||||
drm_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
|
@ -1083,7 +1063,7 @@ static const struct drm_mode_config_funcs ast_mode_config_funcs = {
|
|||
|
||||
int ast_mode_config_init(struct ast_private *ast)
|
||||
{
|
||||
struct drm_device *dev = ast->dev;
|
||||
struct drm_device *dev = &ast->base;
|
||||
int ret;
|
||||
|
||||
ret = ast_cursor_init(ast);
|
||||
|
@ -1099,7 +1079,7 @@ int ast_mode_config_init(struct ast_private *ast)
|
|||
dev->mode_config.min_height = 0;
|
||||
dev->mode_config.preferred_depth = 24;
|
||||
dev->mode_config.prefer_shadow = 1;
|
||||
dev->mode_config.fb_base = pci_resource_start(ast->dev->pdev, 0);
|
||||
dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0);
|
||||
|
||||
if (ast->chip == AST2100 ||
|
||||
ast->chip == AST2200 ||
|
||||
|
|
|
@ -365,12 +365,12 @@ static void ast_init_dram_reg(struct drm_device *dev)
|
|||
|
||||
void ast_post_gpu(struct drm_device *dev)
|
||||
{
|
||||
u32 reg;
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
u32 reg;
|
||||
|
||||
pci_read_config_dword(ast->dev->pdev, 0x04, ®);
|
||||
pci_read_config_dword(dev->pdev, 0x04, ®);
|
||||
reg |= 0x3;
|
||||
pci_write_config_dword(ast->dev->pdev, 0x04, reg);
|
||||
pci_write_config_dword(dev->pdev, 0x04, reg);
|
||||
|
||||
ast_enable_vga(dev);
|
||||
ast_open_key(ast);
|
||||
|
|
|
@ -48,6 +48,19 @@ config DRM_DISPLAY_CONNECTOR
|
|||
on ARM-based platforms. Saying Y here when this driver is not needed
|
||||
will not cause any issue.
|
||||
|
||||
config DRM_LONTIUM_LT9611
|
||||
tristate "Lontium LT9611 DSI/HDMI bridge"
|
||||
select SND_SOC_HDMI_CODEC if SND_SOC
|
||||
depends on OF
|
||||
select DRM_PANEL_BRIDGE
|
||||
select DRM_KMS_HELPER
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Driver for Lontium LT9611 DSI to HDMI bridge
|
||||
chip driver that converts dual DSI and I2S to
|
||||
HDMI signals
|
||||
Please say Y if you have such hardware.
|
||||
|
||||
config DRM_LVDS_CODEC
|
||||
tristate "Transparent LVDS encoders and decoders support"
|
||||
depends on OF
|
||||
|
@ -153,6 +166,14 @@ config DRM_THINE_THC63LVD1024
|
|||
help
|
||||
Thine THC63LVD1024 LVDS/parallel converter driver.
|
||||
|
||||
config DRM_TOSHIBA_TC358762
|
||||
tristate "TC358762 DSI/DPI bridge"
|
||||
depends on OF
|
||||
select DRM_MIPI_DSI
|
||||
select DRM_PANEL_BRIDGE
|
||||
help
|
||||
Toshiba TC358762 DSI/DPI bridge driver.
|
||||
|
||||
config DRM_TOSHIBA_TC358764
|
||||
tristate "TC358764 DSI/LVDS bridge"
|
||||
depends on OF
|
||||
|
@ -181,6 +202,16 @@ config DRM_TOSHIBA_TC358768
|
|||
help
|
||||
Toshiba TC358768AXBG/TC358778XBG DSI bridge chip driver.
|
||||
|
||||
config DRM_TOSHIBA_TC358775
|
||||
tristate "Toshiba TC358775 DSI/LVDS bridge"
|
||||
depends on OF
|
||||
select DRM_KMS_HELPER
|
||||
select REGMAP_I2C
|
||||
select DRM_PANEL
|
||||
select DRM_MIPI_DSI
|
||||
help
|
||||
Toshiba TC358775 DSI/LVDS bridge chip driver.
|
||||
|
||||
config DRM_TI_TFP410
|
||||
tristate "TI TFP410 DVI/HDMI bridge"
|
||||
depends on OF
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
|
||||
obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
|
||||
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
|
||||
obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o
|
||||
obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
|
||||
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
|
||||
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
|
||||
|
@ -12,9 +13,11 @@ obj-$(CONFIG_DRM_SII902X) += sii902x.o
|
|||
obj-$(CONFIG_DRM_SII9234) += sii9234.o
|
||||
obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o
|
||||
obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358762) += tc358762.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358768) += tc358768.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358775) += tc358775.o
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
|
||||
obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
|
||||
obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
|
||||
|
|
|
@ -507,10 +507,6 @@ static const struct drm_connector_helper_funcs anx6345_connector_helper_funcs =
|
|||
static void
|
||||
anx6345_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct anx6345 *anx6345 = connector_to_anx6345(connector);
|
||||
|
||||
if (anx6345->panel)
|
||||
drm_panel_detach(anx6345->panel);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
|
@ -575,14 +571,6 @@ static int anx6345_bridge_attach(struct drm_bridge *bridge,
|
|||
return err;
|
||||
}
|
||||
|
||||
if (anx6345->panel) {
|
||||
err = drm_panel_attach(anx6345->panel, &anx6345->connector);
|
||||
if (err) {
|
||||
DRM_ERROR("Failed to attach panel: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1265,14 +1265,6 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
|
|||
}
|
||||
}
|
||||
|
||||
if (dp->plat_data->panel) {
|
||||
ret = drm_panel_attach(dp->plat_data->panel, &dp->connector);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to attach panel\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1803,7 +1795,6 @@ void analogix_dp_unbind(struct analogix_dp_device *dp)
|
|||
if (dp->plat_data->panel) {
|
||||
if (drm_panel_unprepare(dp->plat_data->panel))
|
||||
DRM_ERROR("failed to turnoff the panel\n");
|
||||
drm_panel_detach(dp->plat_data->panel);
|
||||
}
|
||||
|
||||
drm_dp_aux_unregister(&dp->aux);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -61,7 +61,6 @@ struct ge_b850v3_lvds {
|
|||
struct drm_bridge bridge;
|
||||
struct i2c_client *stdp4028_i2c;
|
||||
struct i2c_client *stdp2690_i2c;
|
||||
struct edid *edid;
|
||||
};
|
||||
|
||||
static struct ge_b850v3_lvds *ge_b850v3_lvds_ptr;
|
||||
|
@ -131,22 +130,26 @@ err:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int ge_b850v3_lvds_get_modes(struct drm_connector *connector)
|
||||
static struct edid *ge_b850v3_lvds_get_edid(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
int num_modes = 0;
|
||||
|
||||
client = ge_b850v3_lvds_ptr->stdp2690_i2c;
|
||||
|
||||
kfree(ge_b850v3_lvds_ptr->edid);
|
||||
ge_b850v3_lvds_ptr->edid = (struct edid *)stdp2690_get_edid(client);
|
||||
return (struct edid *)stdp2690_get_edid(client);
|
||||
}
|
||||
|
||||
if (ge_b850v3_lvds_ptr->edid) {
|
||||
drm_connector_update_edid_property(connector,
|
||||
ge_b850v3_lvds_ptr->edid);
|
||||
num_modes = drm_add_edid_modes(connector,
|
||||
ge_b850v3_lvds_ptr->edid);
|
||||
}
|
||||
static int ge_b850v3_lvds_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct edid *edid;
|
||||
int num_modes;
|
||||
|
||||
edid = ge_b850v3_lvds_get_edid(&ge_b850v3_lvds_ptr->bridge, connector);
|
||||
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
num_modes = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
return num_modes;
|
||||
}
|
||||
|
@ -163,8 +166,7 @@ drm_connector_helper_funcs ge_b850v3_lvds_connector_helper_funcs = {
|
|||
.mode_valid = ge_b850v3_lvds_mode_valid,
|
||||
};
|
||||
|
||||
static enum drm_connector_status ge_b850v3_lvds_detect(
|
||||
struct drm_connector *connector, bool force)
|
||||
static enum drm_connector_status ge_b850v3_lvds_bridge_detect(struct drm_bridge *bridge)
|
||||
{
|
||||
struct i2c_client *stdp4028_i2c =
|
||||
ge_b850v3_lvds_ptr->stdp4028_i2c;
|
||||
|
@ -182,6 +184,12 @@ static enum drm_connector_status ge_b850v3_lvds_detect(
|
|||
return connector_status_unknown;
|
||||
}
|
||||
|
||||
static enum drm_connector_status ge_b850v3_lvds_detect(struct drm_connector *connector,
|
||||
bool force)
|
||||
{
|
||||
return ge_b850v3_lvds_bridge_detect(&ge_b850v3_lvds_ptr->bridge);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = ge_b850v3_lvds_detect,
|
||||
|
@ -191,34 +199,11 @@ static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = {
|
|||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_client *stdp4028_i2c
|
||||
= ge_b850v3_lvds_ptr->stdp4028_i2c;
|
||||
|
||||
i2c_smbus_write_word_data(stdp4028_i2c,
|
||||
STDP4028_DPTX_IRQ_STS_REG,
|
||||
STDP4028_DPTX_IRQ_CLEAR);
|
||||
|
||||
if (ge_b850v3_lvds_ptr->connector.dev)
|
||||
drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->connector.dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
static int ge_b850v3_lvds_create_connector(struct drm_bridge *bridge)
|
||||
{
|
||||
struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector;
|
||||
struct i2c_client *stdp4028_i2c
|
||||
= ge_b850v3_lvds_ptr->stdp4028_i2c;
|
||||
int ret;
|
||||
|
||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
|
||||
DRM_ERROR("Fix bridge driver to make connector optional!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bridge->encoder) {
|
||||
DRM_ERROR("Parent encoder object not found");
|
||||
return -ENODEV;
|
||||
|
@ -237,9 +222,29 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_connector_attach_encoder(connector, bridge->encoder);
|
||||
if (ret)
|
||||
return ret;
|
||||
return drm_connector_attach_encoder(connector, bridge->encoder);
|
||||
}
|
||||
|
||||
static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_client *stdp4028_i2c
|
||||
= ge_b850v3_lvds_ptr->stdp4028_i2c;
|
||||
|
||||
i2c_smbus_write_word_data(stdp4028_i2c,
|
||||
STDP4028_DPTX_IRQ_STS_REG,
|
||||
STDP4028_DPTX_IRQ_CLEAR);
|
||||
|
||||
if (ge_b850v3_lvds_ptr->bridge.dev)
|
||||
drm_kms_helper_hotplug_event(ge_b850v3_lvds_ptr->bridge.dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct i2c_client *stdp4028_i2c
|
||||
= ge_b850v3_lvds_ptr->stdp4028_i2c;
|
||||
|
||||
/* Configures the bridge to re-enable interrupts after each ack. */
|
||||
i2c_smbus_write_word_data(stdp4028_i2c,
|
||||
|
@ -251,11 +256,16 @@ static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
|
|||
STDP4028_DPTX_IRQ_EN_REG,
|
||||
STDP4028_DPTX_IRQ_CONFIG);
|
||||
|
||||
return 0;
|
||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
|
||||
return 0;
|
||||
|
||||
return ge_b850v3_lvds_create_connector(bridge);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs ge_b850v3_lvds_funcs = {
|
||||
.attach = ge_b850v3_lvds_attach,
|
||||
.detect = ge_b850v3_lvds_bridge_detect,
|
||||
.get_edid = ge_b850v3_lvds_get_edid,
|
||||
};
|
||||
|
||||
static int ge_b850v3_lvds_init(struct device *dev)
|
||||
|
@ -291,8 +301,6 @@ static void ge_b850v3_lvds_remove(void)
|
|||
|
||||
drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge);
|
||||
|
||||
kfree(ge_b850v3_lvds_ptr->edid);
|
||||
|
||||
ge_b850v3_lvds_ptr = NULL;
|
||||
out:
|
||||
mutex_unlock(&ge_b850v3_lvds_dev_mutex);
|
||||
|
@ -302,14 +310,21 @@ static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &stdp4028_i2c->dev;
|
||||
int ret;
|
||||
|
||||
ge_b850v3_lvds_init(dev);
|
||||
ret = ge_b850v3_lvds_init(dev);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c;
|
||||
i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr);
|
||||
|
||||
/* drm bridge initialization */
|
||||
ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs;
|
||||
ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT |
|
||||
DRM_BRIDGE_OP_EDID;
|
||||
ge_b850v3_lvds_ptr->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
|
||||
ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node;
|
||||
drm_bridge_add(&ge_b850v3_lvds_ptr->bridge);
|
||||
|
||||
|
@ -361,8 +376,12 @@ static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &stdp2690_i2c->dev;
|
||||
int ret;
|
||||
|
||||
ge_b850v3_lvds_init(dev);
|
||||
ret = ge_b850v3_lvds_init(dev);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ge_b850v3_lvds_ptr->stdp2690_i2c = stdp2690_i2c;
|
||||
i2c_set_clientdata(stdp2690_i2c, ge_b850v3_lvds_ptr);
|
||||
|
|
|
@ -29,8 +29,7 @@ struct ptn3460_bridge {
|
|||
struct drm_connector connector;
|
||||
struct i2c_client *client;
|
||||
struct drm_bridge bridge;
|
||||
struct edid *edid;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct gpio_desc *gpio_pd_n;
|
||||
struct gpio_desc *gpio_rst_n;
|
||||
u32 edid_emulation;
|
||||
|
@ -127,11 +126,6 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
|
|||
usleep_range(10, 20);
|
||||
gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
|
||||
|
||||
if (drm_panel_prepare(ptn_bridge->panel)) {
|
||||
DRM_ERROR("failed to prepare panel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* There's a bug in the PTN chip where it falsely asserts hotplug before
|
||||
* it is fully functional. We're forced to wait for the maximum start up
|
||||
|
@ -146,16 +140,6 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
|
|||
ptn_bridge->enabled = true;
|
||||
}
|
||||
|
||||
static void ptn3460_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
|
||||
|
||||
if (drm_panel_enable(ptn_bridge->panel)) {
|
||||
DRM_ERROR("failed to enable panel\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void ptn3460_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
|
||||
|
@ -165,36 +149,18 @@ static void ptn3460_disable(struct drm_bridge *bridge)
|
|||
|
||||
ptn_bridge->enabled = false;
|
||||
|
||||
if (drm_panel_disable(ptn_bridge->panel)) {
|
||||
DRM_ERROR("failed to disable panel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
|
||||
gpiod_set_value(ptn_bridge->gpio_pd_n, 0);
|
||||
}
|
||||
|
||||
static void ptn3460_post_disable(struct drm_bridge *bridge)
|
||||
|
||||
static struct edid *ptn3460_get_edid(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
|
||||
|
||||
if (drm_panel_unprepare(ptn_bridge->panel)) {
|
||||
DRM_ERROR("failed to unprepare panel\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int ptn3460_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct ptn3460_bridge *ptn_bridge;
|
||||
u8 *edid;
|
||||
int ret, num_modes = 0;
|
||||
bool power_off;
|
||||
|
||||
ptn_bridge = connector_to_ptn3460(connector);
|
||||
|
||||
if (ptn_bridge->edid)
|
||||
return drm_add_edid_modes(connector, ptn_bridge->edid);
|
||||
u8 *edid;
|
||||
int ret;
|
||||
|
||||
power_off = !ptn_bridge->enabled;
|
||||
ptn3460_pre_enable(&ptn_bridge->bridge);
|
||||
|
@ -202,30 +168,40 @@ static int ptn3460_get_modes(struct drm_connector *connector)
|
|||
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
||||
if (!edid) {
|
||||
DRM_ERROR("Failed to allocate EDID\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
|
||||
EDID_LENGTH);
|
||||
if (ret) {
|
||||
kfree(edid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ptn_bridge->edid = (struct edid *)edid;
|
||||
drm_connector_update_edid_property(connector, ptn_bridge->edid);
|
||||
|
||||
num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
|
||||
ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
|
||||
EDID_LENGTH);
|
||||
if (ret) {
|
||||
kfree(edid);
|
||||
edid = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (power_off)
|
||||
ptn3460_disable(&ptn_bridge->bridge);
|
||||
|
||||
return (struct edid *)edid;
|
||||
}
|
||||
|
||||
static int ptn3460_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector);
|
||||
struct edid *edid;
|
||||
int num_modes;
|
||||
|
||||
edid = ptn3460_get_edid(&ptn_bridge->bridge, connector);
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
num_modes = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
return num_modes;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
|
||||
.get_modes = ptn3460_get_modes,
|
||||
.get_modes = ptn3460_connector_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs ptn3460_connector_funcs = {
|
||||
|
@ -242,10 +218,14 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge,
|
|||
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
|
||||
int ret;
|
||||
|
||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
|
||||
DRM_ERROR("Fix bridge driver to make connector optional!");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Let this driver create connector if requested */
|
||||
ret = drm_bridge_attach(bridge->encoder, ptn_bridge->panel_bridge,
|
||||
bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
|
||||
return 0;
|
||||
|
||||
if (!bridge->encoder) {
|
||||
DRM_ERROR("Parent encoder object not found");
|
||||
|
@ -265,9 +245,6 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge,
|
|||
drm_connector_attach_encoder(&ptn_bridge->connector,
|
||||
bridge->encoder);
|
||||
|
||||
if (ptn_bridge->panel)
|
||||
drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
|
||||
|
||||
drm_helper_hpd_irq_event(ptn_bridge->connector.dev);
|
||||
|
||||
return ret;
|
||||
|
@ -275,10 +252,9 @@ static int ptn3460_bridge_attach(struct drm_bridge *bridge,
|
|||
|
||||
static const struct drm_bridge_funcs ptn3460_bridge_funcs = {
|
||||
.pre_enable = ptn3460_pre_enable,
|
||||
.enable = ptn3460_enable,
|
||||
.disable = ptn3460_disable,
|
||||
.post_disable = ptn3460_post_disable,
|
||||
.attach = ptn3460_bridge_attach,
|
||||
.get_edid = ptn3460_get_edid,
|
||||
};
|
||||
|
||||
static int ptn3460_probe(struct i2c_client *client,
|
||||
|
@ -286,6 +262,8 @@ static int ptn3460_probe(struct i2c_client *client,
|
|||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct ptn3460_bridge *ptn_bridge;
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL);
|
||||
|
@ -293,10 +271,15 @@ static int ptn3460_probe(struct i2c_client *client,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &ptn_bridge->panel, NULL);
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
if (IS_ERR(panel_bridge))
|
||||
return PTR_ERR(panel_bridge);
|
||||
|
||||
ptn_bridge->panel_bridge = panel_bridge;
|
||||
ptn_bridge->client = client;
|
||||
|
||||
ptn_bridge->gpio_pd_n = devm_gpiod_get(&client->dev, "powerdown",
|
||||
|
@ -327,6 +310,8 @@ static int ptn3460_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs;
|
||||
ptn_bridge->bridge.ops = DRM_BRIDGE_OP_EDID;
|
||||
ptn_bridge->bridge.type = DRM_MODE_CONNECTOR_LVDS;
|
||||
ptn_bridge->bridge.of_node = dev->of_node;
|
||||
drm_bridge_add(&ptn_bridge->bridge);
|
||||
|
||||
|
|
|
@ -82,18 +82,11 @@ static int panel_bridge_attach(struct drm_bridge *bridge,
|
|||
drm_connector_attach_encoder(&panel_bridge->connector,
|
||||
bridge->encoder);
|
||||
|
||||
ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void panel_bridge_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_panel_detach(panel_bridge->panel);
|
||||
}
|
||||
|
||||
static void panel_bridge_pre_enable(struct drm_bridge *bridge)
|
||||
|
|
|
@ -42,10 +42,9 @@
|
|||
#endif
|
||||
|
||||
struct ps8622_bridge {
|
||||
struct drm_connector connector;
|
||||
struct i2c_client *client;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct regulator *v12;
|
||||
struct backlight_device *bl;
|
||||
|
||||
|
@ -64,12 +63,6 @@ static inline struct ps8622_bridge *
|
|||
return container_of(bridge, struct ps8622_bridge, bridge);
|
||||
}
|
||||
|
||||
static inline struct ps8622_bridge *
|
||||
connector_to_ps8622(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct ps8622_bridge, connector);
|
||||
}
|
||||
|
||||
static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
@ -365,11 +358,6 @@ static void ps8622_pre_enable(struct drm_bridge *bridge)
|
|||
DRM_ERROR("fails to enable ps8622->v12");
|
||||
}
|
||||
|
||||
if (drm_panel_prepare(ps8622->panel)) {
|
||||
DRM_ERROR("failed to prepare panel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gpiod_set_value(ps8622->gpio_slp, 1);
|
||||
|
||||
/*
|
||||
|
@ -399,24 +387,9 @@ static void ps8622_pre_enable(struct drm_bridge *bridge)
|
|||
ps8622->enabled = true;
|
||||
}
|
||||
|
||||
static void ps8622_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
|
||||
|
||||
if (drm_panel_enable(ps8622->panel)) {
|
||||
DRM_ERROR("failed to enable panel\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void ps8622_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
|
||||
|
||||
if (drm_panel_disable(ps8622->panel)) {
|
||||
DRM_ERROR("failed to disable panel\n");
|
||||
return;
|
||||
}
|
||||
/* Delay after panel is disabled */
|
||||
msleep(PS8622_PWMO_END_T12_MS);
|
||||
}
|
||||
|
||||
|
@ -436,11 +409,6 @@ static void ps8622_post_disable(struct drm_bridge *bridge)
|
|||
*/
|
||||
gpiod_set_value(ps8622->gpio_slp, 0);
|
||||
|
||||
if (drm_panel_unprepare(ps8622->panel)) {
|
||||
DRM_ERROR("failed to unprepare panel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ps8622->v12)
|
||||
regulator_disable(ps8622->v12);
|
||||
|
||||
|
@ -455,67 +423,17 @@ static void ps8622_post_disable(struct drm_bridge *bridge)
|
|||
msleep(PS8622_POWER_OFF_T17_MS);
|
||||
}
|
||||
|
||||
static int ps8622_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct ps8622_bridge *ps8622;
|
||||
|
||||
ps8622 = connector_to_ps8622(connector);
|
||||
|
||||
return drm_panel_get_modes(ps8622->panel, connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
|
||||
.get_modes = ps8622_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs ps8622_connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int ps8622_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
|
||||
int ret;
|
||||
|
||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
|
||||
DRM_ERROR("Fix bridge driver to make connector optional!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bridge->encoder) {
|
||||
DRM_ERROR("Parent encoder object not found");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ps8622->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
ret = drm_connector_init(bridge->dev, &ps8622->connector,
|
||||
&ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to initialize connector with drm\n");
|
||||
return ret;
|
||||
}
|
||||
drm_connector_helper_add(&ps8622->connector,
|
||||
&ps8622_connector_helper_funcs);
|
||||
drm_connector_register(&ps8622->connector);
|
||||
drm_connector_attach_encoder(&ps8622->connector,
|
||||
bridge->encoder);
|
||||
|
||||
if (ps8622->panel)
|
||||
drm_panel_attach(ps8622->panel, &ps8622->connector);
|
||||
|
||||
drm_helper_hpd_irq_event(ps8622->connector.dev);
|
||||
|
||||
return ret;
|
||||
return drm_bridge_attach(ps8622->bridge.encoder, ps8622->panel_bridge,
|
||||
&ps8622->bridge, flags);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs ps8622_bridge_funcs = {
|
||||
.pre_enable = ps8622_pre_enable,
|
||||
.enable = ps8622_enable,
|
||||
.disable = ps8622_disable,
|
||||
.post_disable = ps8622_post_disable,
|
||||
.attach = ps8622_attach,
|
||||
|
@ -533,16 +451,23 @@ static int ps8622_probe(struct i2c_client *client,
|
|||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct ps8622_bridge *ps8622;
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL);
|
||||
if (!ps8622)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &ps8622->panel, NULL);
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
if (IS_ERR(panel_bridge))
|
||||
return PTR_ERR(panel_bridge);
|
||||
|
||||
ps8622->panel_bridge = panel_bridge;
|
||||
ps8622->client = client;
|
||||
|
||||
ps8622->v12 = devm_regulator_get(dev, "vdd12");
|
||||
|
@ -595,6 +520,7 @@ static int ps8622_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
ps8622->bridge.funcs = &ps8622_bridge_funcs;
|
||||
ps8622->bridge.type = DRM_MODE_CONNECTOR_LVDS;
|
||||
ps8622->bridge.of_node = dev->of_node;
|
||||
drm_bridge_add(&ps8622->bridge);
|
||||
|
||||
|
|
|
@ -82,8 +82,11 @@ static int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge,
|
|||
ret = i2c_smbus_write_i2c_block_data(client, PAGE3_SET_ADD,
|
||||
sizeof(vdo_ctrl_buf),
|
||||
vdo_ctrl_buf);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to %sable VDO: %d\n",
|
||||
ctrl == ENABLE ? "en" : "dis", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -150,10 +153,8 @@ static void ps8640_pre_enable(struct drm_bridge *bridge)
|
|||
}
|
||||
|
||||
ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to enable VDO: %d\n", ret);
|
||||
if (ret)
|
||||
goto err_regulators_disable;
|
||||
}
|
||||
|
||||
/* Switch access edp panel's edid through i2c */
|
||||
ret = i2c_smbus_write_byte_data(client, PAGE2_I2C_BYPASS,
|
||||
|
@ -175,9 +176,7 @@ static void ps8640_post_disable(struct drm_bridge *bridge)
|
|||
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
|
||||
int ret;
|
||||
|
||||
ret = ps8640_bridge_vdo_control(ps_bridge, DISABLE);
|
||||
if (ret < 0)
|
||||
DRM_ERROR("failed to disable VDO: %d\n", ret);
|
||||
ps8640_bridge_vdo_control(ps_bridge, DISABLE);
|
||||
|
||||
gpiod_set_value(ps_bridge->gpio_reset, 1);
|
||||
gpiod_set_value(ps_bridge->gpio_powerdown, 1);
|
||||
|
@ -200,6 +199,10 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge,
|
|||
.channel = 0,
|
||||
.node = NULL,
|
||||
};
|
||||
|
||||
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
|
||||
return -EINVAL;
|
||||
|
||||
/* port@0 is ps8640 dsi input port */
|
||||
in_ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
|
||||
if (!in_ep)
|
||||
|
@ -242,8 +245,18 @@ err_dsi_attach:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
|
||||
|
||||
return drm_get_edid(connector,
|
||||
ps_bridge->page[PAGE0_DP_CNTL]->adapter);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs ps8640_bridge_funcs = {
|
||||
.attach = ps8640_bridge_attach,
|
||||
.get_edid = ps8640_bridge_get_edid,
|
||||
.post_disable = ps8640_post_disable,
|
||||
.pre_enable = ps8640_pre_enable,
|
||||
};
|
||||
|
@ -294,6 +307,8 @@ static int ps8640_probe(struct i2c_client *client)
|
|||
|
||||
ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
|
||||
ps_bridge->bridge.of_node = dev->of_node;
|
||||
ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID;
|
||||
ps_bridge->bridge.type = DRM_MODE_CONNECTOR_eDP;
|
||||
|
||||
ps_bridge->page[PAGE0_DP_CNTL] = client;
|
||||
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2020 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* Based on tc358764.c by
|
||||
* Andrzej Hajda <a.hajda@samsung.com>
|
||||
* Maciej Purski <m.purski@samsung.com>
|
||||
*
|
||||
* Based on rpi_touchscreen.c by
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
/* PPI layer registers */
|
||||
#define PPI_STARTPPI 0x0104 /* START control bit */
|
||||
#define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */
|
||||
#define PPI_D0S_ATMR 0x0144
|
||||
#define PPI_D1S_ATMR 0x0148
|
||||
#define PPI_D0S_CLRSIPOCOUNT 0x0164 /* Assertion timer for Lane 0 */
|
||||
#define PPI_D1S_CLRSIPOCOUNT 0x0168 /* Assertion timer for Lane 1 */
|
||||
#define PPI_START_FUNCTION 1
|
||||
|
||||
/* DSI layer registers */
|
||||
#define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */
|
||||
#define DSI_LANEENABLE 0x0210 /* Enables each lane */
|
||||
#define DSI_RX_START 1
|
||||
|
||||
/* LCDC/DPI Host Registers */
|
||||
#define LCDCTRL 0x0420
|
||||
|
||||
/* SPI Master Registers */
|
||||
#define SPICMR 0x0450
|
||||
#define SPITCR 0x0454
|
||||
|
||||
/* System Controller Registers */
|
||||
#define SYSCTRL 0x0464
|
||||
|
||||
/* System registers */
|
||||
#define LPX_PERIOD 3
|
||||
|
||||
/* Lane enable PPI and DSI register bits */
|
||||
#define LANEENABLE_CLEN BIT(0)
|
||||
#define LANEENABLE_L0EN BIT(1)
|
||||
#define LANEENABLE_L1EN BIT(2)
|
||||
|
||||
struct tc358762 {
|
||||
struct device *dev;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_connector connector;
|
||||
struct regulator *regulator;
|
||||
struct drm_bridge *panel_bridge;
|
||||
bool pre_enabled;
|
||||
int error;
|
||||
};
|
||||
|
||||
static int tc358762_clear_error(struct tc358762 *ctx)
|
||||
{
|
||||
int ret = ctx->error;
|
||||
|
||||
ctx->error = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tc358762_write(struct tc358762 *ctx, u16 addr, u32 val)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
ssize_t ret;
|
||||
u8 data[6];
|
||||
|
||||
if (ctx->error)
|
||||
return;
|
||||
|
||||
data[0] = addr;
|
||||
data[1] = addr >> 8;
|
||||
data[2] = val;
|
||||
data[3] = val >> 8;
|
||||
data[4] = val >> 16;
|
||||
data[5] = val >> 24;
|
||||
|
||||
ret = mipi_dsi_generic_write(dsi, data, sizeof(data));
|
||||
if (ret < 0)
|
||||
ctx->error = ret;
|
||||
}
|
||||
|
||||
static inline struct tc358762 *bridge_to_tc358762(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct tc358762, bridge);
|
||||
}
|
||||
|
||||
static int tc358762_init(struct tc358762 *ctx)
|
||||
{
|
||||
tc358762_write(ctx, DSI_LANEENABLE,
|
||||
LANEENABLE_L0EN | LANEENABLE_CLEN);
|
||||
tc358762_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5);
|
||||
tc358762_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5);
|
||||
tc358762_write(ctx, PPI_D0S_ATMR, 0);
|
||||
tc358762_write(ctx, PPI_D1S_ATMR, 0);
|
||||
tc358762_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD);
|
||||
|
||||
tc358762_write(ctx, SPICMR, 0x00);
|
||||
tc358762_write(ctx, LCDCTRL, 0x00100150);
|
||||
tc358762_write(ctx, SYSCTRL, 0x040f);
|
||||
msleep(100);
|
||||
|
||||
tc358762_write(ctx, PPI_STARTPPI, PPI_START_FUNCTION);
|
||||
tc358762_write(ctx, DSI_STARTDSI, DSI_RX_START);
|
||||
|
||||
msleep(100);
|
||||
|
||||
return tc358762_clear_error(ctx);
|
||||
}
|
||||
|
||||
static void tc358762_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc358762 *ctx = bridge_to_tc358762(bridge);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The post_disable hook might be called multiple times.
|
||||
* We want to avoid regulator imbalance below.
|
||||
*/
|
||||
if (!ctx->pre_enabled)
|
||||
return;
|
||||
|
||||
ctx->pre_enabled = false;
|
||||
|
||||
ret = regulator_disable(ctx->regulator);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "error disabling regulators (%d)\n", ret);
|
||||
}
|
||||
|
||||
static void tc358762_pre_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc358762 *ctx = bridge_to_tc358762(bridge);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(ctx->regulator);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "error enabling regulators (%d)\n", ret);
|
||||
|
||||
ret = tc358762_init(ctx);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
|
||||
|
||||
ctx->pre_enabled = true;
|
||||
}
|
||||
|
||||
static int tc358762_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct tc358762 *ctx = bridge_to_tc358762(bridge);
|
||||
|
||||
return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
|
||||
bridge, flags);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs tc358762_bridge_funcs = {
|
||||
.post_disable = tc358762_post_disable,
|
||||
.pre_enable = tc358762_pre_enable,
|
||||
.attach = tc358762_attach,
|
||||
};
|
||||
|
||||
static int tc358762_parse_dt(struct tc358762 *ctx)
|
||||
{
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct device *dev = ctx->dev;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
|
||||
if (IS_ERR(panel_bridge))
|
||||
return PTR_ERR(panel_bridge);
|
||||
|
||||
ctx->panel_bridge = panel_bridge;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc358762_configure_regulators(struct tc358762 *ctx)
|
||||
{
|
||||
ctx->regulator = devm_regulator_get(ctx->dev, "vddc");
|
||||
if (IS_ERR(ctx->regulator))
|
||||
return PTR_ERR(ctx->regulator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc358762_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct device *dev = &dsi->dev;
|
||||
struct tc358762 *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(struct tc358762), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
mipi_dsi_set_drvdata(dsi, ctx);
|
||||
|
||||
ctx->dev = dev;
|
||||
ctx->pre_enabled = false;
|
||||
|
||||
/* TODO: Find out how to get dual-lane mode working */
|
||||
dsi->lanes = 1;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
|
||||
MIPI_DSI_MODE_LPM;
|
||||
|
||||
ret = tc358762_parse_dt(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tc358762_configure_regulators(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->bridge.funcs = &tc358762_bridge_funcs;
|
||||
ctx->bridge.type = DRM_MODE_CONNECTOR_DPI;
|
||||
ctx->bridge.of_node = dev->of_node;
|
||||
|
||||
drm_bridge_add(&ctx->bridge);
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0) {
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
dev_err(dev, "failed to attach dsi\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tc358762_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
mipi_dsi_detach(dsi);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tc358762_of_match[] = {
|
||||
{ .compatible = "toshiba,tc358762" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tc358762_of_match);
|
||||
|
||||
static struct mipi_dsi_driver tc358762_driver = {
|
||||
.probe = tc358762_probe,
|
||||
.remove = tc358762_remove,
|
||||
.driver = {
|
||||
.name = "tc358762",
|
||||
.of_match_table = tc358762_of_match,
|
||||
},
|
||||
};
|
||||
module_mipi_dsi_driver(tc358762_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
|
||||
MODULE_DESCRIPTION("MIPI-DSI based Driver for TC358762 DSI/DPI Bridge");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -153,10 +153,9 @@ static const char * const tc358764_supplies[] = {
|
|||
struct tc358764 {
|
||||
struct device *dev;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_connector connector;
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(tc358764_supplies)];
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *panel_bridge;
|
||||
int error;
|
||||
};
|
||||
|
||||
|
@ -210,12 +209,6 @@ static inline struct tc358764 *bridge_to_tc358764(struct drm_bridge *bridge)
|
|||
return container_of(bridge, struct tc358764, bridge);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct tc358764 *connector_to_tc358764(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct tc358764, connector);
|
||||
}
|
||||
|
||||
static int tc358764_init(struct tc358764 *ctx)
|
||||
{
|
||||
u32 v = 0;
|
||||
|
@ -278,43 +271,11 @@ static void tc358764_reset(struct tc358764 *ctx)
|
|||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
static int tc358764_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct tc358764 *ctx = connector_to_tc358764(connector);
|
||||
|
||||
return drm_panel_get_modes(ctx->panel, connector);
|
||||
}
|
||||
|
||||
static const
|
||||
struct drm_connector_helper_funcs tc358764_connector_helper_funcs = {
|
||||
.get_modes = tc358764_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs tc358764_connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static void tc358764_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc358764 *ctx = bridge_to_tc358764(bridge);
|
||||
int ret = drm_panel_disable(bridge_to_tc358764(bridge)->panel);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "error disabling panel (%d)\n", ret);
|
||||
}
|
||||
|
||||
static void tc358764_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc358764 *ctx = bridge_to_tc358764(bridge);
|
||||
int ret;
|
||||
|
||||
ret = drm_panel_unprepare(ctx->panel);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "error unpreparing panel (%d)\n", ret);
|
||||
tc358764_reset(ctx);
|
||||
usleep_range(10000, 15000);
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
|
@ -335,73 +296,28 @@ static void tc358764_pre_enable(struct drm_bridge *bridge)
|
|||
ret = tc358764_init(ctx);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
|
||||
ret = drm_panel_prepare(ctx->panel);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "error preparing panel (%d)\n", ret);
|
||||
}
|
||||
|
||||
static void tc358764_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc358764 *ctx = bridge_to_tc358764(bridge);
|
||||
int ret = drm_panel_enable(ctx->panel);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "error enabling panel (%d)\n", ret);
|
||||
}
|
||||
|
||||
static int tc358764_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct tc358764 *ctx = bridge_to_tc358764(bridge);
|
||||
struct drm_device *drm = bridge->dev;
|
||||
int ret;
|
||||
|
||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
|
||||
DRM_ERROR("Fix bridge driver to make connector optional!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
ret = drm_connector_init(drm, &ctx->connector,
|
||||
&tc358764_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to initialize connector\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_connector_helper_add(&ctx->connector,
|
||||
&tc358764_connector_helper_funcs);
|
||||
drm_connector_attach_encoder(&ctx->connector, bridge->encoder);
|
||||
drm_panel_attach(ctx->panel, &ctx->connector);
|
||||
ctx->connector.funcs->reset(&ctx->connector);
|
||||
drm_connector_register(&ctx->connector);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc358764_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc358764 *ctx = bridge_to_tc358764(bridge);
|
||||
|
||||
drm_connector_unregister(&ctx->connector);
|
||||
drm_panel_detach(ctx->panel);
|
||||
ctx->panel = NULL;
|
||||
drm_connector_put(&ctx->connector);
|
||||
return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
|
||||
bridge, flags);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs tc358764_bridge_funcs = {
|
||||
.disable = tc358764_disable,
|
||||
.post_disable = tc358764_post_disable,
|
||||
.enable = tc358764_enable,
|
||||
.pre_enable = tc358764_pre_enable,
|
||||
.attach = tc358764_attach,
|
||||
.detach = tc358764_detach,
|
||||
};
|
||||
|
||||
static int tc358764_parse_dt(struct tc358764 *ctx)
|
||||
{
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct device *dev = ctx->dev;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
|
@ -410,12 +326,16 @@ static int tc358764_parse_dt(struct tc358764 *ctx)
|
|||
return PTR_ERR(ctx->gpio_reset);
|
||||
}
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(ctx->dev->of_node, 1, 0, &ctx->panel,
|
||||
NULL);
|
||||
if (ret && ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "cannot find panel (%d)\n", ret);
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
if (IS_ERR(panel_bridge))
|
||||
return PTR_ERR(panel_bridge);
|
||||
|
||||
ctx->panel_bridge = panel_bridge;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc358764_configure_regulators(struct tc358764 *ctx)
|
||||
|
@ -461,6 +381,7 @@ static int tc358764_probe(struct mipi_dsi_device *dsi)
|
|||
return ret;
|
||||
|
||||
ctx->bridge.funcs = &tc358764_bridge_funcs;
|
||||
ctx->bridge.type = DRM_MODE_CONNECTOR_LVDS;
|
||||
ctx->bridge.of_node = dev->of_node;
|
||||
|
||||
drm_bridge_add(&ctx->bridge);
|
||||
|
|
|
@ -244,14 +244,12 @@ struct tc_data {
|
|||
struct drm_dp_aux aux;
|
||||
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
|
||||
/* link settings */
|
||||
struct tc_edp_link link;
|
||||
|
||||
/* display edid */
|
||||
struct edid *edid;
|
||||
/* current mode */
|
||||
struct drm_display_mode mode;
|
||||
|
||||
|
@ -1236,13 +1234,6 @@ static int tc_stream_disable(struct tc_data *tc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void tc_bridge_pre_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
|
||||
drm_panel_prepare(tc->panel);
|
||||
}
|
||||
|
||||
static void tc_bridge_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
|
@ -1266,8 +1257,6 @@ static void tc_bridge_enable(struct drm_bridge *bridge)
|
|||
tc_main_link_disable(tc);
|
||||
return;
|
||||
}
|
||||
|
||||
drm_panel_enable(tc->panel);
|
||||
}
|
||||
|
||||
static void tc_bridge_disable(struct drm_bridge *bridge)
|
||||
|
@ -1275,8 +1264,6 @@ static void tc_bridge_disable(struct drm_bridge *bridge)
|
|||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
int ret;
|
||||
|
||||
drm_panel_disable(tc->panel);
|
||||
|
||||
ret = tc_stream_disable(tc);
|
||||
if (ret < 0)
|
||||
dev_err(tc->dev, "main link stream stop error: %d\n", ret);
|
||||
|
@ -1286,13 +1273,6 @@ static void tc_bridge_disable(struct drm_bridge *bridge)
|
|||
dev_err(tc->dev, "main link disable error: %d\n", ret);
|
||||
}
|
||||
|
||||
static void tc_bridge_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
|
||||
drm_panel_unprepare(tc->panel);
|
||||
}
|
||||
|
||||
static bool tc_bridge_mode_fixup(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj)
|
||||
|
@ -1335,11 +1315,19 @@ static void tc_bridge_mode_set(struct drm_bridge *bridge,
|
|||
tc->mode = *mode;
|
||||
}
|
||||
|
||||
static struct edid *tc_get_edid(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
|
||||
return drm_get_edid(connector, &tc->aux.ddc);
|
||||
}
|
||||
|
||||
static int tc_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct tc_data *tc = connector_to_tc(connector);
|
||||
int num_modes;
|
||||
struct edid *edid;
|
||||
int count;
|
||||
int ret;
|
||||
|
||||
ret = tc_get_display_props(tc);
|
||||
|
@ -1348,42 +1336,30 @@ static int tc_connector_get_modes(struct drm_connector *connector)
|
|||
return 0;
|
||||
}
|
||||
|
||||
count = drm_panel_get_modes(tc->panel, connector);
|
||||
if (count > 0)
|
||||
return count;
|
||||
if (tc->panel_bridge) {
|
||||
num_modes = drm_bridge_get_modes(tc->panel_bridge, connector);
|
||||
if (num_modes > 0)
|
||||
return num_modes;
|
||||
}
|
||||
|
||||
edid = drm_get_edid(connector, &tc->aux.ddc);
|
||||
edid = tc_get_edid(&tc->bridge, connector);
|
||||
num_modes = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
kfree(tc->edid);
|
||||
tc->edid = edid;
|
||||
if (!edid)
|
||||
return 0;
|
||||
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
count = drm_add_edid_modes(connector, edid);
|
||||
|
||||
return count;
|
||||
return num_modes;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs tc_connector_helper_funcs = {
|
||||
.get_modes = tc_connector_get_modes,
|
||||
};
|
||||
|
||||
static enum drm_connector_status tc_connector_detect(struct drm_connector *connector,
|
||||
bool force)
|
||||
static enum drm_connector_status tc_bridge_detect(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc_data *tc = connector_to_tc(connector);
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
bool conn;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (tc->hpd_pin < 0) {
|
||||
if (tc->panel)
|
||||
return connector_status_connected;
|
||||
else
|
||||
return connector_status_unknown;
|
||||
}
|
||||
|
||||
ret = regmap_read(tc->regmap, GPIOI, &val);
|
||||
if (ret)
|
||||
return connector_status_unknown;
|
||||
|
@ -1396,6 +1372,20 @@ static enum drm_connector_status tc_connector_detect(struct drm_connector *conne
|
|||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
tc_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct tc_data *tc = connector_to_tc(connector);
|
||||
|
||||
if (tc->hpd_pin >= 0)
|
||||
return tc_bridge_detect(&tc->bridge);
|
||||
|
||||
if (tc->panel_bridge)
|
||||
return connector_status_connected;
|
||||
else
|
||||
return connector_status_unknown;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs tc_connector_funcs = {
|
||||
.detect = tc_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
|
@ -1413,16 +1403,20 @@ static int tc_bridge_attach(struct drm_bridge *bridge,
|
|||
struct drm_device *drm = bridge->dev;
|
||||
int ret;
|
||||
|
||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
|
||||
DRM_ERROR("Fix bridge driver to make connector optional!");
|
||||
return -EINVAL;
|
||||
if (tc->panel_bridge) {
|
||||
/* If a connector is required then this driver shall create it */
|
||||
ret = drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge,
|
||||
&tc->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
|
||||
return 0;
|
||||
|
||||
/* Create DP/eDP connector */
|
||||
drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
|
||||
ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs,
|
||||
tc->panel ? DRM_MODE_CONNECTOR_eDP :
|
||||
DRM_MODE_CONNECTOR_DisplayPort);
|
||||
ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, tc->bridge.type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1435,9 +1429,6 @@ static int tc_bridge_attach(struct drm_bridge *bridge,
|
|||
DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
}
|
||||
|
||||
if (tc->panel)
|
||||
drm_panel_attach(tc->panel, &tc->connector);
|
||||
|
||||
drm_display_info_set_bus_formats(&tc->connector.display_info,
|
||||
&bus_format, 1);
|
||||
tc->connector.display_info.bus_flags =
|
||||
|
@ -1453,11 +1444,11 @@ static const struct drm_bridge_funcs tc_bridge_funcs = {
|
|||
.attach = tc_bridge_attach,
|
||||
.mode_valid = tc_mode_valid,
|
||||
.mode_set = tc_bridge_mode_set,
|
||||
.pre_enable = tc_bridge_pre_enable,
|
||||
.enable = tc_bridge_enable,
|
||||
.disable = tc_bridge_disable,
|
||||
.post_disable = tc_bridge_post_disable,
|
||||
.mode_fixup = tc_bridge_mode_fixup,
|
||||
.detect = tc_bridge_detect,
|
||||
.get_edid = tc_get_edid,
|
||||
};
|
||||
|
||||
static bool tc_readable_reg(struct device *dev, unsigned int reg)
|
||||
|
@ -1547,6 +1538,7 @@ static irqreturn_t tc_irq_handler(int irq, void *arg)
|
|||
static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct drm_panel *panel;
|
||||
struct tc_data *tc;
|
||||
int ret;
|
||||
|
||||
|
@ -1557,10 +1549,23 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||
tc->dev = dev;
|
||||
|
||||
/* port@2 is the output port */
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &tc->panel, NULL);
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL);
|
||||
if (ret && ret != -ENODEV)
|
||||
return ret;
|
||||
|
||||
if (panel) {
|
||||
struct drm_bridge *panel_bridge;
|
||||
|
||||
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
if (IS_ERR(panel_bridge))
|
||||
return PTR_ERR(panel_bridge);
|
||||
|
||||
tc->panel_bridge = panel_bridge;
|
||||
tc->bridge.type = DRM_MODE_CONNECTOR_eDP;
|
||||
} else {
|
||||
tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
|
||||
}
|
||||
|
||||
/* Shut down GPIO is optional */
|
||||
tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tc->sd_gpio))
|
||||
|
@ -1680,6 +1685,10 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||
return ret;
|
||||
|
||||
tc->bridge.funcs = &tc_bridge_funcs;
|
||||
if (tc->hpd_pin >= 0)
|
||||
tc->bridge.ops |= DRM_BRIDGE_OP_DETECT;
|
||||
tc->bridge.ops |= DRM_BRIDGE_OP_EDID;
|
||||
|
||||
tc->bridge.of_node = dev->of_node;
|
||||
drm_bridge_add(&tc->bridge);
|
||||
|
||||
|
|
|
@ -0,0 +1,749 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* TC358775 DSI to LVDS bridge driver
|
||||
*
|
||||
* Copyright (C) 2020 SMART Wireless Computing
|
||||
* Author: Vinay Simha BN <simhavcs@gmail.com>
|
||||
*
|
||||
*/
|
||||
/* #define DEBUG */
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#define FLD_VAL(val, start, end) FIELD_PREP(GENMASK(start, end), val)
|
||||
|
||||
/* Registers */
|
||||
|
||||
/* DSI D-PHY Layer Registers */
|
||||
#define D0W_DPHYCONTTX 0x0004 /* Data Lane 0 DPHY Tx Control */
|
||||
#define CLW_DPHYCONTRX 0x0020 /* Clock Lane DPHY Rx Control */
|
||||
#define D0W_DPHYCONTRX 0x0024 /* Data Lane 0 DPHY Rx Control */
|
||||
#define D1W_DPHYCONTRX 0x0028 /* Data Lane 1 DPHY Rx Control */
|
||||
#define D2W_DPHYCONTRX 0x002C /* Data Lane 2 DPHY Rx Control */
|
||||
#define D3W_DPHYCONTRX 0x0030 /* Data Lane 3 DPHY Rx Control */
|
||||
#define COM_DPHYCONTRX 0x0038 /* DPHY Rx Common Control */
|
||||
#define CLW_CNTRL 0x0040 /* Clock Lane Control */
|
||||
#define D0W_CNTRL 0x0044 /* Data Lane 0 Control */
|
||||
#define D1W_CNTRL 0x0048 /* Data Lane 1 Control */
|
||||
#define D2W_CNTRL 0x004C /* Data Lane 2 Control */
|
||||
#define D3W_CNTRL 0x0050 /* Data Lane 3 Control */
|
||||
#define DFTMODE_CNTRL 0x0054 /* DFT Mode Control */
|
||||
|
||||
/* DSI PPI Layer Registers */
|
||||
#define PPI_STARTPPI 0x0104 /* START control bit of PPI-TX function. */
|
||||
#define PPI_START_FUNCTION 1
|
||||
|
||||
#define PPI_BUSYPPI 0x0108
|
||||
#define PPI_LINEINITCNT 0x0110 /* Line Initialization Wait Counter */
|
||||
#define PPI_LPTXTIMECNT 0x0114
|
||||
#define PPI_LANEENABLE 0x0134 /* Enables each lane at the PPI layer. */
|
||||
#define PPI_TX_RX_TA 0x013C /* DSI Bus Turn Around timing parameters */
|
||||
|
||||
/* Analog timer function enable */
|
||||
#define PPI_CLS_ATMR 0x0140 /* Delay for Clock Lane in LPRX */
|
||||
#define PPI_D0S_ATMR 0x0144 /* Delay for Data Lane 0 in LPRX */
|
||||
#define PPI_D1S_ATMR 0x0148 /* Delay for Data Lane 1 in LPRX */
|
||||
#define PPI_D2S_ATMR 0x014C /* Delay for Data Lane 2 in LPRX */
|
||||
#define PPI_D3S_ATMR 0x0150 /* Delay for Data Lane 3 in LPRX */
|
||||
|
||||
#define PPI_D0S_CLRSIPOCOUNT 0x0164 /* For lane 0 */
|
||||
#define PPI_D1S_CLRSIPOCOUNT 0x0168 /* For lane 1 */
|
||||
#define PPI_D2S_CLRSIPOCOUNT 0x016C /* For lane 2 */
|
||||
#define PPI_D3S_CLRSIPOCOUNT 0x0170 /* For lane 3 */
|
||||
|
||||
#define CLS_PRE 0x0180 /* Digital Counter inside of PHY IO */
|
||||
#define D0S_PRE 0x0184 /* Digital Counter inside of PHY IO */
|
||||
#define D1S_PRE 0x0188 /* Digital Counter inside of PHY IO */
|
||||
#define D2S_PRE 0x018C /* Digital Counter inside of PHY IO */
|
||||
#define D3S_PRE 0x0190 /* Digital Counter inside of PHY IO */
|
||||
#define CLS_PREP 0x01A0 /* Digital Counter inside of PHY IO */
|
||||
#define D0S_PREP 0x01A4 /* Digital Counter inside of PHY IO */
|
||||
#define D1S_PREP 0x01A8 /* Digital Counter inside of PHY IO */
|
||||
#define D2S_PREP 0x01AC /* Digital Counter inside of PHY IO */
|
||||
#define D3S_PREP 0x01B0 /* Digital Counter inside of PHY IO */
|
||||
#define CLS_ZERO 0x01C0 /* Digital Counter inside of PHY IO */
|
||||
#define D0S_ZERO 0x01C4 /* Digital Counter inside of PHY IO */
|
||||
#define D1S_ZERO 0x01C8 /* Digital Counter inside of PHY IO */
|
||||
#define D2S_ZERO 0x01CC /* Digital Counter inside of PHY IO */
|
||||
#define D3S_ZERO 0x01D0 /* Digital Counter inside of PHY IO */
|
||||
|
||||
#define PPI_CLRFLG 0x01E0 /* PRE Counters has reached set values */
|
||||
#define PPI_CLRSIPO 0x01E4 /* Clear SIPO values, Slave mode use only. */
|
||||
#define HSTIMEOUT 0x01F0 /* HS Rx Time Out Counter */
|
||||
#define HSTIMEOUTENABLE 0x01F4 /* Enable HS Rx Time Out Counter */
|
||||
#define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX function */
|
||||
#define DSI_RX_START 1
|
||||
|
||||
#define DSI_BUSYDSI 0x0208
|
||||
#define DSI_LANEENABLE 0x0210 /* Enables each lane at the Protocol layer. */
|
||||
#define DSI_LANESTATUS0 0x0214 /* Displays lane is in HS RX mode. */
|
||||
#define DSI_LANESTATUS1 0x0218 /* Displays lane is in ULPS or STOP state */
|
||||
|
||||
#define DSI_INTSTATUS 0x0220 /* Interrupt Status */
|
||||
#define DSI_INTMASK 0x0224 /* Interrupt Mask */
|
||||
#define DSI_INTCLR 0x0228 /* Interrupt Clear */
|
||||
#define DSI_LPTXTO 0x0230 /* Low Power Tx Time Out Counter */
|
||||
|
||||
#define DSIERRCNT 0x0300 /* DSI Error Count */
|
||||
#define APLCTRL 0x0400 /* Application Layer Control */
|
||||
#define RDPKTLN 0x0404 /* Command Read Packet Length */
|
||||
|
||||
#define VPCTRL 0x0450 /* Video Path Control */
|
||||
#define HTIM1 0x0454 /* Horizontal Timing Control 1 */
|
||||
#define HTIM2 0x0458 /* Horizontal Timing Control 2 */
|
||||
#define VTIM1 0x045C /* Vertical Timing Control 1 */
|
||||
#define VTIM2 0x0460 /* Vertical Timing Control 2 */
|
||||
#define VFUEN 0x0464 /* Video Frame Timing Update Enable */
|
||||
#define VFUEN_EN BIT(0) /* Upload Enable */
|
||||
|
||||
/* Mux Input Select for LVDS LINK Input */
|
||||
#define LV_MX0003 0x0480 /* Bit 0 to 3 */
|
||||
#define LV_MX0407 0x0484 /* Bit 4 to 7 */
|
||||
#define LV_MX0811 0x0488 /* Bit 8 to 11 */
|
||||
#define LV_MX1215 0x048C /* Bit 12 to 15 */
|
||||
#define LV_MX1619 0x0490 /* Bit 16 to 19 */
|
||||
#define LV_MX2023 0x0494 /* Bit 20 to 23 */
|
||||
#define LV_MX2427 0x0498 /* Bit 24 to 27 */
|
||||
#define LV_MX(b0, b1, b2, b3) (FLD_VAL(b0, 4, 0) | FLD_VAL(b1, 12, 8) | \
|
||||
FLD_VAL(b2, 20, 16) | FLD_VAL(b3, 28, 24))
|
||||
|
||||
/* Input bit numbers used in mux registers */
|
||||
enum {
|
||||
LVI_R0,
|
||||
LVI_R1,
|
||||
LVI_R2,
|
||||
LVI_R3,
|
||||
LVI_R4,
|
||||
LVI_R5,
|
||||
LVI_R6,
|
||||
LVI_R7,
|
||||
LVI_G0,
|
||||
LVI_G1,
|
||||
LVI_G2,
|
||||
LVI_G3,
|
||||
LVI_G4,
|
||||
LVI_G5,
|
||||
LVI_G6,
|
||||
LVI_G7,
|
||||
LVI_B0,
|
||||
LVI_B1,
|
||||
LVI_B2,
|
||||
LVI_B3,
|
||||
LVI_B4,
|
||||
LVI_B5,
|
||||
LVI_B6,
|
||||
LVI_B7,
|
||||
LVI_HS,
|
||||
LVI_VS,
|
||||
LVI_DE,
|
||||
LVI_L0
|
||||
};
|
||||
|
||||
#define LVCFG 0x049C /* LVDS Configuration */
|
||||
#define LVPHY0 0x04A0 /* LVDS PHY 0 */
|
||||
#define LV_PHY0_RST(v) FLD_VAL(v, 22, 22) /* PHY reset */
|
||||
#define LV_PHY0_IS(v) FLD_VAL(v, 15, 14)
|
||||
#define LV_PHY0_ND(v) FLD_VAL(v, 4, 0) /* Frequency range select */
|
||||
#define LV_PHY0_PRBS_ON(v) FLD_VAL(v, 20, 16) /* Clock/Data Flag pins */
|
||||
|
||||
#define LVPHY1 0x04A4 /* LVDS PHY 1 */
|
||||
#define SYSSTAT 0x0500 /* System Status */
|
||||
#define SYSRST 0x0504 /* System Reset */
|
||||
|
||||
#define SYS_RST_I2CS BIT(0) /* Reset I2C-Slave controller */
|
||||
#define SYS_RST_I2CM BIT(1) /* Reset I2C-Master controller */
|
||||
#define SYS_RST_LCD BIT(2) /* Reset LCD controller */
|
||||
#define SYS_RST_BM BIT(3) /* Reset Bus Management controller */
|
||||
#define SYS_RST_DSIRX BIT(4) /* Reset DSI-RX and App controller */
|
||||
#define SYS_RST_REG BIT(5) /* Reset Register module */
|
||||
|
||||
/* GPIO Registers */
|
||||
#define GPIOC 0x0520 /* GPIO Control */
|
||||
#define GPIOO 0x0524 /* GPIO Output */
|
||||
#define GPIOI 0x0528 /* GPIO Input */
|
||||
|
||||
/* I2C Registers */
|
||||
#define I2CTIMCTRL 0x0540 /* I2C IF Timing and Enable Control */
|
||||
#define I2CMADDR 0x0544 /* I2C Master Addressing */
|
||||
#define WDATAQ 0x0548 /* Write Data Queue */
|
||||
#define RDATAQ 0x054C /* Read Data Queue */
|
||||
|
||||
/* Chip ID and Revision ID Register */
|
||||
#define IDREG 0x0580
|
||||
|
||||
#define LPX_PERIOD 4
|
||||
#define TTA_GET 0x40000
|
||||
#define TTA_SURE 6
|
||||
#define SINGLE_LINK 1
|
||||
#define DUAL_LINK 2
|
||||
|
||||
#define TC358775XBG_ID 0x00007500
|
||||
|
||||
/* Debug Registers */
|
||||
#define DEBUG00 0x05A0 /* Debug */
|
||||
#define DEBUG01 0x05A4 /* LVDS Data */
|
||||
|
||||
#define DSI_CLEN_BIT BIT(0)
|
||||
#define DIVIDE_BY_3 3 /* PCLK=DCLK/3 */
|
||||
#define DIVIDE_BY_6 6 /* PCLK=DCLK/6 */
|
||||
#define LVCFG_LVEN_BIT BIT(0)
|
||||
|
||||
#define L0EN BIT(1)
|
||||
|
||||
#define TC358775_VPCTRL_VSDELAY__MASK 0x3FF00000
|
||||
#define TC358775_VPCTRL_VSDELAY__SHIFT 20
|
||||
static inline u32 TC358775_VPCTRL_VSDELAY(uint32_t val)
|
||||
{
|
||||
return ((val) << TC358775_VPCTRL_VSDELAY__SHIFT) &
|
||||
TC358775_VPCTRL_VSDELAY__MASK;
|
||||
}
|
||||
|
||||
#define TC358775_VPCTRL_OPXLFMT__MASK 0x00000100
|
||||
#define TC358775_VPCTRL_OPXLFMT__SHIFT 8
|
||||
static inline u32 TC358775_VPCTRL_OPXLFMT(uint32_t val)
|
||||
{
|
||||
return ((val) << TC358775_VPCTRL_OPXLFMT__SHIFT) &
|
||||
TC358775_VPCTRL_OPXLFMT__MASK;
|
||||
}
|
||||
|
||||
#define TC358775_VPCTRL_MSF__MASK 0x00000001
|
||||
#define TC358775_VPCTRL_MSF__SHIFT 0
|
||||
static inline u32 TC358775_VPCTRL_MSF(uint32_t val)
|
||||
{
|
||||
return ((val) << TC358775_VPCTRL_MSF__SHIFT) &
|
||||
TC358775_VPCTRL_MSF__MASK;
|
||||
}
|
||||
|
||||
#define TC358775_LVCFG_PCLKDIV__MASK 0x000000f0
|
||||
#define TC358775_LVCFG_PCLKDIV__SHIFT 4
|
||||
static inline u32 TC358775_LVCFG_PCLKDIV(uint32_t val)
|
||||
{
|
||||
return ((val) << TC358775_LVCFG_PCLKDIV__SHIFT) &
|
||||
TC358775_LVCFG_PCLKDIV__MASK;
|
||||
}
|
||||
|
||||
#define TC358775_LVCFG_LVDLINK__MASK 0x00000002
|
||||
#define TC358775_LVCFG_LVDLINK__SHIFT 0
|
||||
static inline u32 TC358775_LVCFG_LVDLINK(uint32_t val)
|
||||
{
|
||||
return ((val) << TC358775_LVCFG_LVDLINK__SHIFT) &
|
||||
TC358775_LVCFG_LVDLINK__MASK;
|
||||
}
|
||||
|
||||
enum tc358775_ports {
|
||||
TC358775_DSI_IN,
|
||||
TC358775_LVDS_OUT0,
|
||||
TC358775_LVDS_OUT1,
|
||||
};
|
||||
|
||||
struct tc_data {
|
||||
struct i2c_client *i2c;
|
||||
struct device *dev;
|
||||
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *panel_bridge;
|
||||
|
||||
struct device_node *host_node;
|
||||
struct mipi_dsi_device *dsi;
|
||||
u8 num_dsi_lanes;
|
||||
|
||||
struct regulator *vdd;
|
||||
struct regulator *vddio;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct gpio_desc *stby_gpio;
|
||||
u8 lvds_link; /* single-link or dual-link */
|
||||
u8 bpc;
|
||||
};
|
||||
|
||||
static inline struct tc_data *bridge_to_tc(struct drm_bridge *b)
|
||||
{
|
||||
return container_of(b, struct tc_data, bridge);
|
||||
}
|
||||
|
||||
static void tc_bridge_pre_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
struct device *dev = &tc->dsi->dev;
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(tc->vddio);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "regulator vddio enable failed, %d\n", ret);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
ret = regulator_enable(tc->vdd);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "regulator vdd enable failed, %d\n", ret);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
gpiod_set_value(tc->stby_gpio, 0);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
gpiod_set_value(tc->reset_gpio, 0);
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
|
||||
static void tc_bridge_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
struct device *dev = &tc->dsi->dev;
|
||||
int ret;
|
||||
|
||||
gpiod_set_value(tc->reset_gpio, 1);
|
||||
usleep_range(10, 20);
|
||||
|
||||
gpiod_set_value(tc->stby_gpio, 1);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
ret = regulator_disable(tc->vdd);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "regulator vdd disable failed, %d\n", ret);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
ret = regulator_disable(tc->vddio);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "regulator vddio disable failed, %d\n", ret);
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
|
||||
static void d2l_read(struct i2c_client *i2c, u16 addr, u32 *val)
|
||||
{
|
||||
int ret;
|
||||
u8 buf_addr[2];
|
||||
|
||||
put_unaligned_be16(addr, buf_addr);
|
||||
ret = i2c_master_send(i2c, buf_addr, sizeof(buf_addr));
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = i2c_master_recv(i2c, (u8 *)val, sizeof(*val));
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
pr_debug("d2l: I2C : addr:%04x value:%08x\n", addr, *val);
|
||||
|
||||
fail:
|
||||
dev_err(&i2c->dev, "Error %d reading from subaddress 0x%x\n",
|
||||
ret, addr);
|
||||
}
|
||||
|
||||
static void d2l_write(struct i2c_client *i2c, u16 addr, u32 val)
|
||||
{
|
||||
u8 data[6];
|
||||
int ret;
|
||||
|
||||
put_unaligned_be16(addr, data);
|
||||
put_unaligned_le32(val, data + 2);
|
||||
|
||||
ret = i2c_master_send(i2c, data, ARRAY_SIZE(data));
|
||||
if (ret < 0)
|
||||
dev_err(&i2c->dev, "Error %d writing to subaddress 0x%x\n",
|
||||
ret, addr);
|
||||
}
|
||||
|
||||
/* helper function to access bus_formats */
|
||||
static struct drm_connector *get_connector(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_connector *connector;
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
|
||||
if (connector->encoder == encoder)
|
||||
return connector;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void tc_bridge_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
u32 hback_porch, hsync_len, hfront_porch, hactive, htime1, htime2;
|
||||
u32 vback_porch, vsync_len, vfront_porch, vactive, vtime1, vtime2;
|
||||
u32 val = 0;
|
||||
u16 dsiclk, clkdiv, byteclk, t1, t2, t3, vsdelay;
|
||||
struct drm_display_mode *mode;
|
||||
struct drm_connector *connector = get_connector(bridge->encoder);
|
||||
|
||||
mode = &bridge->encoder->crtc->state->adjusted_mode;
|
||||
|
||||
hback_porch = mode->htotal - mode->hsync_end;
|
||||
hsync_len = mode->hsync_end - mode->hsync_start;
|
||||
vback_porch = mode->vtotal - mode->vsync_end;
|
||||
vsync_len = mode->vsync_end - mode->vsync_start;
|
||||
|
||||
htime1 = (hback_porch << 16) + hsync_len;
|
||||
vtime1 = (vback_porch << 16) + vsync_len;
|
||||
|
||||
hfront_porch = mode->hsync_start - mode->hdisplay;
|
||||
hactive = mode->hdisplay;
|
||||
vfront_porch = mode->vsync_start - mode->vdisplay;
|
||||
vactive = mode->vdisplay;
|
||||
|
||||
htime2 = (hfront_porch << 16) + hactive;
|
||||
vtime2 = (vfront_porch << 16) + vactive;
|
||||
|
||||
d2l_read(tc->i2c, IDREG, &val);
|
||||
|
||||
dev_info(tc->dev, "DSI2LVDS Chip ID.%02x Revision ID. %02x **\n",
|
||||
(val >> 8) & 0xFF, val & 0xFF);
|
||||
|
||||
d2l_write(tc->i2c, SYSRST, SYS_RST_REG | SYS_RST_DSIRX | SYS_RST_BM |
|
||||
SYS_RST_LCD | SYS_RST_I2CM | SYS_RST_I2CS);
|
||||
usleep_range(30000, 40000);
|
||||
|
||||
d2l_write(tc->i2c, PPI_TX_RX_TA, TTA_GET | TTA_SURE);
|
||||
d2l_write(tc->i2c, PPI_LPTXTIMECNT, LPX_PERIOD);
|
||||
d2l_write(tc->i2c, PPI_D0S_CLRSIPOCOUNT, 3);
|
||||
d2l_write(tc->i2c, PPI_D1S_CLRSIPOCOUNT, 3);
|
||||
d2l_write(tc->i2c, PPI_D2S_CLRSIPOCOUNT, 3);
|
||||
d2l_write(tc->i2c, PPI_D3S_CLRSIPOCOUNT, 3);
|
||||
|
||||
val = ((L0EN << tc->num_dsi_lanes) - L0EN) | DSI_CLEN_BIT;
|
||||
d2l_write(tc->i2c, PPI_LANEENABLE, val);
|
||||
d2l_write(tc->i2c, DSI_LANEENABLE, val);
|
||||
|
||||
d2l_write(tc->i2c, PPI_STARTPPI, PPI_START_FUNCTION);
|
||||
d2l_write(tc->i2c, DSI_STARTDSI, DSI_RX_START);
|
||||
|
||||
if (tc->bpc == 8)
|
||||
val = TC358775_VPCTRL_OPXLFMT(1);
|
||||
else /* bpc = 6; */
|
||||
val = TC358775_VPCTRL_MSF(1);
|
||||
|
||||
dsiclk = mode->crtc_clock * 3 * tc->bpc / tc->num_dsi_lanes / 1000;
|
||||
clkdiv = dsiclk / DIVIDE_BY_3 * tc->lvds_link;
|
||||
byteclk = dsiclk / 4;
|
||||
t1 = hactive * (tc->bpc * 3 / 8) / tc->num_dsi_lanes;
|
||||
t2 = ((100000 / clkdiv)) * (hactive + hback_porch + hsync_len + hfront_porch) / 1000;
|
||||
t3 = ((t2 * byteclk) / 100) - (hactive * (tc->bpc * 3 / 8) /
|
||||
tc->num_dsi_lanes);
|
||||
|
||||
vsdelay = (clkdiv * (t1 + t3) / byteclk) - hback_porch - hsync_len - hactive;
|
||||
|
||||
val |= TC358775_VPCTRL_VSDELAY(vsdelay);
|
||||
d2l_write(tc->i2c, VPCTRL, val);
|
||||
|
||||
d2l_write(tc->i2c, HTIM1, htime1);
|
||||
d2l_write(tc->i2c, VTIM1, vtime1);
|
||||
d2l_write(tc->i2c, HTIM2, htime2);
|
||||
d2l_write(tc->i2c, VTIM2, vtime2);
|
||||
|
||||
d2l_write(tc->i2c, VFUEN, VFUEN_EN);
|
||||
d2l_write(tc->i2c, SYSRST, SYS_RST_LCD);
|
||||
d2l_write(tc->i2c, LVPHY0, LV_PHY0_PRBS_ON(4) | LV_PHY0_ND(6));
|
||||
|
||||
dev_dbg(tc->dev, "bus_formats %04x bpc %d\n",
|
||||
connector->display_info.bus_formats[0],
|
||||
tc->bpc);
|
||||
/*
|
||||
* Default hardware register settings of tc358775 configured
|
||||
* with MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA jeida-24 format
|
||||
*/
|
||||
if (connector->display_info.bus_formats[0] ==
|
||||
MEDIA_BUS_FMT_RGB888_1X7X4_SPWG) {
|
||||
/* VESA-24 */
|
||||
d2l_write(tc->i2c, LV_MX0003, LV_MX(LVI_R0, LVI_R1, LVI_R2, LVI_R3));
|
||||
d2l_write(tc->i2c, LV_MX0407, LV_MX(LVI_R4, LVI_R7, LVI_R5, LVI_G0));
|
||||
d2l_write(tc->i2c, LV_MX0811, LV_MX(LVI_G1, LVI_G2, LVI_G6, LVI_G7));
|
||||
d2l_write(tc->i2c, LV_MX1215, LV_MX(LVI_G3, LVI_G4, LVI_G5, LVI_B0));
|
||||
d2l_write(tc->i2c, LV_MX1619, LV_MX(LVI_B6, LVI_B7, LVI_B1, LVI_B2));
|
||||
d2l_write(tc->i2c, LV_MX2023, LV_MX(LVI_B3, LVI_B4, LVI_B5, LVI_L0));
|
||||
d2l_write(tc->i2c, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_R6));
|
||||
} else { /* MEDIA_BUS_FMT_RGB666_1X7X3_SPWG - JEIDA-18 */
|
||||
d2l_write(tc->i2c, LV_MX0003, LV_MX(LVI_R0, LVI_R1, LVI_R2, LVI_R3));
|
||||
d2l_write(tc->i2c, LV_MX0407, LV_MX(LVI_R4, LVI_L0, LVI_R5, LVI_G0));
|
||||
d2l_write(tc->i2c, LV_MX0811, LV_MX(LVI_G1, LVI_G2, LVI_L0, LVI_L0));
|
||||
d2l_write(tc->i2c, LV_MX1215, LV_MX(LVI_G3, LVI_G4, LVI_G5, LVI_B0));
|
||||
d2l_write(tc->i2c, LV_MX1619, LV_MX(LVI_L0, LVI_L0, LVI_B1, LVI_B2));
|
||||
d2l_write(tc->i2c, LV_MX2023, LV_MX(LVI_B3, LVI_B4, LVI_B5, LVI_L0));
|
||||
d2l_write(tc->i2c, LV_MX2427, LV_MX(LVI_HS, LVI_VS, LVI_DE, LVI_L0));
|
||||
}
|
||||
|
||||
d2l_write(tc->i2c, VFUEN, VFUEN_EN);
|
||||
|
||||
val = LVCFG_LVEN_BIT;
|
||||
if (tc->lvds_link == DUAL_LINK) {
|
||||
val |= TC358775_LVCFG_LVDLINK(1);
|
||||
val |= TC358775_LVCFG_PCLKDIV(DIVIDE_BY_6);
|
||||
} else {
|
||||
val |= TC358775_LVCFG_PCLKDIV(DIVIDE_BY_3);
|
||||
};
|
||||
d2l_write(tc->i2c, LVCFG, val);
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
tc_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_info *info,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
|
||||
/*
|
||||
* Maximum pixel clock speed 135MHz for single-link
|
||||
* 270MHz for dual-link
|
||||
*/
|
||||
if ((mode->clock > 135000 && tc->lvds_link == SINGLE_LINK) ||
|
||||
(mode->clock > 270000 && tc->lvds_link == DUAL_LINK))
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
switch (info->bus_formats[0]) {
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
|
||||
/* RGB888 */
|
||||
tc->bpc = 8;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
|
||||
/* RGB666 */
|
||||
tc->bpc = 6;
|
||||
break;
|
||||
default:
|
||||
dev_warn(tc->dev,
|
||||
"unsupported LVDS bus format 0x%04x\n",
|
||||
info->bus_formats[0]);
|
||||
return MODE_NOMODE;
|
||||
}
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static int tc358775_parse_dt(struct device_node *np, struct tc_data *tc)
|
||||
{
|
||||
struct device_node *endpoint;
|
||||
struct device_node *parent;
|
||||
struct device_node *remote;
|
||||
struct property *prop;
|
||||
int len = 0;
|
||||
|
||||
/*
|
||||
* To get the data-lanes of dsi, we need to access the dsi0_out of port1
|
||||
* of dsi0 endpoint from bridge port0 of d2l_in
|
||||
*/
|
||||
endpoint = of_graph_get_endpoint_by_regs(tc->dev->of_node,
|
||||
TC358775_DSI_IN, -1);
|
||||
if (endpoint) {
|
||||
/* dsi0_out node */
|
||||
parent = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
if (parent) {
|
||||
/* dsi0 port 1 */
|
||||
endpoint = of_graph_get_endpoint_by_regs(parent, 1, -1);
|
||||
of_node_put(parent);
|
||||
if (endpoint) {
|
||||
prop = of_find_property(endpoint, "data-lanes",
|
||||
&len);
|
||||
of_node_put(endpoint);
|
||||
if (!prop) {
|
||||
dev_err(tc->dev,
|
||||
"failed to find data lane\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tc->num_dsi_lanes = len / sizeof(u32);
|
||||
|
||||
if (tc->num_dsi_lanes < 1 || tc->num_dsi_lanes > 4)
|
||||
return -EINVAL;
|
||||
|
||||
tc->host_node = of_graph_get_remote_node(np, 0, 0);
|
||||
if (!tc->host_node)
|
||||
return -ENODEV;
|
||||
|
||||
of_node_put(tc->host_node);
|
||||
|
||||
tc->lvds_link = SINGLE_LINK;
|
||||
endpoint = of_graph_get_endpoint_by_regs(tc->dev->of_node,
|
||||
TC358775_LVDS_OUT1, -1);
|
||||
if (endpoint) {
|
||||
remote = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
|
||||
if (remote) {
|
||||
if (of_device_is_available(remote))
|
||||
tc->lvds_link = DUAL_LINK;
|
||||
of_node_put(remote);
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(tc->dev, "no.of dsi lanes: %d\n", tc->num_dsi_lanes);
|
||||
dev_dbg(tc->dev, "operating in %d-link mode\n", tc->lvds_link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc_bridge_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct tc_data *tc = bridge_to_tc(bridge);
|
||||
struct device *dev = &tc->i2c->dev;
|
||||
struct mipi_dsi_host *host;
|
||||
struct mipi_dsi_device *dsi;
|
||||
int ret;
|
||||
|
||||
const struct mipi_dsi_device_info info = { .type = "tc358775",
|
||||
.channel = 0,
|
||||
.node = NULL,
|
||||
};
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(tc->host_node);
|
||||
if (!host) {
|
||||
dev_err(dev, "failed to find dsi host\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
dsi = mipi_dsi_device_register_full(host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
dev_err(dev, "failed to create dsi device\n");
|
||||
ret = PTR_ERR(dsi);
|
||||
goto err_dsi_device;
|
||||
}
|
||||
|
||||
tc->dsi = dsi;
|
||||
|
||||
dsi->lanes = tc->num_dsi_lanes;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to attach dsi to host\n");
|
||||
goto err_dsi_attach;
|
||||
}
|
||||
|
||||
/* Attach the panel-bridge to the dsi bridge */
|
||||
return drm_bridge_attach(bridge->encoder, tc->panel_bridge,
|
||||
&tc->bridge, flags);
|
||||
err_dsi_attach:
|
||||
mipi_dsi_device_unregister(dsi);
|
||||
err_dsi_device:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs tc_bridge_funcs = {
|
||||
.attach = tc_bridge_attach,
|
||||
.pre_enable = tc_bridge_pre_enable,
|
||||
.enable = tc_bridge_enable,
|
||||
.mode_valid = tc_mode_valid,
|
||||
.post_disable = tc_bridge_post_disable,
|
||||
};
|
||||
|
||||
static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct drm_panel *panel;
|
||||
struct tc_data *tc;
|
||||
int ret;
|
||||
|
||||
tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL);
|
||||
if (!tc)
|
||||
return -ENOMEM;
|
||||
|
||||
tc->dev = dev;
|
||||
tc->i2c = client;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, TC358775_LVDS_OUT0,
|
||||
0, &panel, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!panel)
|
||||
return -ENODEV;
|
||||
|
||||
tc->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
if (IS_ERR(tc->panel_bridge))
|
||||
return PTR_ERR(tc->panel_bridge);
|
||||
|
||||
ret = tc358775_parse_dt(dev->of_node, tc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tc->vddio = devm_regulator_get(dev, "vddio-supply");
|
||||
if (IS_ERR(tc->vddio)) {
|
||||
ret = PTR_ERR(tc->vddio);
|
||||
dev_err(dev, "vddio-supply not found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
tc->vdd = devm_regulator_get(dev, "vdd-supply");
|
||||
if (IS_ERR(tc->vdd)) {
|
||||
ret = PTR_ERR(tc->vdd);
|
||||
dev_err(dev, "vdd-supply not found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
tc->stby_gpio = devm_gpiod_get(dev, "stby", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tc->stby_gpio)) {
|
||||
ret = PTR_ERR(tc->stby_gpio);
|
||||
dev_err(dev, "cannot get stby-gpio %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tc->reset_gpio)) {
|
||||
ret = PTR_ERR(tc->reset_gpio);
|
||||
dev_err(dev, "cannot get reset-gpios %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tc->bridge.funcs = &tc_bridge_funcs;
|
||||
tc->bridge.of_node = dev->of_node;
|
||||
drm_bridge_add(&tc->bridge);
|
||||
|
||||
i2c_set_clientdata(client, tc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tc_data *tc = i2c_get_clientdata(client);
|
||||
|
||||
drm_bridge_remove(&tc->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tc358775_i2c_ids[] = {
|
||||
{ "tc358775", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tc358775_i2c_ids);
|
||||
|
||||
static const struct of_device_id tc358775_of_ids[] = {
|
||||
{ .compatible = "toshiba,tc358775", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tc358775_of_ids);
|
||||
|
||||
static struct i2c_driver tc358775_driver = {
|
||||
.driver = {
|
||||
.name = "tc358775",
|
||||
.of_match_table = tc358775_of_ids,
|
||||
},
|
||||
.id_table = tc358775_i2c_ids,
|
||||
.probe = tc_probe,
|
||||
.remove = tc_remove,
|
||||
};
|
||||
module_i2c_driver(tc358775_driver);
|
||||
|
||||
MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
|
||||
MODULE_DESCRIPTION("TC358775 DSI/LVDS bridge driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -394,9 +394,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge,
|
|||
}
|
||||
pdata->dsi = dsi;
|
||||
|
||||
/* attach panel to bridge */
|
||||
drm_panel_attach(pdata->panel, &pdata->connector);
|
||||
|
||||
return 0;
|
||||
|
||||
err_dsi_attach:
|
||||
|
|
|
@ -187,6 +187,7 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force)
|
|||
case DRM_MODE_CONNECTOR_DPI:
|
||||
case DRM_MODE_CONNECTOR_LVDS:
|
||||
case DRM_MODE_CONNECTOR_DSI:
|
||||
case DRM_MODE_CONNECTOR_eDP:
|
||||
status = connector_status_connected;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -850,7 +850,7 @@ static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
|
|||
DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
|
||||
|
||||
static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
|
||||
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
|
||||
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I, TV-out and DP */
|
||||
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
|
||||
{ DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */
|
||||
};
|
||||
|
@ -867,7 +867,7 @@ static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
|
|||
DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
|
||||
|
||||
static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
|
||||
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
|
||||
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I, TV-out and DP */
|
||||
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
|
||||
{ DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
|
||||
{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
|
||||
|
@ -876,6 +876,19 @@ static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
|
|||
DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
|
||||
drm_tv_subconnector_enum_list)
|
||||
|
||||
static const struct drm_prop_enum_list drm_dp_subconnector_enum_list[] = {
|
||||
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I, TV-out and DP */
|
||||
{ DRM_MODE_SUBCONNECTOR_VGA, "VGA" }, /* DP */
|
||||
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DP */
|
||||
{ DRM_MODE_SUBCONNECTOR_HDMIA, "HDMI" }, /* DP */
|
||||
{ DRM_MODE_SUBCONNECTOR_DisplayPort, "DP" }, /* DP */
|
||||
{ DRM_MODE_SUBCONNECTOR_Wireless, "Wireless" }, /* DP */
|
||||
{ DRM_MODE_SUBCONNECTOR_Native, "Native" }, /* DP */
|
||||
};
|
||||
|
||||
DRM_ENUM_NAME_FN(drm_get_dp_subconnector_name,
|
||||
drm_dp_subconnector_enum_list)
|
||||
|
||||
static const struct drm_prop_enum_list hdmi_colorspaces[] = {
|
||||
/* For Default case, driver will set the colorspace */
|
||||
{ DRM_MODE_COLORIMETRY_DEFAULT, "Default" },
|
||||
|
@ -1217,6 +1230,14 @@ static const struct drm_prop_enum_list dp_colorspaces[] = {
|
|||
* can also expose this property to external outputs, in which case they
|
||||
* must support "None", which should be the default (since external screens
|
||||
* have a built-in scaler).
|
||||
*
|
||||
* subconnector:
|
||||
* This property is used by DVI-I, TVout and DisplayPort to indicate different
|
||||
* connector subtypes. Enum values more or less match with those from main
|
||||
* connector types.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
int drm_connector_create_standard_properties(struct drm_device *dev)
|
||||
|
@ -1305,6 +1326,30 @@ int drm_mode_create_dvi_i_properties(struct drm_device *dev)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
|
||||
|
||||
/**
|
||||
* drm_connector_attach_dp_subconnector_property - create subconnector property for DP
|
||||
* @connector: drm_connector to attach property
|
||||
*
|
||||
* Called by a driver when DP connector is created.
|
||||
*/
|
||||
void drm_connector_attach_dp_subconnector_property(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_mode_config *mode_config = &connector->dev->mode_config;
|
||||
|
||||
if (!mode_config->dp_subconnector_property)
|
||||
mode_config->dp_subconnector_property =
|
||||
drm_property_create_enum(connector->dev,
|
||||
DRM_MODE_PROP_IMMUTABLE,
|
||||
"subconnector",
|
||||
drm_dp_subconnector_enum_list,
|
||||
ARRAY_SIZE(drm_dp_subconnector_enum_list));
|
||||
|
||||
drm_object_attach_property(&connector->base,
|
||||
mode_config->dp_subconnector_property,
|
||||
DRM_MODE_SUBCONNECTOR_Unknown);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property);
|
||||
|
||||
/**
|
||||
* DOC: HDMI connector properties
|
||||
*
|
||||
|
|
|
@ -597,6 +597,77 @@ void drm_dp_downstream_debug(struct seq_file *m,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_dp_downstream_debug);
|
||||
|
||||
/**
|
||||
* drm_dp_subconnector_type() - get DP branch device type
|
||||
*
|
||||
*/
|
||||
enum drm_mode_subconnector
|
||||
drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
const u8 port_cap[4])
|
||||
{
|
||||
int type;
|
||||
if (!drm_dp_is_branch(dpcd))
|
||||
return DRM_MODE_SUBCONNECTOR_Native;
|
||||
/* DP 1.0 approach */
|
||||
if (dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) {
|
||||
type = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
|
||||
DP_DWN_STRM_PORT_TYPE_MASK;
|
||||
|
||||
switch (type) {
|
||||
case DP_DWN_STRM_PORT_TYPE_TMDS:
|
||||
/* Can be HDMI or DVI-D, DVI-D is a safer option */
|
||||
return DRM_MODE_SUBCONNECTOR_DVID;
|
||||
case DP_DWN_STRM_PORT_TYPE_ANALOG:
|
||||
/* Can be VGA or DVI-A, VGA is more popular */
|
||||
return DRM_MODE_SUBCONNECTOR_VGA;
|
||||
case DP_DWN_STRM_PORT_TYPE_DP:
|
||||
return DRM_MODE_SUBCONNECTOR_DisplayPort;
|
||||
case DP_DWN_STRM_PORT_TYPE_OTHER:
|
||||
default:
|
||||
return DRM_MODE_SUBCONNECTOR_Unknown;
|
||||
}
|
||||
}
|
||||
type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
|
||||
|
||||
switch (type) {
|
||||
case DP_DS_PORT_TYPE_DP:
|
||||
case DP_DS_PORT_TYPE_DP_DUALMODE:
|
||||
return DRM_MODE_SUBCONNECTOR_DisplayPort;
|
||||
case DP_DS_PORT_TYPE_VGA:
|
||||
return DRM_MODE_SUBCONNECTOR_VGA;
|
||||
case DP_DS_PORT_TYPE_DVI:
|
||||
return DRM_MODE_SUBCONNECTOR_DVID;
|
||||
case DP_DS_PORT_TYPE_HDMI:
|
||||
return DRM_MODE_SUBCONNECTOR_HDMIA;
|
||||
case DP_DS_PORT_TYPE_WIRELESS:
|
||||
return DRM_MODE_SUBCONNECTOR_Wireless;
|
||||
case DP_DS_PORT_TYPE_NON_EDID:
|
||||
default:
|
||||
return DRM_MODE_SUBCONNECTOR_Unknown;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_subconnector_type);
|
||||
|
||||
/**
|
||||
* drm_mode_set_dp_subconnector_property - set subconnector for DP connector
|
||||
*
|
||||
* Called by a driver on every detect event.
|
||||
*/
|
||||
void drm_dp_set_subconnector_property(struct drm_connector *connector,
|
||||
enum drm_connector_status status,
|
||||
const u8 *dpcd,
|
||||
const u8 port_cap[4])
|
||||
{
|
||||
enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
|
||||
|
||||
if (status == connector_status_connected)
|
||||
subconnector = drm_dp_subconnector_type(dpcd, port_cap);
|
||||
drm_object_property_set_value(&connector->base,
|
||||
connector->dev->mode_config.dp_subconnector_property,
|
||||
subconnector);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_set_subconnector_property);
|
||||
|
||||
/*
|
||||
* I2C-over-AUX implementation
|
||||
*/
|
||||
|
|
|
@ -653,7 +653,7 @@ static void drm_gem_vram_bo_driver_evict_flags(struct drm_gem_vram_object *gbo,
|
|||
|
||||
static void drm_gem_vram_bo_driver_move_notify(struct drm_gem_vram_object *gbo,
|
||||
bool evict,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
struct ttm_resource *new_mem)
|
||||
{
|
||||
struct ttm_bo_kmap_obj *kmap = &gbo->kmap;
|
||||
|
||||
|
@ -1004,28 +1004,6 @@ err_ttm_tt_init:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int bo_driver_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
||||
struct ttm_mem_type_manager *man)
|
||||
{
|
||||
switch (type) {
|
||||
case TTM_PL_SYSTEM:
|
||||
man->flags = 0;
|
||||
man->available_caching = TTM_PL_MASK_CACHING;
|
||||
man->default_caching = TTM_PL_FLAG_CACHED;
|
||||
break;
|
||||
case TTM_PL_VRAM:
|
||||
man->func = &ttm_bo_manager_func;
|
||||
man->flags = TTM_MEMTYPE_FLAG_FIXED;
|
||||
man->available_caching = TTM_PL_FLAG_UNCACHED |
|
||||
TTM_PL_FLAG_WC;
|
||||
man->default_caching = TTM_PL_FLAG_WC;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bo_driver_evict_flags(struct ttm_buffer_object *bo,
|
||||
struct ttm_placement *placement)
|
||||
{
|
||||
|
@ -1042,7 +1020,7 @@ static void bo_driver_evict_flags(struct ttm_buffer_object *bo,
|
|||
|
||||
static void bo_driver_move_notify(struct ttm_buffer_object *bo,
|
||||
bool evict,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
struct ttm_resource *new_mem)
|
||||
{
|
||||
struct drm_gem_vram_object *gbo;
|
||||
|
||||
|
@ -1056,18 +1034,12 @@ static void bo_driver_move_notify(struct ttm_buffer_object *bo,
|
|||
}
|
||||
|
||||
static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev,
|
||||
struct ttm_mem_reg *mem)
|
||||
struct ttm_resource *mem)
|
||||
{
|
||||
struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev);
|
||||
|
||||
mem->bus.addr = NULL;
|
||||
mem->bus.size = mem->num_pages << PAGE_SHIFT;
|
||||
|
||||
switch (mem->mem_type) {
|
||||
case TTM_PL_SYSTEM: /* nothing to do */
|
||||
mem->bus.offset = 0;
|
||||
mem->bus.base = 0;
|
||||
mem->bus.is_iomem = false;
|
||||
break;
|
||||
case TTM_PL_VRAM:
|
||||
mem->bus.offset = mem->start << PAGE_SHIFT;
|
||||
|
@ -1083,9 +1055,6 @@ static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev,
|
|||
|
||||
static struct ttm_bo_driver bo_driver = {
|
||||
.ttm_tt_create = bo_driver_ttm_tt_create,
|
||||
.ttm_tt_populate = ttm_pool_populate,
|
||||
.ttm_tt_unpopulate = ttm_pool_unpopulate,
|
||||
.init_mem_type = bo_driver_init_mem_type,
|
||||
.eviction_valuable = ttm_bo_eviction_valuable,
|
||||
.evict_flags = bo_driver_evict_flags,
|
||||
.move_notify = bo_driver_move_notify,
|
||||
|
@ -1100,12 +1069,10 @@ static int drm_vram_mm_debugfs(struct seq_file *m, void *data)
|
|||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_vram_mm *vmm = node->minor->dev->vram_mm;
|
||||
struct drm_mm *mm = vmm->bdev.man[TTM_PL_VRAM].priv;
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&vmm->bdev, TTM_PL_VRAM);
|
||||
struct drm_printer p = drm_seq_file_printer(m);
|
||||
|
||||
spin_lock(&ttm_bo_glob.lru_lock);
|
||||
drm_mm_print(mm, &p);
|
||||
spin_unlock(&ttm_bo_glob.lru_lock);
|
||||
ttm_resource_manager_debug(man, &p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1142,7 +1109,10 @@ static int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ttm_bo_init_mm(&vmm->bdev, TTM_PL_VRAM, vram_size >> PAGE_SHIFT);
|
||||
ret = ttm_range_man_init(&vmm->bdev, TTM_PL_VRAM,
|
||||
TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC,
|
||||
TTM_PL_FLAG_WC, false,
|
||||
vram_size >> PAGE_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1151,6 +1121,7 @@ static int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
|
|||
|
||||
static void drm_vram_mm_cleanup(struct drm_vram_mm *vmm)
|
||||
{
|
||||
ttm_range_man_fini(&vmm->bdev, TTM_PL_VRAM);
|
||||
ttm_bo_device_release(&vmm->bdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,16 +70,12 @@ EXPORT_SYMBOL(drm_panel_init);
|
|||
*
|
||||
* Add a panel to the global registry so that it can be looked up by display
|
||||
* drivers.
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_panel_add(struct drm_panel *panel)
|
||||
void drm_panel_add(struct drm_panel *panel)
|
||||
{
|
||||
mutex_lock(&panel_lock);
|
||||
list_add_tail(&panel->list, &panel_list);
|
||||
mutex_unlock(&panel_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_add);
|
||||
|
||||
|
@ -97,42 +93,6 @@ void drm_panel_remove(struct drm_panel *panel)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_panel_remove);
|
||||
|
||||
/**
|
||||
* drm_panel_attach - attach a panel to a connector
|
||||
* @panel: DRM panel
|
||||
* @connector: DRM connector
|
||||
*
|
||||
* After obtaining a pointer to a DRM panel a display driver calls this
|
||||
* function to attach a panel to a connector.
|
||||
*
|
||||
* An error is returned if the panel is already attached to another connector.
|
||||
*
|
||||
* When unloading, the driver should detach from the panel by calling
|
||||
* drm_panel_detach().
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_attach);
|
||||
|
||||
/**
|
||||
* drm_panel_detach - detach a panel from a connector
|
||||
* @panel: DRM panel
|
||||
*
|
||||
* Detaches a panel from the connector it is attached to. If a panel is not
|
||||
* attached to any connector this is effectively a no-op.
|
||||
*
|
||||
* This function should not be called by the panel device itself. It
|
||||
* is only for the drm device that called drm_panel_attach().
|
||||
*/
|
||||
void drm_panel_detach(struct drm_panel *panel)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_detach);
|
||||
|
||||
/**
|
||||
* drm_panel_prepare - power on a panel
|
||||
* @panel: DRM panel
|
||||
|
@ -300,6 +260,49 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
|
|||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
EXPORT_SYMBOL(of_drm_find_panel);
|
||||
|
||||
/**
|
||||
* of_drm_get_panel_orientation - look up the orientation of the panel through
|
||||
* the "rotation" binding from a device tree node
|
||||
* @np: device tree node of the panel
|
||||
* @orientation: orientation enum to be filled in
|
||||
*
|
||||
* Looks up the rotation of a panel in the device tree. The orientation of the
|
||||
* panel is expressed as a property name "rotation" in the device tree. The
|
||||
* rotation in the device tree is counter clockwise.
|
||||
*
|
||||
* Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the
|
||||
* rotation property doesn't exist. Return a negative error code on failure.
|
||||
*/
|
||||
int of_drm_get_panel_orientation(const struct device_node *np,
|
||||
enum drm_panel_orientation *orientation)
|
||||
{
|
||||
int rotation, ret;
|
||||
|
||||
ret = of_property_read_u32(np, "rotation", &rotation);
|
||||
if (ret == -EINVAL) {
|
||||
/* Don't return an error if there's no rotation property. */
|
||||
*orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (rotation == 0)
|
||||
*orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
|
||||
else if (rotation == 90)
|
||||
*orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
|
||||
else if (rotation == 180)
|
||||
*orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
|
||||
else if (rotation == 270)
|
||||
*orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(of_drm_get_panel_orientation);
|
||||
#endif
|
||||
|
||||
#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
|
||||
|
|
|
@ -297,7 +297,7 @@ void drm_syncobj_add_point(struct drm_syncobj *syncobj,
|
|||
prev = drm_syncobj_fence_get(syncobj);
|
||||
/* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
|
||||
if (prev && prev->seqno >= point)
|
||||
DRM_ERROR("You are adding an unorder point to timeline!\n");
|
||||
DRM_DEBUG("You are adding an unorder point to timeline!\n");
|
||||
dma_fence_chain_init(chain, prev, fence, point);
|
||||
rcu_assign_pointer(syncobj->fence, &chain->base);
|
||||
|
||||
|
|
|
@ -42,11 +42,6 @@ static inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e)
|
|||
static enum drm_connector_status
|
||||
exynos_dpi_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct exynos_dpi *ctx = connector_to_dpi(connector);
|
||||
|
||||
if (ctx->panel)
|
||||
drm_panel_attach(ctx->panel, &ctx->connector);
|
||||
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
|
@ -249,8 +244,5 @@ int exynos_dpi_remove(struct drm_encoder *encoder)
|
|||
|
||||
exynos_dpi_disable(&ctx->encoder);
|
||||
|
||||
if (ctx->panel)
|
||||
drm_panel_detach(ctx->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1551,12 +1551,10 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
|
|||
}
|
||||
|
||||
dsi->panel = of_drm_find_panel(device->dev.of_node);
|
||||
if (IS_ERR(dsi->panel)) {
|
||||
if (IS_ERR(dsi->panel))
|
||||
dsi->panel = NULL;
|
||||
} else {
|
||||
drm_panel_attach(dsi->panel, &dsi->connector);
|
||||
else
|
||||
dsi->connector.status = connector_status_connected;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1596,7 +1594,6 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
|
|||
if (dsi->panel) {
|
||||
mutex_lock(&drm->mode_config.mutex);
|
||||
exynos_dsi_disable(&dsi->encoder);
|
||||
drm_panel_detach(dsi->panel);
|
||||
dsi->panel = NULL;
|
||||
dsi->connector.status = connector_status_disconnected;
|
||||
mutex_unlock(&drm->mode_config.mutex);
|
||||
|
|
|
@ -40,10 +40,7 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
|
|||
|
||||
static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector);
|
||||
|
||||
drm_connector_unregister(connector);
|
||||
drm_panel_detach(fsl_con->panel);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
|
@ -101,12 +98,6 @@ static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev,
|
|||
if (ret < 0)
|
||||
goto err_sysfs;
|
||||
|
||||
ret = drm_panel_attach(panel, connector);
|
||||
if (ret) {
|
||||
dev_err(fsl_dev->dev, "failed to attach panel\n");
|
||||
goto err_sysfs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
|
|
|
@ -2078,7 +2078,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
|
|||
intel_dp->dpcd,
|
||||
sizeof(intel_dp->dpcd));
|
||||
cdv_intel_edp_panel_vdd_off(gma_encoder);
|
||||
if (ret == 0) {
|
||||
if (ret <= 0) {
|
||||
/* if this fails, presume the device is a ghost */
|
||||
DRM_INFO("failed to retrieve link info, disabling eDP\n");
|
||||
drm_encoder_cleanup(encoder);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
**************************************************************************/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
|
@ -505,12 +506,31 @@ static const struct psb_offset mdfld_regmap[3] = {
|
|||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* The GPIO lines for resetting DSI pipe 0 and 2 are available in the
|
||||
* PCI device 0000:00:0c.0 on the Medfield.
|
||||
*/
|
||||
static struct gpiod_lookup_table mdfld_dsi_pipe_gpio_table = {
|
||||
.table = {
|
||||
GPIO_LOOKUP("0000:00:0c.0", 128, "dsi-pipe0-reset",
|
||||
GPIO_ACTIVE_HIGH),
|
||||
GPIO_LOOKUP("0000:00:0c.0", 34, "dsi-pipe2-reset",
|
||||
GPIO_ACTIVE_HIGH),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static int mdfld_chip_setup(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
if (pci_enable_msi(dev->pdev))
|
||||
dev_warn(dev->dev, "Enabling MSI failed!\n");
|
||||
dev_priv->regmap = mdfld_regmap;
|
||||
|
||||
/* Associate the GPIO lines with the DRM device */
|
||||
mdfld_dsi_pipe_gpio_table.dev_id = dev_name(dev->dev);
|
||||
gpiod_add_lookup_table(&mdfld_dsi_pipe_gpio_table);
|
||||
|
||||
return mid_chip_setup(dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -955,7 +955,7 @@ struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev,
|
|||
|
||||
/* panel hard-reset */
|
||||
if (p_funcs->reset) {
|
||||
ret = p_funcs->reset(pipe);
|
||||
ret = p_funcs->reset(dev, pipe);
|
||||
if (ret) {
|
||||
DRM_ERROR("Panel %d hard-reset failed\n", pipe);
|
||||
return NULL;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
|
@ -366,7 +367,7 @@ static enum drm_mode_status mdfld_dsi_connector_mode_valid(struct drm_connector
|
|||
/**
|
||||
* FIXME: current DC has no fitting unit, reject any mode setting
|
||||
* request
|
||||
* Will figure out a way to do up-scaling(pannel fitting) later.
|
||||
* Will figure out a way to do up-scaling(panel fitting) later.
|
||||
**/
|
||||
if (fixed_mode) {
|
||||
if (mode->hdisplay != fixed_mode->hdisplay)
|
||||
|
@ -432,42 +433,42 @@ static int mdfld_dsi_get_default_config(struct drm_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int mdfld_dsi_panel_reset(int pipe)
|
||||
int mdfld_dsi_panel_reset(struct drm_device *ddev, int pipe)
|
||||
{
|
||||
unsigned gpio;
|
||||
int ret = 0;
|
||||
struct device *dev = ddev->dev;
|
||||
struct gpio_desc *gpiod;
|
||||
|
||||
/*
|
||||
* Raise the GPIO reset line for the corresponding pipe to HIGH,
|
||||
* this is probably because it is active low so this takes the
|
||||
* respective pipe out of reset. (We have no code to put it back
|
||||
* into reset in this driver.)
|
||||
*/
|
||||
switch (pipe) {
|
||||
case 0:
|
||||
gpio = 128;
|
||||
gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpiod))
|
||||
return PTR_ERR(gpiod);
|
||||
break;
|
||||
case 2:
|
||||
gpio = 34;
|
||||
gpiod = gpiod_get(dev, "dsi-pipe2-reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpiod))
|
||||
return PTR_ERR(gpiod);
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("Invalid output\n");
|
||||
DRM_DEV_ERROR(dev, "Invalid output pipe\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
gpiod_put(gpiod);
|
||||
|
||||
ret = gpio_request(gpio, "gfx");
|
||||
if (ret) {
|
||||
DRM_ERROR("gpio_rqueset failed\n");
|
||||
return ret;
|
||||
}
|
||||
/* Flush posted writes on the device */
|
||||
gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_ASIS);
|
||||
if (IS_ERR(gpiod))
|
||||
return PTR_ERR(gpiod);
|
||||
gpiod_get_value(gpiod);
|
||||
gpiod_put(gpiod);
|
||||
|
||||
ret = gpio_direction_output(gpio, 1);
|
||||
if (ret) {
|
||||
DRM_ERROR("gpio_direction_output failed\n");
|
||||
goto gpio_error;
|
||||
}
|
||||
|
||||
gpio_get_value(128);
|
||||
|
||||
gpio_error:
|
||||
if (gpio_is_valid(gpio))
|
||||
gpio_free(gpio);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -531,7 +532,7 @@ void mdfld_dsi_output_init(struct drm_device *dev,
|
|||
dsi_config->connector = dsi_connector;
|
||||
|
||||
if (!dsi_config->fixed_mode) {
|
||||
DRM_ERROR("No pannel fixed mode was found\n");
|
||||
DRM_ERROR("No panel fixed mode was found\n");
|
||||
goto dsi_init_err0;
|
||||
}
|
||||
|
||||
|
|
|
@ -372,6 +372,6 @@ extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config,
|
|||
|
||||
extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config,
|
||||
u32 *mode, bool hs);
|
||||
extern int mdfld_dsi_panel_reset(int pipe);
|
||||
extern int mdfld_dsi_panel_reset(struct drm_device *dev, int pipe);
|
||||
|
||||
#endif /*__MDFLD_DSI_OUTPUT_H__*/
|
||||
|
|
|
@ -54,7 +54,7 @@ struct panel_funcs {
|
|||
const struct drm_encoder_helper_funcs *encoder_helper_funcs;
|
||||
struct drm_display_mode * (*get_config_mode)(struct drm_device *);
|
||||
int (*get_panel_info)(struct drm_device *, int, struct panel_info *);
|
||||
int (*reset)(int pipe);
|
||||
int (*reset)(struct drm_device *, int);
|
||||
void (*drv_ic_init)(struct mdfld_dsi_config *dsi_config, int pipe);
|
||||
};
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
#include <linux/gpio.h>
|
||||
#include "gma_display.h"
|
||||
|
||||
/*
|
||||
|
|
|
@ -125,7 +125,7 @@ struct psb_intel_sdvo {
|
|||
bool is_lvds;
|
||||
|
||||
/**
|
||||
* This is sdvo fixed pannel mode pointer
|
||||
* This is sdvo fixed panel mode pointer
|
||||
*/
|
||||
struct drm_display_mode *sdvo_lvds_fixed_mode;
|
||||
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_gem_vram_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "hibmc_drm_drv.h"
|
||||
|
@ -74,12 +71,12 @@ static int hibmc_plane_atomic_check(struct drm_plane *plane,
|
|||
return PTR_ERR(crtc_state);
|
||||
|
||||
if (src_w != state->crtc_w || src_h != state->crtc_h) {
|
||||
DRM_DEBUG_ATOMIC("scale not support\n");
|
||||
drm_dbg_atomic(plane->dev, "scale not support\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state->crtc_x < 0 || state->crtc_y < 0) {
|
||||
DRM_DEBUG_ATOMIC("crtc_x/y of drm_plane state is invalid\n");
|
||||
drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -90,12 +87,12 @@ static int hibmc_plane_atomic_check(struct drm_plane *plane,
|
|||
crtc_state->adjusted_mode.hdisplay ||
|
||||
state->crtc_y + state->crtc_h >
|
||||
crtc_state->adjusted_mode.vdisplay) {
|
||||
DRM_DEBUG_ATOMIC("visible portion of plane is invalid\n");
|
||||
drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state->fb->pitches[0] % 128 != 0) {
|
||||
DRM_DEBUG_ATOMIC("wrong stride with 128-byte aligned\n");
|
||||
drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
|
@ -160,37 +157,6 @@ static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = {
|
|||
.atomic_update = hibmc_plane_atomic_update,
|
||||
};
|
||||
|
||||
static struct drm_plane *hibmc_plane_init(struct hibmc_drm_private *priv)
|
||||
{
|
||||
struct drm_device *dev = priv->dev;
|
||||
struct drm_plane *plane;
|
||||
int ret = 0;
|
||||
|
||||
plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
|
||||
if (!plane) {
|
||||
DRM_ERROR("failed to alloc memory when init plane\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
/*
|
||||
* plane init
|
||||
* TODO: Now only support primary plane, overlay planes
|
||||
* need to do.
|
||||
*/
|
||||
ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs,
|
||||
channel_formats1,
|
||||
ARRAY_SIZE(channel_formats1),
|
||||
NULL,
|
||||
DRM_PLANE_TYPE_PRIMARY,
|
||||
NULL);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to init plane: %d\n", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
|
||||
return plane;
|
||||
}
|
||||
|
||||
static void hibmc_crtc_dpms(struct drm_crtc *crtc, int dpms)
|
||||
{
|
||||
struct hibmc_drm_private *priv = crtc->dev->dev_private;
|
||||
|
@ -537,32 +503,34 @@ static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
|
|||
int hibmc_de_init(struct hibmc_drm_private *priv)
|
||||
{
|
||||
struct drm_device *dev = priv->dev;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_plane *plane;
|
||||
struct drm_crtc *crtc = &priv->crtc;
|
||||
struct drm_plane *plane = &priv->primary_plane;
|
||||
int ret;
|
||||
|
||||
plane = hibmc_plane_init(priv);
|
||||
if (IS_ERR(plane)) {
|
||||
DRM_ERROR("failed to create plane: %ld\n", PTR_ERR(plane));
|
||||
return PTR_ERR(plane);
|
||||
ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs,
|
||||
channel_formats1,
|
||||
ARRAY_SIZE(channel_formats1),
|
||||
NULL,
|
||||
DRM_PLANE_TYPE_PRIMARY,
|
||||
NULL);
|
||||
|
||||
if (ret) {
|
||||
drm_err(dev, "failed to init plane: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
crtc = devm_kzalloc(dev->dev, sizeof(*crtc), GFP_KERNEL);
|
||||
if (!crtc) {
|
||||
DRM_ERROR("failed to alloc memory when init crtc\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
|
||||
|
||||
ret = drm_crtc_init_with_planes(dev, crtc, plane,
|
||||
NULL, &hibmc_crtc_funcs, NULL);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to init crtc: %d\n", ret);
|
||||
drm_err(dev, "failed to init crtc: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_mode_crtc_set_gamma_size(crtc, 256);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to set gamma size: %d\n", ret);
|
||||
drm_err(dev, "failed to set gamma size: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs);
|
||||
|
|
|
@ -11,18 +11,14 @@
|
|||
* Jianhua Li <lijianhua@huawei.com>
|
||||
*/
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_vram_helper.h>
|
||||
#include <drm/drm_irq.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "hibmc_drm_drv.h"
|
||||
|
@ -102,13 +98,13 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv)
|
|||
|
||||
ret = hibmc_de_init(priv);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to init de: %d\n", ret);
|
||||
drm_err(priv->dev, "failed to init de: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hibmc_vdac_init(priv);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to init vdac: %d\n", ret);
|
||||
drm_err(priv->dev, "failed to init vdac: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -216,7 +212,7 @@ static int hibmc_hw_map(struct hibmc_drm_private *priv)
|
|||
iosize = pci_resource_len(pdev, 1);
|
||||
priv->mmio = devm_ioremap(dev->dev, ioaddr, iosize);
|
||||
if (!priv->mmio) {
|
||||
DRM_ERROR("Cannot map mmio region\n");
|
||||
drm_err(dev, "Cannot map mmio region\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -224,7 +220,7 @@ static int hibmc_hw_map(struct hibmc_drm_private *priv)
|
|||
size = pci_resource_len(pdev, 0);
|
||||
priv->fb_map = devm_ioremap(dev->dev, addr, size);
|
||||
if (!priv->fb_map) {
|
||||
DRM_ERROR("Cannot map framebuffer\n");
|
||||
drm_err(dev, "Cannot map framebuffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
priv->fb_base = addr;
|
||||
|
@ -254,9 +250,8 @@ static int hibmc_unload(struct drm_device *dev)
|
|||
|
||||
if (dev->irq_enabled)
|
||||
drm_irq_uninstall(dev);
|
||||
if (priv->msi_enabled)
|
||||
pci_disable_msi(dev->pdev);
|
||||
|
||||
pci_disable_msi(dev->pdev);
|
||||
hibmc_kms_fini(priv);
|
||||
hibmc_mm_fini(priv);
|
||||
dev->dev_private = NULL;
|
||||
|
@ -270,7 +265,7 @@ static int hibmc_load(struct drm_device *dev)
|
|||
|
||||
priv = drmm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
DRM_ERROR("no memory to allocate for hibmc_drm_private\n");
|
||||
drm_err(dev, "no memory to allocate for hibmc_drm_private\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev->dev_private = priv;
|
||||
|
@ -290,19 +285,17 @@ static int hibmc_load(struct drm_device *dev)
|
|||
|
||||
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to initialize vblank: %d\n", ret);
|
||||
drm_err(dev, "failed to initialize vblank: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
priv->msi_enabled = 0;
|
||||
ret = pci_enable_msi(dev->pdev);
|
||||
if (ret) {
|
||||
DRM_WARN("enabling MSI failed: %d\n", ret);
|
||||
drm_warn(dev, "enabling MSI failed: %d\n", ret);
|
||||
} else {
|
||||
priv->msi_enabled = 1;
|
||||
ret = drm_irq_install(dev, dev->pdev->irq);
|
||||
if (ret)
|
||||
DRM_WARN("install irq failed: %d\n", ret);
|
||||
drm_warn(dev, "install irq failed: %d\n", ret);
|
||||
}
|
||||
|
||||
/* reset all the states of crtc/plane/encoder/connector */
|
||||
|
@ -312,7 +305,7 @@ static int hibmc_load(struct drm_device *dev)
|
|||
|
||||
err:
|
||||
hibmc_unload(dev);
|
||||
DRM_ERROR("failed to initialize drm driver: %d\n", ret);
|
||||
drm_err(dev, "failed to initialize drm driver: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -338,19 +331,19 @@ static int hibmc_pci_probe(struct pci_dev *pdev,
|
|||
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to enable pci device: %d\n", ret);
|
||||
drm_err(dev, "failed to enable pci device: %d\n", ret);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = hibmc_load(dev);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to load hibmc: %d\n", ret);
|
||||
drm_err(dev, "failed to load hibmc: %d\n", ret);
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
ret = drm_dev_register(dev, 0);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to register drv for userspace access: %d\n",
|
||||
drm_err(dev, "failed to register drv for userspace access: %d\n",
|
||||
ret);
|
||||
goto err_unload;
|
||||
}
|
||||
|
|
|
@ -25,10 +25,11 @@ struct hibmc_drm_private {
|
|||
void __iomem *fb_map;
|
||||
unsigned long fb_base;
|
||||
unsigned long fb_size;
|
||||
bool msi_enabled;
|
||||
|
||||
/* drm */
|
||||
struct drm_device *dev;
|
||||
struct drm_plane primary_plane;
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
bool mode_config_initialized;
|
||||
|
|
|
@ -11,10 +11,8 @@
|
|||
* Jianhua Li <lijianhua@huawei.com>
|
||||
*/
|
||||
|
||||
#include <drm/drm_gem_vram_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "hibmc_drm_drv.h"
|
||||
|
@ -87,7 +85,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv)
|
|||
ret = drm_encoder_init(dev, encoder, &hibmc_encoder_funcs,
|
||||
DRM_MODE_ENCODER_DAC, NULL);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to init encoder: %d\n", ret);
|
||||
drm_err(dev, "failed to init encoder: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -96,7 +94,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv)
|
|||
ret = drm_connector_init(dev, connector, &hibmc_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VGA);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to init connector: %d\n", ret);
|
||||
drm_err(dev, "failed to init connector: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
drm_connector_helper_add(connector, &hibmc_connector_helper_funcs);
|
||||
|
|
|
@ -32,7 +32,7 @@ int hibmc_mm_init(struct hibmc_drm_private *hibmc)
|
|||
hibmc->fb_size);
|
||||
if (IS_ERR(vmm)) {
|
||||
ret = PTR_ERR(vmm);
|
||||
DRM_ERROR("Error initializing VRAM MM; %d\n", ret);
|
||||
drm_err(dev, "Error initializing VRAM MM; %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -6243,6 +6243,11 @@ out:
|
|||
*/
|
||||
intel_display_power_flush_work(dev_priv);
|
||||
|
||||
if (!intel_dp_is_edp(intel_dp))
|
||||
drm_dp_set_subconnector_property(connector,
|
||||
status,
|
||||
intel_dp->dpcd,
|
||||
intel_dp->downstream_ports);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -7312,6 +7317,9 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
|
|||
struct drm_i915_private *dev_priv = to_i915(connector->dev);
|
||||
enum port port = dp_to_dig_port(intel_dp)->base.port;
|
||||
|
||||
if (!intel_dp_is_edp(intel_dp))
|
||||
drm_connector_attach_dp_subconnector_property(connector);
|
||||
|
||||
if (!IS_G4X(dev_priv) && port != PORT_A)
|
||||
intel_attach_force_audio_property(connector);
|
||||
|
||||
|
|
|
@ -455,13 +455,6 @@ static int imx_ldb_register(struct drm_device *drm,
|
|||
drm_connector_attach_encoder(&imx_ldb_ch->connector, encoder);
|
||||
}
|
||||
|
||||
if (imx_ldb_ch->panel) {
|
||||
ret = drm_panel_attach(imx_ldb_ch->panel,
|
||||
&imx_ldb_ch->connector);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -702,9 +695,6 @@ static void imx_ldb_unbind(struct device *dev, struct device *master,
|
|||
for (i = 0; i < 2; i++) {
|
||||
struct imx_ldb_channel *channel = &imx_ldb->channel[i];
|
||||
|
||||
if (channel->panel)
|
||||
drm_panel_detach(channel->panel);
|
||||
|
||||
kfree(channel->edid);
|
||||
i2c_put_adapter(channel->ddc);
|
||||
}
|
||||
|
|
|
@ -289,9 +289,6 @@ static int imx_pd_register(struct drm_device *drm,
|
|||
DRM_MODE_CONNECTOR_DPI);
|
||||
}
|
||||
|
||||
if (imxpd->panel)
|
||||
drm_panel_attach(imxpd->panel, &imxpd->connector);
|
||||
|
||||
if (imxpd->next_bridge) {
|
||||
ret = drm_bridge_attach(encoder, imxpd->next_bridge,
|
||||
&imxpd->bridge, 0);
|
||||
|
@ -357,9 +354,6 @@ static void imx_pd_unbind(struct device *dev, struct device *master,
|
|||
{
|
||||
struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
|
||||
|
||||
if (imxpd->panel)
|
||||
drm_panel_detach(imxpd->panel);
|
||||
|
||||
kfree(imxpd->edid);
|
||||
}
|
||||
|
||||
|
|
|
@ -199,26 +199,20 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
|
|||
{
|
||||
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
|
||||
struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL;
|
||||
long rate;
|
||||
|
||||
if (!drm_atomic_crtc_needs_modeset(state))
|
||||
return 0;
|
||||
|
||||
if (state->mode.hdisplay > priv->soc_info->max_width ||
|
||||
state->mode.vdisplay > priv->soc_info->max_height)
|
||||
return -EINVAL;
|
||||
|
||||
rate = clk_round_rate(priv->pix_clk,
|
||||
state->adjusted_mode.clock * 1000);
|
||||
if (rate < 0)
|
||||
return rate;
|
||||
|
||||
if (priv->soc_info->has_osd) {
|
||||
if (drm_atomic_crtc_needs_modeset(state) && priv->soc_info->has_osd) {
|
||||
f1_state = drm_atomic_get_plane_state(state->state, &priv->f1);
|
||||
if (IS_ERR(f1_state))
|
||||
return PTR_ERR(f1_state);
|
||||
|
||||
f0_state = drm_atomic_get_plane_state(state->state, &priv->f0);
|
||||
if (IS_ERR(f0_state))
|
||||
return PTR_ERR(f0_state);
|
||||
|
||||
if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && priv->ipu_plane) {
|
||||
ipu_state = drm_atomic_get_plane_state(state->state, priv->ipu_plane);
|
||||
if (IS_ERR(ipu_state))
|
||||
return PTR_ERR(ipu_state);
|
||||
|
||||
/* IPU and F1 planes cannot be enabled at the same time. */
|
||||
if (f1_state->fb && ipu_state->fb) {
|
||||
|
@ -235,6 +229,24 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
ingenic_drm_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
|
||||
{
|
||||
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
|
||||
long rate;
|
||||
|
||||
if (mode->hdisplay > priv->soc_info->max_width)
|
||||
return MODE_BAD_HVALUE;
|
||||
if (mode->vdisplay > priv->soc_info->max_height)
|
||||
return MODE_BAD_VVALUE;
|
||||
|
||||
rate = clk_round_rate(priv->pix_clk, mode->clock * 1000);
|
||||
if (rate < 0)
|
||||
return MODE_CLOCK_RANGE;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *oldstate)
|
||||
{
|
||||
|
@ -648,6 +660,7 @@ static const struct drm_crtc_helper_funcs ingenic_drm_crtc_helper_funcs = {
|
|||
.atomic_begin = ingenic_drm_crtc_atomic_begin,
|
||||
.atomic_flush = ingenic_drm_crtc_atomic_flush,
|
||||
.atomic_check = ingenic_drm_crtc_atomic_check,
|
||||
.mode_valid = ingenic_drm_crtc_mode_valid,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs ingenic_drm_encoder_helper_funcs = {
|
||||
|
|
|
@ -35,6 +35,7 @@ struct soc_info {
|
|||
const u32 *formats;
|
||||
size_t num_formats;
|
||||
bool has_bicubic;
|
||||
bool manual_restart;
|
||||
|
||||
void (*set_coefs)(struct ingenic_ipu *ipu, unsigned int reg,
|
||||
unsigned int sharpness, bool downscale,
|
||||
|
@ -48,6 +49,7 @@ struct ingenic_ipu {
|
|||
struct regmap *map;
|
||||
struct clk *clk;
|
||||
const struct soc_info *soc_info;
|
||||
bool clk_enabled;
|
||||
|
||||
unsigned int num_w, num_h, denom_w, denom_h;
|
||||
|
||||
|
@ -287,12 +289,23 @@ static void ingenic_ipu_plane_atomic_update(struct drm_plane *plane,
|
|||
const struct drm_format_info *finfo;
|
||||
u32 ctrl, stride = 0, coef_index = 0, format = 0;
|
||||
bool needs_modeset, upscaling_w, upscaling_h;
|
||||
int err;
|
||||
|
||||
if (!state || !state->fb)
|
||||
return;
|
||||
|
||||
finfo = drm_format_info(state->fb->format->format);
|
||||
|
||||
if (!ipu->clk_enabled) {
|
||||
err = clk_enable(ipu->clk);
|
||||
if (err) {
|
||||
dev_err(ipu->dev, "Unable to enable clock: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
ipu->clk_enabled = true;
|
||||
}
|
||||
|
||||
/* Reset all the registers if needed */
|
||||
needs_modeset = drm_atomic_crtc_needs_modeset(state->crtc->state);
|
||||
if (needs_modeset) {
|
||||
|
@ -577,6 +590,11 @@ static void ingenic_ipu_plane_atomic_disable(struct drm_plane *plane,
|
|||
regmap_clear_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CHIP_EN);
|
||||
|
||||
ingenic_drm_plane_disable(ipu->master, plane);
|
||||
|
||||
if (ipu->clk_enabled) {
|
||||
clk_disable(ipu->clk);
|
||||
ipu->clk_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs ingenic_ipu_plane_helper_funcs = {
|
||||
|
@ -645,7 +663,8 @@ static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg)
|
|||
unsigned int dummy;
|
||||
|
||||
/* dummy read allows CPU to reconfigure IPU */
|
||||
regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy);
|
||||
if (ipu->soc_info->manual_restart)
|
||||
regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy);
|
||||
|
||||
/* ACK interrupt */
|
||||
regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0);
|
||||
|
@ -656,7 +675,8 @@ static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg)
|
|||
regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v);
|
||||
|
||||
/* Run IPU for the new frame */
|
||||
regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN);
|
||||
if (ipu->soc_info->manual_restart)
|
||||
regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN);
|
||||
|
||||
drm_crtc_handle_vblank(crtc);
|
||||
|
||||
|
@ -758,9 +778,9 @@ static int ingenic_ipu_bind(struct device *dev, struct device *master, void *d)
|
|||
drm_object_attach_property(&plane->base, ipu->sharpness_prop,
|
||||
ipu->sharpness);
|
||||
|
||||
err = clk_prepare_enable(ipu->clk);
|
||||
err = clk_prepare(ipu->clk);
|
||||
if (err) {
|
||||
dev_err(dev, "Unable to enable clock\n");
|
||||
dev_err(dev, "Unable to prepare clock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -772,7 +792,7 @@ static void ingenic_ipu_unbind(struct device *dev,
|
|||
{
|
||||
struct ingenic_ipu *ipu = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(ipu->clk);
|
||||
clk_unprepare(ipu->clk);
|
||||
}
|
||||
|
||||
static const struct component_ops ingenic_ipu_ops = {
|
||||
|
@ -792,10 +812,16 @@ static int ingenic_ipu_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const u32 jz4725b_ipu_formats[] = {
|
||||
/*
|
||||
* While officially supported, packed YUV 4:2:2 formats can cause
|
||||
* random hardware crashes on JZ4725B under certain circumstances.
|
||||
* It seems to happen with some specific resize ratios.
|
||||
* Until a proper workaround or fix is found, disable these formats.
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_YVYU,
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_VYUY,
|
||||
*/
|
||||
DRM_FORMAT_YUV411,
|
||||
DRM_FORMAT_YUV420,
|
||||
DRM_FORMAT_YUV422,
|
||||
|
@ -806,6 +832,7 @@ static const struct soc_info jz4725b_soc_info = {
|
|||
.formats = jz4725b_ipu_formats,
|
||||
.num_formats = ARRAY_SIZE(jz4725b_ipu_formats),
|
||||
.has_bicubic = false,
|
||||
.manual_restart = true,
|
||||
.set_coefs = jz4725b_set_coefs,
|
||||
};
|
||||
|
||||
|
@ -831,6 +858,7 @@ static const struct soc_info jz4760_soc_info = {
|
|||
.formats = jz4760_ipu_formats,
|
||||
.num_formats = ARRAY_SIZE(jz4760_ipu_formats),
|
||||
.has_bicubic = true,
|
||||
.manual_restart = false,
|
||||
.set_coefs = jz4760_set_coefs,
|
||||
};
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
@ -89,7 +90,7 @@ void mcde_display_irq(struct mcde *mcde)
|
|||
* the update function is called, then we disable the
|
||||
* flow on the channel once we get the TE IRQ.
|
||||
*/
|
||||
if (mcde->oneshot_mode) {
|
||||
if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) {
|
||||
spin_lock(&mcde->flow_lock);
|
||||
if (--mcde->flow_active == 0) {
|
||||
dev_dbg(mcde->dev, "TE0 IRQ\n");
|
||||
|
@ -333,7 +334,7 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
|
|||
enum mcde_extsrc src,
|
||||
enum mcde_channel ch,
|
||||
const struct drm_display_mode *mode,
|
||||
u32 format)
|
||||
u32 format, int cpp)
|
||||
{
|
||||
u32 val;
|
||||
u32 conf1;
|
||||
|
@ -342,6 +343,7 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
|
|||
u32 ljinc;
|
||||
u32 cr;
|
||||
u32 comp;
|
||||
u32 pixel_fetcher_watermark;
|
||||
|
||||
switch (ovl) {
|
||||
case MCDE_OVERLAY_0:
|
||||
|
@ -426,8 +428,33 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
|
|||
format);
|
||||
break;
|
||||
}
|
||||
/* The default watermark level for overlay 0 is 48 */
|
||||
val |= 48 << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT;
|
||||
|
||||
/*
|
||||
* Pixel fetch watermark level is max 0x1FFF pixels.
|
||||
* Two basic rules should be followed:
|
||||
* 1. The value should be at least 256 bits.
|
||||
* 2. The sum of all active overlays pixelfetch watermark level
|
||||
* multiplied with bits per pixel, should be lower than the
|
||||
* size of input_fifo_size in bits.
|
||||
* 3. The value should be a multiple of a line (256 bits).
|
||||
*/
|
||||
switch (cpp) {
|
||||
case 2:
|
||||
pixel_fetcher_watermark = 128;
|
||||
break;
|
||||
case 3:
|
||||
pixel_fetcher_watermark = 96;
|
||||
break;
|
||||
case 4:
|
||||
pixel_fetcher_watermark = 48;
|
||||
break;
|
||||
default:
|
||||
pixel_fetcher_watermark = 48;
|
||||
break;
|
||||
}
|
||||
dev_dbg(mcde->dev, "pixel fetcher watermark level %d pixels\n",
|
||||
pixel_fetcher_watermark);
|
||||
val |= pixel_fetcher_watermark << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT;
|
||||
writel(val, mcde->regs + conf2);
|
||||
|
||||
/* Number of bytes to fetch per line */
|
||||
|
@ -498,19 +525,47 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch,
|
|||
}
|
||||
|
||||
/* Set up channel 0 sync (based on chnl_update_registers()) */
|
||||
if (mcde->video_mode || mcde->te_sync)
|
||||
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
|
||||
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
|
||||
else
|
||||
switch (mcde->flow_mode) {
|
||||
case MCDE_COMMAND_ONESHOT_FLOW:
|
||||
/* Oneshot is achieved with software sync */
|
||||
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE
|
||||
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
|
||||
|
||||
if (mcde->te_sync)
|
||||
break;
|
||||
case MCDE_COMMAND_TE_FLOW:
|
||||
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
|
||||
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
|
||||
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0
|
||||
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
|
||||
else
|
||||
break;
|
||||
case MCDE_COMMAND_BTA_TE_FLOW:
|
||||
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
|
||||
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
|
||||
/*
|
||||
* TODO:
|
||||
* The vendor driver uses the formatter as sync source
|
||||
* for BTA TE mode. Test to use TE if you have a panel
|
||||
* that uses this mode.
|
||||
*/
|
||||
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER
|
||||
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
|
||||
break;
|
||||
case MCDE_VIDEO_TE_FLOW:
|
||||
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
|
||||
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
|
||||
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0
|
||||
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
|
||||
break;
|
||||
case MCDE_VIDEO_FORMATTER_FLOW:
|
||||
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
|
||||
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
|
||||
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER
|
||||
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
|
||||
break;
|
||||
default:
|
||||
dev_err(mcde->dev, "unknown flow mode %d\n",
|
||||
mcde->flow_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
writel(val, mcde->regs + sync);
|
||||
|
||||
|
@ -825,6 +880,14 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
u32 formatter_frame;
|
||||
u32 pkt_div;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* This powers up the entire MCDE block and the DSI hardware */
|
||||
ret = regulator_enable(mcde->epod);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "can't re-enable EPOD regulator\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_info(drm->dev, "enable MCDE, %d x %d format %s\n",
|
||||
mode->hdisplay, mode->vdisplay,
|
||||
|
@ -835,6 +898,26 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
return;
|
||||
}
|
||||
|
||||
/* Set up the main control, watermark level at 7 */
|
||||
val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT;
|
||||
/* 24 bits DPI: connect LSB Ch B to D[0:7] */
|
||||
val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT;
|
||||
/* TV out: connect LSB Ch B to D[8:15] */
|
||||
val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT;
|
||||
/* Don't care about this muxing */
|
||||
val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT;
|
||||
/* 24 bits DPI: connect MID Ch B to D[24:31] */
|
||||
val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT;
|
||||
/* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */
|
||||
val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT;
|
||||
/* Syncmux bits zero: DPI channel A and B on output pins A and B resp */
|
||||
writel(val, mcde->regs + MCDE_CONF0);
|
||||
|
||||
/* Clear any pending interrupts */
|
||||
mcde_display_disable_irqs(mcde);
|
||||
writel(0, mcde->regs + MCDE_IMSCERR);
|
||||
writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
|
||||
|
||||
dev_info(drm->dev, "output in %s mode, format %dbpp\n",
|
||||
(mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ?
|
||||
"VIDEO" : "CMD",
|
||||
|
@ -904,7 +987,7 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
* channel 0
|
||||
*/
|
||||
mcde_configure_overlay(mcde, MCDE_OVERLAY_0, MCDE_EXTSRC_0,
|
||||
MCDE_CHANNEL_0, mode, format);
|
||||
MCDE_CHANNEL_0, mode, format, cpp);
|
||||
|
||||
/*
|
||||
* Configure pixel-per-line and line-per-frame for channel 0 and then
|
||||
|
@ -916,11 +999,25 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0,
|
||||
fifo_wtrmrk);
|
||||
|
||||
/*
|
||||
* This brings up the DSI bridge which is tightly connected
|
||||
* to the MCDE DSI formatter.
|
||||
*
|
||||
* FIXME: if we want to use another formatter, such as DPI,
|
||||
* we need to be more elaborate here and select the appropriate
|
||||
* bridge.
|
||||
*/
|
||||
mcde_dsi_enable(mcde->bridge);
|
||||
|
||||
/* Configure the DSI formatter 0 for the DSI panel output */
|
||||
mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
|
||||
formatter_frame, pkt_size);
|
||||
|
||||
if (mcde->te_sync) {
|
||||
switch (mcde->flow_mode) {
|
||||
case MCDE_COMMAND_TE_FLOW:
|
||||
case MCDE_COMMAND_BTA_TE_FLOW:
|
||||
case MCDE_VIDEO_TE_FLOW:
|
||||
/* We are using TE in some comination */
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
val = MCDE_VSCRC_VSPOL;
|
||||
else
|
||||
|
@ -930,16 +1027,31 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
val = readl(mcde->regs + MCDE_CRC);
|
||||
val |= MCDE_CRC_SYCEN0;
|
||||
writel(val, mcde->regs + MCDE_CRC);
|
||||
break;
|
||||
default:
|
||||
/* No TE capture */
|
||||
break;
|
||||
}
|
||||
|
||||
drm_crtc_vblank_on(crtc);
|
||||
|
||||
if (mcde->video_mode)
|
||||
/*
|
||||
* Keep FIFO permanently enabled in video mode,
|
||||
* otherwise MCDE will stop feeding data to the panel.
|
||||
*/
|
||||
/*
|
||||
* If we're using oneshot mode we don't start the flow
|
||||
* until each time the display is given an update, and
|
||||
* then we disable it immediately after. For all other
|
||||
* modes (command or video) we start the FIFO flow
|
||||
* right here. This is necessary for the hardware to
|
||||
* behave right.
|
||||
*/
|
||||
if (mcde->flow_mode != MCDE_COMMAND_ONESHOT_FLOW) {
|
||||
mcde_enable_fifo(mcde, MCDE_FIFO_A);
|
||||
dev_dbg(mcde->dev, "started MCDE video FIFO flow\n");
|
||||
}
|
||||
|
||||
/* Enable MCDE with automatic clock gating */
|
||||
val = readl(mcde->regs + MCDE_CR);
|
||||
val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
|
||||
writel(val, mcde->regs + MCDE_CR);
|
||||
|
||||
dev_info(drm->dev, "MCDE display is enabled\n");
|
||||
}
|
||||
|
@ -950,12 +1062,16 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
|
|||
struct drm_device *drm = crtc->dev;
|
||||
struct mcde *mcde = to_mcde(drm);
|
||||
struct drm_pending_vblank_event *event;
|
||||
int ret;
|
||||
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
/* Disable FIFO A flow */
|
||||
mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
|
||||
|
||||
/* This disables the DSI bridge */
|
||||
mcde_dsi_disable(mcde->bridge);
|
||||
|
||||
event = crtc->state->event;
|
||||
if (event) {
|
||||
crtc->state->event = NULL;
|
||||
|
@ -965,43 +1081,47 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
|
|||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
}
|
||||
|
||||
ret = regulator_disable(mcde->epod);
|
||||
if (ret)
|
||||
dev_err(drm->dev, "can't disable EPOD regulator\n");
|
||||
/* Make sure we are powered down (before we may power up again) */
|
||||
usleep_range(50000, 70000);
|
||||
|
||||
dev_info(drm->dev, "MCDE display is disabled\n");
|
||||
}
|
||||
|
||||
static void mcde_display_send_one_frame(struct mcde *mcde)
|
||||
static void mcde_start_flow(struct mcde *mcde)
|
||||
{
|
||||
/* Request a TE ACK */
|
||||
if (mcde->te_sync)
|
||||
/* Request a TE ACK only in TE+BTA mode */
|
||||
if (mcde->flow_mode == MCDE_COMMAND_BTA_TE_FLOW)
|
||||
mcde_dsi_te_request(mcde->mdsi);
|
||||
|
||||
/* Enable FIFO A flow */
|
||||
mcde_enable_fifo(mcde, MCDE_FIFO_A);
|
||||
|
||||
if (mcde->te_sync) {
|
||||
/*
|
||||
* If oneshot mode is enabled, the flow will be disabled
|
||||
* when the TE0 IRQ arrives in the interrupt handler. Otherwise
|
||||
* updates are continuously streamed to the display after this
|
||||
* point.
|
||||
*/
|
||||
|
||||
if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) {
|
||||
/* Trigger a software sync out on channel 0 */
|
||||
writel(MCDE_CHNLXSYNCHSW_SW_TRIG,
|
||||
mcde->regs + MCDE_CHNL0SYNCHSW);
|
||||
|
||||
/*
|
||||
* If oneshot mode is enabled, the flow will be disabled
|
||||
* when the TE0 IRQ arrives in the interrupt handler. Otherwise
|
||||
* updates are continuously streamed to the display after this
|
||||
* point.
|
||||
* Disable FIFO A flow again: since we are using TE sync we
|
||||
* need to wait for the FIFO to drain before we continue
|
||||
* so repeated calls to this function will not cause a mess
|
||||
* in the hardware by pushing updates will updates are going
|
||||
* on already.
|
||||
*/
|
||||
dev_dbg(mcde->dev, "sent TE0 framebuffer update\n");
|
||||
return;
|
||||
mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
|
||||
}
|
||||
|
||||
/* Trigger a software sync out on channel 0 */
|
||||
writel(MCDE_CHNLXSYNCHSW_SW_TRIG,
|
||||
mcde->regs + MCDE_CHNL0SYNCHSW);
|
||||
|
||||
/*
|
||||
* Disable FIFO A flow again: since we are using TE sync we
|
||||
* need to wait for the FIFO to drain before we continue
|
||||
* so repeated calls to this function will not cause a mess
|
||||
* in the hardware by pushing updates will updates are going
|
||||
* on already.
|
||||
*/
|
||||
mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
|
||||
|
||||
dev_dbg(mcde->dev, "sent SW framebuffer update\n");
|
||||
dev_dbg(mcde->dev, "started MCDE FIFO flow\n");
|
||||
}
|
||||
|
||||
static void mcde_set_extsrc(struct mcde *mcde, u32 buffer_address)
|
||||
|
@ -1060,15 +1180,13 @@ static void mcde_display_update(struct drm_simple_display_pipe *pipe,
|
|||
*/
|
||||
if (fb) {
|
||||
mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0));
|
||||
if (!mcde->video_mode) {
|
||||
/*
|
||||
* Send a single frame using software sync if the flow
|
||||
* is not active yet.
|
||||
*/
|
||||
if (mcde->flow_active == 0)
|
||||
mcde_display_send_one_frame(mcde);
|
||||
}
|
||||
dev_info_once(mcde->dev, "sent first display update\n");
|
||||
dev_info_once(mcde->dev, "first update of display contents\n");
|
||||
/*
|
||||
* Usually the flow is already active, unless we are in
|
||||
* oneshot mode, then we need to kick the flow right here.
|
||||
*/
|
||||
if (mcde->flow_active == 0)
|
||||
mcde_start_flow(mcde);
|
||||
} else {
|
||||
/*
|
||||
* If an update is receieved before the MCDE is enabled
|
||||
|
|
|
@ -9,6 +9,61 @@
|
|||
#ifndef _MCDE_DRM_H_
|
||||
#define _MCDE_DRM_H_
|
||||
|
||||
/* Shared basic registers */
|
||||
#define MCDE_CR 0x00000000
|
||||
#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_SHIFT 0
|
||||
#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_MASK 0x0000003F
|
||||
#define MCDE_CR_IFIFOCTRLEN BIT(15)
|
||||
#define MCDE_CR_UFRECOVERY_MODE_V422 BIT(16)
|
||||
#define MCDE_CR_WRAP_MODE_V422_SHIFT BIT(17)
|
||||
#define MCDE_CR_AUTOCLKG_EN BIT(30)
|
||||
#define MCDE_CR_MCDEEN BIT(31)
|
||||
|
||||
#define MCDE_CONF0 0x00000004
|
||||
#define MCDE_CONF0_SYNCMUX0 BIT(0)
|
||||
#define MCDE_CONF0_SYNCMUX1 BIT(1)
|
||||
#define MCDE_CONF0_SYNCMUX2 BIT(2)
|
||||
#define MCDE_CONF0_SYNCMUX3 BIT(3)
|
||||
#define MCDE_CONF0_SYNCMUX4 BIT(4)
|
||||
#define MCDE_CONF0_SYNCMUX5 BIT(5)
|
||||
#define MCDE_CONF0_SYNCMUX6 BIT(6)
|
||||
#define MCDE_CONF0_SYNCMUX7 BIT(7)
|
||||
#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12
|
||||
#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000
|
||||
#define MCDE_CONF0_OUTMUX0_SHIFT 16
|
||||
#define MCDE_CONF0_OUTMUX0_MASK 0x00070000
|
||||
#define MCDE_CONF0_OUTMUX1_SHIFT 19
|
||||
#define MCDE_CONF0_OUTMUX1_MASK 0x00380000
|
||||
#define MCDE_CONF0_OUTMUX2_SHIFT 22
|
||||
#define MCDE_CONF0_OUTMUX2_MASK 0x01C00000
|
||||
#define MCDE_CONF0_OUTMUX3_SHIFT 25
|
||||
#define MCDE_CONF0_OUTMUX3_MASK 0x0E000000
|
||||
#define MCDE_CONF0_OUTMUX4_SHIFT 28
|
||||
#define MCDE_CONF0_OUTMUX4_MASK 0x70000000
|
||||
|
||||
#define MCDE_SSP 0x00000008
|
||||
#define MCDE_AIS 0x00000100
|
||||
#define MCDE_IMSCERR 0x00000110
|
||||
#define MCDE_RISERR 0x00000120
|
||||
#define MCDE_MISERR 0x00000130
|
||||
#define MCDE_SISERR 0x00000140
|
||||
|
||||
enum mcde_flow_mode {
|
||||
/* One-shot mode: flow stops after one frame */
|
||||
MCDE_COMMAND_ONESHOT_FLOW,
|
||||
/* Command mode with tearing effect (TE) IRQ sync */
|
||||
MCDE_COMMAND_TE_FLOW,
|
||||
/*
|
||||
* Command mode with bus turn-around (BTA) and tearing effect
|
||||
* (TE) IRQ sync.
|
||||
*/
|
||||
MCDE_COMMAND_BTA_TE_FLOW,
|
||||
/* Video mode with tearing effect (TE) sync IRQ */
|
||||
MCDE_VIDEO_TE_FLOW,
|
||||
/* Video mode with the formatter itself as sync source */
|
||||
MCDE_VIDEO_FORMATTER_FLOW,
|
||||
};
|
||||
|
||||
struct mcde {
|
||||
struct drm_device drm;
|
||||
struct device *dev;
|
||||
|
@ -18,9 +73,7 @@ struct mcde {
|
|||
struct drm_simple_display_pipe pipe;
|
||||
struct mipi_dsi_device *mdsi;
|
||||
s16 stride;
|
||||
bool te_sync;
|
||||
bool video_mode;
|
||||
bool oneshot_mode;
|
||||
enum mcde_flow_mode flow_mode;
|
||||
unsigned int flow_active;
|
||||
spinlock_t flow_lock; /* Locks the channel flow control */
|
||||
|
||||
|
@ -36,8 +89,16 @@ struct mcde {
|
|||
|
||||
#define to_mcde(dev) container_of(dev, struct mcde, drm)
|
||||
|
||||
static inline bool mcde_flow_is_video(struct mcde *mcde)
|
||||
{
|
||||
return (mcde->flow_mode == MCDE_VIDEO_TE_FLOW ||
|
||||
mcde->flow_mode == MCDE_VIDEO_FORMATTER_FLOW);
|
||||
}
|
||||
|
||||
bool mcde_dsi_irq(struct mipi_dsi_device *mdsi);
|
||||
void mcde_dsi_te_request(struct mipi_dsi_device *mdsi);
|
||||
void mcde_dsi_enable(struct drm_bridge *bridge);
|
||||
void mcde_dsi_disable(struct drm_bridge *bridge);
|
||||
extern struct platform_driver mcde_dsi_driver;
|
||||
|
||||
void mcde_display_irq(struct mcde *mcde);
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
|
@ -82,44 +83,6 @@
|
|||
|
||||
#define DRIVER_DESC "DRM module for MCDE"
|
||||
|
||||
#define MCDE_CR 0x00000000
|
||||
#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_SHIFT 0
|
||||
#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_MASK 0x0000003F
|
||||
#define MCDE_CR_IFIFOCTRLEN BIT(15)
|
||||
#define MCDE_CR_UFRECOVERY_MODE_V422 BIT(16)
|
||||
#define MCDE_CR_WRAP_MODE_V422_SHIFT BIT(17)
|
||||
#define MCDE_CR_AUTOCLKG_EN BIT(30)
|
||||
#define MCDE_CR_MCDEEN BIT(31)
|
||||
|
||||
#define MCDE_CONF0 0x00000004
|
||||
#define MCDE_CONF0_SYNCMUX0 BIT(0)
|
||||
#define MCDE_CONF0_SYNCMUX1 BIT(1)
|
||||
#define MCDE_CONF0_SYNCMUX2 BIT(2)
|
||||
#define MCDE_CONF0_SYNCMUX3 BIT(3)
|
||||
#define MCDE_CONF0_SYNCMUX4 BIT(4)
|
||||
#define MCDE_CONF0_SYNCMUX5 BIT(5)
|
||||
#define MCDE_CONF0_SYNCMUX6 BIT(6)
|
||||
#define MCDE_CONF0_SYNCMUX7 BIT(7)
|
||||
#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12
|
||||
#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000
|
||||
#define MCDE_CONF0_OUTMUX0_SHIFT 16
|
||||
#define MCDE_CONF0_OUTMUX0_MASK 0x00070000
|
||||
#define MCDE_CONF0_OUTMUX1_SHIFT 19
|
||||
#define MCDE_CONF0_OUTMUX1_MASK 0x00380000
|
||||
#define MCDE_CONF0_OUTMUX2_SHIFT 22
|
||||
#define MCDE_CONF0_OUTMUX2_MASK 0x01C00000
|
||||
#define MCDE_CONF0_OUTMUX3_SHIFT 25
|
||||
#define MCDE_CONF0_OUTMUX3_MASK 0x0E000000
|
||||
#define MCDE_CONF0_OUTMUX4_SHIFT 28
|
||||
#define MCDE_CONF0_OUTMUX4_MASK 0x70000000
|
||||
|
||||
#define MCDE_SSP 0x00000008
|
||||
#define MCDE_AIS 0x00000100
|
||||
#define MCDE_IMSCERR 0x00000110
|
||||
#define MCDE_RISERR 0x00000120
|
||||
#define MCDE_MISERR 0x00000130
|
||||
#define MCDE_SISERR 0x00000140
|
||||
|
||||
#define MCDE_PID 0x000001FC
|
||||
#define MCDE_PID_METALFIX_VERSION_SHIFT 0
|
||||
#define MCDE_PID_METALFIX_VERSION_MASK 0x000000FF
|
||||
|
@ -293,7 +256,6 @@ static int mcde_probe(struct platform_device *pdev)
|
|||
struct component_match *match = NULL;
|
||||
struct resource *res;
|
||||
u32 pid;
|
||||
u32 val;
|
||||
int irq;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -305,9 +267,6 @@ static int mcde_probe(struct platform_device *pdev)
|
|||
mcde->dev = dev;
|
||||
platform_set_drvdata(pdev, drm);
|
||||
|
||||
/* Enable continuous updates: this is what Linux' framebuffer expects */
|
||||
mcde->oneshot_mode = false;
|
||||
|
||||
/* First obtain and turn on the main power */
|
||||
mcde->epod = devm_regulator_get(dev, "epod");
|
||||
if (IS_ERR(mcde->epod)) {
|
||||
|
@ -405,27 +364,7 @@ static int mcde_probe(struct platform_device *pdev)
|
|||
goto clk_disable;
|
||||
}
|
||||
|
||||
/* Set up the main control, watermark level at 7 */
|
||||
val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT;
|
||||
/* 24 bits DPI: connect LSB Ch B to D[0:7] */
|
||||
val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT;
|
||||
/* TV out: connect LSB Ch B to D[8:15] */
|
||||
val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT;
|
||||
/* Don't care about this muxing */
|
||||
val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT;
|
||||
/* 24 bits DPI: connect MID Ch B to D[24:31] */
|
||||
val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT;
|
||||
/* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */
|
||||
val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT;
|
||||
/* Syncmux bits zero: DPI channel A and B on output pins A and B resp */
|
||||
writel(val, mcde->regs + MCDE_CONF0);
|
||||
|
||||
/* Enable automatic clock gating */
|
||||
val = readl(mcde->regs + MCDE_CR);
|
||||
val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
|
||||
writel(val, mcde->regs + MCDE_CR);
|
||||
|
||||
/* Clear any pending interrupts */
|
||||
/* Disable and clear any pending interrupts */
|
||||
mcde_display_disable_irqs(mcde);
|
||||
writel(0, mcde->regs + MCDE_IMSCERR);
|
||||
writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
|
||||
|
@ -455,12 +394,28 @@ static int mcde_probe(struct platform_device *pdev)
|
|||
ret = PTR_ERR(match);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform an invasive reset of the MCDE and all blocks by
|
||||
* cutting the power to the subsystem, then bring it back up
|
||||
* later when we enable the display as a result of
|
||||
* component_master_add_with_match().
|
||||
*/
|
||||
ret = regulator_disable(mcde->epod);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't disable EPOD regulator\n");
|
||||
return ret;
|
||||
}
|
||||
/* Wait 50 ms so we are sure we cut the power */
|
||||
usleep_range(50000, 70000);
|
||||
|
||||
ret = component_master_add_with_match(&pdev->dev, &mcde_drm_comp_ops,
|
||||
match);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add component master\n");
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
clk_disable:
|
||||
|
|
|
@ -43,6 +43,7 @@ struct mcde_dsi {
|
|||
struct drm_bridge *bridge_out;
|
||||
struct mipi_dsi_host dsi_host;
|
||||
struct mipi_dsi_device *mdsi;
|
||||
const struct drm_display_mode *mode;
|
||||
struct clk *hs_clk;
|
||||
struct clk *lp_clk;
|
||||
unsigned long hs_freq;
|
||||
|
@ -148,9 +149,22 @@ static void mcde_dsi_attach_to_mcde(struct mcde_dsi *d)
|
|||
{
|
||||
d->mcde->mdsi = d->mdsi;
|
||||
|
||||
d->mcde->video_mode = !!(d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO);
|
||||
/* Enable use of the TE signal for all command mode panels */
|
||||
d->mcde->te_sync = !d->mcde->video_mode;
|
||||
/*
|
||||
* Select the way the DSI data flow is pushing to the display:
|
||||
* currently we just support video or command mode depending
|
||||
* on the type of display. Video mode defaults to using the
|
||||
* formatter itself for synchronization (stateless video panel).
|
||||
*
|
||||
* FIXME: add flags to struct mipi_dsi_device .flags to indicate
|
||||
* displays that require BTA (bus turn around) so we can handle
|
||||
* such displays as well. Figure out how to properly handle
|
||||
* single frame on-demand updates with DRM for command mode
|
||||
* displays (MCDE_COMMAND_ONESHOT_FLOW).
|
||||
*/
|
||||
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)
|
||||
d->mcde->flow_mode = MCDE_VIDEO_FORMATTER_FLOW;
|
||||
else
|
||||
d->mcde->flow_mode = MCDE_COMMAND_TE_FLOW;
|
||||
}
|
||||
|
||||
static int mcde_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
|
@ -194,15 +208,97 @@ static int mcde_dsi_host_detach(struct mipi_dsi_host *host,
|
|||
(type == MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM) || \
|
||||
(type == MIPI_DSI_DCS_READ))
|
||||
|
||||
static int mcde_dsi_execute_transfer(struct mcde_dsi *d,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
const u32 loop_delay_us = 10; /* us */
|
||||
u32 loop_counter;
|
||||
size_t txlen = msg->tx_len;
|
||||
size_t rxlen = msg->rx_len;
|
||||
int i;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
|
||||
writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
|
||||
/* Send command */
|
||||
writel(1, d->regs + DSI_DIRECT_CMD_SEND);
|
||||
|
||||
loop_counter = 1000 * 1000 / loop_delay_us;
|
||||
if (MCDE_DSI_HOST_IS_READ(msg->type)) {
|
||||
/* Read command */
|
||||
while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
|
||||
(DSI_DIRECT_CMD_STS_READ_COMPLETED |
|
||||
DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR))
|
||||
&& --loop_counter)
|
||||
usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
|
||||
if (!loop_counter) {
|
||||
dev_err(d->dev, "DSI read timeout!\n");
|
||||
/* Set exit code and retry */
|
||||
return -ETIME;
|
||||
}
|
||||
} else {
|
||||
/* Writing only */
|
||||
while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
|
||||
DSI_DIRECT_CMD_STS_WRITE_COMPLETED)
|
||||
&& --loop_counter)
|
||||
usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
|
||||
|
||||
if (!loop_counter) {
|
||||
/* Set exit code and retry */
|
||||
dev_err(d->dev, "DSI write timeout!\n");
|
||||
return -ETIME;
|
||||
}
|
||||
}
|
||||
|
||||
val = readl(d->regs + DSI_DIRECT_CMD_STS);
|
||||
if (val & DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR) {
|
||||
dev_err(d->dev, "read completed with error\n");
|
||||
writel(1, d->regs + DSI_DIRECT_CMD_RD_INIT);
|
||||
return -EIO;
|
||||
}
|
||||
if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) {
|
||||
val >>= DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT;
|
||||
dev_err(d->dev, "error during transmission: %04x\n",
|
||||
val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!MCDE_DSI_HOST_IS_READ(msg->type)) {
|
||||
/* Return number of bytes written */
|
||||
ret = txlen;
|
||||
} else {
|
||||
/* OK this is a read command, get the response */
|
||||
u32 rdsz;
|
||||
u32 rddat;
|
||||
u8 *rx = msg->rx_buf;
|
||||
|
||||
rdsz = readl(d->regs + DSI_DIRECT_CMD_RD_PROPERTY);
|
||||
rdsz &= DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK;
|
||||
rddat = readl(d->regs + DSI_DIRECT_CMD_RDDAT);
|
||||
if (rdsz < rxlen) {
|
||||
dev_err(d->dev, "read error, requested %zd got %d\n",
|
||||
rxlen, rdsz);
|
||||
return -EIO;
|
||||
}
|
||||
/* FIXME: read more than 4 bytes */
|
||||
for (i = 0; i < 4 && i < rxlen; i++)
|
||||
rx[i] = (rddat >> (i * 8)) & 0xff;
|
||||
ret = rdsz;
|
||||
}
|
||||
|
||||
/* Successful transmission */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
struct mcde_dsi *d = host_to_mcde_dsi(host);
|
||||
const u32 loop_delay_us = 10; /* us */
|
||||
const u8 *tx = msg->tx_buf;
|
||||
u32 loop_counter;
|
||||
size_t txlen = msg->tx_len;
|
||||
size_t rxlen = msg->rx_len;
|
||||
unsigned int retries = 0;
|
||||
u32 val;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -268,72 +364,16 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
|
|||
writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3);
|
||||
}
|
||||
|
||||
writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
|
||||
writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
|
||||
/* Send command */
|
||||
writel(1, d->regs + DSI_DIRECT_CMD_SEND);
|
||||
|
||||
loop_counter = 1000 * 1000 / loop_delay_us;
|
||||
if (MCDE_DSI_HOST_IS_READ(msg->type)) {
|
||||
/* Read command */
|
||||
while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
|
||||
(DSI_DIRECT_CMD_STS_READ_COMPLETED |
|
||||
DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR))
|
||||
&& --loop_counter)
|
||||
usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
|
||||
if (!loop_counter) {
|
||||
dev_err(d->dev, "DSI read timeout!\n");
|
||||
return -ETIME;
|
||||
}
|
||||
} else {
|
||||
/* Writing only */
|
||||
while (!(readl(d->regs + DSI_DIRECT_CMD_STS) &
|
||||
DSI_DIRECT_CMD_STS_WRITE_COMPLETED)
|
||||
&& --loop_counter)
|
||||
usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
|
||||
|
||||
if (!loop_counter) {
|
||||
dev_err(d->dev, "DSI write timeout!\n");
|
||||
return -ETIME;
|
||||
}
|
||||
}
|
||||
|
||||
val = readl(d->regs + DSI_DIRECT_CMD_STS);
|
||||
if (val & DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR) {
|
||||
dev_err(d->dev, "read completed with error\n");
|
||||
writel(1, d->regs + DSI_DIRECT_CMD_RD_INIT);
|
||||
return -EIO;
|
||||
}
|
||||
if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) {
|
||||
val >>= DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT;
|
||||
dev_err(d->dev, "error during transmission: %04x\n",
|
||||
val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!MCDE_DSI_HOST_IS_READ(msg->type)) {
|
||||
/* Return number of bytes written */
|
||||
ret = txlen;
|
||||
} else {
|
||||
/* OK this is a read command, get the response */
|
||||
u32 rdsz;
|
||||
u32 rddat;
|
||||
u8 *rx = msg->rx_buf;
|
||||
|
||||
rdsz = readl(d->regs + DSI_DIRECT_CMD_RD_PROPERTY);
|
||||
rdsz &= DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK;
|
||||
rddat = readl(d->regs + DSI_DIRECT_CMD_RDDAT);
|
||||
if (rdsz < rxlen) {
|
||||
dev_err(d->dev, "read error, requested %zd got %d\n",
|
||||
rxlen, rdsz);
|
||||
return -EIO;
|
||||
}
|
||||
/* FIXME: read more than 4 bytes */
|
||||
for (i = 0; i < 4 && i < rxlen; i++)
|
||||
rx[i] = (rddat >> (i * 8)) & 0xff;
|
||||
ret = rdsz;
|
||||
while (retries < 3) {
|
||||
ret = mcde_dsi_execute_transfer(d, msg);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
retries++;
|
||||
}
|
||||
if (ret < 0 && retries)
|
||||
dev_err(d->dev, "gave up after %d retries\n", retries);
|
||||
|
||||
/* Clear any errors */
|
||||
writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
|
||||
writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
|
||||
|
||||
|
@ -799,10 +839,11 @@ static void mcde_dsi_start(struct mcde_dsi *d)
|
|||
/* Command mode, clear IF1 ID */
|
||||
val = readl(d->regs + DSI_CMD_MODE_CTL);
|
||||
/*
|
||||
* If we enable low-power mode here, with
|
||||
* val |= DSI_CMD_MODE_CTL_IF1_LP_EN
|
||||
* If we enable low-power mode here,
|
||||
* then display updates become really slow.
|
||||
*/
|
||||
if (d->mdsi->mode_flags & MIPI_DSI_MODE_LPM)
|
||||
val |= DSI_CMD_MODE_CTL_IF1_LP_EN;
|
||||
val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
|
||||
writel(val, d->regs + DSI_CMD_MODE_CTL);
|
||||
|
||||
|
@ -811,23 +852,11 @@ static void mcde_dsi_start(struct mcde_dsi *d)
|
|||
dev_info(d->dev, "DSI link enabled\n");
|
||||
}
|
||||
|
||||
|
||||
static void mcde_dsi_bridge_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
|
||||
u32 val;
|
||||
|
||||
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
|
||||
/* Enable video mode */
|
||||
val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
|
||||
val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
|
||||
writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
|
||||
}
|
||||
|
||||
dev_info(d->dev, "enable DSI master\n");
|
||||
};
|
||||
|
||||
static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
|
||||
/*
|
||||
* Notice that this is called from inside the display controller
|
||||
* and not from the bridge callbacks.
|
||||
*/
|
||||
void mcde_dsi_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
|
||||
unsigned long hs_freq, lp_freq;
|
||||
|
@ -871,7 +900,25 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
|
|||
dev_info(d->dev, "DSI HS clock rate %lu Hz\n",
|
||||
d->hs_freq);
|
||||
|
||||
/* Assert RESET through the PRCMU, active low */
|
||||
/* FIXME: which DSI block? */
|
||||
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
|
||||
PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
|
||||
|
||||
usleep_range(100, 200);
|
||||
|
||||
/* De-assert RESET again */
|
||||
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
|
||||
PRCM_DSI_SW_RESET_DSI0_SW_RESETN,
|
||||
PRCM_DSI_SW_RESET_DSI0_SW_RESETN);
|
||||
|
||||
/* Start up the hardware */
|
||||
mcde_dsi_start(d);
|
||||
|
||||
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
|
||||
/* Set up the video mode from the DRM mode */
|
||||
mcde_dsi_setup_video_mode(d, d->mode);
|
||||
|
||||
/* Put IF1 into video mode */
|
||||
val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
|
||||
val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE;
|
||||
|
@ -887,17 +934,25 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
|
|||
val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC;
|
||||
val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA;
|
||||
writel(val, d->regs + DSI_VID_MODE_STS_CTL);
|
||||
|
||||
/* Enable video mode */
|
||||
val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
|
||||
val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
|
||||
writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
|
||||
} else {
|
||||
/* Command mode, clear IF1 ID */
|
||||
val = readl(d->regs + DSI_CMD_MODE_CTL);
|
||||
/*
|
||||
* If we enable low-power mode here with
|
||||
* val |= DSI_CMD_MODE_CTL_IF1_LP_EN
|
||||
* If we enable low-power mode here
|
||||
* the display updates become really slow.
|
||||
*/
|
||||
if (d->mdsi->mode_flags & MIPI_DSI_MODE_LPM)
|
||||
val |= DSI_CMD_MODE_CTL_IF1_LP_EN;
|
||||
val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
|
||||
writel(val, d->regs + DSI_CMD_MODE_CTL);
|
||||
}
|
||||
|
||||
dev_info(d->dev, "enabled MCDE DSI master\n");
|
||||
}
|
||||
|
||||
static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
|
@ -911,13 +966,12 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
|
|||
return;
|
||||
}
|
||||
|
||||
d->mode = mode;
|
||||
|
||||
dev_info(d->dev, "set DSI master to %dx%d %u Hz %s mode\n",
|
||||
mode->hdisplay, mode->vdisplay, mode->clock * 1000,
|
||||
(d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD"
|
||||
);
|
||||
|
||||
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)
|
||||
mcde_dsi_setup_video_mode(d, mode);
|
||||
}
|
||||
|
||||
static void mcde_dsi_wait_for_command_mode_stop(struct mcde_dsi *d)
|
||||
|
@ -961,14 +1015,15 @@ static void mcde_dsi_wait_for_video_mode_stop(struct mcde_dsi *d)
|
|||
}
|
||||
}
|
||||
|
||||
static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
|
||||
/*
|
||||
* Notice that this is called from inside the display controller
|
||||
* and not from the bridge callbacks.
|
||||
*/
|
||||
void mcde_dsi_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
|
||||
u32 val;
|
||||
|
||||
/* Disable all error interrupts */
|
||||
writel(0, d->regs + DSI_VID_MODE_STS_CTL);
|
||||
|
||||
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
|
||||
/* Stop video mode */
|
||||
val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
|
||||
|
@ -980,7 +1035,14 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
|
|||
mcde_dsi_wait_for_command_mode_stop(d);
|
||||
}
|
||||
|
||||
/* Stop clocks */
|
||||
/*
|
||||
* Stop clocks and terminate any DSI traffic here so the panel can
|
||||
* send commands to shut down the display using DSI direct write until
|
||||
* this point.
|
||||
*/
|
||||
|
||||
/* Disable all error interrupts */
|
||||
writel(0, d->regs + DSI_VID_MODE_STS_CTL);
|
||||
clk_disable_unprepare(d->hs_clk);
|
||||
clk_disable_unprepare(d->lp_clk);
|
||||
}
|
||||
|
@ -1010,9 +1072,6 @@ static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
|
|||
static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
|
||||
.attach = mcde_dsi_bridge_attach,
|
||||
.mode_set = mcde_dsi_bridge_mode_set,
|
||||
.disable = mcde_dsi_bridge_disable,
|
||||
.enable = mcde_dsi_bridge_enable,
|
||||
.pre_enable = mcde_dsi_bridge_pre_enable,
|
||||
};
|
||||
|
||||
static int mcde_dsi_bind(struct device *dev, struct device *master,
|
||||
|
@ -1048,21 +1107,6 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
|
|||
return PTR_ERR(d->lp_clk);
|
||||
}
|
||||
|
||||
/* Assert RESET through the PRCMU, active low */
|
||||
/* FIXME: which DSI block? */
|
||||
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
|
||||
PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
|
||||
|
||||
usleep_range(100, 200);
|
||||
|
||||
/* De-assert RESET again */
|
||||
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
|
||||
PRCM_DSI_SW_RESET_DSI0_SW_RESETN,
|
||||
PRCM_DSI_SW_RESET_DSI0_SW_RESETN);
|
||||
|
||||
/* Start up the hardware */
|
||||
mcde_dsi_start(d);
|
||||
|
||||
/* Look for a panel as a child to this node */
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
panel = of_drm_find_panel(child);
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config DRM_MGAG200
|
||||
tristate "Kernel modesetting driver for MGA G200 server engines"
|
||||
tristate "Matrox G200"
|
||||
depends on DRM && PCI && MMU
|
||||
select DRM_GEM_SHMEM_HELPER
|
||||
select DRM_KMS_HELPER
|
||||
help
|
||||
This is a KMS driver for the MGA G200 server chips, it
|
||||
does not support the original MGA G200 or any of the desktop
|
||||
chips. It requires 0.3.0 of the modesetting userspace driver,
|
||||
and a version of mga driver that will fail on KMS enabled
|
||||
devices.
|
||||
|
||||
This is a KMS driver for Matrox G200 chips. It supports the original
|
||||
MGA G200 desktop chips and the server variants. It requires 0.3.0
|
||||
of the modesetting userspace driver, and a version of mga driver
|
||||
that will fail on KMS enabled devices.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/console.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_file.h>
|
||||
|
@ -36,6 +37,7 @@ static struct drm_driver mgag200_driver = {
|
|||
.major = DRIVER_MAJOR,
|
||||
.minor = DRIVER_MINOR,
|
||||
.patchlevel = DRIVER_PATCHLEVEL,
|
||||
.gem_create_object = drm_gem_shmem_create_object_cached,
|
||||
DRM_GEM_SHMEM_DRIVER_OPS,
|
||||
};
|
||||
|
||||
|
@ -43,18 +45,66 @@ static struct drm_driver mgag200_driver = {
|
|||
* DRM device
|
||||
*/
|
||||
|
||||
static int mgag200_device_init(struct mga_device *mdev, unsigned long flags)
|
||||
static bool mgag200_has_sgram(struct mga_device *mdev)
|
||||
{
|
||||
struct drm_device *dev = &mdev->base;
|
||||
int ret, option;
|
||||
u32 option;
|
||||
int ret;
|
||||
|
||||
mdev->flags = mgag200_flags_from_driver_data(flags);
|
||||
mdev->type = mgag200_type_from_driver_data(flags);
|
||||
ret = pci_read_config_dword(dev->pdev, PCI_MGA_OPTION, &option);
|
||||
if (drm_WARN(dev, ret, "failed to read PCI config dword: %d\n", ret))
|
||||
return false;
|
||||
|
||||
pci_read_config_dword(dev->pdev, PCI_MGA_OPTION, &option);
|
||||
mdev->has_sdram = !(option & (1 << 14));
|
||||
return !!(option & PCI_MGA_OPTION_HARDPWMSK);
|
||||
}
|
||||
|
||||
/* BAR 0 is the framebuffer, BAR 1 contains registers */
|
||||
static int mgag200_regs_init(struct mga_device *mdev)
|
||||
{
|
||||
struct drm_device *dev = &mdev->base;
|
||||
u32 option, option2;
|
||||
u8 crtcext3;
|
||||
|
||||
switch (mdev->type) {
|
||||
case G200_PCI:
|
||||
case G200_AGP:
|
||||
if (mgag200_has_sgram(mdev))
|
||||
option = 0x4049cd21;
|
||||
else
|
||||
option = 0x40499121;
|
||||
option2 = 0x00008000;
|
||||
break;
|
||||
case G200_SE_A:
|
||||
case G200_SE_B:
|
||||
option = 0x40049120;
|
||||
if (mgag200_has_sgram(mdev))
|
||||
option |= PCI_MGA_OPTION_HARDPWMSK;
|
||||
option2 = 0x00008000;
|
||||
break;
|
||||
case G200_WB:
|
||||
case G200_EW3:
|
||||
option = 0x41049120;
|
||||
option2 = 0x0000b000;
|
||||
break;
|
||||
case G200_EV:
|
||||
option = 0x00000120;
|
||||
option2 = 0x0000b000;
|
||||
break;
|
||||
case G200_EH:
|
||||
case G200_EH3:
|
||||
option = 0x00000120;
|
||||
option2 = 0x0000b000;
|
||||
break;
|
||||
default:
|
||||
option = 0;
|
||||
option2 = 0;
|
||||
}
|
||||
|
||||
if (option)
|
||||
pci_write_config_dword(dev->pdev, PCI_MGA_OPTION, option);
|
||||
if (option2)
|
||||
pci_write_config_dword(dev->pdev, PCI_MGA_OPTION2, option2);
|
||||
|
||||
/* BAR 1 contains registers */
|
||||
mdev->rmmio_base = pci_resource_start(dev->pdev, 1);
|
||||
mdev->rmmio_size = pci_resource_len(dev->pdev, 1);
|
||||
|
||||
|
@ -68,13 +118,164 @@ static int mgag200_device_init(struct mga_device *mdev, unsigned long flags)
|
|||
if (mdev->rmmio == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* stash G200 SE model number for later use */
|
||||
if (IS_G200_SE(mdev)) {
|
||||
mdev->unique_rev_id = RREG32(0x1e24);
|
||||
drm_dbg(dev, "G200 SE unique revision id is 0x%x\n",
|
||||
mdev->unique_rev_id);
|
||||
RREG_ECRT(0x03, crtcext3);
|
||||
crtcext3 |= MGAREG_CRTCEXT3_MGAMODE;
|
||||
WREG_ECRT(0x03, crtcext3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mgag200_g200_interpret_bios(struct mga_device *mdev,
|
||||
const unsigned char *bios,
|
||||
size_t size)
|
||||
{
|
||||
static const char matrox[] = {'M', 'A', 'T', 'R', 'O', 'X'};
|
||||
static const unsigned int expected_length[6] = {
|
||||
0, 64, 64, 64, 128, 128
|
||||
};
|
||||
struct drm_device *dev = &mdev->base;
|
||||
const unsigned char *pins;
|
||||
unsigned int pins_len, version;
|
||||
int offset;
|
||||
int tmp;
|
||||
|
||||
/* Test for MATROX string. */
|
||||
if (size < 45 + sizeof(matrox))
|
||||
return;
|
||||
if (memcmp(&bios[45], matrox, sizeof(matrox)) != 0)
|
||||
return;
|
||||
|
||||
/* Get the PInS offset. */
|
||||
if (size < MGA_BIOS_OFFSET + 2)
|
||||
return;
|
||||
offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET];
|
||||
|
||||
/* Get PInS data structure. */
|
||||
|
||||
if (size < offset + 6)
|
||||
return;
|
||||
pins = bios + offset;
|
||||
if (pins[0] == 0x2e && pins[1] == 0x41) {
|
||||
version = pins[5];
|
||||
pins_len = pins[2];
|
||||
} else {
|
||||
version = 1;
|
||||
pins_len = pins[0] + (pins[1] << 8);
|
||||
}
|
||||
|
||||
if (version < 1 || version > 5) {
|
||||
drm_warn(dev, "Unknown BIOS PInS version: %d\n", version);
|
||||
return;
|
||||
}
|
||||
if (pins_len != expected_length[version]) {
|
||||
drm_warn(dev, "Unexpected BIOS PInS size: %d expected: %d\n",
|
||||
pins_len, expected_length[version]);
|
||||
return;
|
||||
}
|
||||
if (size < offset + pins_len)
|
||||
return;
|
||||
|
||||
drm_dbg_kms(dev, "MATROX BIOS PInS version %d size: %d found\n",
|
||||
version, pins_len);
|
||||
|
||||
/* Extract the clock values */
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
tmp = pins[24] + (pins[25] << 8);
|
||||
if (tmp)
|
||||
mdev->model.g200.pclk_max = tmp * 10;
|
||||
break;
|
||||
case 2:
|
||||
if (pins[41] != 0xff)
|
||||
mdev->model.g200.pclk_max = (pins[41] + 100) * 1000;
|
||||
break;
|
||||
case 3:
|
||||
if (pins[36] != 0xff)
|
||||
mdev->model.g200.pclk_max = (pins[36] + 100) * 1000;
|
||||
if (pins[52] & 0x20)
|
||||
mdev->model.g200.ref_clk = 14318;
|
||||
break;
|
||||
case 4:
|
||||
if (pins[39] != 0xff)
|
||||
mdev->model.g200.pclk_max = pins[39] * 4 * 1000;
|
||||
if (pins[92] & 0x01)
|
||||
mdev->model.g200.ref_clk = 14318;
|
||||
break;
|
||||
case 5:
|
||||
tmp = pins[4] ? 8000 : 6000;
|
||||
if (pins[123] != 0xff)
|
||||
mdev->model.g200.pclk_min = pins[123] * tmp;
|
||||
if (pins[38] != 0xff)
|
||||
mdev->model.g200.pclk_max = pins[38] * tmp;
|
||||
if (pins[110] & 0x01)
|
||||
mdev->model.g200.ref_clk = 14318;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mgag200_g200_init_refclk(struct mga_device *mdev)
|
||||
{
|
||||
struct drm_device *dev = &mdev->base;
|
||||
unsigned char __iomem *rom;
|
||||
unsigned char *bios;
|
||||
size_t size;
|
||||
|
||||
mdev->model.g200.pclk_min = 50000;
|
||||
mdev->model.g200.pclk_max = 230000;
|
||||
mdev->model.g200.ref_clk = 27050;
|
||||
|
||||
rom = pci_map_rom(dev->pdev, &size);
|
||||
if (!rom)
|
||||
return;
|
||||
|
||||
bios = vmalloc(size);
|
||||
if (!bios)
|
||||
goto out;
|
||||
memcpy_fromio(bios, rom, size);
|
||||
|
||||
if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa)
|
||||
mgag200_g200_interpret_bios(mdev, bios, size);
|
||||
|
||||
drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n",
|
||||
mdev->model.g200.pclk_min, mdev->model.g200.pclk_max,
|
||||
mdev->model.g200.ref_clk);
|
||||
|
||||
vfree(bios);
|
||||
out:
|
||||
pci_unmap_rom(dev->pdev, rom);
|
||||
}
|
||||
|
||||
static void mgag200_g200se_init_unique_id(struct mga_device *mdev)
|
||||
{
|
||||
struct drm_device *dev = &mdev->base;
|
||||
|
||||
/* stash G200 SE model number for later use */
|
||||
mdev->model.g200se.unique_rev_id = RREG32(0x1e24);
|
||||
|
||||
drm_dbg(dev, "G200 SE unique revision id is 0x%x\n",
|
||||
mdev->model.g200se.unique_rev_id);
|
||||
}
|
||||
|
||||
static int mgag200_device_init(struct mga_device *mdev, unsigned long flags)
|
||||
{
|
||||
struct drm_device *dev = &mdev->base;
|
||||
int ret;
|
||||
|
||||
mdev->flags = mgag200_flags_from_driver_data(flags);
|
||||
mdev->type = mgag200_type_from_driver_data(flags);
|
||||
|
||||
ret = mgag200_regs_init(mdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mdev->type == G200_PCI || mdev->type == G200_AGP)
|
||||
mgag200_g200_init_refclk(mdev);
|
||||
else if (IS_G200_SE(mdev))
|
||||
mgag200_g200se_init_unique_id(mdev);
|
||||
|
||||
ret = mgag200_mm_init(mdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -116,6 +317,8 @@ mgag200_device_create(struct pci_dev *pdev, unsigned long flags)
|
|||
*/
|
||||
|
||||
static const struct pci_device_id mgag200_pciidlist[] = {
|
||||
{ PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI },
|
||||
{ PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP },
|
||||
{ PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
G200_SE_A | MGAG200_FLAG_HW_BUG_NO_STARTADD},
|
||||
{ PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#define RREG32(reg) ioread32(((void __iomem *)mdev->rmmio) + (reg))
|
||||
#define WREG32(reg, v) iowrite32(v, ((void __iomem *)mdev->rmmio) + (reg))
|
||||
|
||||
#define MGA_BIOS_OFFSET 0x7ffc
|
||||
|
||||
#define ATTR_INDEX 0x1fc0
|
||||
#define ATTR_DATA 0x1fc1
|
||||
|
||||
|
@ -129,6 +131,8 @@ struct mga_mc {
|
|||
};
|
||||
|
||||
enum mga_type {
|
||||
G200_PCI,
|
||||
G200_AGP,
|
||||
G200_SE_A,
|
||||
G200_SE_B,
|
||||
G200_WB,
|
||||
|
@ -161,14 +165,23 @@ struct mga_device {
|
|||
size_t vram_fb_available;
|
||||
|
||||
enum mga_type type;
|
||||
int has_sdram;
|
||||
|
||||
int bpp_shifts[4];
|
||||
|
||||
int fb_mtrr;
|
||||
|
||||
/* SE model number stored in reg 0x1e24 */
|
||||
u32 unique_rev_id;
|
||||
union {
|
||||
struct {
|
||||
long ref_clk;
|
||||
long pclk_min;
|
||||
long pclk_max;
|
||||
} g200;
|
||||
struct {
|
||||
/* SE model number stored in reg 0x1e24 */
|
||||
u32 unique_rev_id;
|
||||
} g200se;
|
||||
} model;
|
||||
|
||||
|
||||
struct mga_connector connector;
|
||||
struct drm_simple_display_pipe display_pipe;
|
||||
|
|
|
@ -90,9 +90,17 @@ static void mgag200_mm_release(struct drm_device *dev, void *ptr)
|
|||
int mgag200_mm_init(struct mga_device *mdev)
|
||||
{
|
||||
struct drm_device *dev = &mdev->base;
|
||||
u8 misc;
|
||||
resource_size_t start, len;
|
||||
int ret;
|
||||
|
||||
WREG_ECRT(0x04, 0x00);
|
||||
|
||||
misc = RREG8(MGA_MISC_IN);
|
||||
misc |= MGAREG_MISC_RAMMAPEN |
|
||||
MGAREG_MISC_HIGH_PG_SEL;
|
||||
WREG8(MGA_MISC_OUT, misc);
|
||||
|
||||
/* BAR 0 is VRAM */
|
||||
start = pci_resource_start(dev->pdev, 0);
|
||||
len = pci_resource_len(dev->pdev, 0);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_atomic_state_helper.h>
|
||||
|
@ -109,10 +108,82 @@ static inline void mga_wait_busy(struct mga_device *mdev)
|
|||
} while ((status & 0x01) && time_before(jiffies, timeout));
|
||||
}
|
||||
|
||||
/*
|
||||
* PLL setup
|
||||
*/
|
||||
|
||||
static int mgag200_g200_set_plls(struct mga_device *mdev, long clock)
|
||||
{
|
||||
struct drm_device *dev = &mdev->base;
|
||||
const int post_div_max = 7;
|
||||
const int in_div_min = 1;
|
||||
const int in_div_max = 6;
|
||||
const int feed_div_min = 7;
|
||||
const int feed_div_max = 127;
|
||||
u8 testm, testn;
|
||||
u8 n = 0, m = 0, p, s;
|
||||
long f_vco;
|
||||
long computed;
|
||||
long delta, tmp_delta;
|
||||
long ref_clk = mdev->model.g200.ref_clk;
|
||||
long p_clk_min = mdev->model.g200.pclk_min;
|
||||
long p_clk_max = mdev->model.g200.pclk_max;
|
||||
|
||||
if (clock > p_clk_max) {
|
||||
drm_err(dev, "Pixel Clock %ld too high\n", clock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (clock < p_clk_min >> 3)
|
||||
clock = p_clk_min >> 3;
|
||||
|
||||
f_vco = clock;
|
||||
for (p = 0;
|
||||
p <= post_div_max && f_vco < p_clk_min;
|
||||
p = (p << 1) + 1, f_vco <<= 1)
|
||||
;
|
||||
|
||||
delta = clock;
|
||||
|
||||
for (testm = in_div_min; testm <= in_div_max; testm++) {
|
||||
for (testn = feed_div_min; testn <= feed_div_max; testn++) {
|
||||
computed = ref_clk * (testn + 1) / (testm + 1);
|
||||
if (computed < f_vco)
|
||||
tmp_delta = f_vco - computed;
|
||||
else
|
||||
tmp_delta = computed - f_vco;
|
||||
if (tmp_delta < delta) {
|
||||
delta = tmp_delta;
|
||||
m = testm;
|
||||
n = testn;
|
||||
}
|
||||
}
|
||||
}
|
||||
f_vco = ref_clk * (n + 1) / (m + 1);
|
||||
if (f_vco < 100000)
|
||||
s = 0;
|
||||
else if (f_vco < 140000)
|
||||
s = 1;
|
||||
else if (f_vco < 180000)
|
||||
s = 2;
|
||||
else
|
||||
s = 3;
|
||||
|
||||
drm_dbg_kms(dev, "clock: %ld vco: %ld m: %d n: %d p: %d s: %d\n",
|
||||
clock, f_vco, m, n, p, s);
|
||||
|
||||
WREG_DAC(MGA1064_PIX_PLLC_M, m);
|
||||
WREG_DAC(MGA1064_PIX_PLLC_N, n);
|
||||
WREG_DAC(MGA1064_PIX_PLLC_P, (p | (s << 3)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define P_ARRAY_SIZE 9
|
||||
|
||||
static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
|
||||
{
|
||||
u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
|
||||
unsigned int vcomax, vcomin, pllreffreq;
|
||||
unsigned int delta, tmpdelta, permitteddelta;
|
||||
unsigned int testp, testm, testn;
|
||||
|
@ -122,7 +193,7 @@ static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
|
|||
unsigned int fvv;
|
||||
unsigned int i;
|
||||
|
||||
if (mdev->unique_rev_id <= 0x03) {
|
||||
if (unique_rev_id <= 0x03) {
|
||||
|
||||
m = n = p = 0;
|
||||
vcomax = 320000;
|
||||
|
@ -220,7 +291,7 @@ static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
|
|||
WREG_DAC(MGA1064_PIX_PLLC_N, n);
|
||||
WREG_DAC(MGA1064_PIX_PLLC_P, p);
|
||||
|
||||
if (mdev->unique_rev_id >= 0x04) {
|
||||
if (unique_rev_id >= 0x04) {
|
||||
WREG_DAC(0x1a, 0x09);
|
||||
msleep(20);
|
||||
WREG_DAC(0x1a, 0x01);
|
||||
|
@ -717,6 +788,9 @@ static int mgag200_crtc_set_plls(struct mga_device *mdev, long clock)
|
|||
u8 misc;
|
||||
|
||||
switch(mdev->type) {
|
||||
case G200_PCI:
|
||||
case G200_AGP:
|
||||
return mgag200_g200_set_plls(mdev, clock);
|
||||
case G200_SE_A:
|
||||
case G200_SE_B:
|
||||
return mga_g200se_set_plls(mdev, clock);
|
||||
|
@ -877,45 +951,6 @@ static void mgag200_set_startadd(struct mga_device *mdev,
|
|||
WREG_ECRT(0x00, crtcext0);
|
||||
}
|
||||
|
||||
static void mgag200_set_pci_regs(struct mga_device *mdev)
|
||||
{
|
||||
uint32_t option = 0, option2 = 0;
|
||||
struct drm_device *dev = &mdev->base;
|
||||
|
||||
switch (mdev->type) {
|
||||
case G200_SE_A:
|
||||
case G200_SE_B:
|
||||
if (mdev->has_sdram)
|
||||
option = 0x40049120;
|
||||
else
|
||||
option = 0x4004d120;
|
||||
option2 = 0x00008000;
|
||||
break;
|
||||
case G200_WB:
|
||||
case G200_EW3:
|
||||
option = 0x41049120;
|
||||
option2 = 0x0000b000;
|
||||
break;
|
||||
case G200_EV:
|
||||
option = 0x00000120;
|
||||
option2 = 0x0000b000;
|
||||
break;
|
||||
case G200_EH:
|
||||
case G200_EH3:
|
||||
option = 0x00000120;
|
||||
option2 = 0x0000b000;
|
||||
break;
|
||||
case G200_ER:
|
||||
break;
|
||||
}
|
||||
|
||||
if (option)
|
||||
pci_write_config_dword(dev->pdev, PCI_MGA_OPTION, option);
|
||||
|
||||
if (option2)
|
||||
pci_write_config_dword(dev->pdev, PCI_MGA_OPTION2, option2);
|
||||
}
|
||||
|
||||
static void mgag200_set_dac_regs(struct mga_device *mdev)
|
||||
{
|
||||
size_t i;
|
||||
|
@ -933,6 +968,12 @@ static void mgag200_set_dac_regs(struct mga_device *mdev)
|
|||
};
|
||||
|
||||
switch (mdev->type) {
|
||||
case G200_PCI:
|
||||
case G200_AGP:
|
||||
dacvalue[MGA1064_SYS_PLL_M] = 0x04;
|
||||
dacvalue[MGA1064_SYS_PLL_N] = 0x2D;
|
||||
dacvalue[MGA1064_SYS_PLL_P] = 0x19;
|
||||
break;
|
||||
case G200_SE_A:
|
||||
case G200_SE_B:
|
||||
dacvalue[MGA1064_VREF_CTL] = 0x03;
|
||||
|
@ -986,9 +1027,8 @@ static void mgag200_set_dac_regs(struct mga_device *mdev)
|
|||
|
||||
static void mgag200_init_regs(struct mga_device *mdev)
|
||||
{
|
||||
u8 crtc11, crtcext3, crtcext4, misc;
|
||||
u8 crtc11, misc;
|
||||
|
||||
mgag200_set_pci_regs(mdev);
|
||||
mgag200_set_dac_regs(mdev);
|
||||
|
||||
WREG_SEQ(2, 0x0f);
|
||||
|
@ -1002,14 +1042,6 @@ static void mgag200_init_regs(struct mga_device *mdev)
|
|||
WREG_CRT(14, 0);
|
||||
WREG_CRT(15, 0);
|
||||
|
||||
RREG_ECRT(0x03, crtcext3);
|
||||
|
||||
crtcext3 |= BIT(7); /* enable MGA mode */
|
||||
crtcext4 = 0x00;
|
||||
|
||||
WREG_ECRT(0x03, crtcext3);
|
||||
WREG_ECRT(0x04, crtcext4);
|
||||
|
||||
RREG_CRT(0x11, crtc11);
|
||||
crtc11 &= ~(MGAREG_CRTC11_CRTCPROTECT |
|
||||
MGAREG_CRTC11_VINTEN |
|
||||
|
@ -1023,9 +1055,7 @@ static void mgag200_init_regs(struct mga_device *mdev)
|
|||
WREG_ECRT(0x34, 0x5);
|
||||
|
||||
misc = RREG8(MGA_MISC_IN);
|
||||
misc |= MGAREG_MISC_IOADSEL |
|
||||
MGAREG_MISC_RAMMAPEN |
|
||||
MGAREG_MISC_HIGH_PG_SEL;
|
||||
misc |= MGAREG_MISC_IOADSEL;
|
||||
WREG8(MGA_MISC_OUT, misc);
|
||||
}
|
||||
|
||||
|
@ -1234,12 +1264,13 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
|
|||
const struct drm_display_mode *mode,
|
||||
const struct drm_framebuffer *fb)
|
||||
{
|
||||
u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
|
||||
unsigned int hiprilvl;
|
||||
u8 crtcext6;
|
||||
|
||||
if (mdev->unique_rev_id >= 0x04) {
|
||||
if (unique_rev_id >= 0x04) {
|
||||
hiprilvl = 0;
|
||||
} else if (mdev->unique_rev_id >= 0x02) {
|
||||
} else if (unique_rev_id >= 0x02) {
|
||||
unsigned int bpp;
|
||||
unsigned long mb;
|
||||
|
||||
|
@ -1264,7 +1295,7 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
|
|||
else
|
||||
hiprilvl = 5;
|
||||
|
||||
} else if (mdev->unique_rev_id >= 0x01) {
|
||||
} else if (unique_rev_id >= 0x01) {
|
||||
hiprilvl = 3;
|
||||
} else {
|
||||
hiprilvl = 4;
|
||||
|
@ -1388,7 +1419,9 @@ static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector,
|
|||
int bpp = 32;
|
||||
|
||||
if (IS_G200_SE(mdev)) {
|
||||
if (mdev->unique_rev_id == 0x01) {
|
||||
u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
|
||||
|
||||
if (unique_rev_id == 0x01) {
|
||||
if (mode->hdisplay > 1600)
|
||||
return MODE_VIRTUAL_X;
|
||||
if (mode->vdisplay > 1200)
|
||||
|
@ -1396,7 +1429,7 @@ static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector,
|
|||
if (mga_vga_calculate_mode_bandwidth(mode, bpp)
|
||||
> (24400 * 1024))
|
||||
return MODE_BANDWIDTH;
|
||||
} else if (mdev->unique_rev_id == 0x02) {
|
||||
} else if (unique_rev_id == 0x02) {
|
||||
if (mode->hdisplay > 1920)
|
||||
return MODE_VIRTUAL_X;
|
||||
if (mode->vdisplay > 1200)
|
||||
|
|
|
@ -256,6 +256,8 @@
|
|||
#define MGAREG_CRTCEXT1_VSYNCOFF BIT(5)
|
||||
#define MGAREG_CRTCEXT1_HSYNCOFF BIT(4)
|
||||
|
||||
#define MGAREG_CRTCEXT3_MGAMODE BIT(7)
|
||||
|
||||
/* Cursor X and Y position */
|
||||
#define MGA_CURPOSXL 0x3c0c
|
||||
#define MGA_CURPOSXH 0x3c0d
|
||||
|
@ -282,6 +284,8 @@
|
|||
#define PCI_MGA_OPTION2 0x50
|
||||
#define PCI_MGA_OPTION3 0x54
|
||||
|
||||
#define PCI_MGA_OPTION_HARDPWMSK BIT(14)
|
||||
|
||||
#define RAMDAC_OFFSET 0x3c00
|
||||
|
||||
/* TVP3026 direct registers */
|
||||
|
|
|
@ -50,14 +50,9 @@ static int mdp4_lvds_connector_get_modes(struct drm_connector *connector)
|
|||
struct drm_panel *panel = mdp4_lvds_connector->panel;
|
||||
int ret = 0;
|
||||
|
||||
if (panel) {
|
||||
drm_panel_attach(panel, connector);
|
||||
|
||||
if (panel)
|
||||
ret = drm_panel_get_modes(panel, connector);
|
||||
|
||||
drm_panel_detach(panel);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -328,7 +328,6 @@ static int dsi_mgr_connector_get_modes(struct drm_connector *connector)
|
|||
* In dual DSI mode, we have one connector that can be
|
||||
* attached to the drm_panel.
|
||||
*/
|
||||
drm_panel_attach(panel, connector);
|
||||
num = drm_panel_get_modes(panel, connector);
|
||||
if (!num)
|
||||
return 0;
|
||||
|
|
|
@ -5,7 +5,7 @@ config DRM_MXS
|
|||
Choose this option to select drivers for MXS FB devices
|
||||
|
||||
config DRM_MXSFB
|
||||
tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller"
|
||||
tristate "i.MX (e)LCDIF LCD controller"
|
||||
depends on DRM && OF
|
||||
depends on COMMON_CLK
|
||||
select DRM_MXS
|
||||
|
@ -13,8 +13,10 @@ config DRM_MXSFB
|
|||
select DRM_KMS_FB_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_PANEL
|
||||
select DRM_PANEL_BRIDGE
|
||||
help
|
||||
Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB
|
||||
LCD controller.
|
||||
Choose this option if you have an LCDIF or eLCDIF LCD controller.
|
||||
Those devices are found in various i.MX SoC (including i.MX23,
|
||||
i.MX28, i.MX6SX, i.MX7 and i.MX8M).
|
||||
|
||||
If M is selected the module will be called mxsfb.
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o
|
||||
mxsfb-y := mxsfb_drv.o mxsfb_kms.o
|
||||
obj-$(CONFIG_DRM_MXSFB) += mxsfb.o
|
||||
|
|
|
@ -1,343 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* This code is based on drivers/video/fbdev/mxsfb.c :
|
||||
* Copyright (C) 2010 Juergen Beisert, Pengutronix
|
||||
* Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_data/simplefb.h>
|
||||
|
||||
#include <video/videomode.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "mxsfb_drv.h"
|
||||
#include "mxsfb_regs.h"
|
||||
|
||||
#define MXS_SET_ADDR 0x4
|
||||
#define MXS_CLR_ADDR 0x8
|
||||
#define MODULE_CLKGATE BIT(30)
|
||||
#define MODULE_SFTRST BIT(31)
|
||||
/* 1 second delay should be plenty of time for block reset */
|
||||
#define RESET_TIMEOUT 1000000
|
||||
|
||||
static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
|
||||
{
|
||||
return (val & mxsfb->devdata->hs_wdth_mask) <<
|
||||
mxsfb->devdata->hs_wdth_shift;
|
||||
}
|
||||
|
||||
/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */
|
||||
static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_crtc *crtc = &mxsfb->pipe.crtc;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
const u32 format = crtc->primary->state->fb->format->format;
|
||||
u32 ctrl, ctrl1;
|
||||
|
||||
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
|
||||
|
||||
/*
|
||||
* WARNING: The bus width, CTRL_SET_BUS_WIDTH(), is configured to
|
||||
* match the selected mode here. This differs from the original
|
||||
* MXSFB driver, which had the option to configure the bus width
|
||||
* to arbitrary value. This limitation should not pose an issue.
|
||||
*/
|
||||
|
||||
/* CTRL1 contains IRQ config and status bits, preserve those. */
|
||||
ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
|
||||
ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
dev_dbg(drm->dev, "Setting up RGB565 mode\n");
|
||||
ctrl |= CTRL_SET_WORD_LENGTH(0);
|
||||
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
|
||||
ctrl |= CTRL_SET_WORD_LENGTH(3);
|
||||
/* Do not use packed pixels = one pixel per word instead. */
|
||||
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
|
||||
break;
|
||||
default:
|
||||
dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(ctrl1, mxsfb->base + LCDC_CTRL1);
|
||||
writel(ctrl, mxsfb->base + LCDC_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_crtc *crtc = &mxsfb->pipe.crtc;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(mxsfb->base + LCDC_CTRL);
|
||||
|
||||
if (mxsfb->connector->display_info.num_bus_formats)
|
||||
bus_format = mxsfb->connector->display_info.bus_formats[0];
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
|
||||
bus_format);
|
||||
|
||||
reg &= ~CTRL_BUS_WIDTH_MASK;
|
||||
switch (bus_format) {
|
||||
case MEDIA_BUS_FMT_RGB565_1X16:
|
||||
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT);
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB666_1X18:
|
||||
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_18BIT);
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X24:
|
||||
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT);
|
||||
break;
|
||||
default:
|
||||
dev_err(drm->dev, "Unknown media bus format %d\n", bus_format);
|
||||
break;
|
||||
}
|
||||
writel(reg, mxsfb->base + LCDC_CTRL);
|
||||
}
|
||||
|
||||
static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (mxsfb->clk_disp_axi)
|
||||
clk_prepare_enable(mxsfb->clk_disp_axi);
|
||||
clk_prepare_enable(mxsfb->clk);
|
||||
|
||||
/* If it was disabled, re-enable the mode again */
|
||||
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
|
||||
|
||||
/* Enable the SYNC signals first, then the DMA engine */
|
||||
reg = readl(mxsfb->base + LCDC_VDCTRL4);
|
||||
reg |= VDCTRL4_SYNC_SIGNALS_ON;
|
||||
writel(reg, mxsfb->base + LCDC_VDCTRL4);
|
||||
|
||||
writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
|
||||
}
|
||||
|
||||
static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* Even if we disable the controller here, it will still continue
|
||||
* until its FIFOs are running out of data
|
||||
*/
|
||||
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);
|
||||
|
||||
readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
|
||||
0, 1000);
|
||||
|
||||
reg = readl(mxsfb->base + LCDC_VDCTRL4);
|
||||
reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
|
||||
writel(reg, mxsfb->base + LCDC_VDCTRL4);
|
||||
|
||||
clk_disable_unprepare(mxsfb->clk);
|
||||
if (mxsfb->clk_disp_axi)
|
||||
clk_disable_unprepare(mxsfb->clk_disp_axi);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the bit and poll it cleared. This is usually called with
|
||||
* a reset address and mask being either SFTRST(bit 31) or CLKGATE
|
||||
* (bit 30).
|
||||
*/
|
||||
static int clear_poll_bit(void __iomem *addr, u32 mask)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
writel(mask, addr + MXS_CLR_ADDR);
|
||||
return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int mxsfb_reset_block(void __iomem *reset_addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
|
||||
|
||||
ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clear_poll_bit(reset_addr, MODULE_CLKGATE);
|
||||
}
|
||||
|
||||
static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_framebuffer *fb = mxsfb->pipe.plane.state->fb;
|
||||
struct drm_gem_cma_object *gem;
|
||||
|
||||
if (!fb)
|
||||
return 0;
|
||||
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
if (!gem)
|
||||
return 0;
|
||||
|
||||
return gem->paddr;
|
||||
}
|
||||
|
||||
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_device *drm = mxsfb->pipe.crtc.dev;
|
||||
struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
|
||||
u32 bus_flags = mxsfb->connector->display_info.bus_flags;
|
||||
u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* It seems, you can't re-program the controller if it is still
|
||||
* running. This may lead to shifted pictures (FIFO issue?), so
|
||||
* first stop the controller and drain its FIFOs.
|
||||
*/
|
||||
|
||||
/* Mandatory eLCDIF reset as per the Reference Manual */
|
||||
err = mxsfb_reset_block(mxsfb->base);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
/* Clear the FIFOs */
|
||||
writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
err = mxsfb_set_pixel_fmt(mxsfb);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
|
||||
|
||||
if (mxsfb->bridge && mxsfb->bridge->timings)
|
||||
bus_flags = mxsfb->bridge->timings->input_bus_flags;
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
|
||||
m->crtc_clock,
|
||||
(int)(clk_get_rate(mxsfb->clk) / 1000));
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
|
||||
bus_flags);
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
|
||||
|
||||
writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
|
||||
TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
|
||||
mxsfb->base + mxsfb->devdata->transfer_count);
|
||||
|
||||
vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;
|
||||
|
||||
vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */
|
||||
VDCTRL0_VSYNC_PERIOD_UNIT |
|
||||
VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
|
||||
VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);
|
||||
if (m->flags & DRM_MODE_FLAG_PHSYNC)
|
||||
vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
|
||||
if (m->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
|
||||
/* Make sure Data Enable is high active by default */
|
||||
if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))
|
||||
vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
|
||||
/*
|
||||
* DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,
|
||||
* controllers VDCTRL0_DOTCLK is display centric.
|
||||
* Drive on positive edge -> display samples on falling edge
|
||||
* DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
|
||||
*/
|
||||
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
|
||||
vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
|
||||
|
||||
writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
|
||||
|
||||
mxsfb_set_bus_fmt(mxsfb);
|
||||
|
||||
/* Frame length in lines. */
|
||||
writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
|
||||
|
||||
/* Line length in units of clocks or pixels. */
|
||||
hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;
|
||||
writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |
|
||||
VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
|
||||
mxsfb->base + LCDC_VDCTRL2);
|
||||
|
||||
writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) |
|
||||
SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start),
|
||||
mxsfb->base + LCDC_VDCTRL3);
|
||||
|
||||
writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
|
||||
mxsfb->base + LCDC_VDCTRL4);
|
||||
}
|
||||
|
||||
void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
dma_addr_t paddr;
|
||||
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
mxsfb_crtc_mode_set_nofb(mxsfb);
|
||||
|
||||
/* Write cur_buf as well to avoid an initial corrupt frame */
|
||||
paddr = mxsfb_get_fb_paddr(mxsfb);
|
||||
if (paddr) {
|
||||
writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf);
|
||||
writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
|
||||
}
|
||||
|
||||
mxsfb_enable_controller(mxsfb);
|
||||
}
|
||||
|
||||
void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
mxsfb_disable_controller(mxsfb);
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
}
|
||||
|
||||
void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_simple_display_pipe *pipe = &mxsfb->pipe;
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_pending_vblank_event *event;
|
||||
dma_addr_t paddr;
|
||||
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
event = crtc->state->event;
|
||||
if (event) {
|
||||
crtc->state->event = NULL;
|
||||
|
||||
if (drm_crtc_vblank_get(crtc) == 0) {
|
||||
drm_crtc_arm_vblank_event(crtc, event);
|
||||
} else {
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
|
||||
paddr = mxsfb_get_fb_paddr(mxsfb);
|
||||
if (paddr) {
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
}
|
||||
}
|
|
@ -9,30 +9,24 @@
|
|||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/dma-resv.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_irq.h>
|
||||
#include <drm/drm_mode_config.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "mxsfb_drv.h"
|
||||
|
@ -41,6 +35,11 @@
|
|||
enum mxsfb_devtype {
|
||||
MXSFB_V3,
|
||||
MXSFB_V4,
|
||||
/*
|
||||
* Starting at i.MX6 the hardware version register is gone, use the
|
||||
* i.MX family number as the version.
|
||||
*/
|
||||
MXSFB_V6,
|
||||
};
|
||||
|
||||
static const struct mxsfb_devdata mxsfb_devdata[] = {
|
||||
|
@ -48,38 +47,28 @@ static const struct mxsfb_devdata mxsfb_devdata[] = {
|
|||
.transfer_count = LCDC_V3_TRANSFER_COUNT,
|
||||
.cur_buf = LCDC_V3_CUR_BUF,
|
||||
.next_buf = LCDC_V3_NEXT_BUF,
|
||||
.debug0 = LCDC_V3_DEBUG0,
|
||||
.hs_wdth_mask = 0xff,
|
||||
.hs_wdth_shift = 24,
|
||||
.ipversion = 3,
|
||||
.has_overlay = false,
|
||||
},
|
||||
[MXSFB_V4] = {
|
||||
.transfer_count = LCDC_V4_TRANSFER_COUNT,
|
||||
.cur_buf = LCDC_V4_CUR_BUF,
|
||||
.next_buf = LCDC_V4_NEXT_BUF,
|
||||
.debug0 = LCDC_V4_DEBUG0,
|
||||
.hs_wdth_mask = 0x3fff,
|
||||
.hs_wdth_shift = 18,
|
||||
.ipversion = 4,
|
||||
.has_overlay = false,
|
||||
},
|
||||
[MXSFB_V6] = {
|
||||
.transfer_count = LCDC_V4_TRANSFER_COUNT,
|
||||
.cur_buf = LCDC_V4_CUR_BUF,
|
||||
.next_buf = LCDC_V4_NEXT_BUF,
|
||||
.hs_wdth_mask = 0x3fff,
|
||||
.hs_wdth_shift = 18,
|
||||
.has_overlay = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const uint32_t mxsfb_formats[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_RGB565
|
||||
};
|
||||
|
||||
static const uint64_t mxsfb_modifiers[] = {
|
||||
DRM_FORMAT_MOD_LINEAR,
|
||||
DRM_FORMAT_MOD_INVALID
|
||||
};
|
||||
|
||||
static struct mxsfb_drm_private *
|
||||
drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
return container_of(pipe, struct mxsfb_drm_private, pipe);
|
||||
}
|
||||
|
||||
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
if (mxsfb->clk_axi)
|
||||
|
@ -102,101 +91,51 @@ static const struct drm_mode_config_helper_funcs mxsfb_mode_config_helpers = {
|
|||
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
|
||||
};
|
||||
|
||||
static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_plane_state *plane_state)
|
||||
static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
struct drm_device *drm = pipe->plane.dev;
|
||||
struct drm_device *drm = mxsfb->drm;
|
||||
struct drm_connector_list_iter iter;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
int ret;
|
||||
|
||||
if (!mxsfb->connector) {
|
||||
list_for_each_entry(connector,
|
||||
&drm->mode_config.connector_list,
|
||||
head)
|
||||
if (connector->encoder == &mxsfb->pipe.encoder) {
|
||||
mxsfb->connector = connector;
|
||||
break;
|
||||
}
|
||||
ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
|
||||
&bridge);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (panel) {
|
||||
bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
|
||||
DRM_MODE_CONNECTOR_DPI);
|
||||
if (IS_ERR(bridge))
|
||||
return PTR_ERR(bridge);
|
||||
}
|
||||
|
||||
if (!mxsfb->connector) {
|
||||
dev_warn(drm->dev, "No connector attached, using default\n");
|
||||
mxsfb->connector = &mxsfb->panel_connector;
|
||||
if (!bridge)
|
||||
return -ENODEV;
|
||||
|
||||
ret = drm_bridge_attach(&mxsfb->encoder, bridge, NULL, 0);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(drm->dev,
|
||||
"failed to attach bridge: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(drm->dev);
|
||||
drm_panel_prepare(mxsfb->panel);
|
||||
mxsfb_crtc_enable(mxsfb);
|
||||
drm_panel_enable(mxsfb->panel);
|
||||
}
|
||||
mxsfb->bridge = bridge;
|
||||
|
||||
static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
struct drm_device *drm = pipe->plane.dev;
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_pending_vblank_event *event;
|
||||
|
||||
drm_panel_disable(mxsfb->panel);
|
||||
mxsfb_crtc_disable(mxsfb);
|
||||
drm_panel_unprepare(mxsfb->panel);
|
||||
pm_runtime_put_sync(drm->dev);
|
||||
|
||||
spin_lock_irq(&drm->event_lock);
|
||||
event = crtc->state->event;
|
||||
if (event) {
|
||||
crtc->state->event = NULL;
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
}
|
||||
spin_unlock_irq(&drm->event_lock);
|
||||
|
||||
if (mxsfb->connector != &mxsfb->panel_connector)
|
||||
mxsfb->connector = NULL;
|
||||
}
|
||||
|
||||
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
|
||||
mxsfb_plane_atomic_update(mxsfb, plane_state);
|
||||
}
|
||||
|
||||
static int mxsfb_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
|
||||
/* Clear and enable VBLANK IRQ */
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
/*
|
||||
* Get hold of the connector. This is a bit of a hack, until the bridge
|
||||
* API gives us bus flags and formats.
|
||||
*/
|
||||
drm_connector_list_iter_begin(drm, &iter);
|
||||
mxsfb->connector = drm_connector_list_iter_next(&iter);
|
||||
drm_connector_list_iter_end(&iter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxsfb_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
|
||||
/* Disable and clear VBLANK IRQ */
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
}
|
||||
|
||||
static struct drm_simple_display_pipe_funcs mxsfb_funcs = {
|
||||
.enable = mxsfb_pipe_enable,
|
||||
.disable = mxsfb_pipe_disable,
|
||||
.update = mxsfb_pipe_update,
|
||||
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
|
||||
.enable_vblank = mxsfb_pipe_enable_vblank,
|
||||
.disable_vblank = mxsfb_pipe_disable_vblank,
|
||||
};
|
||||
|
||||
static int mxsfb_load(struct drm_device *drm)
|
||||
static int mxsfb_load(struct drm_device *drm,
|
||||
const struct mxsfb_devdata *devdata)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(drm->dev);
|
||||
struct mxsfb_drm_private *mxsfb;
|
||||
|
@ -207,8 +146,9 @@ static int mxsfb_load(struct drm_device *drm)
|
|||
if (!mxsfb)
|
||||
return -ENOMEM;
|
||||
|
||||
mxsfb->drm = drm;
|
||||
drm->dev_private = mxsfb;
|
||||
mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
|
||||
mxsfb->devdata = devdata;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mxsfb->base = devm_ioremap_resource(drm->dev, res);
|
||||
|
@ -233,52 +173,30 @@ static int mxsfb_load(struct drm_device *drm)
|
|||
|
||||
pm_runtime_enable(drm->dev);
|
||||
|
||||
/* Modeset init */
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
ret = mxsfb_kms_init(mxsfb);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Failed to initialize KMS pipeline\n");
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Failed to initialise vblank\n");
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
/* Modeset init */
|
||||
drm_mode_config_init(drm);
|
||||
/* Start with vertical blanking interrupt reporting disabled. */
|
||||
drm_crtc_vblank_off(&mxsfb->crtc);
|
||||
|
||||
ret = mxsfb_create_output(drm);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Failed to create outputs\n");
|
||||
ret = mxsfb_attach_bridge(mxsfb);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
|
||||
mxsfb_formats, ARRAY_SIZE(mxsfb_formats),
|
||||
mxsfb_modifiers, mxsfb->connector);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Cannot setup simple display pipe\n");
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach panel only if there is one.
|
||||
* If there is no panel attach, it must be a bridge. In this case, we
|
||||
* need a reference to its connector for a proper initialization.
|
||||
* We will do this check in pipe->enable(), since the connector won't
|
||||
* be attached to an encoder until then.
|
||||
*/
|
||||
|
||||
if (mxsfb->panel) {
|
||||
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
|
||||
goto err_vblank;
|
||||
}
|
||||
} else if (mxsfb->bridge) {
|
||||
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
|
||||
mxsfb->bridge);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
|
||||
goto err_vblank;
|
||||
}
|
||||
}
|
||||
|
||||
drm->mode_config.min_width = MXSFB_MIN_XRES;
|
||||
drm->mode_config.min_height = MXSFB_MIN_YRES;
|
||||
drm->mode_config.max_width = MXSFB_MAX_XRES;
|
||||
|
@ -294,7 +212,7 @@ static int mxsfb_load(struct drm_device *drm)
|
|||
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Failed to install IRQ handler\n");
|
||||
goto err_irq;
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
drm_kms_helper_poll_init(drm);
|
||||
|
@ -305,8 +223,6 @@ static int mxsfb_load(struct drm_device *drm)
|
|||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
drm_panel_detach(mxsfb->panel);
|
||||
err_vblank:
|
||||
pm_runtime_disable(drm->dev);
|
||||
|
||||
|
@ -327,11 +243,13 @@ static void mxsfb_unload(struct drm_device *drm)
|
|||
pm_runtime_disable(drm->dev);
|
||||
}
|
||||
|
||||
static void mxsfb_irq_preinstall(struct drm_device *drm)
|
||||
static void mxsfb_irq_disable(struct drm_device *drm)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
|
||||
mxsfb_pipe_disable_vblank(&mxsfb->pipe);
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
mxsfb->crtc.funcs->disable_vblank(&mxsfb->crtc);
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
}
|
||||
|
||||
static irqreturn_t mxsfb_irq_handler(int irq, void *data)
|
||||
|
@ -340,17 +258,13 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
|
|||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
u32 reg;
|
||||
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
|
||||
reg = readl(mxsfb->base + LCDC_CTRL1);
|
||||
|
||||
if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
|
||||
drm_crtc_handle_vblank(&mxsfb->pipe.crtc);
|
||||
drm_crtc_handle_vblank(&mxsfb->crtc);
|
||||
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -359,8 +273,8 @@ DEFINE_DRM_GEM_CMA_FOPS(fops);
|
|||
static struct drm_driver mxsfb_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
|
||||
.irq_handler = mxsfb_irq_handler,
|
||||
.irq_preinstall = mxsfb_irq_preinstall,
|
||||
.irq_uninstall = mxsfb_irq_preinstall,
|
||||
.irq_preinstall = mxsfb_irq_disable,
|
||||
.irq_uninstall = mxsfb_irq_disable,
|
||||
DRM_GEM_CMA_DRIVER_OPS,
|
||||
.fops = &fops,
|
||||
.name = "mxsfb-drm",
|
||||
|
@ -370,18 +284,10 @@ static struct drm_driver mxsfb_driver = {
|
|||
.minor = 0,
|
||||
};
|
||||
|
||||
static const struct platform_device_id mxsfb_devtype[] = {
|
||||
{ .name = "imx23-fb", .driver_data = MXSFB_V3, },
|
||||
{ .name = "imx28-fb", .driver_data = MXSFB_V4, },
|
||||
{ .name = "imx6sx-fb", .driver_data = MXSFB_V4, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
|
||||
|
||||
static const struct of_device_id mxsfb_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
|
||||
{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
|
||||
{ .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devtype[2], },
|
||||
{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devdata[MXSFB_V3], },
|
||||
{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devdata[MXSFB_V4], },
|
||||
{ .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devdata[MXSFB_V6], },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);
|
||||
|
@ -396,14 +302,11 @@ static int mxsfb_probe(struct platform_device *pdev)
|
|||
if (!pdev->dev.of_node)
|
||||
return -ENODEV;
|
||||
|
||||
if (of_id)
|
||||
pdev->id_entry = of_id->data;
|
||||
|
||||
drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev);
|
||||
if (IS_ERR(drm))
|
||||
return PTR_ERR(drm);
|
||||
|
||||
ret = mxsfb_load(drm);
|
||||
ret = mxsfb_load(drm, of_id->data);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
|
@ -457,7 +360,6 @@ static const struct dev_pm_ops mxsfb_pm_ops = {
|
|||
static struct platform_driver mxsfb_platform_driver = {
|
||||
.probe = mxsfb_probe,
|
||||
.remove = mxsfb_remove,
|
||||
.id_table = mxsfb_devtype,
|
||||
.driver = {
|
||||
.name = "mxsfb",
|
||||
.of_match_table = mxsfb_dt_ids,
|
||||
|
|
|
@ -8,14 +8,20 @@
|
|||
#ifndef __MXSFB_DRV_H__
|
||||
#define __MXSFB_DRV_H__
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_plane.h>
|
||||
|
||||
struct clk;
|
||||
|
||||
struct mxsfb_devdata {
|
||||
unsigned int transfer_count;
|
||||
unsigned int cur_buf;
|
||||
unsigned int next_buf;
|
||||
unsigned int debug0;
|
||||
unsigned int hs_wdth_mask;
|
||||
unsigned int hs_wdth_shift;
|
||||
unsigned int ipversion;
|
||||
unsigned int transfer_count;
|
||||
unsigned int cur_buf;
|
||||
unsigned int next_buf;
|
||||
unsigned int hs_wdth_mask;
|
||||
unsigned int hs_wdth_shift;
|
||||
bool has_overlay;
|
||||
};
|
||||
|
||||
struct mxsfb_drm_private {
|
||||
|
@ -26,22 +32,26 @@ struct mxsfb_drm_private {
|
|||
struct clk *clk_axi;
|
||||
struct clk *clk_disp_axi;
|
||||
|
||||
struct drm_simple_display_pipe pipe;
|
||||
struct drm_connector panel_connector;
|
||||
struct drm_device *drm;
|
||||
struct {
|
||||
struct drm_plane primary;
|
||||
struct drm_plane overlay;
|
||||
} planes;
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector *connector;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
};
|
||||
|
||||
int mxsfb_setup_crtc(struct drm_device *dev);
|
||||
int mxsfb_create_output(struct drm_device *dev);
|
||||
static inline struct mxsfb_drm_private *
|
||||
to_mxsfb_drm_private(struct drm_device *drm)
|
||||
{
|
||||
return drm->dev_private;
|
||||
}
|
||||
|
||||
void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb);
|
||||
void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb);
|
||||
|
||||
void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb);
|
||||
void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb);
|
||||
void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
|
||||
struct drm_plane_state *state);
|
||||
int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb);
|
||||
|
||||
#endif /* __MXSFB_DRV_H__ */
|
||||
|
|
|
@ -0,0 +1,571 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* This code is based on drivers/video/fbdev/mxsfb.c :
|
||||
* Copyright (C) 2010 Juergen Beisert, Pengutronix
|
||||
* Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "mxsfb_drv.h"
|
||||
#include "mxsfb_regs.h"
|
||||
|
||||
/* 1 second delay should be plenty of time for block reset */
|
||||
#define RESET_TIMEOUT 1000000
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* CRTC
|
||||
*/
|
||||
|
||||
static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
|
||||
{
|
||||
return (val & mxsfb->devdata->hs_wdth_mask) <<
|
||||
mxsfb->devdata->hs_wdth_shift;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the MXSFB registers for decoding the pixels out of the framebuffer and
|
||||
* outputting them on the bus.
|
||||
*/
|
||||
static void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_device *drm = mxsfb->drm;
|
||||
const u32 format = mxsfb->crtc.primary->state->fb->format->format;
|
||||
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
u32 ctrl, ctrl1;
|
||||
|
||||
if (mxsfb->connector->display_info.num_bus_formats)
|
||||
bus_format = mxsfb->connector->display_info.bus_formats[0];
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
|
||||
bus_format);
|
||||
|
||||
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
|
||||
|
||||
/* CTRL1 contains IRQ config and status bits, preserve those. */
|
||||
ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
|
||||
ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
dev_dbg(drm->dev, "Setting up RGB565 mode\n");
|
||||
ctrl |= CTRL_WORD_LENGTH_16;
|
||||
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
|
||||
ctrl |= CTRL_WORD_LENGTH_24;
|
||||
/* Do not use packed pixels = one pixel per word instead. */
|
||||
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (bus_format) {
|
||||
case MEDIA_BUS_FMT_RGB565_1X16:
|
||||
ctrl |= CTRL_BUS_WIDTH_16;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB666_1X18:
|
||||
ctrl |= CTRL_BUS_WIDTH_18;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X24:
|
||||
ctrl |= CTRL_BUS_WIDTH_24;
|
||||
break;
|
||||
default:
|
||||
dev_err(drm->dev, "Unknown media bus format %d\n", bus_format);
|
||||
break;
|
||||
}
|
||||
|
||||
writel(ctrl1, mxsfb->base + LCDC_CTRL1);
|
||||
writel(ctrl, mxsfb->base + LCDC_CTRL);
|
||||
}
|
||||
|
||||
static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (mxsfb->clk_disp_axi)
|
||||
clk_prepare_enable(mxsfb->clk_disp_axi);
|
||||
clk_prepare_enable(mxsfb->clk);
|
||||
|
||||
/* If it was disabled, re-enable the mode again */
|
||||
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
|
||||
|
||||
/* Enable the SYNC signals first, then the DMA engine */
|
||||
reg = readl(mxsfb->base + LCDC_VDCTRL4);
|
||||
reg |= VDCTRL4_SYNC_SIGNALS_ON;
|
||||
writel(reg, mxsfb->base + LCDC_VDCTRL4);
|
||||
|
||||
writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
|
||||
}
|
||||
|
||||
static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* Even if we disable the controller here, it will still continue
|
||||
* until its FIFOs are running out of data
|
||||
*/
|
||||
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);
|
||||
|
||||
readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
|
||||
0, 1000);
|
||||
|
||||
reg = readl(mxsfb->base + LCDC_VDCTRL4);
|
||||
reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
|
||||
writel(reg, mxsfb->base + LCDC_VDCTRL4);
|
||||
|
||||
clk_disable_unprepare(mxsfb->clk);
|
||||
if (mxsfb->clk_disp_axi)
|
||||
clk_disable_unprepare(mxsfb->clk_disp_axi);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the bit and poll it cleared. This is usually called with
|
||||
* a reset address and mask being either SFTRST(bit 31) or CLKGATE
|
||||
* (bit 30).
|
||||
*/
|
||||
static int clear_poll_bit(void __iomem *addr, u32 mask)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
writel(mask, addr + REG_CLR);
|
||||
return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(CTRL_CLKGATE, mxsfb->base + LCDC_CTRL + REG_CLR);
|
||||
|
||||
ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE);
|
||||
}
|
||||
|
||||
static dma_addr_t mxsfb_get_fb_paddr(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
struct drm_gem_cma_object *gem;
|
||||
|
||||
if (!fb)
|
||||
return 0;
|
||||
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
if (!gem)
|
||||
return 0;
|
||||
|
||||
return gem->paddr;
|
||||
}
|
||||
|
||||
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_device *drm = mxsfb->crtc.dev;
|
||||
struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode;
|
||||
u32 bus_flags = mxsfb->connector->display_info.bus_flags;
|
||||
u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* It seems, you can't re-program the controller if it is still
|
||||
* running. This may lead to shifted pictures (FIFO issue?), so
|
||||
* first stop the controller and drain its FIFOs.
|
||||
*/
|
||||
|
||||
/* Mandatory eLCDIF reset as per the Reference Manual */
|
||||
err = mxsfb_reset_block(mxsfb);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
/* Clear the FIFOs */
|
||||
writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
if (mxsfb->devdata->has_overlay)
|
||||
writel(0, mxsfb->base + LCDC_AS_CTRL);
|
||||
|
||||
mxsfb_set_formats(mxsfb);
|
||||
|
||||
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
|
||||
|
||||
if (mxsfb->bridge && mxsfb->bridge->timings)
|
||||
bus_flags = mxsfb->bridge->timings->input_bus_flags;
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
|
||||
m->crtc_clock,
|
||||
(int)(clk_get_rate(mxsfb->clk) / 1000));
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
|
||||
bus_flags);
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
|
||||
|
||||
writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
|
||||
TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
|
||||
mxsfb->base + mxsfb->devdata->transfer_count);
|
||||
|
||||
vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;
|
||||
|
||||
vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */
|
||||
VDCTRL0_VSYNC_PERIOD_UNIT |
|
||||
VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
|
||||
VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);
|
||||
if (m->flags & DRM_MODE_FLAG_PHSYNC)
|
||||
vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
|
||||
if (m->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
|
||||
/* Make sure Data Enable is high active by default */
|
||||
if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))
|
||||
vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
|
||||
/*
|
||||
* DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,
|
||||
* controllers VDCTRL0_DOTCLK is display centric.
|
||||
* Drive on positive edge -> display samples on falling edge
|
||||
* DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
|
||||
*/
|
||||
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
|
||||
vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
|
||||
|
||||
writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
|
||||
|
||||
/* Frame length in lines. */
|
||||
writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
|
||||
|
||||
/* Line length in units of clocks or pixels. */
|
||||
hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;
|
||||
writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |
|
||||
VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
|
||||
mxsfb->base + LCDC_VDCTRL2);
|
||||
|
||||
writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) |
|
||||
SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start),
|
||||
mxsfb->base + LCDC_VDCTRL3);
|
||||
|
||||
writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
|
||||
mxsfb->base + LCDC_VDCTRL4);
|
||||
}
|
||||
|
||||
static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
bool has_primary = state->plane_mask &
|
||||
drm_plane_mask(crtc->primary);
|
||||
|
||||
/* The primary plane has to be enabled when the CRTC is active. */
|
||||
if (state->active && !has_primary)
|
||||
return -EINVAL;
|
||||
|
||||
/* TODO: Is this needed ? */
|
||||
return drm_atomic_add_affected_planes(state->state, crtc);
|
||||
}
|
||||
|
||||
static void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
struct drm_pending_vblank_event *event;
|
||||
|
||||
event = crtc->state->event;
|
||||
crtc->state->event = NULL;
|
||||
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
if (drm_crtc_vblank_get(crtc) == 0)
|
||||
drm_crtc_arm_vblank_event(crtc, event);
|
||||
else
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
}
|
||||
|
||||
static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
|
||||
struct drm_device *drm = mxsfb->drm;
|
||||
dma_addr_t paddr;
|
||||
|
||||
pm_runtime_get_sync(drm->dev);
|
||||
mxsfb_enable_axi_clk(mxsfb);
|
||||
|
||||
drm_crtc_vblank_on(crtc);
|
||||
|
||||
mxsfb_crtc_mode_set_nofb(mxsfb);
|
||||
|
||||
/* Write cur_buf as well to avoid an initial corrupt frame */
|
||||
paddr = mxsfb_get_fb_paddr(crtc->primary);
|
||||
if (paddr) {
|
||||
writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf);
|
||||
writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
|
||||
}
|
||||
|
||||
mxsfb_enable_controller(mxsfb);
|
||||
}
|
||||
|
||||
static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
|
||||
struct drm_device *drm = mxsfb->drm;
|
||||
struct drm_pending_vblank_event *event;
|
||||
|
||||
mxsfb_disable_controller(mxsfb);
|
||||
|
||||
spin_lock_irq(&drm->event_lock);
|
||||
event = crtc->state->event;
|
||||
if (event) {
|
||||
crtc->state->event = NULL;
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
}
|
||||
spin_unlock_irq(&drm->event_lock);
|
||||
|
||||
drm_crtc_vblank_off(crtc);
|
||||
|
||||
mxsfb_disable_axi_clk(mxsfb);
|
||||
pm_runtime_put_sync(drm->dev);
|
||||
}
|
||||
|
||||
static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
|
||||
|
||||
/* Clear and enable VBLANK IRQ */
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
|
||||
|
||||
/* Disable and clear VBLANK IRQ */
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
|
||||
.atomic_check = mxsfb_crtc_atomic_check,
|
||||
.atomic_flush = mxsfb_crtc_atomic_flush,
|
||||
.atomic_enable = mxsfb_crtc_atomic_enable,
|
||||
.atomic_disable = mxsfb_crtc_atomic_disable,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.enable_vblank = mxsfb_crtc_enable_vblank,
|
||||
.disable_vblank = mxsfb_crtc_disable_vblank,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Encoder
|
||||
*/
|
||||
|
||||
static const struct drm_encoder_funcs mxsfb_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Planes
|
||||
*/
|
||||
|
||||
static int mxsfb_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
|
||||
struct drm_crtc_state *crtc_state;
|
||||
|
||||
crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
|
||||
&mxsfb->crtc);
|
||||
|
||||
return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
false, true);
|
||||
}
|
||||
|
||||
static void mxsfb_plane_primary_atomic_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_pstate)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
|
||||
dma_addr_t paddr;
|
||||
|
||||
paddr = mxsfb_get_fb_paddr(plane);
|
||||
if (paddr)
|
||||
writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
|
||||
}
|
||||
|
||||
static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_pstate)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
|
||||
struct drm_plane_state *state = plane->state;
|
||||
dma_addr_t paddr;
|
||||
u32 ctrl;
|
||||
|
||||
paddr = mxsfb_get_fb_paddr(plane);
|
||||
if (!paddr) {
|
||||
writel(0, mxsfb->base + LCDC_AS_CTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* HACK: The hardware seems to output 64 bytes of data of unknown
|
||||
* origin, and then to proceed with the framebuffer. Until the reason
|
||||
* is understood, live with the 16 initial invalid pixels on the first
|
||||
* line and start 64 bytes within the framebuffer.
|
||||
*/
|
||||
paddr += 64;
|
||||
|
||||
writel(paddr, mxsfb->base + LCDC_AS_NEXT_BUF);
|
||||
|
||||
/*
|
||||
* If the plane was previously disabled, write LCDC_AS_BUF as well to
|
||||
* provide the first buffer.
|
||||
*/
|
||||
if (!old_pstate->fb)
|
||||
writel(paddr, mxsfb->base + LCDC_AS_BUF);
|
||||
|
||||
ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255);
|
||||
|
||||
switch (state->fb->format->format) {
|
||||
case DRM_FORMAT_XRGB4444:
|
||||
ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
|
||||
break;
|
||||
}
|
||||
|
||||
writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
|
||||
.atomic_check = mxsfb_plane_atomic_check,
|
||||
.atomic_update = mxsfb_plane_primary_atomic_update,
|
||||
};
|
||||
|
||||
static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = {
|
||||
.atomic_check = mxsfb_plane_atomic_check,
|
||||
.atomic_update = mxsfb_plane_overlay_atomic_update,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs mxsfb_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
};
|
||||
|
||||
static const uint32_t mxsfb_primary_plane_formats[] = {
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
};
|
||||
|
||||
static const uint32_t mxsfb_overlay_plane_formats[] = {
|
||||
DRM_FORMAT_XRGB4444,
|
||||
DRM_FORMAT_ARGB4444,
|
||||
DRM_FORMAT_XRGB1555,
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
};
|
||||
|
||||
static const uint64_t mxsfb_modifiers[] = {
|
||||
DRM_FORMAT_MOD_LINEAR,
|
||||
DRM_FORMAT_MOD_INVALID
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_encoder *encoder = &mxsfb->encoder;
|
||||
struct drm_crtc *crtc = &mxsfb->crtc;
|
||||
int ret;
|
||||
|
||||
drm_plane_helper_add(&mxsfb->planes.primary,
|
||||
&mxsfb_plane_primary_helper_funcs);
|
||||
ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,
|
||||
&mxsfb_plane_funcs,
|
||||
mxsfb_primary_plane_formats,
|
||||
ARRAY_SIZE(mxsfb_primary_plane_formats),
|
||||
mxsfb_modifiers, DRM_PLANE_TYPE_PRIMARY,
|
||||
NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mxsfb->devdata->has_overlay) {
|
||||
drm_plane_helper_add(&mxsfb->planes.overlay,
|
||||
&mxsfb_plane_overlay_helper_funcs);
|
||||
ret = drm_universal_plane_init(mxsfb->drm,
|
||||
&mxsfb->planes.overlay, 1,
|
||||
&mxsfb_plane_funcs,
|
||||
mxsfb_overlay_plane_formats,
|
||||
ARRAY_SIZE(mxsfb_overlay_plane_formats),
|
||||
mxsfb_modifiers, DRM_PLANE_TYPE_OVERLAY,
|
||||
NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
|
||||
ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
|
||||
&mxsfb->planes.primary, NULL,
|
||||
&mxsfb_crtc_funcs, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
||||
return drm_encoder_init(mxsfb->drm, encoder, &mxsfb_encoder_funcs,
|
||||
DRM_MODE_ENCODER_NONE, NULL);
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче