mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 06:34:12 +08:00
drm-misc-next for v4.18:
UAPI Changes: - Add support for a generic plane alpha property to sun4i, rcar-du and atmel-hclcdc. (Maxime) Core Changes: - Stop looking at legacy plane->fb and crtc members in atomic drivers. (Ville) - mode_valid return type fixes. (Luc) - Handle zpos normalization in the core. (Peter) Driver Changes: - Implement CTM, plane alpha and generic async cursor support in vc4. (Stefan) - Various fixes for HPD and aux chan in drm_bridge/analogix_dp. (Lin, Zain, Douglas) - Add support for MIPI DSI to sun4i. (Maxime) -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAlrhqA0ACgkQ/lWMcqZw E8PcQQ//ZfZE5SgklBEuqil4X30z0y/ikFy3rEPhJOYpCGYjwDpOXoHjbgeIqFYD NWiYmeq8OfFBDQz8EkqalVJkqH10w2rcOJszM2t86FlSUWTmVxTqWjuVIIySkv67 UNJZo9+ppvwPahvO8aZqs90fJOGS2dqslgRa5v91S1IT+AZMZ7UNBlbhjCsmCPod TKypdZ4rOzlI7E3NR2CVSngPgLbUvTLnGDx0xKCej3Pp4MSW2g4kDNzHOPY82uLZ P2GyacmNN1yKTL9qfvqOoEWr8lu2bJuQ3mSzuYn6bBl8lHLPVEjdSMrtA3SGhM1v 4gTNRmnN1nCv+q3umLNSPLqKN6OHO1nstqbcTf753+g6gtJBGotCK2aWmTSKW8FC DtezLQIaFVf+Vyhr2XIgHfuDetk+f6E1u0/01yqxpGDP2WpQCzadbgIqCdJviMZS W9i/as2nJssg3ekoCbkA1leteAc088HPzFHVyqt4zKNTdCmUwtx/HdCoF2uXjsFV fTj7+sFkLHMQWdR3WqKQHqnxoknD1mfBbR7SCjvNXfhjQPGZQZgdyoJXvOhuKAnO cTygEZhCxlM1G0afStY18It/uZlNpxVz393nTiFtPp4RHmPO3jyIApTDPyqgXGCu 5L6MTBOrAntlOQ7kSWqrm9dbiv8ZDvPAhm7eer5txH7kQK2ywJo= =9LYY -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2018-04-26' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for v4.18: UAPI Changes: - Add support for a generic plane alpha property to sun4i, rcar-du and atmel-hclcdc. (Maxime) Core Changes: - Stop looking at legacy plane->fb and crtc members in atomic drivers. (Ville) - mode_valid return type fixes. (Luc) - Handle zpos normalization in the core. (Peter) Driver Changes: - Implement CTM, plane alpha and generic async cursor support in vc4. (Stefan) - Various fixes for HPD and aux chan in drm_bridge/analogix_dp. (Lin, Zain, Douglas) - Add support for MIPI DSI to sun4i. (Maxime) Signed-off-by: Dave Airlie <airlied@redhat.com> # gpg: Signature made Thu 26 Apr 2018 08:21:01 PM AEST # gpg: using RSA key FE558C72A67013C3 # gpg: Can't check signature: public key not found Link: https://patchwork.freedesktop.org/patch/msgid/b33da7eb-efc9-ae6f-6f69-b7acd6df6797@mblankhorst.nl
This commit is contained in:
commit
0ab390262c
@ -14,7 +14,13 @@ Required properties:
|
||||
"adi,adv7513"
|
||||
"adi,adv7533"
|
||||
|
||||
- reg: I2C slave address
|
||||
- reg: I2C slave addresses
|
||||
The ADV7511 internal registers are split into four pages exposed through
|
||||
different I2C addresses, creating four register maps. Each map has it own
|
||||
I2C address and acts as a standard slave device on the I2C bus. The main
|
||||
address is mandatory, others are optional and revert to defaults if not
|
||||
specified.
|
||||
|
||||
|
||||
The ADV7511 supports a large number of input data formats that differ by their
|
||||
color depth, color format, clock mode, bit justification and random
|
||||
@ -70,6 +76,9 @@ Optional properties:
|
||||
rather than generate its own timings for HDMI output.
|
||||
- clocks: from common clock binding: reference to the CEC clock.
|
||||
- clock-names: from common clock binding: must be "cec".
|
||||
- reg-names : Names of maps with programmable addresses.
|
||||
It can contain any map needing a non-default address.
|
||||
Possible maps names are : "main", "edid", "cec", "packet"
|
||||
|
||||
Required nodes:
|
||||
|
||||
@ -88,7 +97,12 @@ Example
|
||||
|
||||
adv7511w: hdmi@39 {
|
||||
compatible = "adi,adv7511w";
|
||||
reg = <39>;
|
||||
/*
|
||||
* The EDID page will be accessible on address 0x66 on the I2C
|
||||
* bus. All other maps continue to use their default addresses.
|
||||
*/
|
||||
reg = <0x39>, <0x66>;
|
||||
reg-names = "main", "edid";
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
|
||||
clocks = <&cec_clock>;
|
||||
|
133
Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
Normal file
133
Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
Normal file
@ -0,0 +1,133 @@
|
||||
Cadence DSI bridge
|
||||
==================
|
||||
|
||||
The Cadence DSI bridge is a DPI to DSI bridge supporting up to 4 DSI lanes.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be set to "cdns,dsi".
|
||||
- reg: physical base address and length of the controller's registers.
|
||||
- interrupts: interrupt line connected to the DSI bridge.
|
||||
- clocks: DSI bridge clocks.
|
||||
- clock-names: must contain "dsi_p_clk" and "dsi_sys_clk".
|
||||
- phys: phandle link to the MIPI D-PHY controller.
|
||||
- phy-names: must contain "dphy".
|
||||
- #address-cells: must be set to 1.
|
||||
- #size-cells: must be set to 0.
|
||||
|
||||
Optional properties:
|
||||
- resets: DSI reset lines.
|
||||
- reset-names: can contain "dsi_p_rst".
|
||||
|
||||
Required subnodes:
|
||||
- ports: Ports as described in Documentation/devicetree/bindings/graph.txt.
|
||||
2 ports are available:
|
||||
* port 0: this port is only needed if some of your DSI devices are
|
||||
controlled through an external bus like I2C or SPI. Can have at
|
||||
most 4 endpoints. The endpoint number is directly encoding the
|
||||
DSI virtual channel used by this device.
|
||||
* port 1: represents the DPI input.
|
||||
Other ports will be added later to support the new kind of inputs.
|
||||
|
||||
- one subnode per DSI device connected on the DSI bus. Each DSI device should
|
||||
contain a reg property encoding its virtual channel.
|
||||
|
||||
Cadence DPHY
|
||||
============
|
||||
|
||||
Cadence DPHY block.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be set to "cdns,dphy".
|
||||
- reg: physical base address and length of the DPHY registers.
|
||||
- clocks: DPHY reference clocks.
|
||||
- clock-names: must contain "psm" and "pll_ref".
|
||||
- #phy-cells: must be set to 0.
|
||||
|
||||
|
||||
Example:
|
||||
dphy0: dphy@fd0e0000{
|
||||
compatible = "cdns,dphy";
|
||||
reg = <0x0 0xfd0e0000 0x0 0x1000>;
|
||||
clocks = <&psm_clk>, <&pll_ref_clk>;
|
||||
clock-names = "psm", "pll_ref";
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
dsi0: dsi@fd0c0000 {
|
||||
compatible = "cdns,dsi";
|
||||
reg = <0x0 0xfd0c0000 0x0 0x1000>;
|
||||
clocks = <&pclk>, <&sysclk>;
|
||||
clock-names = "dsi_p_clk", "dsi_sys_clk";
|
||||
interrupts = <1>;
|
||||
phys = <&dphy0>;
|
||||
phy-names = "dphy";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
dsi0_dpi_input: endpoint {
|
||||
remote-endpoint = <&xxx_dpi_output>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
panel: dsi-dev@0 {
|
||||
compatible = "<vendor,panel>";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
or
|
||||
|
||||
dsi0: dsi@fd0c0000 {
|
||||
compatible = "cdns,dsi";
|
||||
reg = <0x0 0xfd0c0000 0x0 0x1000>;
|
||||
clocks = <&pclk>, <&sysclk>;
|
||||
clock-names = "dsi_p_clk", "dsi_sys_clk";
|
||||
interrupts = <1>;
|
||||
phys = <&dphy1>;
|
||||
phy-names = "dphy";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dsi0_output: endpoint@0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <&dsi_panel_input>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
dsi0_dpi_input: endpoint {
|
||||
remote-endpoint = <&xxx_dpi_output>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
i2c@xxx {
|
||||
panel: panel@59 {
|
||||
compatible = "<vendor,panel>";
|
||||
reg = <0x59>;
|
||||
|
||||
port {
|
||||
dsi_panel_input: endpoint {
|
||||
remote-endpoint = <&dsi0_output>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
Thine Electronics THC63LVD1024 LVDS decoder
|
||||
-------------------------------------------
|
||||
|
||||
The THC63LVD1024 is a dual link LVDS receiver designed to convert LVDS streams
|
||||
to parallel data outputs. The chip supports single/dual input/output modes,
|
||||
handling up to two LVDS input streams and up to two digital CMOS/TTL outputs.
|
||||
|
||||
Single or dual operation mode, output data mapping and DDR output modes are
|
||||
configured through input signals and the chip does not expose any control bus.
|
||||
|
||||
Required properties:
|
||||
- compatible: Shall be "thine,thc63lvd1024"
|
||||
- vcc-supply: Power supply for TTL output, TTL CLOCKOUT signal, LVDS input,
|
||||
PPL and digital circuitry
|
||||
|
||||
Optional properties:
|
||||
- powerdown-gpios: Power down GPIO signal, pin name "/PDWN". Active low
|
||||
- oe-gpios: Output enable GPIO signal, pin name "OE". Active high
|
||||
|
||||
The THC63LVD1024 video port connections are modeled according
|
||||
to OF graph bindings specified by Documentation/devicetree/bindings/graph.txt
|
||||
|
||||
Required video port nodes:
|
||||
- port@0: First LVDS input port
|
||||
- port@2: First digital CMOS/TTL parallel output
|
||||
|
||||
Optional video port nodes:
|
||||
- port@1: Second LVDS input port
|
||||
- port@3: Second digital CMOS/TTL parallel output
|
||||
|
||||
Example:
|
||||
--------
|
||||
|
||||
thc63lvd1024: lvds-decoder {
|
||||
compatible = "thine,thc63lvd1024";
|
||||
|
||||
vcc-supply = <®_lvds_vcc>;
|
||||
powerdown-gpios = <&gpio4 15 GPIO_ACTIVE_LOW>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
lvds_dec_in_0: endpoint {
|
||||
remote-endpoint = <&lvds_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2{
|
||||
reg = <2>;
|
||||
|
||||
lvds_dec_out_2: endpoint {
|
||||
remote-endpoint = <&adv7511_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,93 @@
|
||||
Allwinner A31 DSI Encoder
|
||||
=========================
|
||||
|
||||
The DSI pipeline consists of two separate blocks: the DSI controller
|
||||
itself, and its associated D-PHY.
|
||||
|
||||
DSI Encoder
|
||||
-----------
|
||||
|
||||
The DSI Encoder generates the DSI signal from the TCON's.
|
||||
|
||||
Required properties:
|
||||
- compatible: value must be one of:
|
||||
* allwinner,sun6i-a31-mipi-dsi
|
||||
- reg: base address and size of memory-mapped region
|
||||
- interrupts: interrupt associated to this IP
|
||||
- clocks: phandles to the clocks feeding the DSI encoder
|
||||
* bus: the DSI interface clock
|
||||
* mod: the DSI module clock
|
||||
- clock-names: the clock names mentioned above
|
||||
- phys: phandle to the D-PHY
|
||||
- phy-names: must be "dphy"
|
||||
- resets: phandle to the reset controller driving the encoder
|
||||
|
||||
- ports: A ports node with endpoint definitions as defined in
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt. The
|
||||
first port should be the input endpoint, usually coming from the
|
||||
associated TCON.
|
||||
|
||||
Any MIPI-DSI device attached to this should be described according to
|
||||
the bindings defined in ../mipi-dsi-bus.txt
|
||||
|
||||
D-PHY
|
||||
-----
|
||||
|
||||
Required properties:
|
||||
- compatible: value must be one of:
|
||||
* allwinner,sun6i-a31-mipi-dphy
|
||||
- reg: base address and size of memory-mapped region
|
||||
- clocks: phandles to the clocks feeding the DSI encoder
|
||||
* bus: the DSI interface clock
|
||||
* mod: the DSI module clock
|
||||
- clock-names: the clock names mentioned above
|
||||
- resets: phandle to the reset controller driving the encoder
|
||||
|
||||
Example:
|
||||
|
||||
dsi0: dsi@1ca0000 {
|
||||
compatible = "allwinner,sun6i-a31-mipi-dsi";
|
||||
reg = <0x01ca0000 0x1000>;
|
||||
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu CLK_BUS_MIPI_DSI>,
|
||||
<&ccu CLK_DSI_SCLK>;
|
||||
clock-names = "bus", "mod";
|
||||
resets = <&ccu RST_BUS_MIPI_DSI>;
|
||||
phys = <&dphy0>;
|
||||
phy-names = "dphy";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "bananapi,lhr050h41", "ilitek,ili9881c";
|
||||
reg = <0>;
|
||||
power-gpios = <&pio 1 7 GPIO_ACTIVE_HIGH>; /* PB07 */
|
||||
reset-gpios = <&r_pio 0 5 GPIO_ACTIVE_LOW>; /* PL05 */
|
||||
backlight = <&pwm_bl>;
|
||||
};
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
|
||||
dsi0_in_tcon0: endpoint {
|
||||
remote-endpoint = <&tcon0_out_dsi0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
dphy0: d-phy@1ca1000 {
|
||||
compatible = "allwinner,sun6i-a31-mipi-dphy";
|
||||
reg = <0x01ca1000 0x1000>;
|
||||
clocks = <&ccu CLK_BUS_MIPI_DSI>,
|
||||
<&ccu CLK_DSI_DPHY>;
|
||||
clock-names = "bus", "mod";
|
||||
resets = <&ccu RST_BUS_MIPI_DSI>;
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -12,6 +12,7 @@ GPU Driver Documentation
|
||||
tve200
|
||||
vc4
|
||||
bridge/dw-hdmi
|
||||
xen-front
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
@ -98,5 +98,4 @@ radeon,DVI-I,“coherent”,RANGE,"Min=0, Max=1",Connector,TBD
|
||||
,,"""underscan vborder""",RANGE,"Min=0, Max=128",Connector,TBD
|
||||
,Audio,“audio”,ENUM,"{ ""off"", ""on"", ""auto"" }",Connector,TBD
|
||||
,FMT Dithering,“dither”,ENUM,"{ ""off"", ""on"" }",Connector,TBD
|
||||
rcar-du,Generic,"""alpha""",RANGE,"Min=0, Max=255",Plane,TBD
|
||||
,,"""colorkey""",RANGE,"Min=0, Max=0x01ffffff",Plane,TBD
|
||||
|
|
@ -212,6 +212,24 @@ probably use drm_fb_helper_fbdev_teardown().
|
||||
|
||||
Contact: Maintainer of the driver you plan to convert
|
||||
|
||||
Clean up mmap forwarding
|
||||
------------------------
|
||||
|
||||
A lot of drivers forward gem mmap calls to dma-buf mmap for imported buffers.
|
||||
And also a lot of them forward dma-buf mmap to the gem mmap implementations.
|
||||
Would be great to refactor this all into a set of small common helpers.
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Put a reservation_object into drm_gem_object
|
||||
--------------------------------------------
|
||||
|
||||
This would remove the need for the ->gem_prime_res_obj callback. It would also
|
||||
allow us to implement generic helpers for waiting for a bo, allowing for quite a
|
||||
bit of refactoring in the various wait ioctl implementations.
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
idr_init_base()
|
||||
---------------
|
||||
|
||||
|
31
Documentation/gpu/xen-front.rst
Normal file
31
Documentation/gpu/xen-front.rst
Normal file
@ -0,0 +1,31 @@
|
||||
====================================================
|
||||
drm/xen-front Xen para-virtualized frontend driver
|
||||
====================================================
|
||||
|
||||
This frontend driver implements Xen para-virtualized display
|
||||
according to the display protocol described at
|
||||
include/xen/interface/io/displif.h
|
||||
|
||||
Driver modes of operation in terms of display buffers used
|
||||
==========================================================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
|
||||
:doc: Driver modes of operation in terms of display buffers used
|
||||
|
||||
Buffers allocated by the frontend driver
|
||||
----------------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
|
||||
:doc: Buffers allocated by the frontend driver
|
||||
|
||||
Buffers allocated by the backend
|
||||
--------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
|
||||
:doc: Buffers allocated by the backend
|
||||
|
||||
Driver limitations
|
||||
==================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
|
||||
:doc: Driver limitations
|
@ -4830,6 +4830,15 @@ S: Maintained
|
||||
F: drivers/gpu/drm/tinydrm/
|
||||
F: include/drm/tinydrm/
|
||||
|
||||
DRM DRIVERS FOR XEN
|
||||
M: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: xen-devel@lists.xen.org
|
||||
S: Supported
|
||||
F: drivers/gpu/drm/xen/
|
||||
F: Documentation/gpu/xen-front.rst
|
||||
|
||||
DRM TTM SUBSYSTEM
|
||||
M: Christian Koenig <christian.koenig@amd.com>
|
||||
M: Roger He <Hongbo.He@amd.com>
|
||||
|
@ -289,6 +289,8 @@ source "drivers/gpu/drm/pl111/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/tve200/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/xen/Kconfig"
|
||||
|
||||
# Keep legacy drivers last
|
||||
|
||||
menuconfig DRM_LEGACY
|
||||
|
@ -103,3 +103,4 @@ obj-$(CONFIG_DRM_MXSFB) += mxsfb/
|
||||
obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
|
||||
obj-$(CONFIG_DRM_PL111) += pl111/
|
||||
obj-$(CONFIG_DRM_TVE200) += tve200/
|
||||
obj-$(CONFIG_DRM_XEN) += xen/
|
||||
|
@ -799,7 +799,7 @@ static int ast_get_modes(struct drm_connector *connector)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status ast_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct ast_private *ast = connector->dev->dev_private;
|
||||
|
@ -299,7 +299,6 @@ struct atmel_hlcdc_layer {
|
||||
struct atmel_hlcdc_plane {
|
||||
struct drm_plane base;
|
||||
struct atmel_hlcdc_layer layer;
|
||||
struct atmel_hlcdc_plane_properties *properties;
|
||||
};
|
||||
|
||||
static inline struct atmel_hlcdc_plane *
|
||||
@ -345,18 +344,6 @@ struct atmel_hlcdc_dc_desc {
|
||||
int nlayers;
|
||||
};
|
||||
|
||||
/**
|
||||
* Atmel HLCDC Plane properties.
|
||||
*
|
||||
* This structure stores plane property definitions.
|
||||
*
|
||||
* @alpha: alpha blending (or transparency) property
|
||||
* @rotation: rotation property
|
||||
*/
|
||||
struct atmel_hlcdc_plane_properties {
|
||||
struct drm_property *alpha;
|
||||
};
|
||||
|
||||
/**
|
||||
* Atmel HLCDC Display Controller.
|
||||
*
|
||||
|
@ -31,7 +31,6 @@
|
||||
* @src_y: y buffer position
|
||||
* @src_w: buffer width
|
||||
* @src_h: buffer height
|
||||
* @alpha: alpha blending of the plane
|
||||
* @disc_x: x discard position
|
||||
* @disc_y: y discard position
|
||||
* @disc_w: discard width
|
||||
@ -54,8 +53,6 @@ struct atmel_hlcdc_plane_state {
|
||||
uint32_t src_w;
|
||||
uint32_t src_h;
|
||||
|
||||
u8 alpha;
|
||||
|
||||
int disc_x;
|
||||
int disc_y;
|
||||
int disc_w;
|
||||
@ -385,7 +382,7 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
|
||||
cfg |= ATMEL_HLCDC_LAYER_LAEN;
|
||||
else
|
||||
cfg |= ATMEL_HLCDC_LAYER_GAEN |
|
||||
ATMEL_HLCDC_LAYER_GA(state->alpha);
|
||||
ATMEL_HLCDC_LAYER_GA(state->base.alpha >> 8);
|
||||
}
|
||||
|
||||
if (state->disc_h && state->disc_w)
|
||||
@ -553,7 +550,7 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
|
||||
|
||||
if (!ovl_s->fb ||
|
||||
ovl_s->fb->format->has_alpha ||
|
||||
ovl_state->alpha != 255)
|
||||
ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE)
|
||||
continue;
|
||||
|
||||
/* TODO: implement a smarter hidden area detection */
|
||||
@ -829,51 +826,18 @@ static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
|
||||
drm_plane_cleanup(p);
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
|
||||
struct drm_plane_state *s,
|
||||
struct drm_property *property,
|
||||
uint64_t val)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
struct atmel_hlcdc_plane_properties *props = plane->properties;
|
||||
struct atmel_hlcdc_plane_state *state =
|
||||
drm_plane_state_to_atmel_hlcdc_plane_state(s);
|
||||
|
||||
if (property == props->alpha)
|
||||
state->alpha = val;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
|
||||
const struct drm_plane_state *s,
|
||||
struct drm_property *property,
|
||||
uint64_t *val)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
struct atmel_hlcdc_plane_properties *props = plane->properties;
|
||||
const struct atmel_hlcdc_plane_state *state =
|
||||
container_of(s, const struct atmel_hlcdc_plane_state, base);
|
||||
|
||||
if (property == props->alpha)
|
||||
*val = state->alpha;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_plane_properties *props)
|
||||
static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)
|
||||
{
|
||||
const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
|
||||
|
||||
if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
|
||||
desc->type == ATMEL_HLCDC_CURSOR_LAYER)
|
||||
drm_object_attach_property(&plane->base.base,
|
||||
props->alpha, 255);
|
||||
desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
|
||||
int ret;
|
||||
|
||||
ret = drm_plane_create_alpha_property(&plane->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (desc->layout.xstride && desc->layout.pstride) {
|
||||
int ret;
|
||||
@ -988,8 +952,8 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p)
|
||||
return;
|
||||
}
|
||||
|
||||
state->alpha = 255;
|
||||
p->state = &state->base;
|
||||
p->state->alpha = DRM_BLEND_ALPHA_OPAQUE;
|
||||
p->state->plane = p;
|
||||
}
|
||||
}
|
||||
@ -1042,13 +1006,10 @@ static const struct drm_plane_funcs layer_plane_funcs = {
|
||||
.reset = atmel_hlcdc_plane_reset,
|
||||
.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
|
||||
.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
|
||||
.atomic_set_property = atmel_hlcdc_plane_atomic_set_property,
|
||||
.atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
|
||||
};
|
||||
|
||||
static int atmel_hlcdc_plane_create(struct drm_device *dev,
|
||||
const struct atmel_hlcdc_layer_desc *desc,
|
||||
struct atmel_hlcdc_plane_properties *props)
|
||||
const struct atmel_hlcdc_layer_desc *desc)
|
||||
{
|
||||
struct atmel_hlcdc_dc *dc = dev->dev_private;
|
||||
struct atmel_hlcdc_plane *plane;
|
||||
@ -1060,7 +1021,6 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
|
||||
return -ENOMEM;
|
||||
|
||||
atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
|
||||
plane->properties = props;
|
||||
|
||||
if (desc->type == ATMEL_HLCDC_BASE_LAYER)
|
||||
type = DRM_PLANE_TYPE_PRIMARY;
|
||||
@ -1081,7 +1041,7 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
|
||||
&atmel_hlcdc_layer_plane_helper_funcs);
|
||||
|
||||
/* Set default property values*/
|
||||
ret = atmel_hlcdc_plane_init_properties(plane, props);
|
||||
ret = atmel_hlcdc_plane_init_properties(plane);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1090,34 +1050,13 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct atmel_hlcdc_plane_properties *
|
||||
atmel_hlcdc_plane_create_properties(struct drm_device *dev)
|
||||
{
|
||||
struct atmel_hlcdc_plane_properties *props;
|
||||
|
||||
props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
|
||||
if (!props)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
|
||||
if (!props->alpha)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
int atmel_hlcdc_create_planes(struct drm_device *dev)
|
||||
{
|
||||
struct atmel_hlcdc_dc *dc = dev->dev_private;
|
||||
struct atmel_hlcdc_plane_properties *props;
|
||||
const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
|
||||
int nlayers = dc->desc->nlayers;
|
||||
int i, ret;
|
||||
|
||||
props = atmel_hlcdc_plane_create_properties(dev);
|
||||
if (IS_ERR(props))
|
||||
return PTR_ERR(props);
|
||||
|
||||
dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
|
||||
sizeof(struct atmel_hlcdc_dma_channel_dscr),
|
||||
sizeof(u64), 0);
|
||||
@ -1130,7 +1069,7 @@ int atmel_hlcdc_create_planes(struct drm_device *dev)
|
||||
descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
|
||||
continue;
|
||||
|
||||
ret = atmel_hlcdc_plane_create(dev, &descs[i], props);
|
||||
ret = atmel_hlcdc_plane_create(dev, &descs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ static int bochs_connector_get_modes(struct drm_connector *connector)
|
||||
return count;
|
||||
}
|
||||
|
||||
static int bochs_connector_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct bochs_device *bochs =
|
||||
|
@ -25,6 +25,16 @@ config DRM_ANALOGIX_ANX78XX
|
||||
the HDMI output of an application processor to MyDP
|
||||
or DisplayPort.
|
||||
|
||||
config DRM_CDNS_DSI
|
||||
tristate "Cadence DPI/DSI bridge"
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_MIPI_DSI
|
||||
select DRM_PANEL_BRIDGE
|
||||
depends on OF
|
||||
help
|
||||
Support Cadence DPI to DSI bridge. This is an internal
|
||||
bridge and is meant to be directly embedded in a SoC.
|
||||
|
||||
config DRM_DUMB_VGA_DAC
|
||||
tristate "Dumb VGA DAC Bridge support"
|
||||
depends on OF
|
||||
@ -93,6 +103,12 @@ config DRM_SII9234
|
||||
It is an I2C driver, that detects connection of MHL bridge
|
||||
and starts encapsulation of HDMI signal.
|
||||
|
||||
config DRM_THINE_THC63LVD1024
|
||||
tristate "Thine THC63LVD1024 LVDS decoder bridge"
|
||||
depends on OF
|
||||
---help---
|
||||
Thine THC63LVD1024 LVDS/parallel converter driver.
|
||||
|
||||
config DRM_TOSHIBA_TC358767
|
||||
tristate "Toshiba TC358767 eDP bridge"
|
||||
depends on OF
|
||||
|
@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
|
||||
obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
|
||||
obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
|
||||
obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o
|
||||
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
|
||||
@ -8,6 +9,7 @@ obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
|
||||
obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
|
||||
obj-$(CONFIG_DRM_SII902X) += sii902x.o
|
||||
obj-$(CONFIG_DRM_SII9234) += sii9234.o
|
||||
obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
|
||||
obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
|
||||
|
@ -93,6 +93,11 @@
|
||||
#define ADV7511_REG_CHIP_ID_HIGH 0xf5
|
||||
#define ADV7511_REG_CHIP_ID_LOW 0xf6
|
||||
|
||||
/* Hardware defined default addresses for I2C register maps */
|
||||
#define ADV7511_CEC_I2C_ADDR_DEFAULT 0x3c
|
||||
#define ADV7511_EDID_I2C_ADDR_DEFAULT 0x3f
|
||||
#define ADV7511_PACKET_I2C_ADDR_DEFAULT 0x38
|
||||
|
||||
#define ADV7511_CSC_ENABLE BIT(7)
|
||||
#define ADV7511_CSC_UPDATE_MODE BIT(5)
|
||||
|
||||
@ -321,6 +326,7 @@ enum adv7511_type {
|
||||
struct adv7511 {
|
||||
struct i2c_client *i2c_main;
|
||||
struct i2c_client *i2c_edid;
|
||||
struct i2c_client *i2c_packet;
|
||||
struct i2c_client *i2c_cec;
|
||||
|
||||
struct regmap *regmap;
|
||||
|
@ -586,7 +586,7 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
|
||||
/* Reading the EDID only works if the device is powered */
|
||||
if (!adv7511->powered) {
|
||||
unsigned int edid_i2c_addr =
|
||||
(adv7511->i2c_main->addr << 1) + 4;
|
||||
(adv7511->i2c_edid->addr << 1);
|
||||
|
||||
__adv7511_power_on(adv7511);
|
||||
|
||||
@ -654,7 +654,7 @@ adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int adv7511_mode_valid(struct adv7511 *adv7511,
|
||||
static enum drm_mode_status adv7511_mode_valid(struct adv7511 *adv7511,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock > 165000)
|
||||
@ -969,10 +969,10 @@ static int adv7511_init_cec_regmap(struct adv7511 *adv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter,
|
||||
adv->i2c_main->addr - 1);
|
||||
adv->i2c_cec = i2c_new_secondary_device(adv->i2c_main, "cec",
|
||||
ADV7511_CEC_I2C_ADDR_DEFAULT);
|
||||
if (!adv->i2c_cec)
|
||||
return -ENOMEM;
|
||||
return -EINVAL;
|
||||
i2c_set_clientdata(adv->i2c_cec, adv);
|
||||
|
||||
adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
|
||||
@ -1082,8 +1082,6 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
struct adv7511_link_config link_config;
|
||||
struct adv7511 *adv7511;
|
||||
struct device *dev = &i2c->dev;
|
||||
unsigned int main_i2c_addr = i2c->addr << 1;
|
||||
unsigned int edid_i2c_addr = main_i2c_addr + 4;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
@ -1153,23 +1151,34 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
if (ret)
|
||||
goto uninit_regulators;
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr);
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR,
|
||||
main_i2c_addr - 0xa);
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR,
|
||||
main_i2c_addr - 2);
|
||||
|
||||
adv7511_packet_disable(adv7511, 0xffff);
|
||||
|
||||
adv7511->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1);
|
||||
adv7511->i2c_edid = i2c_new_secondary_device(i2c, "edid",
|
||||
ADV7511_EDID_I2C_ADDR_DEFAULT);
|
||||
if (!adv7511->i2c_edid) {
|
||||
ret = -ENOMEM;
|
||||
ret = -EINVAL;
|
||||
goto uninit_regulators;
|
||||
}
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR,
|
||||
adv7511->i2c_edid->addr << 1);
|
||||
|
||||
adv7511->i2c_packet = i2c_new_secondary_device(i2c, "packet",
|
||||
ADV7511_PACKET_I2C_ADDR_DEFAULT);
|
||||
if (!adv7511->i2c_packet) {
|
||||
ret = -EINVAL;
|
||||
goto err_i2c_unregister_edid;
|
||||
}
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR,
|
||||
adv7511->i2c_packet->addr << 1);
|
||||
|
||||
ret = adv7511_init_cec_regmap(adv7511);
|
||||
if (ret)
|
||||
goto err_i2c_unregister_edid;
|
||||
goto err_i2c_unregister_packet;
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR,
|
||||
adv7511->i2c_cec->addr << 1);
|
||||
|
||||
INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
|
||||
|
||||
@ -1207,6 +1216,8 @@ err_unregister_cec:
|
||||
i2c_unregister_device(adv7511->i2c_cec);
|
||||
if (adv7511->cec_clk)
|
||||
clk_disable_unprepare(adv7511->cec_clk);
|
||||
err_i2c_unregister_packet:
|
||||
i2c_unregister_device(adv7511->i2c_packet);
|
||||
err_i2c_unregister_edid:
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
uninit_regulators:
|
||||
@ -1233,6 +1244,7 @@ static int adv7511_remove(struct i2c_client *i2c)
|
||||
|
||||
cec_unregister_adapter(adv7511->cec_adap);
|
||||
|
||||
i2c_unregister_device(adv7511->i2c_packet);
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
|
||||
return 0;
|
||||
|
@ -43,8 +43,10 @@ struct bridge_init {
|
||||
struct device_node *node;
|
||||
};
|
||||
|
||||
static void analogix_dp_init_dp(struct analogix_dp_device *dp)
|
||||
static int analogix_dp_init_dp(struct analogix_dp_device *dp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
analogix_dp_reset(dp);
|
||||
|
||||
analogix_dp_swreset(dp);
|
||||
@ -56,10 +58,13 @@ static void analogix_dp_init_dp(struct analogix_dp_device *dp)
|
||||
analogix_dp_enable_sw_function(dp);
|
||||
|
||||
analogix_dp_config_interrupt(dp);
|
||||
analogix_dp_init_analog_func(dp);
|
||||
ret = analogix_dp_init_analog_func(dp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
analogix_dp_init_hpd(dp);
|
||||
analogix_dp_init_aux(dp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
|
||||
@ -71,7 +76,7 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
|
||||
return 0;
|
||||
|
||||
timeout_loop++;
|
||||
usleep_range(10, 11);
|
||||
usleep_range(1000, 1100);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -148,87 +153,146 @@ int analogix_dp_disable_psr(struct analogix_dp_device *dp)
|
||||
psr_vsc.DB1 = 0;
|
||||
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
|
||||
if (ret != 1)
|
||||
if (ret != 1) {
|
||||
dev_err(dp->dev, "Failed to set DP Power0 %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return analogix_dp_send_psr_spd(dp, &psr_vsc, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
|
||||
|
||||
static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
|
||||
static int analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
|
||||
{
|
||||
unsigned char psr_version;
|
||||
int ret;
|
||||
|
||||
ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version);
|
||||
if (ret != 1) {
|
||||
dev_err(dp->dev, "failed to get PSR version, disable it\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version);
|
||||
dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
|
||||
|
||||
return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
|
||||
dp->psr_enable = (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
|
||||
static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
|
||||
{
|
||||
unsigned char psr_en;
|
||||
int ret;
|
||||
|
||||
/* Disable psr function */
|
||||
drm_dp_dpcd_readb(&dp->aux, DP_PSR_EN_CFG, &psr_en);
|
||||
ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_EN_CFG, &psr_en);
|
||||
if (ret != 1) {
|
||||
dev_err(dp->dev, "failed to get psr config\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
psr_en &= ~DP_PSR_ENABLE;
|
||||
drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
|
||||
if (ret != 1) {
|
||||
dev_err(dp->dev, "failed to disable panel psr\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Main-Link transmitter remains active during PSR active states */
|
||||
psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
|
||||
drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
|
||||
if (ret != 1) {
|
||||
dev_err(dp->dev, "failed to set panel psr\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Enable psr function */
|
||||
psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
|
||||
DP_PSR_CRC_VERIFICATION;
|
||||
drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
|
||||
if (ret != 1) {
|
||||
dev_err(dp->dev, "failed to set panel psr\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
analogix_dp_enable_psr_crc(dp);
|
||||
|
||||
return 0;
|
||||
end:
|
||||
dev_err(dp->dev, "enable psr fail, force to disable psr\n");
|
||||
dp->psr_enable = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp,
|
||||
bool enable)
|
||||
{
|
||||
u8 data;
|
||||
int ret;
|
||||
|
||||
drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &data);
|
||||
ret = drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &data);
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
|
||||
DP_LANE_COUNT_ENHANCED_FRAME_EN |
|
||||
DPCD_LANE_COUNT_SET(data));
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
|
||||
DP_LANE_COUNT_ENHANCED_FRAME_EN |
|
||||
DPCD_LANE_COUNT_SET(data));
|
||||
else
|
||||
drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
|
||||
DPCD_LANE_COUNT_SET(data));
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
|
||||
DPCD_LANE_COUNT_SET(data));
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp)
|
||||
static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp,
|
||||
u8 *enhanced_mode_support)
|
||||
{
|
||||
u8 data;
|
||||
int retval;
|
||||
int ret;
|
||||
|
||||
drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data);
|
||||
retval = DPCD_ENHANCED_FRAME_CAP(data);
|
||||
ret = drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data);
|
||||
if (ret != 1) {
|
||||
*enhanced_mode_support = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return retval;
|
||||
*enhanced_mode_support = DPCD_ENHANCED_FRAME_CAP(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void analogix_dp_set_enhanced_mode(struct analogix_dp_device *dp)
|
||||
static int analogix_dp_set_enhanced_mode(struct analogix_dp_device *dp)
|
||||
{
|
||||
u8 data;
|
||||
int ret;
|
||||
|
||||
ret = analogix_dp_is_enhanced_mode_available(dp, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = analogix_dp_enable_rx_to_enhanced_mode(dp, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data = analogix_dp_is_enhanced_mode_available(dp);
|
||||
analogix_dp_enable_rx_to_enhanced_mode(dp, data);
|
||||
analogix_dp_enable_enhanced_mode(dp, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp)
|
||||
static int analogix_dp_training_pattern_dis(struct analogix_dp_device *dp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
analogix_dp_set_training_pattern(dp, DP_NONE);
|
||||
|
||||
drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
DP_TRAINING_PATTERN_DISABLE);
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
DP_TRAINING_PATTERN_DISABLE);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -276,6 +340,12 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
|
||||
retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
/* set enhanced mode if available */
|
||||
retval = analogix_dp_set_enhanced_mode(dp);
|
||||
if (retval < 0) {
|
||||
dev_err(dp->dev, "failed to set enhance mode\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Set TX pre-emphasis to minimum */
|
||||
for (lane = 0; lane < lane_count; lane++)
|
||||
@ -531,7 +601,7 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
|
||||
{
|
||||
int lane, lane_count, retval;
|
||||
u32 reg;
|
||||
u8 link_align, link_status[2], adjust_request[2], spread;
|
||||
u8 link_align, link_status[2], adjust_request[2];
|
||||
|
||||
usleep_range(400, 401);
|
||||
|
||||
@ -560,10 +630,11 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
|
||||
|
||||
if (!analogix_dp_channel_eq_ok(link_status, link_align, lane_count)) {
|
||||
/* traing pattern Set to Normal */
|
||||
analogix_dp_training_pattern_dis(dp);
|
||||
retval = analogix_dp_training_pattern_dis(dp);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
dev_info(dp->dev, "Link Training success!\n");
|
||||
|
||||
analogix_dp_get_link_bandwidth(dp, ®);
|
||||
dp->link_train.link_rate = reg;
|
||||
dev_dbg(dp->dev, "final bandwidth = %.2x\n",
|
||||
@ -574,22 +645,6 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
|
||||
dev_dbg(dp->dev, "final lane count = %.2x\n",
|
||||
dp->link_train.lane_count);
|
||||
|
||||
retval = drm_dp_dpcd_readb(&dp->aux, DP_MAX_DOWNSPREAD,
|
||||
&spread);
|
||||
if (retval != 1) {
|
||||
dev_err(dp->dev, "failed to read downspread %d\n",
|
||||
retval);
|
||||
dp->fast_train_support = false;
|
||||
} else {
|
||||
dp->fast_train_support =
|
||||
(spread & DP_NO_AUX_HANDSHAKE_LINK_TRAINING) ?
|
||||
true : false;
|
||||
}
|
||||
dev_dbg(dp->dev, "fast link training %s\n",
|
||||
dp->fast_train_support ? "supported" : "unsupported");
|
||||
|
||||
/* set enhanced mode if available */
|
||||
analogix_dp_set_enhanced_mode(dp);
|
||||
dp->link_train.lt_state = FINISHED;
|
||||
|
||||
return 0;
|
||||
@ -793,7 +848,7 @@ static int analogix_dp_fast_link_train(struct analogix_dp_device *dp)
|
||||
|
||||
static int analogix_dp_train_link(struct analogix_dp_device *dp)
|
||||
{
|
||||
if (dp->fast_train_support)
|
||||
if (dp->fast_train_enable)
|
||||
return analogix_dp_fast_link_train(dp);
|
||||
|
||||
return analogix_dp_full_link_train(dp, dp->video_info.max_lane_count,
|
||||
@ -819,11 +874,10 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
|
||||
if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0)
|
||||
break;
|
||||
if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
|
||||
dev_err(dp->dev, "Timeout of video streamclk ok\n");
|
||||
dev_err(dp->dev, "Timeout of slave video streamclk ok\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
usleep_range(1, 2);
|
||||
usleep_range(1000, 1001);
|
||||
}
|
||||
|
||||
/* Set to use the register calculated M/N video */
|
||||
@ -838,6 +892,9 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
|
||||
/* Configure video slave mode */
|
||||
analogix_dp_enable_video_master(dp, 0);
|
||||
|
||||
/* Enable video */
|
||||
analogix_dp_start_video(dp);
|
||||
|
||||
timeout_loop = 0;
|
||||
|
||||
for (;;) {
|
||||
@ -850,8 +907,9 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
|
||||
done_count = 0;
|
||||
}
|
||||
if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
|
||||
dev_err(dp->dev, "Timeout of video streamclk ok\n");
|
||||
return -ETIMEDOUT;
|
||||
dev_warn(dp->dev,
|
||||
"Ignoring timeout of video streamclk ok\n");
|
||||
break;
|
||||
}
|
||||
|
||||
usleep_range(1000, 1001);
|
||||
@ -860,24 +918,32 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void analogix_dp_enable_scramble(struct analogix_dp_device *dp,
|
||||
bool enable)
|
||||
static int analogix_dp_enable_scramble(struct analogix_dp_device *dp,
|
||||
bool enable)
|
||||
{
|
||||
u8 data;
|
||||
int ret;
|
||||
|
||||
if (enable) {
|
||||
analogix_dp_enable_scrambling(dp);
|
||||
|
||||
drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data);
|
||||
drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
ret = drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
&data);
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
(u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
|
||||
} else {
|
||||
analogix_dp_disable_scrambling(dp);
|
||||
|
||||
drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data);
|
||||
drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
ret = drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
&data);
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
|
||||
(u8)(data | DP_LINK_SCRAMBLING_DISABLE));
|
||||
}
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static irqreturn_t analogix_dp_hardirq(int irq, void *arg)
|
||||
@ -916,7 +982,23 @@ static irqreturn_t analogix_dp_irq_thread(int irq, void *arg)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void analogix_dp_commit(struct analogix_dp_device *dp)
|
||||
static int analogix_dp_fast_link_train_detection(struct analogix_dp_device *dp)
|
||||
{
|
||||
int ret;
|
||||
u8 spread;
|
||||
|
||||
ret = drm_dp_dpcd_readb(&dp->aux, DP_MAX_DOWNSPREAD, &spread);
|
||||
if (ret != 1) {
|
||||
dev_err(dp->dev, "failed to read downspread %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dp->fast_train_enable = !!(spread & DP_NO_AUX_HANDSHAKE_LINK_TRAINING);
|
||||
dev_dbg(dp->dev, "fast link training %s\n",
|
||||
dp->fast_train_enable ? "supported" : "unsupported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int analogix_dp_commit(struct analogix_dp_device *dp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -926,34 +1008,50 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
|
||||
DRM_ERROR("failed to disable the panel\n");
|
||||
}
|
||||
|
||||
ret = readx_poll_timeout(analogix_dp_train_link, dp, ret, !ret, 100,
|
||||
DP_TIMEOUT_TRAINING_US * 5);
|
||||
ret = analogix_dp_train_link(dp);
|
||||
if (ret) {
|
||||
dev_err(dp->dev, "unable to do link train, ret=%d\n", ret);
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
|
||||
analogix_dp_enable_scramble(dp, 1);
|
||||
analogix_dp_enable_rx_to_enhanced_mode(dp, 1);
|
||||
analogix_dp_enable_enhanced_mode(dp, 1);
|
||||
ret = analogix_dp_enable_scramble(dp, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "can not enable scramble\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
analogix_dp_init_video(dp);
|
||||
ret = analogix_dp_config_video(dp);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(dp->dev, "unable to config video\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Safe to enable the panel now */
|
||||
if (dp->plat_data->panel) {
|
||||
if (drm_panel_enable(dp->plat_data->panel))
|
||||
ret = drm_panel_enable(dp->plat_data->panel);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to enable the panel\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable video */
|
||||
analogix_dp_start_video(dp);
|
||||
ret = analogix_dp_detect_sink_psr(dp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dp->psr_enable = analogix_dp_detect_sink_psr(dp);
|
||||
if (dp->psr_enable)
|
||||
analogix_dp_enable_sink_psr(dp);
|
||||
if (dp->psr_enable) {
|
||||
ret = analogix_dp_enable_sink_psr(dp);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check whether panel supports fast training */
|
||||
ret = analogix_dp_fast_link_train_detection(dp);
|
||||
if (ret)
|
||||
dp->psr_enable = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1150,24 +1248,80 @@ static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge)
|
||||
DRM_ERROR("failed to setup the panel ret = %d\n", ret);
|
||||
}
|
||||
|
||||
static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_sync(dp->dev);
|
||||
|
||||
ret = clk_prepare_enable(dp->clock);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret);
|
||||
goto out_dp_clk_pre;
|
||||
}
|
||||
|
||||
if (dp->plat_data->power_on_start)
|
||||
dp->plat_data->power_on_start(dp->plat_data);
|
||||
|
||||
phy_power_on(dp->phy);
|
||||
|
||||
ret = analogix_dp_init_dp(dp);
|
||||
if (ret)
|
||||
goto out_dp_init;
|
||||
|
||||
/*
|
||||
* According to DP spec v1.3 chap 3.5.1.2 Link Training,
|
||||
* We should first make sure the HPD signal is asserted high by device
|
||||
* when we want to establish a link with it.
|
||||
*/
|
||||
ret = analogix_dp_detect_hpd(dp);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to get hpd single ret = %d\n", ret);
|
||||
goto out_dp_init;
|
||||
}
|
||||
|
||||
ret = analogix_dp_commit(dp);
|
||||
if (ret) {
|
||||
DRM_ERROR("dp commit error, ret = %d\n", ret);
|
||||
goto out_dp_init;
|
||||
}
|
||||
|
||||
if (dp->plat_data->power_on_end)
|
||||
dp->plat_data->power_on_end(dp->plat_data);
|
||||
|
||||
enable_irq(dp->irq);
|
||||
return 0;
|
||||
|
||||
out_dp_init:
|
||||
phy_power_off(dp->phy);
|
||||
if (dp->plat_data->power_off)
|
||||
dp->plat_data->power_off(dp->plat_data);
|
||||
clk_disable_unprepare(dp->clock);
|
||||
out_dp_clk_pre:
|
||||
pm_runtime_put_sync(dp->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct analogix_dp_device *dp = bridge->driver_private;
|
||||
int timeout_loop = 0;
|
||||
|
||||
if (dp->dpms_mode == DRM_MODE_DPMS_ON)
|
||||
return;
|
||||
|
||||
pm_runtime_get_sync(dp->dev);
|
||||
|
||||
if (dp->plat_data->power_on)
|
||||
dp->plat_data->power_on(dp->plat_data);
|
||||
|
||||
phy_power_on(dp->phy);
|
||||
analogix_dp_init_dp(dp);
|
||||
enable_irq(dp->irq);
|
||||
analogix_dp_commit(dp);
|
||||
|
||||
dp->dpms_mode = DRM_MODE_DPMS_ON;
|
||||
while (timeout_loop < MAX_PLL_LOCK_LOOP) {
|
||||
if (analogix_dp_set_bridge(dp) == 0) {
|
||||
dp->dpms_mode = DRM_MODE_DPMS_ON;
|
||||
return;
|
||||
}
|
||||
dev_err(dp->dev, "failed to set bridge, retry: %d\n",
|
||||
timeout_loop);
|
||||
timeout_loop++;
|
||||
usleep_range(10, 11);
|
||||
}
|
||||
dev_err(dp->dev, "too many times retry set bridge, give it up\n");
|
||||
}
|
||||
|
||||
static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
|
||||
@ -1186,11 +1340,15 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
|
||||
}
|
||||
|
||||
disable_irq(dp->irq);
|
||||
phy_power_off(dp->phy);
|
||||
|
||||
if (dp->plat_data->power_off)
|
||||
dp->plat_data->power_off(dp->plat_data);
|
||||
|
||||
analogix_dp_set_analog_power_down(dp, POWER_ALL, 1);
|
||||
phy_power_off(dp->phy);
|
||||
|
||||
clk_disable_unprepare(dp->clock);
|
||||
|
||||
pm_runtime_put_sync(dp->dev);
|
||||
|
||||
ret = analogix_dp_prepare_panel(dp, false, true);
|
||||
@ -1198,6 +1356,7 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
|
||||
DRM_ERROR("failed to setup the panel ret = %d\n", ret);
|
||||
|
||||
dp->psr_enable = false;
|
||||
dp->fast_train_enable = false;
|
||||
dp->dpms_mode = DRM_MODE_DPMS_OFF;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define DP_TIMEOUT_LOOP_COUNT 100
|
||||
#define MAX_CR_LOOP 5
|
||||
#define MAX_EQ_LOOP 5
|
||||
#define MAX_PLL_LOCK_LOOP 5
|
||||
|
||||
/* Training takes 22ms if AUX channel comm fails. Use this as retry interval */
|
||||
#define DP_TIMEOUT_TRAINING_US 22000
|
||||
@ -173,7 +174,7 @@ struct analogix_dp_device {
|
||||
int hpd_gpio;
|
||||
bool force_hpd;
|
||||
bool psr_enable;
|
||||
bool fast_train_support;
|
||||
bool fast_train_enable;
|
||||
|
||||
struct mutex panel_lock;
|
||||
bool panel_is_modeset;
|
||||
@ -197,7 +198,7 @@ void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable);
|
||||
void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
|
||||
enum analog_power_block block,
|
||||
bool enable);
|
||||
void analogix_dp_init_analog_func(struct analogix_dp_device *dp);
|
||||
int analogix_dp_init_analog_func(struct analogix_dp_device *dp);
|
||||
void analogix_dp_init_hpd(struct analogix_dp_device *dp);
|
||||
void analogix_dp_force_hpd(struct analogix_dp_device *dp);
|
||||
enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp);
|
||||
|
@ -126,9 +126,14 @@ void analogix_dp_reset(struct analogix_dp_device *dp)
|
||||
analogix_dp_stop_video(dp);
|
||||
analogix_dp_enable_video_mute(dp, 0);
|
||||
|
||||
reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
|
||||
AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
|
||||
HDCP_FUNC_EN_N | SW_FUNC_EN_N;
|
||||
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
|
||||
reg = RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N |
|
||||
SW_FUNC_EN_N;
|
||||
else
|
||||
reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
|
||||
AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
|
||||
HDCP_FUNC_EN_N | SW_FUNC_EN_N;
|
||||
|
||||
writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
|
||||
|
||||
reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
|
||||
@ -230,16 +235,20 @@ enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp)
|
||||
void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable)
|
||||
{
|
||||
u32 reg;
|
||||
u32 mask = DP_PLL_PD;
|
||||
u32 pd_addr = ANALOGIX_DP_PLL_CTL;
|
||||
|
||||
if (enable) {
|
||||
reg = readl(dp->reg_base + ANALOGIX_DP_PLL_CTL);
|
||||
reg |= DP_PLL_PD;
|
||||
writel(reg, dp->reg_base + ANALOGIX_DP_PLL_CTL);
|
||||
} else {
|
||||
reg = readl(dp->reg_base + ANALOGIX_DP_PLL_CTL);
|
||||
reg &= ~DP_PLL_PD;
|
||||
writel(reg, dp->reg_base + ANALOGIX_DP_PLL_CTL);
|
||||
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
|
||||
pd_addr = ANALOGIX_DP_PD;
|
||||
mask = RK_PLL_PD;
|
||||
}
|
||||
|
||||
reg = readl(dp->reg_base + pd_addr);
|
||||
if (enable)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
writel(reg, dp->reg_base + pd_addr);
|
||||
}
|
||||
|
||||
void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
|
||||
@ -248,83 +257,98 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
|
||||
{
|
||||
u32 reg;
|
||||
u32 phy_pd_addr = ANALOGIX_DP_PHY_PD;
|
||||
u32 mask;
|
||||
|
||||
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
|
||||
phy_pd_addr = ANALOGIX_DP_PD;
|
||||
|
||||
switch (block) {
|
||||
case AUX_BLOCK:
|
||||
if (enable) {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg |= AUX_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
} else {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg &= ~AUX_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
}
|
||||
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
|
||||
mask = RK_AUX_PD;
|
||||
else
|
||||
mask = AUX_PD;
|
||||
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
if (enable)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
break;
|
||||
case CH0_BLOCK:
|
||||
if (enable) {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg |= CH0_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
} else {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg &= ~CH0_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
}
|
||||
mask = CH0_PD;
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
|
||||
if (enable)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
break;
|
||||
case CH1_BLOCK:
|
||||
if (enable) {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg |= CH1_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
} else {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg &= ~CH1_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
}
|
||||
mask = CH1_PD;
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
|
||||
if (enable)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
break;
|
||||
case CH2_BLOCK:
|
||||
if (enable) {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg |= CH2_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
} else {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg &= ~CH2_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
}
|
||||
mask = CH2_PD;
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
|
||||
if (enable)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
break;
|
||||
case CH3_BLOCK:
|
||||
if (enable) {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg |= CH3_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
} else {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg &= ~CH3_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
}
|
||||
mask = CH3_PD;
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
|
||||
if (enable)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
break;
|
||||
case ANALOG_TOTAL:
|
||||
if (enable) {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg |= DP_PHY_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
} else {
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
reg &= ~DP_PHY_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
}
|
||||
/*
|
||||
* There is no bit named DP_PHY_PD, so We used DP_INC_BG
|
||||
* to power off everything instead of DP_PHY_PD in
|
||||
* Rockchip
|
||||
*/
|
||||
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
|
||||
mask = DP_INC_BG;
|
||||
else
|
||||
mask = DP_PHY_PD;
|
||||
|
||||
reg = readl(dp->reg_base + phy_pd_addr);
|
||||
if (enable)
|
||||
reg |= mask;
|
||||
else
|
||||
reg &= ~mask;
|
||||
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
|
||||
usleep_range(10, 15);
|
||||
break;
|
||||
case POWER_ALL:
|
||||
if (enable) {
|
||||
reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD |
|
||||
CH1_PD | CH0_PD;
|
||||
reg = DP_ALL_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
} else {
|
||||
reg = DP_ALL_PD;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
usleep_range(10, 15);
|
||||
reg &= ~DP_INC_BG;
|
||||
writel(reg, dp->reg_base + phy_pd_addr);
|
||||
usleep_range(10, 15);
|
||||
|
||||
writel(0x00, dp->reg_base + phy_pd_addr);
|
||||
}
|
||||
break;
|
||||
@ -333,7 +357,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
|
||||
}
|
||||
}
|
||||
|
||||
void analogix_dp_init_analog_func(struct analogix_dp_device *dp)
|
||||
int analogix_dp_init_analog_func(struct analogix_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
int timeout_loop = 0;
|
||||
@ -355,7 +379,7 @@ void analogix_dp_init_analog_func(struct analogix_dp_device *dp)
|
||||
timeout_loop++;
|
||||
if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
|
||||
dev_err(dp->dev, "failed to get pll lock status\n");
|
||||
return;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
@ -366,6 +390,7 @@ void analogix_dp_init_analog_func(struct analogix_dp_device *dp)
|
||||
reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
|
||||
| AUX_FUNC_EN_N);
|
||||
writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp)
|
||||
@ -450,17 +475,22 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp)
|
||||
reg = RPLY_RECEIV | AUX_ERR;
|
||||
writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA);
|
||||
|
||||
analogix_dp_set_analog_power_down(dp, AUX_BLOCK, true);
|
||||
usleep_range(10, 11);
|
||||
analogix_dp_set_analog_power_down(dp, AUX_BLOCK, false);
|
||||
|
||||
analogix_dp_reset_aux(dp);
|
||||
|
||||
/* Disable AUX transaction H/W retry */
|
||||
/* AUX_BIT_PERIOD_EXPECTED_DELAY doesn't apply to Rockchip IP */
|
||||
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
|
||||
reg = AUX_BIT_PERIOD_EXPECTED_DELAY(0) |
|
||||
AUX_HW_RETRY_COUNT_SEL(3) |
|
||||
AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
|
||||
reg = 0;
|
||||
else
|
||||
reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) |
|
||||
AUX_HW_RETRY_COUNT_SEL(0) |
|
||||
AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
|
||||
reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3);
|
||||
|
||||
/* Disable AUX transaction H/W retry */
|
||||
reg |= AUX_HW_RETRY_COUNT_SEL(0) |
|
||||
AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
|
||||
|
||||
writel(reg, dp->reg_base + ANALOGIX_DP_AUX_HW_RETRY_CTL);
|
||||
|
||||
/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
|
||||
@ -947,8 +977,12 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
|
||||
u32 reg;
|
||||
|
||||
reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
|
||||
reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N);
|
||||
reg |= MASTER_VID_FUNC_EN_N;
|
||||
if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
|
||||
reg &= ~(RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N);
|
||||
} else {
|
||||
reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N);
|
||||
reg |= MASTER_VID_FUNC_EN_N;
|
||||
}
|
||||
writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
|
||||
|
||||
reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
|
||||
@ -1072,10 +1106,11 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
u32 reg;
|
||||
u32 status_reg;
|
||||
u8 *buffer = msg->buffer;
|
||||
int timeout_loop = 0;
|
||||
unsigned int i;
|
||||
int num_transferred = 0;
|
||||
int ret;
|
||||
|
||||
/* Buffer size of AUX CH is 16 bytes */
|
||||
if (WARN_ON(msg->size > 16))
|
||||
@ -1139,17 +1174,20 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
|
||||
|
||||
writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
|
||||
|
||||
/* Is AUX CH command reply received? */
|
||||
ret = readx_poll_timeout(readl, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2,
|
||||
reg, !(reg & AUX_EN), 25, 500 * 1000);
|
||||
if (ret) {
|
||||
dev_err(dp->dev, "AUX CH enable timeout!\n");
|
||||
goto aux_error;
|
||||
}
|
||||
|
||||
/* TODO: Wait for an interrupt instead of looping? */
|
||||
reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
|
||||
while (!(reg & RPLY_RECEIV)) {
|
||||
timeout_loop++;
|
||||
if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
|
||||
dev_err(dp->dev, "AUX CH command reply failed!\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
|
||||
usleep_range(10, 11);
|
||||
/* Is AUX CH command reply received? */
|
||||
ret = readx_poll_timeout(readl, dp->reg_base + ANALOGIX_DP_INT_STA,
|
||||
reg, reg & RPLY_RECEIV, 10, 20 * 1000);
|
||||
if (ret) {
|
||||
dev_err(dp->dev, "AUX CH cmd reply timeout!\n");
|
||||
goto aux_error;
|
||||
}
|
||||
|
||||
/* Clear interrupt source for AUX CH command reply */
|
||||
@ -1157,17 +1195,13 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
|
||||
|
||||
/* Clear interrupt source for AUX CH access error */
|
||||
reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
|
||||
if (reg & AUX_ERR) {
|
||||
status_reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
|
||||
if ((reg & AUX_ERR) || (status_reg & AUX_STATUS_MASK)) {
|
||||
writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
/* Check AUX CH error access status */
|
||||
reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
|
||||
if ((reg & AUX_STATUS_MASK)) {
|
||||
dev_err(dp->dev, "AUX CH error happened: %d\n\n",
|
||||
reg & AUX_STATUS_MASK);
|
||||
return -EREMOTEIO;
|
||||
dev_warn(dp->dev, "AUX CH error happened: %#x (%d)\n",
|
||||
status_reg & AUX_STATUS_MASK, !!(reg & AUX_ERR));
|
||||
goto aux_error;
|
||||
}
|
||||
|
||||
if (msg->request & DP_AUX_I2C_READ) {
|
||||
@ -1193,4 +1227,10 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
|
||||
msg->reply = DP_AUX_NATIVE_REPLY_ACK;
|
||||
|
||||
return num_transferred > 0 ? num_transferred : -EBUSY;
|
||||
|
||||
aux_error:
|
||||
/* if aux err happen, reset aux */
|
||||
analogix_dp_init_aux(dp);
|
||||
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
@ -127,7 +127,9 @@
|
||||
|
||||
/* ANALOGIX_DP_FUNC_EN_1 */
|
||||
#define MASTER_VID_FUNC_EN_N (0x1 << 7)
|
||||
#define RK_VID_CAP_FUNC_EN_N (0x1 << 6)
|
||||
#define SLAVE_VID_FUNC_EN_N (0x1 << 5)
|
||||
#define RK_VID_FIFO_FUNC_EN_N (0x1 << 5)
|
||||
#define AUD_FIFO_FUNC_EN_N (0x1 << 4)
|
||||
#define AUD_FUNC_EN_N (0x1 << 3)
|
||||
#define HDCP_FUNC_EN_N (0x1 << 2)
|
||||
@ -342,12 +344,17 @@
|
||||
#define DP_PLL_REF_BIT_1_2500V (0x7 << 0)
|
||||
|
||||
/* ANALOGIX_DP_PHY_PD */
|
||||
#define DP_INC_BG (0x1 << 7)
|
||||
#define DP_EXP_BG (0x1 << 6)
|
||||
#define DP_PHY_PD (0x1 << 5)
|
||||
#define RK_AUX_PD (0x1 << 5)
|
||||
#define AUX_PD (0x1 << 4)
|
||||
#define RK_PLL_PD (0x1 << 4)
|
||||
#define CH3_PD (0x1 << 3)
|
||||
#define CH2_PD (0x1 << 2)
|
||||
#define CH1_PD (0x1 << 1)
|
||||
#define CH0_PD (0x1 << 0)
|
||||
#define DP_ALL_PD (0xff)
|
||||
|
||||
/* ANALOGIX_DP_PHY_TEST */
|
||||
#define MACRO_RST (0x1 << 5)
|
||||
|
1623
drivers/gpu/drm/bridge/cdns-dsi.c
Normal file
1623
drivers/gpu/drm/bridge/cdns-dsi.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -152,7 +152,6 @@ static struct platform_driver snd_dw_hdmi_driver = {
|
||||
.remove = snd_dw_hdmi_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
module_platform_driver(snd_dw_hdmi_driver);
|
||||
|
@ -1,12 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
|
||||
* Copyright (C) STMicroelectronics SA 2017
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Modified by Philippe Cornu <philippe.cornu@st.com>
|
||||
* This generic Synopsys DesignWare MIPI DSI host driver is based on the
|
||||
* Rockchip version from rockchip/dw-mipi-dsi.c with phy & bridge APIs.
|
||||
@ -775,20 +771,20 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
|
||||
clk_prepare_enable(dsi->pclk);
|
||||
|
||||
ret = phy_ops->get_lane_mbps(priv_data, mode, dsi->mode_flags,
|
||||
ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi->mode_flags,
|
||||
dsi->lanes, dsi->format, &dsi->lane_mbps);
|
||||
if (ret)
|
||||
DRM_DEBUG_DRIVER("Phy get_lane_mbps() failed\n");
|
||||
|
||||
pm_runtime_get_sync(dsi->dev);
|
||||
dw_mipi_dsi_init(dsi);
|
||||
dw_mipi_dsi_dpi_config(dsi, mode);
|
||||
dw_mipi_dsi_dpi_config(dsi, adjusted_mode);
|
||||
dw_mipi_dsi_packet_handler_config(dsi);
|
||||
dw_mipi_dsi_video_mode_config(dsi);
|
||||
dw_mipi_dsi_video_packet_config(dsi, mode);
|
||||
dw_mipi_dsi_video_packet_config(dsi, adjusted_mode);
|
||||
dw_mipi_dsi_command_mode_config(dsi);
|
||||
dw_mipi_dsi_line_timer_config(dsi, mode);
|
||||
dw_mipi_dsi_vertical_timing_config(dsi, mode);
|
||||
dw_mipi_dsi_line_timer_config(dsi, adjusted_mode);
|
||||
dw_mipi_dsi_vertical_timing_config(dsi, adjusted_mode);
|
||||
|
||||
dw_mipi_dsi_dphy_init(dsi);
|
||||
dw_mipi_dsi_dphy_timing_config(dsi);
|
||||
@ -802,7 +798,7 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
|
||||
dw_mipi_dsi_dphy_enable(dsi);
|
||||
|
||||
dw_mipi_dsi_wait_for_two_frames(mode);
|
||||
dw_mipi_dsi_wait_for_two_frames(adjusted_mode);
|
||||
|
||||
/* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
|
||||
dw_mipi_dsi_set_mode(dsi, 0);
|
||||
|
@ -1102,7 +1102,7 @@ static bool tc_bridge_mode_fixup(struct drm_bridge *bridge,
|
||||
return true;
|
||||
}
|
||||
|
||||
static int tc_connector_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status tc_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
/* DPI interface clock limitation: upto 154 MHz */
|
||||
|
206
drivers/gpu/drm/bridge/thc63lvd1024.c
Normal file
206
drivers/gpu/drm/bridge/thc63lvd1024.c
Normal file
@ -0,0 +1,206 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* THC63LVD1024 LVDS to parallel data DRM bridge driver.
|
||||
*
|
||||
* Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
enum thc63_ports {
|
||||
THC63_LVDS_IN0,
|
||||
THC63_LVDS_IN1,
|
||||
THC63_RGB_OUT0,
|
||||
THC63_RGB_OUT1,
|
||||
};
|
||||
|
||||
struct thc63_dev {
|
||||
struct device *dev;
|
||||
|
||||
struct regulator *vcc;
|
||||
|
||||
struct gpio_desc *pdwn;
|
||||
struct gpio_desc *oe;
|
||||
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next;
|
||||
};
|
||||
|
||||
static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct thc63_dev, bridge);
|
||||
}
|
||||
|
||||
static int thc63_attach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct thc63_dev *thc63 = to_thc63(bridge);
|
||||
|
||||
return drm_bridge_attach(bridge->encoder, thc63->next, bridge);
|
||||
}
|
||||
|
||||
static void thc63_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct thc63_dev *thc63 = to_thc63(bridge);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(thc63->vcc);
|
||||
if (ret) {
|
||||
dev_err(thc63->dev,
|
||||
"Failed to enable regulator \"vcc\": %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
gpiod_set_value(thc63->pdwn, 0);
|
||||
gpiod_set_value(thc63->oe, 1);
|
||||
}
|
||||
|
||||
static void thc63_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct thc63_dev *thc63 = to_thc63(bridge);
|
||||
int ret;
|
||||
|
||||
gpiod_set_value(thc63->oe, 0);
|
||||
gpiod_set_value(thc63->pdwn, 1);
|
||||
|
||||
ret = regulator_disable(thc63->vcc);
|
||||
if (ret)
|
||||
dev_err(thc63->dev,
|
||||
"Failed to disable regulator \"vcc\": %d\n", ret);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs thc63_bridge_func = {
|
||||
.attach = thc63_attach,
|
||||
.enable = thc63_enable,
|
||||
.disable = thc63_disable,
|
||||
};
|
||||
|
||||
static int thc63_parse_dt(struct thc63_dev *thc63)
|
||||
{
|
||||
struct device_node *thc63_out;
|
||||
struct device_node *remote;
|
||||
|
||||
thc63_out = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
|
||||
THC63_RGB_OUT0, -1);
|
||||
if (!thc63_out) {
|
||||
dev_err(thc63->dev, "Missing endpoint in port@%u\n",
|
||||
THC63_RGB_OUT0);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
remote = of_graph_get_remote_port_parent(thc63_out);
|
||||
of_node_put(thc63_out);
|
||||
if (!remote) {
|
||||
dev_err(thc63->dev, "Endpoint in port@%u unconnected\n",
|
||||
THC63_RGB_OUT0);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!of_device_is_available(remote)) {
|
||||
dev_err(thc63->dev, "port@%u remote endpoint is disabled\n",
|
||||
THC63_RGB_OUT0);
|
||||
of_node_put(remote);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
thc63->next = of_drm_find_bridge(remote);
|
||||
of_node_put(remote);
|
||||
if (!thc63->next)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thc63_gpio_init(struct thc63_dev *thc63)
|
||||
{
|
||||
thc63->oe = devm_gpiod_get_optional(thc63->dev, "oe", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(thc63->oe)) {
|
||||
dev_err(thc63->dev, "Unable to get \"oe-gpios\": %ld\n",
|
||||
PTR_ERR(thc63->oe));
|
||||
return PTR_ERR(thc63->oe);
|
||||
}
|
||||
|
||||
thc63->pdwn = devm_gpiod_get_optional(thc63->dev, "powerdown",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(thc63->pdwn)) {
|
||||
dev_err(thc63->dev, "Unable to get \"powerdown-gpios\": %ld\n",
|
||||
PTR_ERR(thc63->pdwn));
|
||||
return PTR_ERR(thc63->pdwn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thc63_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct thc63_dev *thc63;
|
||||
int ret;
|
||||
|
||||
thc63 = devm_kzalloc(&pdev->dev, sizeof(*thc63), GFP_KERNEL);
|
||||
if (!thc63)
|
||||
return -ENOMEM;
|
||||
|
||||
thc63->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, thc63);
|
||||
|
||||
thc63->vcc = devm_regulator_get_optional(thc63->dev, "vcc");
|
||||
if (IS_ERR(thc63->vcc)) {
|
||||
if (PTR_ERR(thc63->vcc) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_err(thc63->dev, "Unable to get \"vcc\" supply: %ld\n",
|
||||
PTR_ERR(thc63->vcc));
|
||||
return PTR_ERR(thc63->vcc);
|
||||
}
|
||||
|
||||
ret = thc63_gpio_init(thc63);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = thc63_parse_dt(thc63);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
thc63->bridge.driver_private = thc63;
|
||||
thc63->bridge.of_node = pdev->dev.of_node;
|
||||
thc63->bridge.funcs = &thc63_bridge_func;
|
||||
|
||||
drm_bridge_add(&thc63->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thc63_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct thc63_dev *thc63 = platform_get_drvdata(pdev);
|
||||
|
||||
drm_bridge_remove(&thc63->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id thc63_match[] = {
|
||||
{ .compatible = "thine,thc63lvd1024", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, thc63_match);
|
||||
|
||||
static struct platform_driver thc63_driver = {
|
||||
.probe = thc63_probe,
|
||||
.remove = thc63_remove,
|
||||
.driver = {
|
||||
.name = "thc63lvd1024",
|
||||
.of_match_table = thc63_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(thc63_driver);
|
||||
|
||||
MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
|
||||
MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -783,6 +783,8 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
|
||||
state->src_w = val;
|
||||
} else if (property == config->prop_src_h) {
|
||||
state->src_h = val;
|
||||
} else if (property == plane->alpha_property) {
|
||||
state->alpha = val;
|
||||
} else if (property == plane->rotation_property) {
|
||||
if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK))
|
||||
return -EINVAL;
|
||||
@ -848,6 +850,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
|
||||
*val = state->src_w;
|
||||
} else if (property == config->prop_src_h) {
|
||||
*val = state->src_h;
|
||||
} else if (property == plane->alpha_property) {
|
||||
*val = state->alpha;
|
||||
} else if (property == plane->rotation_property) {
|
||||
*val = state->rotation;
|
||||
} else if (property == plane->zpos_property) {
|
||||
@ -1492,6 +1496,14 @@ EXPORT_SYMBOL(drm_atomic_set_fb_for_plane);
|
||||
* Otherwise, if &drm_plane_state.fence is not set this function we just set it
|
||||
* with the received implicit fence. In both cases this function consumes a
|
||||
* reference for @fence.
|
||||
*
|
||||
* This way explicit fencing can be used to overrule implicit fencing, which is
|
||||
* important to make explicit fencing use-cases work: One example is using one
|
||||
* buffer for 2 screens with different refresh rates. Implicit fencing will
|
||||
* clamp rendering to the refresh rate of the slower screen, whereas explicit
|
||||
* fence allows 2 independent render and display loops on a single buffer. If a
|
||||
* driver allows obeys both implicit and explicit fences for plane updates, then
|
||||
* it will break all the benefits of explicit fencing.
|
||||
*/
|
||||
void
|
||||
drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state,
|
||||
|
@ -875,6 +875,11 @@ EXPORT_SYMBOL(drm_atomic_helper_check_planes);
|
||||
* functions depend upon an updated adjusted_mode.clock to e.g. properly compute
|
||||
* watermarks.
|
||||
*
|
||||
* Note that zpos normalization will add all enable planes to the state which
|
||||
* might not desired for some drivers.
|
||||
* For example enable/disable of a cursor plane which have fixed zpos value
|
||||
* would trigger all other enabled planes to be forced to the state change.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero for success or -errno
|
||||
*/
|
||||
@ -887,6 +892,12 @@ int drm_atomic_helper_check(struct drm_device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dev->mode_config.normalize_zpos) {
|
||||
ret = drm_atomic_normalize_zpos(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_atomic_helper_check_planes(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1561,6 +1572,17 @@ void drm_atomic_helper_async_commit(struct drm_device *dev,
|
||||
for_each_new_plane_in_state(state, plane, plane_state, i) {
|
||||
funcs = plane->helper_private;
|
||||
funcs->atomic_async_update(plane, plane_state);
|
||||
|
||||
/*
|
||||
* ->atomic_async_update() is supposed to update the
|
||||
* plane->state in-place, make sure at least common
|
||||
* properties have been properly updated.
|
||||
*/
|
||||
WARN_ON_ONCE(plane->state->fb != plane_state->fb);
|
||||
WARN_ON_ONCE(plane->state->crtc_x != plane_state->crtc_x);
|
||||
WARN_ON_ONCE(plane->state->crtc_y != plane_state->crtc_y);
|
||||
WARN_ON_ONCE(plane->state->src_x != plane_state->src_x);
|
||||
WARN_ON_ONCE(plane->state->src_y != plane_state->src_y);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_async_commit);
|
||||
@ -2659,7 +2681,7 @@ int drm_atomic_helper_disable_plane(struct drm_plane *plane,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (plane_state->crtc && (plane == plane->crtc->cursor))
|
||||
if (plane_state->crtc && plane_state->crtc->cursor == plane)
|
||||
plane_state->state->legacy_cursor_update = true;
|
||||
|
||||
ret = __drm_atomic_helper_disable_plane(plane, plane_state);
|
||||
@ -2881,31 +2903,9 @@ commit:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_disable_all - disable all currently active outputs
|
||||
* @dev: DRM device
|
||||
* @ctx: lock acquisition context
|
||||
*
|
||||
* Loops through all connectors, finding those that aren't turned off and then
|
||||
* turns them off by setting their DPMS mode to OFF and deactivating the CRTC
|
||||
* that they are connected to.
|
||||
*
|
||||
* This is used for example in suspend/resume to disable all currently active
|
||||
* functions when suspending. If you just want to shut down everything at e.g.
|
||||
* driver unload, look at drm_atomic_helper_shutdown().
|
||||
*
|
||||
* Note that if callers haven't already acquired all modeset locks this might
|
||||
* return -EDEADLK, which must be handled by calling drm_modeset_backoff().
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*
|
||||
* See also:
|
||||
* drm_atomic_helper_suspend(), drm_atomic_helper_resume() and
|
||||
* drm_atomic_helper_shutdown().
|
||||
*/
|
||||
int drm_atomic_helper_disable_all(struct drm_device *dev,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
static int __drm_atomic_helper_disable_all(struct drm_device *dev,
|
||||
struct drm_modeset_acquire_ctx *ctx,
|
||||
bool clean_old_fbs)
|
||||
{
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_connector_state *conn_state;
|
||||
@ -2957,8 +2957,11 @@ int drm_atomic_helper_disable_all(struct drm_device *dev,
|
||||
goto free;
|
||||
|
||||
drm_atomic_set_fb_for_plane(plane_state, NULL);
|
||||
plane_mask |= BIT(drm_plane_index(plane));
|
||||
plane->old_fb = plane->fb;
|
||||
|
||||
if (clean_old_fbs) {
|
||||
plane->old_fb = plane->fb;
|
||||
plane_mask |= BIT(drm_plane_index(plane));
|
||||
}
|
||||
}
|
||||
|
||||
ret = drm_atomic_commit(state);
|
||||
@ -2969,6 +2972,34 @@ free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_disable_all - disable all currently active outputs
|
||||
* @dev: DRM device
|
||||
* @ctx: lock acquisition context
|
||||
*
|
||||
* Loops through all connectors, finding those that aren't turned off and then
|
||||
* turns them off by setting their DPMS mode to OFF and deactivating the CRTC
|
||||
* that they are connected to.
|
||||
*
|
||||
* This is used for example in suspend/resume to disable all currently active
|
||||
* functions when suspending. If you just want to shut down everything at e.g.
|
||||
* driver unload, look at drm_atomic_helper_shutdown().
|
||||
*
|
||||
* Note that if callers haven't already acquired all modeset locks this might
|
||||
* return -EDEADLK, which must be handled by calling drm_modeset_backoff().
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*
|
||||
* See also:
|
||||
* drm_atomic_helper_suspend(), drm_atomic_helper_resume() and
|
||||
* drm_atomic_helper_shutdown().
|
||||
*/
|
||||
int drm_atomic_helper_disable_all(struct drm_device *dev,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
return __drm_atomic_helper_disable_all(dev, ctx, false);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_disable_all);
|
||||
|
||||
/**
|
||||
@ -2991,7 +3022,7 @@ void drm_atomic_helper_shutdown(struct drm_device *dev)
|
||||
while (1) {
|
||||
ret = drm_modeset_lock_all_ctx(dev, &ctx);
|
||||
if (!ret)
|
||||
ret = drm_atomic_helper_disable_all(dev, &ctx);
|
||||
ret = __drm_atomic_helper_disable_all(dev, &ctx, true);
|
||||
|
||||
if (ret != -EDEADLK)
|
||||
break;
|
||||
@ -3095,14 +3126,14 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state,
|
||||
struct drm_connector_state *new_conn_state;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
unsigned plane_mask = 0;
|
||||
struct drm_device *dev = state->dev;
|
||||
int ret;
|
||||
|
||||
state->acquire_ctx = ctx;
|
||||
|
||||
for_each_new_plane_in_state(state, plane, new_plane_state, i) {
|
||||
plane_mask |= BIT(drm_plane_index(plane));
|
||||
WARN_ON(plane->crtc != new_plane_state->crtc);
|
||||
WARN_ON(plane->fb != new_plane_state->fb);
|
||||
WARN_ON(plane->old_fb);
|
||||
|
||||
state->planes[i].old_state = plane->state;
|
||||
}
|
||||
|
||||
@ -3112,11 +3143,7 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state,
|
||||
for_each_new_connector_in_state(state, connector, new_conn_state, i)
|
||||
state->connectors[i].old_state = connector->state;
|
||||
|
||||
ret = drm_atomic_commit(state);
|
||||
if (plane_mask)
|
||||
drm_atomic_clean_old_fb(dev, plane_mask, ret);
|
||||
|
||||
return ret;
|
||||
return drm_atomic_commit(state);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_commit_duplicated_state);
|
||||
|
||||
@ -3484,6 +3511,10 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
|
||||
if (plane->state) {
|
||||
plane->state->plane = plane;
|
||||
plane->state->rotation = DRM_MODE_ROTATE_0;
|
||||
|
||||
/* Reset the alpha value to fully opaque if it matters */
|
||||
if (plane->alpha_property)
|
||||
plane->state->alpha = plane->alpha_property->values[1];
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
|
||||
|
@ -88,6 +88,13 @@
|
||||
* On top of this basic transformation additional properties can be exposed by
|
||||
* the driver:
|
||||
*
|
||||
* alpha:
|
||||
* Alpha is setup with drm_plane_create_alpha_property(). It controls the
|
||||
* plane-wide opacity, from transparent (0) to opaque (0xffff). It can be
|
||||
* combined with pixel alpha.
|
||||
* The pixel values in the framebuffers are expected to not be
|
||||
* pre-multiplied by the global alpha associated to the plane.
|
||||
*
|
||||
* rotation:
|
||||
* Rotation is set up with drm_plane_create_rotation_property(). It adds a
|
||||
* rotation and reflection step between the source and destination rectangles.
|
||||
@ -105,6 +112,38 @@
|
||||
* exposed and assumed to be black).
|
||||
*/
|
||||
|
||||
/**
|
||||
* drm_plane_create_alpha_property - create a new alpha property
|
||||
* @plane: drm plane
|
||||
*
|
||||
* This function creates a generic, mutable, alpha property and enables support
|
||||
* for it in the DRM core. It is attached to @plane.
|
||||
*
|
||||
* The alpha property will be allowed to be within the bounds of 0
|
||||
* (transparent) to 0xffff (opaque).
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative error code on failure.
|
||||
*/
|
||||
int drm_plane_create_alpha_property(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_property *prop;
|
||||
|
||||
prop = drm_property_create_range(plane->dev, 0, "alpha",
|
||||
0, DRM_BLEND_ALPHA_OPAQUE);
|
||||
if (!prop)
|
||||
return -ENOMEM;
|
||||
|
||||
drm_object_attach_property(&plane->base, prop, DRM_BLEND_ALPHA_OPAQUE);
|
||||
plane->alpha_property = prop;
|
||||
|
||||
if (plane->state)
|
||||
plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_plane_create_alpha_property);
|
||||
|
||||
/**
|
||||
* drm_plane_create_rotation_property - create a new rotation property
|
||||
* @plane: drm plane
|
||||
|
@ -402,6 +402,7 @@ int drm_mode_getcrtc(struct drm_device *dev,
|
||||
{
|
||||
struct drm_mode_crtc *crtc_resp = data;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_plane *plane;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
@ -410,34 +411,36 @@ int drm_mode_getcrtc(struct drm_device *dev,
|
||||
if (!crtc)
|
||||
return -ENOENT;
|
||||
|
||||
plane = crtc->primary;
|
||||
|
||||
crtc_resp->gamma_size = crtc->gamma_size;
|
||||
|
||||
drm_modeset_lock(&crtc->primary->mutex, NULL);
|
||||
if (crtc->primary->state && crtc->primary->state->fb)
|
||||
crtc_resp->fb_id = crtc->primary->state->fb->base.id;
|
||||
else if (!crtc->primary->state && crtc->primary->fb)
|
||||
crtc_resp->fb_id = crtc->primary->fb->base.id;
|
||||
drm_modeset_lock(&plane->mutex, NULL);
|
||||
if (plane->state && plane->state->fb)
|
||||
crtc_resp->fb_id = plane->state->fb->base.id;
|
||||
else if (!plane->state && plane->fb)
|
||||
crtc_resp->fb_id = plane->fb->base.id;
|
||||
else
|
||||
crtc_resp->fb_id = 0;
|
||||
|
||||
if (crtc->primary->state) {
|
||||
crtc_resp->x = crtc->primary->state->src_x >> 16;
|
||||
crtc_resp->y = crtc->primary->state->src_y >> 16;
|
||||
if (plane->state) {
|
||||
crtc_resp->x = plane->state->src_x >> 16;
|
||||
crtc_resp->y = plane->state->src_y >> 16;
|
||||
}
|
||||
drm_modeset_unlock(&crtc->primary->mutex);
|
||||
drm_modeset_unlock(&plane->mutex);
|
||||
|
||||
drm_modeset_lock(&crtc->mutex, NULL);
|
||||
if (crtc->state) {
|
||||
if (crtc->state->enable) {
|
||||
drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode);
|
||||
crtc_resp->mode_valid = 1;
|
||||
|
||||
} else {
|
||||
crtc_resp->mode_valid = 0;
|
||||
}
|
||||
} else {
|
||||
crtc_resp->x = crtc->x;
|
||||
crtc_resp->y = crtc->y;
|
||||
|
||||
if (crtc->enabled) {
|
||||
drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode);
|
||||
crtc_resp->mode_valid = 1;
|
||||
@ -471,7 +474,7 @@ static int __drm_mode_set_config_internal(struct drm_mode_set *set,
|
||||
|
||||
ret = crtc->funcs->set_config(set, ctx);
|
||||
if (ret == 0) {
|
||||
crtc->primary->crtc = crtc;
|
||||
crtc->primary->crtc = fb ? crtc : NULL;
|
||||
crtc->primary->fb = fb;
|
||||
}
|
||||
|
||||
@ -554,6 +557,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
struct drm_mode_crtc *crtc_req = data;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_plane *plane;
|
||||
struct drm_connector **connector_set = NULL, *connector;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
struct drm_display_mode *mode = NULL;
|
||||
@ -580,22 +584,33 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
}
|
||||
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
|
||||
|
||||
plane = crtc->primary;
|
||||
|
||||
mutex_lock(&crtc->dev->mode_config.mutex);
|
||||
drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
|
||||
retry:
|
||||
ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (crtc_req->mode_valid) {
|
||||
/* If we have a mode we need a framebuffer. */
|
||||
/* If we pass -1, set the mode with the currently bound fb */
|
||||
if (crtc_req->fb_id == -1) {
|
||||
if (!crtc->primary->fb) {
|
||||
struct drm_framebuffer *old_fb;
|
||||
|
||||
if (plane->state)
|
||||
old_fb = plane->state->fb;
|
||||
else
|
||||
old_fb = plane->fb;
|
||||
|
||||
if (!old_fb) {
|
||||
DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
fb = crtc->primary->fb;
|
||||
|
||||
fb = old_fb;
|
||||
/* Make refcounting symmetric with the lookup path. */
|
||||
drm_framebuffer_get(fb);
|
||||
} else {
|
||||
@ -627,8 +642,8 @@ retry:
|
||||
* match real hardware capabilities. Skip the check in that
|
||||
* case.
|
||||
*/
|
||||
if (!crtc->primary->format_default) {
|
||||
ret = drm_plane_check_pixel_format(crtc->primary,
|
||||
if (!plane->format_default) {
|
||||
ret = drm_plane_check_pixel_format(plane,
|
||||
fb->format->format,
|
||||
fb->modifier);
|
||||
if (ret) {
|
||||
|
@ -220,3 +220,5 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
||||
|
||||
/* drm_edid.c */
|
||||
void drm_mode_fixup_1366x768(struct drm_display_mode *mode);
|
||||
void drm_reset_display_info(struct drm_connector *connector);
|
||||
u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid);
|
||||
|
@ -2941,12 +2941,14 @@ static void drm_dp_mst_dump_mstb(struct seq_file *m,
|
||||
}
|
||||
}
|
||||
|
||||
#define DP_PAYLOAD_TABLE_SIZE 64
|
||||
|
||||
static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
|
||||
char *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 64; i += 16) {
|
||||
for (i = 0; i < DP_PAYLOAD_TABLE_SIZE; i += 16) {
|
||||
if (drm_dp_dpcd_read(mgr->aux,
|
||||
DP_PAYLOAD_TABLE_UPDATE_STATUS + i,
|
||||
&buf[i], 16) != 16)
|
||||
@ -3015,7 +3017,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
|
||||
|
||||
mutex_lock(&mgr->lock);
|
||||
if (mgr->mst_primary) {
|
||||
u8 buf[64];
|
||||
u8 buf[DP_PAYLOAD_TABLE_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE);
|
||||
@ -3033,8 +3035,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
|
||||
seq_printf(m, " revision: hw: %x.%x sw: %x.%x\n",
|
||||
buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]);
|
||||
if (dump_dp_payload_table(mgr, buf))
|
||||
seq_printf(m, "payload table: %*ph\n", 63, buf);
|
||||
|
||||
seq_printf(m, "payload table: %*ph\n", DP_PAYLOAD_TABLE_SIZE, buf);
|
||||
}
|
||||
|
||||
mutex_unlock(&mgr->lock);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/srcu.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drmP.h>
|
||||
@ -75,6 +76,8 @@ static bool drm_core_init_complete = false;
|
||||
|
||||
static struct dentry *drm_debugfs_root;
|
||||
|
||||
DEFINE_STATIC_SRCU(drm_unplug_srcu);
|
||||
|
||||
/*
|
||||
* DRM Minors
|
||||
* A DRM device can provide several char-dev interfaces on the DRM-Major. Each
|
||||
@ -318,18 +321,51 @@ void drm_put_dev(struct drm_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_put_dev);
|
||||
|
||||
static void drm_device_set_unplugged(struct drm_device *dev)
|
||||
/**
|
||||
* drm_dev_enter - Enter device critical section
|
||||
* @dev: DRM device
|
||||
* @idx: Pointer to index that will be passed to the matching drm_dev_exit()
|
||||
*
|
||||
* This function marks and protects the beginning of a section that should not
|
||||
* be entered after the device has been unplugged. The section end is marked
|
||||
* with drm_dev_exit(). Calls to this function can be nested.
|
||||
*
|
||||
* Returns:
|
||||
* True if it is OK to enter the section, false otherwise.
|
||||
*/
|
||||
bool drm_dev_enter(struct drm_device *dev, int *idx)
|
||||
{
|
||||
smp_wmb();
|
||||
atomic_set(&dev->unplugged, 1);
|
||||
*idx = srcu_read_lock(&drm_unplug_srcu);
|
||||
|
||||
if (dev->unplugged) {
|
||||
srcu_read_unlock(&drm_unplug_srcu, *idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dev_enter);
|
||||
|
||||
/**
|
||||
* drm_dev_exit - Exit device critical section
|
||||
* @idx: index returned from drm_dev_enter()
|
||||
*
|
||||
* This function marks the end of a section that should not be entered after
|
||||
* the device has been unplugged.
|
||||
*/
|
||||
void drm_dev_exit(int idx)
|
||||
{
|
||||
srcu_read_unlock(&drm_unplug_srcu, idx);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dev_exit);
|
||||
|
||||
/**
|
||||
* drm_dev_unplug - unplug a DRM device
|
||||
* @dev: DRM device
|
||||
*
|
||||
* This unplugs a hotpluggable DRM device, which makes it inaccessible to
|
||||
* userspace operations. Entry-points can use drm_dev_is_unplugged(). This
|
||||
* userspace operations. Entry-points can use drm_dev_enter() and
|
||||
* drm_dev_exit() to protect device resources in a race free manner. This
|
||||
* essentially unregisters the device like drm_dev_unregister(), but can be
|
||||
* called while there are still open users of @dev.
|
||||
*/
|
||||
@ -338,10 +374,18 @@ void drm_dev_unplug(struct drm_device *dev)
|
||||
drm_dev_unregister(dev);
|
||||
|
||||
mutex_lock(&drm_global_mutex);
|
||||
drm_device_set_unplugged(dev);
|
||||
if (dev->open_count == 0)
|
||||
drm_dev_put(dev);
|
||||
mutex_unlock(&drm_global_mutex);
|
||||
|
||||
/*
|
||||
* After synchronizing any critical read section is guaranteed to see
|
||||
* the new value of ->unplugged, and any critical section which might
|
||||
* still have seen the old value of ->unplugged is guaranteed to have
|
||||
* finished.
|
||||
*/
|
||||
dev->unplugged = true;
|
||||
synchronize_srcu(&drm_unplug_srcu);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dev_unplug);
|
||||
|
||||
|
@ -4455,7 +4455,6 @@ drm_reset_display_info(struct drm_connector *connector)
|
||||
|
||||
info->non_desktop = 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_reset_display_info);
|
||||
|
||||
u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
|
||||
{
|
||||
@ -4533,7 +4532,6 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
|
||||
info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
|
||||
return quirks;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_add_display_info);
|
||||
|
||||
static int validate_displayid(u8 *displayid, int length, int idx)
|
||||
{
|
||||
|
@ -468,29 +468,31 @@ int drm_mode_getfb(struct drm_device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!fb->funcs->create_handle) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r->height = fb->height;
|
||||
r->width = fb->width;
|
||||
r->depth = fb->format->depth;
|
||||
r->bpp = fb->format->cpp[0] * 8;
|
||||
r->pitch = fb->pitches[0];
|
||||
if (fb->funcs->create_handle) {
|
||||
if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) ||
|
||||
drm_is_control_client(file_priv)) {
|
||||
ret = fb->funcs->create_handle(fb, file_priv,
|
||||
&r->handle);
|
||||
} else {
|
||||
/* GET_FB() is an unprivileged ioctl so we must not
|
||||
* return a buffer-handle to non-master processes! For
|
||||
* backwards-compatibility reasons, we cannot make
|
||||
* GET_FB() privileged, so just return an invalid handle
|
||||
* for non-masters. */
|
||||
r->handle = 0;
|
||||
ret = 0;
|
||||
}
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
|
||||
/* GET_FB() is an unprivileged ioctl so we must not return a
|
||||
* buffer-handle to non-master processes! For
|
||||
* backwards-compatibility reasons, we cannot make GET_FB() privileged,
|
||||
* so just return an invalid handle for non-masters.
|
||||
*/
|
||||
if (!drm_is_current_master(file_priv) && !capable(CAP_SYS_ADMIN) &&
|
||||
!drm_is_control_client(file_priv)) {
|
||||
r->handle = 0;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
|
||||
|
||||
out:
|
||||
drm_framebuffer_put(fb);
|
||||
|
||||
|
@ -436,9 +436,12 @@ err_unref:
|
||||
* @obj: object to register
|
||||
* @handlep: pionter to return the created handle to the caller
|
||||
*
|
||||
* Create a handle for this object. This adds a handle reference
|
||||
* to the object, which includes a regular reference count. Callers
|
||||
* will likely want to dereference the object afterwards.
|
||||
* Create a handle for this object. This adds a handle reference to the object,
|
||||
* which includes a regular reference count. Callers will likely want to
|
||||
* dereference the object afterwards.
|
||||
*
|
||||
* Since this publishes @obj to userspace it must be fully set up by this point,
|
||||
* drivers must call this last in their buffer object creation callbacks.
|
||||
*/
|
||||
int drm_gem_handle_create(struct drm_file *file_priv,
|
||||
struct drm_gem_object *obj,
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
|
||||
/**
|
||||
* DOC: overview
|
||||
@ -265,6 +266,24 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);
|
||||
|
||||
/**
|
||||
* drm_gem_fb_simple_display_pipe_prepare_fb - prepare_fb helper for
|
||||
* &drm_simple_display_pipe
|
||||
* @pipe: Simple display pipe
|
||||
* @plane_state: Plane state
|
||||
*
|
||||
* This function uses drm_gem_fb_prepare_fb() to check if the plane FB has a
|
||||
* &dma_buf attached, extracts the exclusive fence and attaches it to plane
|
||||
* state for the atomic helper to wait on. Drivers can use this as their
|
||||
* &drm_simple_display_pipe_funcs.prepare_fb callback.
|
||||
*/
|
||||
int drm_gem_fb_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
return drm_gem_fb_prepare_fb(&pipe->plane, plane_state);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_fb_simple_display_pipe_prepare_fb);
|
||||
|
||||
/**
|
||||
* drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev
|
||||
* emulation
|
||||
|
@ -340,7 +340,7 @@ static void _drm_lease_revoke(struct drm_master *top)
|
||||
break;
|
||||
|
||||
/* Over */
|
||||
master = list_entry(master->lessee_list.next, struct drm_master, lessee_list);
|
||||
master = list_next_entry(master, lessee_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ static const struct drm_dmi_panel_orientation_data itworks_tw891 = {
|
||||
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
|
||||
};
|
||||
|
||||
static const struct drm_dmi_panel_orientation_data vios_lth17 = {
|
||||
static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = {
|
||||
.width = 800,
|
||||
.height = 1280,
|
||||
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
|
||||
@ -102,12 +102,30 @@ static const struct dmi_system_id orientation_data[] = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"),
|
||||
},
|
||||
.driver_data = (void *)&itworks_tw891,
|
||||
}, { /*
|
||||
* Lenovo Ideapad Miix 310 laptop, only some production batches
|
||||
* have a portrait screen, the resolution checks makes the quirk
|
||||
* apply only to those batches.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
|
||||
},
|
||||
.driver_data = (void *)&lcd800x1280_rightside_up,
|
||||
}, { /* Lenovo Ideapad Miix 320 */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
|
||||
},
|
||||
.driver_data = (void *)&lcd800x1280_rightside_up,
|
||||
}, { /* VIOS LTH17 */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"),
|
||||
},
|
||||
.driver_data = (void *)&vios_lth17,
|
||||
.driver_data = (void *)&lcd800x1280_rightside_up,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
@ -756,6 +756,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_plane *plane = crtc->cursor;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
struct drm_mode_fb_cmd2 fbreq = {
|
||||
.width = req->width,
|
||||
@ -769,8 +770,8 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc,
|
||||
uint32_t src_w = 0, src_h = 0;
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(!crtc->cursor);
|
||||
WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL);
|
||||
BUG_ON(!plane);
|
||||
WARN_ON(plane->crtc != crtc && plane->crtc != NULL);
|
||||
|
||||
/*
|
||||
* Obtain fb we'll be using (either new or existing) and take an extra
|
||||
@ -784,13 +785,18 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc,
|
||||
DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
|
||||
return PTR_ERR(fb);
|
||||
}
|
||||
|
||||
fb->hot_x = req->hot_x;
|
||||
fb->hot_y = req->hot_y;
|
||||
} else {
|
||||
fb = NULL;
|
||||
}
|
||||
} else {
|
||||
fb = crtc->cursor->fb;
|
||||
if (plane->state)
|
||||
fb = plane->state->fb;
|
||||
else
|
||||
fb = plane->fb;
|
||||
|
||||
if (fb)
|
||||
drm_framebuffer_get(fb);
|
||||
}
|
||||
@ -810,7 +816,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc,
|
||||
src_h = fb->height << 16;
|
||||
}
|
||||
|
||||
ret = __setplane_internal(crtc->cursor, crtc, fb,
|
||||
ret = __setplane_internal(plane, crtc, fb,
|
||||
crtc_x, crtc_y, crtc_w, crtc_h,
|
||||
0, 0, src_w, src_h, ctx);
|
||||
|
||||
@ -931,7 +937,8 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
||||
{
|
||||
struct drm_mode_crtc_page_flip_target *page_flip = data;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
struct drm_plane *plane;
|
||||
struct drm_framebuffer *fb = NULL, *old_fb;
|
||||
struct drm_pending_vblank_event *e = NULL;
|
||||
u32 target_vblank = page_flip->sequence;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
@ -959,6 +966,8 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
||||
if (!crtc)
|
||||
return -ENOENT;
|
||||
|
||||
plane = crtc->primary;
|
||||
|
||||
if (crtc->funcs->page_flip_target) {
|
||||
u32 current_vblank;
|
||||
int r;
|
||||
@ -1003,11 +1012,16 @@ retry:
|
||||
ret = drm_modeset_lock(&crtc->mutex, &ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = drm_modeset_lock(&crtc->primary->mutex, &ctx);
|
||||
ret = drm_modeset_lock(&plane->mutex, &ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (crtc->primary->fb == NULL) {
|
||||
if (plane->state)
|
||||
old_fb = plane->state->fb;
|
||||
else
|
||||
old_fb = plane->fb;
|
||||
|
||||
if (old_fb == NULL) {
|
||||
/* The framebuffer is currently unbound, presumably
|
||||
* due to a hotplug event, that userspace has not
|
||||
* yet discovered.
|
||||
@ -1022,8 +1036,8 @@ retry:
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (crtc->state) {
|
||||
const struct drm_plane_state *state = crtc->primary->state;
|
||||
if (plane->state) {
|
||||
const struct drm_plane_state *state = plane->state;
|
||||
|
||||
ret = drm_framebuffer_check_src_coords(state->src_x,
|
||||
state->src_y,
|
||||
@ -1031,12 +1045,13 @@ retry:
|
||||
state->src_h,
|
||||
fb);
|
||||
} else {
|
||||
ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
|
||||
ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y,
|
||||
&crtc->mode, fb);
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (crtc->primary->fb->format != fb->format) {
|
||||
if (old_fb->format != fb->format) {
|
||||
DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@ -1048,10 +1063,12 @@ retry:
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
|
||||
e->event.base.length = sizeof(e->event);
|
||||
e->event.vbl.user_data = page_flip->user_data;
|
||||
e->event.vbl.crtc_id = crtc->base.id;
|
||||
|
||||
ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base);
|
||||
if (ret) {
|
||||
kfree(e);
|
||||
@ -1060,7 +1077,7 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
crtc->primary->old_fb = crtc->primary->fb;
|
||||
plane->old_fb = plane->fb;
|
||||
if (crtc->funcs->page_flip_target)
|
||||
ret = crtc->funcs->page_flip_target(crtc, fb, e,
|
||||
page_flip->flags,
|
||||
@ -1073,19 +1090,18 @@ retry:
|
||||
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT)
|
||||
drm_event_cancel_free(dev, &e->base);
|
||||
/* Keep the old fb, don't unref it. */
|
||||
crtc->primary->old_fb = NULL;
|
||||
plane->old_fb = NULL;
|
||||
} else {
|
||||
crtc->primary->fb = fb;
|
||||
/* Unref only the old framebuffer. */
|
||||
fb = NULL;
|
||||
plane->fb = fb;
|
||||
drm_framebuffer_get(fb);
|
||||
}
|
||||
|
||||
out:
|
||||
if (fb)
|
||||
drm_framebuffer_put(fb);
|
||||
if (crtc->primary->old_fb)
|
||||
drm_framebuffer_put(crtc->primary->old_fb);
|
||||
crtc->primary->old_fb = NULL;
|
||||
if (plane->old_fb)
|
||||
drm_framebuffer_put(plane->old_fb);
|
||||
plane->old_fb = NULL;
|
||||
|
||||
if (ret == -EDEADLK) {
|
||||
ret = drm_modeset_backoff(&ctx);
|
||||
|
@ -331,6 +331,9 @@ EXPORT_SYMBOL(drm_gem_map_dma_buf);
|
||||
|
||||
/**
|
||||
* drm_gem_unmap_dma_buf - unmap_dma_buf implementation for GEM
|
||||
* @attach: attachment to unmap buffer from
|
||||
* @sgt: scatterlist info of the buffer to unmap
|
||||
* @dir: direction of DMA transfer
|
||||
*
|
||||
* Not implemented. The unmap is done at drm_gem_map_detach(). This can be
|
||||
* used as the &dma_buf_ops.unmap_dma_buf callback.
|
||||
@ -429,6 +432,8 @@ EXPORT_SYMBOL(drm_gem_dmabuf_vunmap);
|
||||
|
||||
/**
|
||||
* drm_gem_dmabuf_kmap_atomic - map_atomic implementation for GEM
|
||||
* @dma_buf: buffer to be mapped
|
||||
* @page_num: page number within the buffer
|
||||
*
|
||||
* Not implemented. This can be used as the &dma_buf_ops.map_atomic callback.
|
||||
*/
|
||||
@ -441,6 +446,9 @@ EXPORT_SYMBOL(drm_gem_dmabuf_kmap_atomic);
|
||||
|
||||
/**
|
||||
* drm_gem_dmabuf_kunmap_atomic - unmap_atomic implementation for GEM
|
||||
* @dma_buf: buffer to be unmapped
|
||||
* @page_num: page number within the buffer
|
||||
* @addr: virtual address of the buffer
|
||||
*
|
||||
* Not implemented. This can be used as the &dma_buf_ops.unmap_atomic callback.
|
||||
*/
|
||||
@ -453,6 +461,8 @@ EXPORT_SYMBOL(drm_gem_dmabuf_kunmap_atomic);
|
||||
|
||||
/**
|
||||
* drm_gem_dmabuf_kmap - map implementation for GEM
|
||||
* @dma_buf: buffer to be mapped
|
||||
* @page_num: page number within the buffer
|
||||
*
|
||||
* Not implemented. This can be used as the &dma_buf_ops.map callback.
|
||||
*/
|
||||
@ -464,6 +474,9 @@ EXPORT_SYMBOL(drm_gem_dmabuf_kmap);
|
||||
|
||||
/**
|
||||
* drm_gem_dmabuf_kunmap - unmap implementation for GEM
|
||||
* @dma_buf: buffer to be unmapped
|
||||
* @page_num: page number within the buffer
|
||||
* @addr: virtual address of the buffer
|
||||
*
|
||||
* Not implemented. This can be used as the &dma_buf_ops.unmap callback.
|
||||
*/
|
||||
|
@ -141,7 +141,7 @@ bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
|
||||
|
||||
ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read scrambling status: %d\n", ret);
|
||||
DRM_DEBUG_KMS("Failed to read scrambling status: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -168,7 +168,7 @@ bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
|
||||
|
||||
ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read TMDS config: %d\n", ret);
|
||||
DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
|
||||
|
||||
ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to enable scrambling: %d\n", ret);
|
||||
DRM_DEBUG_KMS("Failed to enable scrambling: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
|
||||
|
||||
ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to read TMDS config: %d\n", ret);
|
||||
DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -234,7 +234,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
|
||||
|
||||
ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to set TMDS clock ratio: %d\n", ret);
|
||||
DRM_DEBUG_KMS("Failed to set TMDS clock ratio: %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -64,13 +64,15 @@ static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
|
||||
static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
struct drm_plane *plane;
|
||||
struct drm_simple_display_pipe *pipe;
|
||||
|
||||
pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
|
||||
if (!pipe->funcs || !pipe->funcs->enable)
|
||||
return;
|
||||
|
||||
pipe->funcs->enable(pipe, crtc->state);
|
||||
plane = &pipe->plane;
|
||||
pipe->funcs->enable(pipe, crtc->state, plane->state);
|
||||
}
|
||||
|
||||
static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
|
||||
|
@ -162,7 +162,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
|
||||
dp->drm_dev = drm_dev;
|
||||
|
||||
dp->plat_data.dev_type = EXYNOS_DP;
|
||||
dp->plat_data.power_on = exynos_dp_poweron;
|
||||
dp->plat_data.power_on_start = exynos_dp_poweron;
|
||||
dp->plat_data.power_off = exynos_dp_poweroff;
|
||||
dp->plat_data.attach = exynos_dp_bridge_attach;
|
||||
dp->plat_data.get_modes = exynos_dp_get_modes;
|
||||
|
@ -37,26 +37,6 @@
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
|
||||
int exynos_atomic_check(struct drm_device *dev,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_atomic_helper_check_modeset(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_normalize_zpos(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_helper_check_planes(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct drm_exynos_file_private *file_priv;
|
||||
|
@ -275,7 +275,6 @@ static inline int exynos_dpi_bind(struct drm_device *dev,
|
||||
|
||||
int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
|
||||
bool nonblock);
|
||||
int exynos_atomic_check(struct drm_device *dev, struct drm_atomic_state *state);
|
||||
|
||||
|
||||
extern struct platform_driver fimd_driver;
|
||||
|
@ -161,7 +161,7 @@ static struct drm_mode_config_helper_funcs exynos_drm_mode_config_helpers = {
|
||||
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
|
||||
.fb_create = exynos_user_fb_create,
|
||||
.output_poll_changed = drm_fb_helper_output_poll_changed,
|
||||
.atomic_check = exynos_atomic_check,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
@ -182,4 +182,6 @@ void exynos_drm_mode_config_init(struct drm_device *dev)
|
||||
dev->mode_config.helper_private = &exynos_drm_mode_config_helpers;
|
||||
|
||||
dev->mode_config.allow_fb_modifiers = true;
|
||||
|
||||
dev->mode_config.normalize_zpos = true;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ static void cdv_intel_crt_dpms(struct drm_encoder *encoder, int mode)
|
||||
REG_WRITE(reg, temp);
|
||||
}
|
||||
|
||||
static int cdv_intel_crt_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status cdv_intel_crt_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
|
@ -505,7 +505,7 @@ static void cdv_intel_edp_backlight_off (struct gma_encoder *intel_encoder)
|
||||
msleep(intel_dp->backlight_off_delay);
|
||||
}
|
||||
|
||||
static int
|
||||
static enum drm_mode_status
|
||||
cdv_intel_dp_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
|
@ -223,7 +223,7 @@ static int cdv_hdmi_get_modes(struct drm_connector *connector)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdv_hdmi_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status cdv_hdmi_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock > 165000)
|
||||
|
@ -244,7 +244,7 @@ static void cdv_intel_lvds_restore(struct drm_connector *connector)
|
||||
{
|
||||
}
|
||||
|
||||
static int cdv_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status cdv_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
|
@ -346,7 +346,7 @@ static int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct mdfld_dsi_connector *dsi_connector =
|
||||
|
@ -509,7 +509,7 @@ static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
||||
HDMI_WRITE(HDMI_VIDEO_REG, temp);
|
||||
}
|
||||
|
||||
static int oaktrail_hdmi_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status oaktrail_hdmi_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock > 165000)
|
||||
|
@ -255,7 +255,7 @@ extern int intelfb_remove(struct drm_device *dev,
|
||||
extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
extern int psb_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
extern enum drm_mode_status psb_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode);
|
||||
extern int psb_intel_lvds_set_property(struct drm_connector *connector,
|
||||
struct drm_property *property,
|
||||
|
@ -343,7 +343,7 @@ static void psb_intel_lvds_restore(struct drm_connector *connector)
|
||||
}
|
||||
}
|
||||
|
||||
int psb_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
enum drm_mode_status psb_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = connector->dev->dev_private;
|
||||
|
@ -1157,7 +1157,7 @@ static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
|
||||
return;
|
||||
}
|
||||
|
||||
static int psb_intel_sdvo_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status psb_intel_sdvo_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector);
|
||||
|
@ -27,7 +27,7 @@ static int hibmc_connector_get_modes(struct drm_connector *connector)
|
||||
return drm_add_modes_noedid(connector, 800, 600);
|
||||
}
|
||||
|
||||
static int hibmc_connector_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status hibmc_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
return MODE_OK;
|
||||
|
@ -1106,7 +1106,7 @@ static int tda998x_connector_get_modes(struct drm_connector *connector)
|
||||
return n;
|
||||
}
|
||||
|
||||
static int tda998x_connector_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status tda998x_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
/* TDA19988 dotclock can go up to 165MHz */
|
||||
|
@ -748,6 +748,11 @@ intel_crt_detect(struct drm_connector *connector,
|
||||
connector->base.id, connector->name,
|
||||
force);
|
||||
|
||||
if (i915_modparams.load_detect_test) {
|
||||
intel_display_power_get(dev_priv, intel_encoder->power_domain);
|
||||
goto load_detect;
|
||||
}
|
||||
|
||||
/* Skip machines without VGA that falsely report hotplug events */
|
||||
if (dmi_check_system(intel_spurious_crt_detect))
|
||||
return connector_status_disconnected;
|
||||
@ -776,11 +781,12 @@ intel_crt_detect(struct drm_connector *connector,
|
||||
* broken monitor (without edid) to work behind a broken kvm (that fails
|
||||
* to have the right resistors for HP detection) needs to fix this up.
|
||||
* For now just bail out. */
|
||||
if (I915_HAS_HOTPLUG(dev_priv) && !i915_modparams.load_detect_test) {
|
||||
if (I915_HAS_HOTPLUG(dev_priv)) {
|
||||
status = connector_status_disconnected;
|
||||
goto out;
|
||||
}
|
||||
|
||||
load_detect:
|
||||
if (!force) {
|
||||
status = connector->status;
|
||||
goto out;
|
||||
|
@ -2824,7 +2824,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
|
||||
continue;
|
||||
|
||||
if (intel_plane_ggtt_offset(state) == plane_config->base) {
|
||||
fb = c->primary->fb;
|
||||
fb = state->base.fb;
|
||||
drm_framebuffer_get(fb);
|
||||
goto valid_fb;
|
||||
}
|
||||
@ -9974,6 +9974,8 @@ found:
|
||||
ret = PTR_ERR_OR_ZERO(drm_atomic_get_connector_state(restore_state, connector));
|
||||
if (!ret)
|
||||
ret = PTR_ERR_OR_ZERO(drm_atomic_get_crtc_state(restore_state, crtc));
|
||||
if (!ret)
|
||||
ret = drm_atomic_add_affected_planes(restore_state, crtc);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("Failed to create a copy of old state to restore: %i\n", ret);
|
||||
goto fail;
|
||||
|
@ -640,7 +640,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
|
||||
if (!crtc->state->active)
|
||||
continue;
|
||||
|
||||
WARN(!crtc->primary->fb,
|
||||
WARN(!crtc->primary->state->fb,
|
||||
"re-used BIOS config but lost an fb on crtc %d\n",
|
||||
crtc->base.id);
|
||||
}
|
||||
|
@ -1586,7 +1586,7 @@ static uint32_t mga_vga_calculate_mode_bandwidth(struct drm_display_mode *mode,
|
||||
|
||||
#define MODE_BANDWIDTH MODE_BAD
|
||||
|
||||
static int mga_vga_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
|
@ -99,7 +99,8 @@ static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
|
||||
};
|
||||
|
||||
static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state)
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
|
||||
@ -125,12 +126,6 @@ static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe,
|
||||
mxsfb_plane_atomic_update(mxsfb, plane_state);
|
||||
}
|
||||
|
||||
static int mxsfb_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
return drm_gem_fb_prepare_fb(&pipe->plane, 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);
|
||||
@ -159,7 +154,7 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = {
|
||||
.enable = mxsfb_pipe_enable,
|
||||
.disable = mxsfb_pipe_disable,
|
||||
.update = mxsfb_pipe_update,
|
||||
.prepare_fb = mxsfb_pipe_prepare_fb,
|
||||
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
|
||||
.enable_vblank = mxsfb_pipe_enable_vblank,
|
||||
.disable_vblank = mxsfb_pipe_disable_vblank,
|
||||
};
|
||||
|
@ -134,7 +134,7 @@ nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate,
|
||||
nvkm_volt_map(volt, volt->max2_id, clk->temp));
|
||||
|
||||
for (cstate = start; &cstate->head != &pstate->list;
|
||||
cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) {
|
||||
cstate = list_prev_entry(cstate, head)) {
|
||||
if (nvkm_cstate_valid(clk, cstate, max_volt, clk->temp))
|
||||
break;
|
||||
}
|
||||
|
@ -319,6 +319,9 @@ static int omap_modeset_init(struct drm_device *dev)
|
||||
dev->mode_config.max_width = 8192;
|
||||
dev->mode_config.max_height = 8192;
|
||||
|
||||
/* We want the zpos to be normalized */
|
||||
dev->mode_config.normalize_zpos = true;
|
||||
|
||||
dev->mode_config.funcs = &omap_mode_config_funcs;
|
||||
dev->mode_config.helper_private = &omap_mode_config_helper_funcs;
|
||||
|
||||
|
@ -65,7 +65,7 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
|
||||
info.rotation_type = OMAP_DSS_ROT_NONE;
|
||||
info.rotation = DRM_MODE_ROTATE_0;
|
||||
info.global_alpha = 0xff;
|
||||
info.zorder = state->zpos;
|
||||
info.zorder = state->normalized_zpos;
|
||||
|
||||
/* update scanout: */
|
||||
omap_framebuffer_update_scanout(state->fb, state, &info);
|
||||
|
@ -120,7 +120,8 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe,
|
||||
}
|
||||
|
||||
static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *cstate)
|
||||
struct drm_crtc_state *cstate,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_plane *plane = &pipe->plane;
|
||||
@ -376,19 +377,13 @@ static void pl111_display_disable_vblank(struct drm_simple_display_pipe *pipe)
|
||||
writel(0, priv->regs + priv->ienb);
|
||||
}
|
||||
|
||||
static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
return drm_gem_fb_prepare_fb(&pipe->plane, plane_state);
|
||||
}
|
||||
|
||||
static struct drm_simple_display_pipe_funcs pl111_display_funcs = {
|
||||
.mode_valid = pl111_mode_valid,
|
||||
.check = pl111_display_check,
|
||||
.enable = pl111_display_enable,
|
||||
.disable = pl111_display_disable,
|
||||
.update = pl111_display_update,
|
||||
.prepare_fb = pl111_display_prepare_fb,
|
||||
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
|
||||
};
|
||||
|
||||
static int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -1037,7 +1037,7 @@ static int qxl_conn_get_modes(struct drm_connector *connector)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qxl_conn_mode_valid(struct drm_connector *connector,
|
||||
static enum drm_mode_status qxl_conn_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_device *ddev = connector->dev;
|
||||
|
@ -87,7 +87,6 @@ struct rcar_du_device {
|
||||
struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
|
||||
|
||||
struct {
|
||||
struct drm_property *alpha;
|
||||
struct drm_property *colorkey;
|
||||
} props;
|
||||
|
||||
|
@ -233,15 +233,7 @@ static int rcar_du_atomic_check(struct drm_device *dev,
|
||||
struct rcar_du_device *rcdu = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
ret = drm_atomic_helper_check_modeset(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_normalize_zpos(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_helper_check_planes(dev, state);
|
||||
ret = drm_atomic_helper_check(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -415,11 +407,6 @@ static int rcar_du_encoders_init(struct rcar_du_device *rcdu)
|
||||
|
||||
static int rcar_du_properties_init(struct rcar_du_device *rcdu)
|
||||
{
|
||||
rcdu->props.alpha =
|
||||
drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
|
||||
if (rcdu->props.alpha == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* The color key is expressed as an RGB888 triplet stored in a 32-bit
|
||||
* integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
|
||||
@ -529,6 +516,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
|
||||
dev->mode_config.min_height = 0;
|
||||
dev->mode_config.max_width = 4095;
|
||||
dev->mode_config.max_height = 2047;
|
||||
dev->mode_config.normalize_zpos = true;
|
||||
dev->mode_config.funcs = &rcar_du_mode_config_funcs;
|
||||
dev->mode_config.helper_private = &rcar_du_mode_config_helper;
|
||||
|
||||
|
@ -423,7 +423,7 @@ static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp,
|
||||
rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
|
||||
else
|
||||
rcar_du_plane_write(rgrp, index, PnALPHAR,
|
||||
PnALPHAR_ABIT_X | state->alpha);
|
||||
PnALPHAR_ABIT_X | state->state.alpha >> 8);
|
||||
|
||||
pnmr = PnMR_BM_MD | state->format->pnmr;
|
||||
|
||||
@ -692,11 +692,11 @@ static void rcar_du_plane_reset(struct drm_plane *plane)
|
||||
|
||||
state->hwindex = -1;
|
||||
state->source = RCAR_DU_PLANE_MEMORY;
|
||||
state->alpha = 255;
|
||||
state->colorkey = RCAR_DU_COLORKEY_NONE;
|
||||
state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
|
||||
|
||||
plane->state = &state->state;
|
||||
plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE;
|
||||
plane->state->plane = plane;
|
||||
}
|
||||
|
||||
@ -708,9 +708,7 @@ static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
|
||||
struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
|
||||
struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
|
||||
|
||||
if (property == rcdu->props.alpha)
|
||||
rstate->alpha = val;
|
||||
else if (property == rcdu->props.colorkey)
|
||||
if (property == rcdu->props.colorkey)
|
||||
rstate->colorkey = val;
|
||||
else
|
||||
return -EINVAL;
|
||||
@ -726,9 +724,7 @@ static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
|
||||
container_of(state, const struct rcar_du_plane_state, state);
|
||||
struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
|
||||
|
||||
if (property == rcdu->props.alpha)
|
||||
*val = rstate->alpha;
|
||||
else if (property == rcdu->props.colorkey)
|
||||
if (property == rcdu->props.colorkey)
|
||||
*val = rstate->colorkey;
|
||||
else
|
||||
return -EINVAL;
|
||||
@ -796,11 +792,10 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp)
|
||||
if (type == DRM_PLANE_TYPE_PRIMARY)
|
||||
continue;
|
||||
|
||||
drm_object_attach_property(&plane->plane.base,
|
||||
rcdu->props.alpha, 255);
|
||||
drm_object_attach_property(&plane->plane.base,
|
||||
rcdu->props.colorkey,
|
||||
RCAR_DU_COLORKEY_NONE);
|
||||
drm_plane_create_alpha_property(&plane->plane);
|
||||
drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,6 @@ static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
|
||||
* @state: base DRM plane state
|
||||
* @format: information about the pixel format used by the plane
|
||||
* @hwindex: 0-based hardware plane index, -1 means unused
|
||||
* @alpha: value of the plane alpha property
|
||||
* @colorkey: value of the plane colorkey property
|
||||
*/
|
||||
struct rcar_du_plane_state {
|
||||
@ -60,7 +59,6 @@ struct rcar_du_plane_state {
|
||||
int hwindex;
|
||||
enum rcar_du_plane_source source;
|
||||
|
||||
unsigned int alpha;
|
||||
unsigned int colorkey;
|
||||
};
|
||||
|
||||
|
@ -54,6 +54,7 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
|
||||
};
|
||||
struct rcar_du_plane_state state = {
|
||||
.state = {
|
||||
.alpha = DRM_BLEND_ALPHA_OPAQUE,
|
||||
.crtc = &crtc->crtc,
|
||||
.dst.x1 = 0,
|
||||
.dst.y1 = 0,
|
||||
@ -67,7 +68,6 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
|
||||
},
|
||||
.format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
|
||||
.source = RCAR_DU_PLANE_VSPD1,
|
||||
.alpha = 255,
|
||||
.colorkey = 0,
|
||||
};
|
||||
|
||||
@ -173,7 +173,7 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
|
||||
struct vsp1_du_atomic_config cfg = {
|
||||
.pixelformat = 0,
|
||||
.pitch = fb->pitches[0],
|
||||
.alpha = state->alpha,
|
||||
.alpha = state->state.alpha >> 8,
|
||||
.zpos = state->state.zpos,
|
||||
};
|
||||
unsigned int i;
|
||||
@ -335,44 +335,13 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
|
||||
if (state == NULL)
|
||||
return;
|
||||
|
||||
state->alpha = 255;
|
||||
state->state.alpha = DRM_BLEND_ALPHA_OPAQUE;
|
||||
state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
|
||||
|
||||
plane->state = &state->state;
|
||||
plane->state->plane = plane;
|
||||
}
|
||||
|
||||
static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
|
||||
struct drm_plane_state *state, struct drm_property *property,
|
||||
uint64_t val)
|
||||
{
|
||||
struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
|
||||
struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
|
||||
|
||||
if (property == rcdu->props.alpha)
|
||||
rstate->alpha = val;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
|
||||
const struct drm_plane_state *state, struct drm_property *property,
|
||||
uint64_t *val)
|
||||
{
|
||||
const struct rcar_du_vsp_plane_state *rstate =
|
||||
container_of(state, const struct rcar_du_vsp_plane_state, state);
|
||||
struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
|
||||
|
||||
if (property == rcdu->props.alpha)
|
||||
*val = rstate->alpha;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
@ -380,8 +349,6 @@ static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
|
||||
.destroy = drm_plane_cleanup,
|
||||
.atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
|
||||
.atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
|
||||
.atomic_set_property = rcar_du_vsp_plane_atomic_set_property,
|
||||
.atomic_get_property = rcar_du_vsp_plane_atomic_get_property,
|
||||
};
|
||||
|
||||
int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
|
||||
@ -438,8 +405,7 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
|
||||
if (type == DRM_PLANE_TYPE_PRIMARY)
|
||||
continue;
|
||||
|
||||
drm_object_attach_property(&plane->plane.base,
|
||||
rcdu->props.alpha, 255);
|
||||
drm_plane_create_alpha_property(&plane->plane);
|
||||
drm_plane_create_zpos_property(&plane->plane, 1, 1,
|
||||
vsp->num_planes - 1);
|
||||
}
|
||||
|
@ -44,15 +44,12 @@ static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p)
|
||||
* @state: base DRM plane state
|
||||
* @format: information about the pixel format used by the plane
|
||||
* @sg_tables: scatter-gather tables for the frame buffer memory
|
||||
* @alpha: value of the plane alpha property
|
||||
*/
|
||||
struct rcar_du_vsp_plane_state {
|
||||
struct drm_plane_state state;
|
||||
|
||||
const struct rcar_du_format_info *format;
|
||||
struct sg_table sg_tables[3];
|
||||
|
||||
unsigned int alpha;
|
||||
};
|
||||
|
||||
static inline struct rcar_du_vsp_plane_state *
|
||||
|
@ -77,13 +77,13 @@ struct rockchip_dp_device {
|
||||
struct analogix_dp_plat_data plat_data;
|
||||
};
|
||||
|
||||
static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
|
||||
static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
|
||||
{
|
||||
struct rockchip_dp_device *dp = to_dp(encoder);
|
||||
int ret;
|
||||
|
||||
if (!analogix_dp_psr_enabled(dp->adp))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
DRM_DEV_DEBUG(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
|
||||
|
||||
@ -91,13 +91,13 @@ static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
|
||||
PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dp->dev, "line flag interrupt did not arrive\n");
|
||||
return;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
analogix_dp_enable_psr(dp->adp);
|
||||
return analogix_dp_enable_psr(dp->adp);
|
||||
else
|
||||
analogix_dp_disable_psr(dp->adp);
|
||||
return analogix_dp_disable_psr(dp->adp);
|
||||
}
|
||||
|
||||
static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
|
||||
@ -109,7 +109,7 @@ static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
|
||||
static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
|
||||
{
|
||||
struct rockchip_dp_device *dp = to_dp(plat_data);
|
||||
int ret;
|
||||
@ -127,7 +127,14 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return rockchip_drm_psr_activate(&dp->encoder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_dp_poweron_end(struct analogix_dp_plat_data *plat_data)
|
||||
{
|
||||
struct rockchip_dp_device *dp = to_dp(plat_data);
|
||||
|
||||
return rockchip_drm_psr_inhibit_put(&dp->encoder);
|
||||
}
|
||||
|
||||
static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
|
||||
@ -135,7 +142,7 @@ static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
|
||||
struct rockchip_dp_device *dp = to_dp(plat_data);
|
||||
int ret;
|
||||
|
||||
ret = rockchip_drm_psr_deactivate(&dp->encoder);
|
||||
ret = rockchip_drm_psr_inhibit_get(&dp->encoder);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
@ -218,6 +225,7 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
|
||||
struct drm_display_info *di = &conn_state->connector->display_info;
|
||||
|
||||
/*
|
||||
* The hardware IC designed that VOP must output the RGB10 video
|
||||
@ -229,6 +237,7 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
|
||||
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
|
||||
s->output_type = DRM_MODE_CONNECTOR_eDP;
|
||||
s->output_bpc = di->bpc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -328,7 +337,8 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
|
||||
dp->plat_data.encoder = &dp->encoder;
|
||||
|
||||
dp->plat_data.dev_type = dp->data->chip_type;
|
||||
dp->plat_data.power_on = rockchip_dp_poweron;
|
||||
dp->plat_data.power_on_start = rockchip_dp_poweron_start;
|
||||
dp->plat_data.power_on_end = rockchip_dp_poweron_end;
|
||||
dp->plat_data.power_off = rockchip_dp_powerdown;
|
||||
dp->plat_data.get_modes = rockchip_dp_get_modes;
|
||||
|
||||
@ -358,6 +368,8 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master,
|
||||
analogix_dp_unbind(dp->adp);
|
||||
rockchip_drm_psr_unregister(&dp->encoder);
|
||||
dp->encoder.funcs->destroy(&dp->encoder);
|
||||
|
||||
dp->adp = ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static const struct component_ops rockchip_dp_component_ops = {
|
||||
@ -381,6 +393,7 @@ static int rockchip_dp_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
dp->dev = dev;
|
||||
dp->adp = ERR_PTR(-ENODEV);
|
||||
dp->plat_data.panel = panel;
|
||||
|
||||
ret = rockchip_dp_of_probe(dp);
|
||||
@ -404,6 +417,9 @@ static int rockchip_dp_suspend(struct device *dev)
|
||||
{
|
||||
struct rockchip_dp_device *dp = dev_get_drvdata(dev);
|
||||
|
||||
if (IS_ERR(dp->adp))
|
||||
return 0;
|
||||
|
||||
return analogix_dp_suspend(dp->adp);
|
||||
}
|
||||
|
||||
@ -411,6 +427,9 @@ static int rockchip_dp_resume(struct device *dev)
|
||||
{
|
||||
struct rockchip_dp_device *dp = dev_get_drvdata(dev);
|
||||
|
||||
if (IS_ERR(dp->adp))
|
||||
return 0;
|
||||
|
||||
return analogix_dp_resume(dp->adp);
|
||||
}
|
||||
#endif
|
||||
|
@ -36,6 +36,7 @@ struct rockchip_crtc_state {
|
||||
struct drm_crtc_state base;
|
||||
int output_type;
|
||||
int output_mode;
|
||||
int output_bpc;
|
||||
};
|
||||
#define to_rockchip_crtc_state(s) \
|
||||
container_of(s, struct rockchip_crtc_state, base)
|
||||
|
@ -167,8 +167,67 @@ err_gem_object_unreference:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
rockchip_drm_psr_inhibit_get_state(struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_encoder *encoder;
|
||||
u32 encoder_mask = 0;
|
||||
int i;
|
||||
|
||||
for_each_old_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
encoder_mask |= crtc_state->encoder_mask;
|
||||
encoder_mask |= crtc->state->encoder_mask;
|
||||
}
|
||||
|
||||
drm_for_each_encoder_mask(encoder, state->dev, encoder_mask)
|
||||
rockchip_drm_psr_inhibit_get(encoder);
|
||||
}
|
||||
|
||||
static void
|
||||
rockchip_drm_psr_inhibit_put_state(struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_encoder *encoder;
|
||||
u32 encoder_mask = 0;
|
||||
int i;
|
||||
|
||||
for_each_old_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
encoder_mask |= crtc_state->encoder_mask;
|
||||
encoder_mask |= crtc->state->encoder_mask;
|
||||
}
|
||||
|
||||
drm_for_each_encoder_mask(encoder, state->dev, encoder_mask)
|
||||
rockchip_drm_psr_inhibit_put(encoder);
|
||||
}
|
||||
|
||||
static void
|
||||
rockchip_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct drm_device *dev = old_state->dev;
|
||||
|
||||
rockchip_drm_psr_inhibit_get_state(old_state);
|
||||
|
||||
drm_atomic_helper_commit_modeset_disables(dev, old_state);
|
||||
|
||||
drm_atomic_helper_commit_modeset_enables(dev, old_state);
|
||||
|
||||
drm_atomic_helper_commit_planes(dev, old_state,
|
||||
DRM_PLANE_COMMIT_ACTIVE_ONLY);
|
||||
|
||||
rockchip_drm_psr_inhibit_put_state(old_state);
|
||||
|
||||
drm_atomic_helper_commit_hw_done(old_state);
|
||||
|
||||
drm_atomic_helper_wait_for_vblanks(dev, old_state);
|
||||
|
||||
drm_atomic_helper_cleanup_planes(dev, old_state);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
|
||||
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
|
||||
.atomic_commit_tail = rockchip_atomic_helper_commit_tail_rpm,
|
||||
};
|
||||
|
||||
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
|
||||
|
@ -357,8 +357,8 @@ err_free_rk_obj:
|
||||
}
|
||||
|
||||
/*
|
||||
* rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
|
||||
* function
|
||||
* rockchip_gem_free_object - (struct drm_driver)->gem_free_object_unlocked
|
||||
* callback function
|
||||
*/
|
||||
void rockchip_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
|
@ -20,42 +20,19 @@
|
||||
|
||||
#define PSR_FLUSH_TIMEOUT_MS 100
|
||||
|
||||
enum psr_state {
|
||||
PSR_FLUSH,
|
||||
PSR_ENABLE,
|
||||
PSR_DISABLE,
|
||||
};
|
||||
|
||||
struct psr_drv {
|
||||
struct list_head list;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
struct mutex lock;
|
||||
bool active;
|
||||
enum psr_state state;
|
||||
int inhibit_count;
|
||||
bool enabled;
|
||||
|
||||
struct delayed_work flush_work;
|
||||
|
||||
void (*set)(struct drm_encoder *encoder, bool enable);
|
||||
int (*set)(struct drm_encoder *encoder, bool enable);
|
||||
};
|
||||
|
||||
static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
|
||||
{
|
||||
struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
|
||||
struct psr_drv *psr;
|
||||
|
||||
mutex_lock(&drm_drv->psr_list_lock);
|
||||
list_for_each_entry(psr, &drm_drv->psr_list, list) {
|
||||
if (psr->encoder->crtc == crtc)
|
||||
goto out;
|
||||
}
|
||||
psr = ERR_PTR(-ENODEV);
|
||||
|
||||
out:
|
||||
mutex_unlock(&drm_drv->psr_list_lock);
|
||||
return psr;
|
||||
}
|
||||
|
||||
static struct psr_drv *find_psr_by_encoder(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
|
||||
@ -73,46 +50,22 @@ out:
|
||||
return psr;
|
||||
}
|
||||
|
||||
static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
|
||||
static int psr_set_state_locked(struct psr_drv *psr, bool enable)
|
||||
{
|
||||
/*
|
||||
* Allowed finite state machine:
|
||||
*
|
||||
* PSR_ENABLE < = = = = = > PSR_FLUSH
|
||||
* | ^ |
|
||||
* | | |
|
||||
* v | |
|
||||
* PSR_DISABLE < - - - - - - - - -
|
||||
*/
|
||||
if (state == psr->state || !psr->active)
|
||||
return;
|
||||
int ret;
|
||||
|
||||
/* Already disabled in flush, change the state, but not the hardware */
|
||||
if (state == PSR_DISABLE && psr->state == PSR_FLUSH) {
|
||||
psr->state = state;
|
||||
return;
|
||||
}
|
||||
if (psr->inhibit_count > 0)
|
||||
return -EINVAL;
|
||||
|
||||
psr->state = state;
|
||||
if (enable == psr->enabled)
|
||||
return 0;
|
||||
|
||||
/* Actually commit the state change to hardware */
|
||||
switch (psr->state) {
|
||||
case PSR_ENABLE:
|
||||
psr->set(psr->encoder, true);
|
||||
break;
|
||||
ret = psr->set(psr->encoder, enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
case PSR_DISABLE:
|
||||
case PSR_FLUSH:
|
||||
psr->set(psr->encoder, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void psr_set_state(struct psr_drv *psr, enum psr_state state)
|
||||
{
|
||||
mutex_lock(&psr->lock);
|
||||
psr_set_state_locked(psr, state);
|
||||
mutex_unlock(&psr->lock);
|
||||
psr->enabled = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void psr_flush_handler(struct work_struct *work)
|
||||
@ -120,21 +73,24 @@ static void psr_flush_handler(struct work_struct *work)
|
||||
struct psr_drv *psr = container_of(to_delayed_work(work),
|
||||
struct psr_drv, flush_work);
|
||||
|
||||
/* If the state has changed since we initiated the flush, do nothing */
|
||||
mutex_lock(&psr->lock);
|
||||
if (psr->state == PSR_FLUSH)
|
||||
psr_set_state_locked(psr, PSR_ENABLE);
|
||||
psr_set_state_locked(psr, true);
|
||||
mutex_unlock(&psr->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* rockchip_drm_psr_activate - activate PSR on the given pipe
|
||||
* rockchip_drm_psr_inhibit_put - release PSR inhibit on given encoder
|
||||
* @encoder: encoder to obtain the PSR encoder
|
||||
*
|
||||
* Decrements PSR inhibit count on given encoder. Should be called only
|
||||
* for a PSR inhibit count increment done before. If PSR inhibit counter
|
||||
* reaches zero, PSR flush work is scheduled to make the hardware enter
|
||||
* PSR mode in PSR_FLUSH_TIMEOUT_MS.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative errno on failure.
|
||||
*/
|
||||
int rockchip_drm_psr_activate(struct drm_encoder *encoder)
|
||||
int rockchip_drm_psr_inhibit_put(struct drm_encoder *encoder)
|
||||
{
|
||||
struct psr_drv *psr = find_psr_by_encoder(encoder);
|
||||
|
||||
@ -142,21 +98,30 @@ int rockchip_drm_psr_activate(struct drm_encoder *encoder)
|
||||
return PTR_ERR(psr);
|
||||
|
||||
mutex_lock(&psr->lock);
|
||||
psr->active = true;
|
||||
--psr->inhibit_count;
|
||||
WARN_ON(psr->inhibit_count < 0);
|
||||
if (!psr->inhibit_count)
|
||||
mod_delayed_work(system_wq, &psr->flush_work,
|
||||
PSR_FLUSH_TIMEOUT_MS);
|
||||
mutex_unlock(&psr->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rockchip_drm_psr_activate);
|
||||
EXPORT_SYMBOL(rockchip_drm_psr_inhibit_put);
|
||||
|
||||
/**
|
||||
* rockchip_drm_psr_deactivate - deactivate PSR on the given pipe
|
||||
* rockchip_drm_psr_inhibit_get - acquire PSR inhibit on given encoder
|
||||
* @encoder: encoder to obtain the PSR encoder
|
||||
*
|
||||
* Increments PSR inhibit count on given encoder. This function guarantees
|
||||
* that after it returns PSR is turned off on given encoder and no PSR-related
|
||||
* hardware state change occurs at least until a matching call to
|
||||
* rockchip_drm_psr_inhibit_put() is done.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative errno on failure.
|
||||
*/
|
||||
int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
|
||||
int rockchip_drm_psr_inhibit_get(struct drm_encoder *encoder)
|
||||
{
|
||||
struct psr_drv *psr = find_psr_by_encoder(encoder);
|
||||
|
||||
@ -164,37 +129,25 @@ int rockchip_drm_psr_deactivate(struct drm_encoder *encoder)
|
||||
return PTR_ERR(psr);
|
||||
|
||||
mutex_lock(&psr->lock);
|
||||
psr->active = false;
|
||||
psr_set_state_locked(psr, false);
|
||||
++psr->inhibit_count;
|
||||
mutex_unlock(&psr->lock);
|
||||
cancel_delayed_work_sync(&psr->flush_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
|
||||
EXPORT_SYMBOL(rockchip_drm_psr_inhibit_get);
|
||||
|
||||
static void rockchip_drm_do_flush(struct psr_drv *psr)
|
||||
{
|
||||
psr_set_state(psr, PSR_FLUSH);
|
||||
mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS);
|
||||
}
|
||||
cancel_delayed_work_sync(&psr->flush_work);
|
||||
|
||||
/**
|
||||
* rockchip_drm_psr_flush - flush a single pipe
|
||||
* @crtc: CRTC of the pipe to flush
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, -errno on fail
|
||||
*/
|
||||
int rockchip_drm_psr_flush(struct drm_crtc *crtc)
|
||||
{
|
||||
struct psr_drv *psr = find_psr_by_crtc(crtc);
|
||||
if (IS_ERR(psr))
|
||||
return PTR_ERR(psr);
|
||||
|
||||
rockchip_drm_do_flush(psr);
|
||||
return 0;
|
||||
mutex_lock(&psr->lock);
|
||||
if (!psr_set_state_locked(psr, false))
|
||||
mod_delayed_work(system_wq, &psr->flush_work,
|
||||
PSR_FLUSH_TIMEOUT_MS);
|
||||
mutex_unlock(&psr->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(rockchip_drm_psr_flush);
|
||||
|
||||
/**
|
||||
* rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
|
||||
@ -225,11 +178,16 @@ EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
|
||||
* @encoder: encoder that obtain the PSR function
|
||||
* @psr_set: call back to set PSR state
|
||||
*
|
||||
* The function returns with PSR inhibit counter initialized with one
|
||||
* and the caller (typically encoder driver) needs to call
|
||||
* rockchip_drm_psr_inhibit_put() when it becomes ready to accept PSR
|
||||
* enable request.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative errno on failure.
|
||||
*/
|
||||
int rockchip_drm_psr_register(struct drm_encoder *encoder,
|
||||
void (*psr_set)(struct drm_encoder *, bool enable))
|
||||
int (*psr_set)(struct drm_encoder *, bool enable))
|
||||
{
|
||||
struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
|
||||
struct psr_drv *psr;
|
||||
@ -244,8 +202,8 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
|
||||
INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
|
||||
mutex_init(&psr->lock);
|
||||
|
||||
psr->active = true;
|
||||
psr->state = PSR_DISABLE;
|
||||
psr->inhibit_count = 1;
|
||||
psr->enabled = false;
|
||||
psr->encoder = encoder;
|
||||
psr->set = psr_set;
|
||||
|
||||
@ -262,6 +220,11 @@ EXPORT_SYMBOL(rockchip_drm_psr_register);
|
||||
* @encoder: encoder that obtain the PSR function
|
||||
* @psr_set: call back to set PSR state
|
||||
*
|
||||
* It is expected that the PSR inhibit counter is 1 when this function is
|
||||
* called, which corresponds to a state when related encoder has been
|
||||
* disconnected from any CRTCs and its driver called
|
||||
* rockchip_drm_psr_inhibit_get() to stop the PSR logic.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative errno on failure.
|
||||
*/
|
||||
@ -273,7 +236,12 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
|
||||
mutex_lock(&drm_drv->psr_list_lock);
|
||||
list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
|
||||
if (psr->encoder == encoder) {
|
||||
cancel_delayed_work_sync(&psr->flush_work);
|
||||
/*
|
||||
* Any other value would mean that the encoder
|
||||
* is still in use.
|
||||
*/
|
||||
WARN_ON(psr->inhibit_count != 1);
|
||||
|
||||
list_del(&psr->list);
|
||||
kfree(psr);
|
||||
}
|
||||
|
@ -16,13 +16,12 @@
|
||||
#define __ROCKCHIP_DRM_PSR___
|
||||
|
||||
void rockchip_drm_psr_flush_all(struct drm_device *dev);
|
||||
int rockchip_drm_psr_flush(struct drm_crtc *crtc);
|
||||
|
||||
int rockchip_drm_psr_activate(struct drm_encoder *encoder);
|
||||
int rockchip_drm_psr_deactivate(struct drm_encoder *encoder);
|
||||
int rockchip_drm_psr_inhibit_put(struct drm_encoder *encoder);
|
||||
int rockchip_drm_psr_inhibit_get(struct drm_encoder *encoder);
|
||||
|
||||
int rockchip_drm_psr_register(struct drm_encoder *encoder,
|
||||
void (*psr_set)(struct drm_encoder *, bool enable));
|
||||
int (*psr_set)(struct drm_encoder *, bool enable));
|
||||
void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
|
||||
|
||||
#endif /* __ROCKCHIP_DRM_PSR__ */
|
||||
|
@ -925,6 +925,12 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
|
||||
!(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
|
||||
s->output_mode = ROCKCHIP_OUT_MODE_P888;
|
||||
|
||||
if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && s->output_bpc == 8)
|
||||
VOP_REG_SET(vop, common, pre_dither_down, 1);
|
||||
else
|
||||
VOP_REG_SET(vop, common, pre_dither_down, 0);
|
||||
|
||||
VOP_REG_SET(vop, common, out_mode, s->output_mode);
|
||||
|
||||
VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len);
|
||||
@ -1017,22 +1023,15 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
continue;
|
||||
|
||||
drm_framebuffer_get(old_plane_state->fb);
|
||||
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
|
||||
drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb);
|
||||
set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
|
||||
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
rockchip_drm_psr_flush(crtc);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
|
||||
.mode_fixup = vop_crtc_mode_fixup,
|
||||
.atomic_flush = vop_crtc_atomic_flush,
|
||||
.atomic_begin = vop_crtc_atomic_begin,
|
||||
.atomic_enable = vop_crtc_atomic_enable,
|
||||
.atomic_disable = vop_crtc_atomic_disable,
|
||||
};
|
||||
|
@ -67,6 +67,7 @@ struct vop_common {
|
||||
struct vop_reg cfg_done;
|
||||
struct vop_reg dsp_blank;
|
||||
struct vop_reg data_blank;
|
||||
struct vop_reg pre_dither_down;
|
||||
struct vop_reg dither_down;
|
||||
struct vop_reg dither_up;
|
||||
struct vop_reg gate_en;
|
||||
|
@ -264,6 +264,7 @@ static const struct vop_common rk3288_common = {
|
||||
.standby = VOP_REG_SYNC(RK3288_SYS_CTRL, 0x1, 22),
|
||||
.gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23),
|
||||
.mmu_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 20),
|
||||
.pre_dither_down = VOP_REG(RK3288_DSP_CTRL1, 0x1, 1),
|
||||
.dither_down = VOP_REG(RK3288_DSP_CTRL1, 0xf, 1),
|
||||
.dither_up = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6),
|
||||
.data_blank = VOP_REG(RK3288_DSP_CTRL0, 0x1, 19),
|
||||
|
@ -1,6 +1,6 @@
|
||||
config DRM_STI
|
||||
tristate "DRM Support for STMicroelectronics SoC stiH4xx Series"
|
||||
depends on DRM && (ARCH_STI || ARCH_MULTIPLATFORM)
|
||||
depends on OF && DRM && (ARCH_STI || ARCH_MULTIPLATFORM)
|
||||
select RESET_CONTROLLER
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
@ -8,6 +8,5 @@ config DRM_STI
|
||||
select DRM_PANEL
|
||||
select FW_LOADER
|
||||
select SND_SOC_HDMI_CODEC if SND_SOC
|
||||
select OF
|
||||
help
|
||||
Choose this option to enable DRM on STM stiH4xx chipset
|
||||
|
@ -119,30 +119,10 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sti_atomic_check(struct drm_device *dev,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_atomic_helper_check_modeset(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_normalize_zpos(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_helper_check_planes(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.output_poll_changed = drm_fb_helper_output_poll_changed,
|
||||
.atomic_check = sti_atomic_check,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
@ -160,6 +140,8 @@ static void sti_mode_config_init(struct drm_device *dev)
|
||||
dev->mode_config.max_height = STI_MAX_FB_HEIGHT;
|
||||
|
||||
dev->mode_config.funcs = &sti_mode_config_funcs;
|
||||
|
||||
dev->mode_config.normalize_zpos = true;
|
||||
}
|
||||
|
||||
DEFINE_DRM_GEM_CMA_FOPS(sti_driver_fops);
|
||||
|
@ -40,6 +40,7 @@ void sti_plane_update_fps(struct sti_plane *plane,
|
||||
bool new_frame,
|
||||
bool new_field)
|
||||
{
|
||||
struct drm_plane_state *state = plane->drm_plane.state;
|
||||
ktime_t now;
|
||||
struct sti_fps_info *fps;
|
||||
int fpks, fipks, ms_since_last, num_frames, num_fields;
|
||||
@ -66,14 +67,14 @@ void sti_plane_update_fps(struct sti_plane *plane,
|
||||
fps->last_timestamp = now;
|
||||
fps->last_frame_counter = fps->curr_frame_counter;
|
||||
|
||||
if (plane->drm_plane.fb) {
|
||||
if (state->fb) {
|
||||
fpks = (num_frames * 1000000) / ms_since_last;
|
||||
snprintf(plane->fps_info.fps_str, FPS_LENGTH,
|
||||
"%-8s %4dx%-4d %.4s @ %3d.%-3.3d fps (%s)",
|
||||
plane->drm_plane.name,
|
||||
plane->drm_plane.fb->width,
|
||||
plane->drm_plane.fb->height,
|
||||
(char *)&plane->drm_plane.fb->format->format,
|
||||
state->fb->width,
|
||||
state->fb->height,
|
||||
(char *)&state->fb->format->format,
|
||||
fpks / 1000, fpks % 1000,
|
||||
sti_plane_to_str(plane));
|
||||
}
|
||||
|
@ -72,8 +72,6 @@ static struct drm_driver drv_driver = {
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
.enable_vblank = ltdc_crtc_enable_vblank,
|
||||
.disable_vblank = ltdc_crtc_disable_vblank,
|
||||
};
|
||||
|
||||
static int drv_load(struct drm_device *ddev)
|
||||
|
@ -392,9 +392,6 @@ static void ltdc_crtc_update_clut(struct drm_crtc *crtc)
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
if (!crtc || !crtc->state)
|
||||
return;
|
||||
|
||||
if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
|
||||
return;
|
||||
|
||||
@ -569,9 +566,9 @@ static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
|
||||
.atomic_disable = ltdc_crtc_atomic_disable,
|
||||
};
|
||||
|
||||
int ltdc_crtc_enable_vblank(struct drm_device *ddev, unsigned int pipe)
|
||||
static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct ltdc_device *ldev = ddev->dev_private;
|
||||
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
||||
|
||||
DRM_DEBUG_DRIVER("\n");
|
||||
reg_set(ldev->regs, LTDC_IER, IER_LIE);
|
||||
@ -579,9 +576,9 @@ int ltdc_crtc_enable_vblank(struct drm_device *ddev, unsigned int pipe)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ltdc_crtc_disable_vblank(struct drm_device *ddev, unsigned int pipe)
|
||||
static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct ltdc_device *ldev = ddev->dev_private;
|
||||
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
||||
|
||||
DRM_DEBUG_DRIVER("\n");
|
||||
reg_clear(ldev->regs, LTDC_IER, IER_LIE);
|
||||
@ -594,6 +591,8 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.enable_vblank = ltdc_crtc_enable_vblank,
|
||||
.disable_vblank = ltdc_crtc_disable_vblank,
|
||||
.gamma_set = drm_atomic_helper_legacy_gamma_set,
|
||||
};
|
||||
|
||||
@ -727,6 +726,8 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
|
||||
reg_update_bits(ldev->regs, LTDC_L1CR + lofs,
|
||||
LXCR_LEN | LXCR_CLUTEN, val);
|
||||
|
||||
ldev->plane_fpsi[plane->index].counter++;
|
||||
|
||||
mutex_lock(&ldev->err_lock);
|
||||
if (ldev->error_status & ISR_FUIF) {
|
||||
DRM_DEBUG_DRIVER("Fifo underrun\n");
|
||||
@ -752,6 +753,25 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
|
||||
oldstate->crtc->base.id, plane->base.id);
|
||||
}
|
||||
|
||||
static void ltdc_plane_atomic_print_state(struct drm_printer *p,
|
||||
const struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_plane *plane = state->plane;
|
||||
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
||||
struct fps_info *fpsi = &ldev->plane_fpsi[plane->index];
|
||||
int ms_since_last;
|
||||
ktime_t now;
|
||||
|
||||
now = ktime_get();
|
||||
ms_since_last = ktime_to_ms(ktime_sub(now, fpsi->last_timestamp));
|
||||
|
||||
drm_printf(p, "\tuser_updates=%dfps\n",
|
||||
DIV_ROUND_CLOSEST(fpsi->counter * 1000, ms_since_last));
|
||||
|
||||
fpsi->last_timestamp = now;
|
||||
fpsi->counter = 0;
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs ltdc_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
@ -759,6 +779,7 @@ static const struct drm_plane_funcs ltdc_plane_funcs = {
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
.atomic_print_state = ltdc_plane_atomic_print_state,
|
||||
};
|
||||
|
||||
static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
|
||||
|
@ -20,6 +20,13 @@ struct ltdc_caps {
|
||||
bool non_alpha_only_l1; /* non-native no-alpha formats on layer 1 */
|
||||
};
|
||||
|
||||
#define LTDC_MAX_LAYER 4
|
||||
|
||||
struct fps_info {
|
||||
unsigned int counter;
|
||||
ktime_t last_timestamp;
|
||||
};
|
||||
|
||||
struct ltdc_device {
|
||||
void __iomem *regs;
|
||||
struct clk *pixel_clk; /* lcd pixel clock */
|
||||
@ -27,10 +34,9 @@ struct ltdc_device {
|
||||
struct ltdc_caps caps;
|
||||
u32 error_status;
|
||||
u32 irq_status;
|
||||
struct fps_info plane_fpsi[LTDC_MAX_LAYER];
|
||||
};
|
||||
|
||||
int ltdc_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe);
|
||||
void ltdc_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe);
|
||||
int ltdc_load(struct drm_device *ddev);
|
||||
void ltdc_unload(struct drm_device *ddev);
|
||||
|
||||
|
@ -40,6 +40,16 @@ config DRM_SUN4I_BACKEND
|
||||
do some alpha blending and feed graphics to TCON. If M is
|
||||
selected the module will be called sun4i-backend.
|
||||
|
||||
config DRM_SUN6I_DSI
|
||||
tristate "Allwinner A31 MIPI-DSI Controller Support"
|
||||
default MACH_SUN8I
|
||||
select CRC_CCITT
|
||||
select DRM_MIPI_DSI
|
||||
help
|
||||
Choose this option if you want have an Allwinner SoC with
|
||||
MIPI-DSI support. If M is selected the module will be called
|
||||
sun6i-dsi
|
||||
|
||||
config DRM_SUN8I_DW_HDMI
|
||||
tristate "Support for Allwinner version of DesignWare HDMI"
|
||||
depends on DRM_SUN4I
|
||||
|
@ -24,6 +24,9 @@ sun4i-tcon-y += sun4i_lvds.o
|
||||
sun4i-tcon-y += sun4i_tcon.o
|
||||
sun4i-tcon-y += sun4i_rgb.o
|
||||
|
||||
sun6i-dsi-y += sun6i_mipi_dphy.o
|
||||
sun6i-dsi-y += sun6i_mipi_dsi.o
|
||||
|
||||
obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o
|
||||
obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o
|
||||
obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
|
||||
@ -31,5 +34,6 @@ obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
|
||||
|
||||
obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o sun4i-frontend.o
|
||||
obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o
|
||||
obj-$(CONFIG_DRM_SUN6I_DSI) += sun6i-dsi.o
|
||||
obj-$(CONFIG_DRM_SUN8I_DW_HDMI) += sun8i-drm-hdmi.o
|
||||
obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o
|
||||
|
@ -295,6 +295,15 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
|
||||
DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
|
||||
interlaced ? "on" : "off");
|
||||
|
||||
val = SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA(state->alpha >> 8);
|
||||
if (state->alpha != DRM_BLEND_ALPHA_OPAQUE)
|
||||
val |= SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN;
|
||||
regmap_update_bits(backend->engine.regs,
|
||||
SUN4I_BACKEND_ATTCTL_REG0(layer),
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_MASK |
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN,
|
||||
val);
|
||||
|
||||
if (sun4i_backend_format_is_yuv(fb->format->format))
|
||||
return sun4i_backend_update_yuv_format(backend, layer, plane);
|
||||
|
||||
@ -490,7 +499,7 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
|
||||
DRM_DEBUG_DRIVER("Plane FB format is %s\n",
|
||||
drm_get_format_name(fb->format->format,
|
||||
&format_name));
|
||||
if (fb->format->has_alpha)
|
||||
if (fb->format->has_alpha || (plane_state->alpha != DRM_BLEND_ALPHA_OPAQUE))
|
||||
num_alpha_planes++;
|
||||
|
||||
if (sun4i_backend_format_is_yuv(fb->format->format)) {
|
||||
@ -548,7 +557,8 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
|
||||
}
|
||||
|
||||
/* We can't have an alpha plane at the lowest position */
|
||||
if (plane_states[0]->fb->format->has_alpha)
|
||||
if (plane_states[0]->fb->format->has_alpha ||
|
||||
(plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 1; i < num_planes; i++) {
|
||||
@ -560,7 +570,7 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
|
||||
* The only alpha position is the lowest plane of the
|
||||
* second pipe.
|
||||
*/
|
||||
if (fb->format->has_alpha)
|
||||
if (fb->format->has_alpha || (p_state->alpha != DRM_BLEND_ALPHA_OPAQUE))
|
||||
current_pipe++;
|
||||
|
||||
s_state->pipe = current_pipe;
|
||||
|
@ -68,12 +68,15 @@
|
||||
#define SUN4I_BACKEND_CKMIN_REG 0x884
|
||||
#define SUN4I_BACKEND_CKCFG_REG 0x888
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0(l) (0x890 + (0x4 * (l)))
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_MASK GENMASK(31, 24)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA(x) ((x) << 24)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK BIT(15)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(x) ((x) << 15)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK GENMASK(11, 10)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(x) ((x) << 10)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN BIT(2)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN BIT(1)
|
||||
#define SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN BIT(0)
|
||||
|
||||
#define SUN4I_BACKEND_ATTCTL_REG1(l) (0x8a0 + (0x4 * (l)))
|
||||
#define SUN4I_BACKEND_ATTCTL_REG1_LAY_HSCAFCT GENMASK(15, 14)
|
||||
|
@ -37,6 +37,7 @@ static void sun4i_backend_layer_reset(struct drm_plane *plane)
|
||||
if (state) {
|
||||
plane->state = &state->state;
|
||||
plane->state->plane = plane;
|
||||
plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE;
|
||||
plane->state->zpos = layer->id;
|
||||
}
|
||||
}
|
||||
@ -167,6 +168,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
|
||||
&sun4i_backend_layer_helper_funcs);
|
||||
layer->backend = backend;
|
||||
|
||||
drm_plane_create_alpha_property(&layer->plane);
|
||||
drm_plane_create_zpos_property(&layer->plane, 0, 0,
|
||||
SUN4I_BACKEND_NUM_LAYERS - 1);
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "sun4i_lvds.h"
|
||||
#include "sun4i_rgb.h"
|
||||
#include "sun4i_tcon.h"
|
||||
#include "sun6i_mipi_dsi.h"
|
||||
#include "sunxi_engine.h"
|
||||
|
||||
static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder)
|
||||
@ -169,6 +170,7 @@ void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
|
||||
case DRM_MODE_ENCODER_LVDS:
|
||||
is_lvds = true;
|
||||
/* Fallthrough */
|
||||
case DRM_MODE_ENCODER_DSI:
|
||||
case DRM_MODE_ENCODER_NONE:
|
||||
channel = 0;
|
||||
break;
|
||||
@ -201,7 +203,8 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
|
||||
DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis");
|
||||
|
||||
mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) |
|
||||
SUN4I_TCON_GINT0_VBLANK_ENABLE(1);
|
||||
SUN4I_TCON_GINT0_VBLANK_ENABLE(1) |
|
||||
SUN4I_TCON_GINT0_TCON0_TRI_FINISH_ENABLE;
|
||||
|
||||
if (enable)
|
||||
val = mask;
|
||||
@ -273,6 +276,71 @@ static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon,
|
||||
SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
|
||||
}
|
||||
|
||||
static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,
|
||||
struct mipi_dsi_device *device,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
u8 bpp = mipi_dsi_pixel_format_to_bpp(device->format);
|
||||
u8 lanes = device->lanes;
|
||||
u32 block_space, start_delay;
|
||||
u32 tcon_div;
|
||||
|
||||
tcon->dclk_min_div = 4;
|
||||
tcon->dclk_max_div = 127;
|
||||
|
||||
sun4i_tcon0_mode_set_common(tcon, mode);
|
||||
|
||||
regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
|
||||
SUN4I_TCON0_CTL_IF_MASK,
|
||||
SUN4I_TCON0_CTL_IF_8080);
|
||||
|
||||
regmap_write(tcon->regs, SUN4I_TCON_ECC_FIFO_REG,
|
||||
SUN4I_TCON_ECC_FIFO_EN);
|
||||
|
||||
regmap_write(tcon->regs, SUN4I_TCON0_CPU_IF_REG,
|
||||
SUN4I_TCON0_CPU_IF_MODE_DSI |
|
||||
SUN4I_TCON0_CPU_IF_TRI_FIFO_FLUSH |
|
||||
SUN4I_TCON0_CPU_IF_TRI_FIFO_EN |
|
||||
SUN4I_TCON0_CPU_IF_TRI_EN);
|
||||
|
||||
/*
|
||||
* This looks suspicious, but it works...
|
||||
*
|
||||
* The datasheet says that this should be set higher than 20 *
|
||||
* pixel cycle, but it's not clear what a pixel cycle is.
|
||||
*/
|
||||
regmap_read(tcon->regs, SUN4I_TCON0_DCLK_REG, &tcon_div);
|
||||
tcon_div &= GENMASK(6, 0);
|
||||
block_space = mode->htotal * bpp / (tcon_div * lanes);
|
||||
block_space -= mode->hdisplay + 40;
|
||||
|
||||
regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI0_REG,
|
||||
SUN4I_TCON0_CPU_TRI0_BLOCK_SPACE(block_space) |
|
||||
SUN4I_TCON0_CPU_TRI0_BLOCK_SIZE(mode->hdisplay));
|
||||
|
||||
regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI1_REG,
|
||||
SUN4I_TCON0_CPU_TRI1_BLOCK_NUM(mode->vdisplay));
|
||||
|
||||
start_delay = (mode->crtc_vtotal - mode->crtc_vdisplay - 10 - 1);
|
||||
start_delay = start_delay * mode->crtc_htotal * 149;
|
||||
start_delay = start_delay / (mode->crtc_clock / 1000) / 8;
|
||||
regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI2_REG,
|
||||
SUN4I_TCON0_CPU_TRI2_TRANS_START_SET(10) |
|
||||
SUN4I_TCON0_CPU_TRI2_START_DELAY(start_delay));
|
||||
|
||||
/*
|
||||
* The Allwinner BSP has a comment that the period should be
|
||||
* the display clock * 15, but uses an hardcoded 3000...
|
||||
*/
|
||||
regmap_write(tcon->regs, SUN4I_TCON_SAFE_PERIOD_REG,
|
||||
SUN4I_TCON_SAFE_PERIOD_NUM(3000) |
|
||||
SUN4I_TCON_SAFE_PERIOD_MODE(3));
|
||||
|
||||
/* Enable the output on the pins */
|
||||
regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG,
|
||||
0xe0000000);
|
||||
}
|
||||
|
||||
static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
|
||||
const struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode)
|
||||
@ -538,7 +606,17 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
|
||||
const struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct sun6i_dsi *dsi;
|
||||
|
||||
switch (encoder->encoder_type) {
|
||||
case DRM_MODE_ENCODER_DSI:
|
||||
/*
|
||||
* This is not really elegant, but it's the "cleaner"
|
||||
* way I could think of...
|
||||
*/
|
||||
dsi = encoder_to_sun6i_dsi(encoder);
|
||||
sun4i_tcon0_mode_set_cpu(tcon, dsi->device, mode);
|
||||
break;
|
||||
case DRM_MODE_ENCODER_LVDS:
|
||||
sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
|
||||
break;
|
||||
@ -582,7 +660,8 @@ static irqreturn_t sun4i_tcon_handler(int irq, void *private)
|
||||
regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status);
|
||||
|
||||
if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) |
|
||||
SUN4I_TCON_GINT0_VBLANK_INT(1))))
|
||||
SUN4I_TCON_GINT0_VBLANK_INT(1) |
|
||||
SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT)))
|
||||
return IRQ_NONE;
|
||||
|
||||
drm_crtc_handle_vblank(&scrtc->crtc);
|
||||
@ -591,7 +670,8 @@ static irqreturn_t sun4i_tcon_handler(int irq, void *private)
|
||||
/* Acknowledge the interrupt */
|
||||
regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG,
|
||||
SUN4I_TCON_GINT0_VBLANK_INT(0) |
|
||||
SUN4I_TCON_GINT0_VBLANK_INT(1),
|
||||
SUN4I_TCON_GINT0_VBLANK_INT(1) |
|
||||
SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT,
|
||||
0);
|
||||
|
||||
if (engine->ops->vblank_quirk)
|
||||
|
@ -28,13 +28,32 @@
|
||||
|
||||
#define SUN4I_TCON_GINT0_REG 0x4
|
||||
#define SUN4I_TCON_GINT0_VBLANK_ENABLE(pipe) BIT(31 - (pipe))
|
||||
#define SUN4I_TCON_GINT0_TCON0_TRI_FINISH_ENABLE BIT(27)
|
||||
#define SUN4I_TCON_GINT0_TCON0_TRI_COUNTER_ENABLE BIT(26)
|
||||
#define SUN4I_TCON_GINT0_VBLANK_INT(pipe) BIT(15 - (pipe))
|
||||
#define SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT BIT(11)
|
||||
#define SUN4I_TCON_GINT0_TCON0_TRI_COUNTER_INT BIT(10)
|
||||
|
||||
#define SUN4I_TCON_GINT1_REG 0x8
|
||||
|
||||
#define SUN4I_TCON_FRM_CTL_REG 0x10
|
||||
#define SUN4I_TCON_FRM_CTL_EN BIT(31)
|
||||
|
||||
#define SUN4I_TCON_FRM_SEED_PR_REG 0x14
|
||||
#define SUN4I_TCON_FRM_SEED_PG_REG 0x18
|
||||
#define SUN4I_TCON_FRM_SEED_PB_REG 0x1c
|
||||
#define SUN4I_TCON_FRM_SEED_LR_REG 0x20
|
||||
#define SUN4I_TCON_FRM_SEED_LG_REG 0x24
|
||||
#define SUN4I_TCON_FRM_SEED_LB_REG 0x28
|
||||
#define SUN4I_TCON_FRM_TBL0_REG 0x2c
|
||||
#define SUN4I_TCON_FRM_TBL1_REG 0x30
|
||||
#define SUN4I_TCON_FRM_TBL2_REG 0x34
|
||||
#define SUN4I_TCON_FRM_TBL3_REG 0x38
|
||||
|
||||
#define SUN4I_TCON0_CTL_REG 0x40
|
||||
#define SUN4I_TCON0_CTL_TCON_ENABLE BIT(31)
|
||||
#define SUN4I_TCON0_CTL_IF_MASK GENMASK(25, 24)
|
||||
#define SUN4I_TCON0_CTL_IF_8080 (1 << 24)
|
||||
#define SUN4I_TCON0_CTL_CLK_DELAY_MASK GENMASK(8, 4)
|
||||
#define SUN4I_TCON0_CTL_CLK_DELAY(delay) ((delay << 4) & SUN4I_TCON0_CTL_CLK_DELAY_MASK)
|
||||
#define SUN4I_TCON0_CTL_SRC_SEL_MASK GENMASK(2, 0)
|
||||
@ -61,7 +80,14 @@
|
||||
#define SUN4I_TCON0_BASIC3_V_SYNC(height) (((height) - 1) & 0x7ff)
|
||||
|
||||
#define SUN4I_TCON0_HV_IF_REG 0x58
|
||||
|
||||
#define SUN4I_TCON0_CPU_IF_REG 0x60
|
||||
#define SUN4I_TCON0_CPU_IF_MODE_MASK GENMASK(31, 28)
|
||||
#define SUN4I_TCON0_CPU_IF_MODE_DSI (1 << 28)
|
||||
#define SUN4I_TCON0_CPU_IF_TRI_FIFO_FLUSH BIT(16)
|
||||
#define SUN4I_TCON0_CPU_IF_TRI_FIFO_EN BIT(2)
|
||||
#define SUN4I_TCON0_CPU_IF_TRI_EN BIT(0)
|
||||
|
||||
#define SUN4I_TCON0_CPU_WR_REG 0x64
|
||||
#define SUN4I_TCON0_CPU_RD0_REG 0x68
|
||||
#define SUN4I_TCON0_CPU_RDA_REG 0x6c
|
||||
@ -128,6 +154,10 @@
|
||||
|
||||
#define SUN4I_TCON1_IO_POL_REG 0xf0
|
||||
#define SUN4I_TCON1_IO_TRI_REG 0xf4
|
||||
|
||||
#define SUN4I_TCON_ECC_FIFO_REG 0xf8
|
||||
#define SUN4I_TCON_ECC_FIFO_EN BIT(3)
|
||||
|
||||
#define SUN4I_TCON_CEU_CTL_REG 0x100
|
||||
#define SUN4I_TCON_CEU_MUL_RR_REG 0x110
|
||||
#define SUN4I_TCON_CEU_MUL_RG_REG 0x114
|
||||
@ -144,6 +174,22 @@
|
||||
#define SUN4I_TCON_CEU_RANGE_R_REG 0x140
|
||||
#define SUN4I_TCON_CEU_RANGE_G_REG 0x144
|
||||
#define SUN4I_TCON_CEU_RANGE_B_REG 0x148
|
||||
|
||||
#define SUN4I_TCON0_CPU_TRI0_REG 0x160
|
||||
#define SUN4I_TCON0_CPU_TRI0_BLOCK_SPACE(space) ((((space) - 1) & 0xfff) << 16)
|
||||
#define SUN4I_TCON0_CPU_TRI0_BLOCK_SIZE(size) (((size) - 1) & 0xfff)
|
||||
|
||||
#define SUN4I_TCON0_CPU_TRI1_REG 0x164
|
||||
#define SUN4I_TCON0_CPU_TRI1_BLOCK_NUM(num) (((num) - 1) & 0xffff)
|
||||
|
||||
#define SUN4I_TCON0_CPU_TRI2_REG 0x168
|
||||
#define SUN4I_TCON0_CPU_TRI2_START_DELAY(delay) (((delay) & 0xffff) << 16)
|
||||
#define SUN4I_TCON0_CPU_TRI2_TRANS_START_SET(set) ((set) & 0xfff)
|
||||
|
||||
#define SUN4I_TCON_SAFE_PERIOD_REG 0x1f0
|
||||
#define SUN4I_TCON_SAFE_PERIOD_NUM(num) (((num) & 0xfff) << 16)
|
||||
#define SUN4I_TCON_SAFE_PERIOD_MODE(mode) ((mode) & 0x3)
|
||||
|
||||
#define SUN4I_TCON_MUX_CTRL_REG 0x200
|
||||
|
||||
#define SUN4I_TCON0_LVDS_ANA0_REG 0x220
|
||||
|
292
drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
Normal file
292
drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
Normal file
@ -0,0 +1,292 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2016 Allwinnertech Co., Ltd.
|
||||
* Copyright (C) 2017-2018 Bootlin
|
||||
*
|
||||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "sun6i_mipi_dsi.h"
|
||||
|
||||
#define SUN6I_DPHY_GCTL_REG 0x00
|
||||
#define SUN6I_DPHY_GCTL_LANE_NUM(n) ((((n) - 1) & 3) << 4)
|
||||
#define SUN6I_DPHY_GCTL_EN BIT(0)
|
||||
|
||||
#define SUN6I_DPHY_TX_CTL_REG 0x04
|
||||
#define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28)
|
||||
|
||||
#define SUN6I_DPHY_TX_TIME0_REG 0x10
|
||||
#define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n) (((n) & 0xff) << 24)
|
||||
#define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n) (((n) & 0xff) << 16)
|
||||
#define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n) ((n) & 0xff)
|
||||
|
||||
#define SUN6I_DPHY_TX_TIME1_REG 0x14
|
||||
#define SUN6I_DPHY_TX_TIME1_CLK_POST(n) (((n) & 0xff) << 24)
|
||||
#define SUN6I_DPHY_TX_TIME1_CLK_PRE(n) (((n) & 0xff) << 16)
|
||||
#define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n) (((n) & 0xff) << 8)
|
||||
#define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n) ((n) & 0xff)
|
||||
|
||||
#define SUN6I_DPHY_TX_TIME2_REG 0x18
|
||||
#define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n) ((n) & 0xff)
|
||||
|
||||
#define SUN6I_DPHY_TX_TIME3_REG 0x1c
|
||||
|
||||
#define SUN6I_DPHY_TX_TIME4_REG 0x20
|
||||
#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n) (((n) & 0xff) << 8)
|
||||
#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n) ((n) & 0xff)
|
||||
|
||||
#define SUN6I_DPHY_ANA0_REG 0x4c
|
||||
#define SUN6I_DPHY_ANA0_REG_PWS BIT(31)
|
||||
#define SUN6I_DPHY_ANA0_REG_DMPC BIT(28)
|
||||
#define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24)
|
||||
#define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12)
|
||||
#define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8)
|
||||
|
||||
#define SUN6I_DPHY_ANA1_REG 0x50
|
||||
#define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31)
|
||||
#define SUN6I_DPHY_ANA1_REG_CSMPS(n) (((n) & 3) << 28)
|
||||
#define SUN6I_DPHY_ANA1_REG_SVTT(n) (((n) & 0xf) << 24)
|
||||
|
||||
#define SUN6I_DPHY_ANA2_REG 0x54
|
||||
#define SUN6I_DPHY_ANA2_EN_P2S_CPU(n) (((n) & 0xf) << 24)
|
||||
#define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK GENMASK(27, 24)
|
||||
#define SUN6I_DPHY_ANA2_EN_CK_CPU BIT(4)
|
||||
#define SUN6I_DPHY_ANA2_REG_ENIB BIT(1)
|
||||
|
||||
#define SUN6I_DPHY_ANA3_REG 0x58
|
||||
#define SUN6I_DPHY_ANA3_EN_VTTD(n) (((n) & 0xf) << 28)
|
||||
#define SUN6I_DPHY_ANA3_EN_VTTD_MASK GENMASK(31, 28)
|
||||
#define SUN6I_DPHY_ANA3_EN_VTTC BIT(27)
|
||||
#define SUN6I_DPHY_ANA3_EN_DIV BIT(26)
|
||||
#define SUN6I_DPHY_ANA3_EN_LDOC BIT(25)
|
||||
#define SUN6I_DPHY_ANA3_EN_LDOD BIT(24)
|
||||
#define SUN6I_DPHY_ANA3_EN_LDOR BIT(18)
|
||||
|
||||
#define SUN6I_DPHY_ANA4_REG 0x5c
|
||||
#define SUN6I_DPHY_ANA4_REG_DMPLVC BIT(24)
|
||||
#define SUN6I_DPHY_ANA4_REG_DMPLVD(n) (((n) & 0xf) << 20)
|
||||
#define SUN6I_DPHY_ANA4_REG_CKDV(n) (((n) & 0x1f) << 12)
|
||||
#define SUN6I_DPHY_ANA4_REG_TMSC(n) (((n) & 3) << 10)
|
||||
#define SUN6I_DPHY_ANA4_REG_TMSD(n) (((n) & 3) << 8)
|
||||
#define SUN6I_DPHY_ANA4_REG_TXDNSC(n) (((n) & 3) << 6)
|
||||
#define SUN6I_DPHY_ANA4_REG_TXDNSD(n) (((n) & 3) << 4)
|
||||
#define SUN6I_DPHY_ANA4_REG_TXPUSC(n) (((n) & 3) << 2)
|
||||
#define SUN6I_DPHY_ANA4_REG_TXPUSD(n) ((n) & 3)
|
||||
|
||||
#define SUN6I_DPHY_DBG5_REG 0xf4
|
||||
|
||||
int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
|
||||
{
|
||||
reset_control_deassert(dphy->reset);
|
||||
clk_prepare_enable(dphy->mod_clk);
|
||||
clk_set_rate_exclusive(dphy->mod_clk, 150000000);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
|
||||
SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
|
||||
SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
|
||||
SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
|
||||
SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
|
||||
SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
|
||||
SUN6I_DPHY_TX_TIME1_CLK_POST(10));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
|
||||
SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
|
||||
SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
|
||||
SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
|
||||
SUN6I_DPHY_GCTL_LANE_NUM(lanes) |
|
||||
SUN6I_DPHY_GCTL_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
|
||||
{
|
||||
u8 lanes_mask = GENMASK(lanes - 1, 0);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
|
||||
SUN6I_DPHY_ANA0_REG_PWS |
|
||||
SUN6I_DPHY_ANA0_REG_DMPC |
|
||||
SUN6I_DPHY_ANA0_REG_SLV(7) |
|
||||
SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) |
|
||||
SUN6I_DPHY_ANA0_REG_DEN(lanes_mask));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
|
||||
SUN6I_DPHY_ANA1_REG_CSMPS(1) |
|
||||
SUN6I_DPHY_ANA1_REG_SVTT(7));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
|
||||
SUN6I_DPHY_ANA4_REG_CKDV(1) |
|
||||
SUN6I_DPHY_ANA4_REG_TMSC(1) |
|
||||
SUN6I_DPHY_ANA4_REG_TMSD(1) |
|
||||
SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
|
||||
SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
|
||||
SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
|
||||
SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
|
||||
SUN6I_DPHY_ANA4_REG_DMPLVC |
|
||||
SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask));
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
|
||||
SUN6I_DPHY_ANA2_REG_ENIB);
|
||||
udelay(5);
|
||||
|
||||
regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
|
||||
SUN6I_DPHY_ANA3_EN_LDOR |
|
||||
SUN6I_DPHY_ANA3_EN_LDOC |
|
||||
SUN6I_DPHY_ANA3_EN_LDOD);
|
||||
udelay(1);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
|
||||
SUN6I_DPHY_ANA3_EN_VTTC |
|
||||
SUN6I_DPHY_ANA3_EN_VTTD_MASK,
|
||||
SUN6I_DPHY_ANA3_EN_VTTC |
|
||||
SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask));
|
||||
udelay(1);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
|
||||
SUN6I_DPHY_ANA3_EN_DIV,
|
||||
SUN6I_DPHY_ANA3_EN_DIV);
|
||||
udelay(1);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
|
||||
SUN6I_DPHY_ANA2_EN_CK_CPU,
|
||||
SUN6I_DPHY_ANA2_EN_CK_CPU);
|
||||
udelay(1);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
|
||||
SUN6I_DPHY_ANA1_REG_VTTMODE,
|
||||
SUN6I_DPHY_ANA1_REG_VTTMODE);
|
||||
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
|
||||
SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK,
|
||||
SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sun6i_dphy_power_off(struct sun6i_dphy *dphy)
|
||||
{
|
||||
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
|
||||
SUN6I_DPHY_ANA1_REG_VTTMODE, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sun6i_dphy_exit(struct sun6i_dphy *dphy)
|
||||
{
|
||||
clk_rate_exclusive_put(dphy->mod_clk);
|
||||
clk_disable_unprepare(dphy->mod_clk);
|
||||
reset_control_assert(dphy->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct regmap_config sun6i_dphy_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = SUN6I_DPHY_DBG5_REG,
|
||||
.name = "mipi-dphy",
|
||||
};
|
||||
|
||||
static const struct of_device_id sun6i_dphy_of_table[] = {
|
||||
{ .compatible = "allwinner,sun6i-a31-mipi-dphy" },
|
||||
{ }
|
||||
};
|
||||
|
||||
int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node)
|
||||
{
|
||||
struct sun6i_dphy *dphy;
|
||||
struct resource res;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
if (!of_match_node(sun6i_dphy_of_table, node)) {
|
||||
dev_err(dsi->dev, "Incompatible D-PHY\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dphy = devm_kzalloc(dsi->dev, sizeof(*dphy), GFP_KERNEL);
|
||||
if (!dphy)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_address_to_resource(node, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(dsi->dev, "phy: Couldn't get our resources\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regs = devm_ioremap_resource(dsi->dev, &res);
|
||||
if (IS_ERR(regs)) {
|
||||
dev_err(dsi->dev, "Couldn't map the DPHY encoder registers\n");
|
||||
return PTR_ERR(regs);
|
||||
}
|
||||
|
||||
dphy->regs = devm_regmap_init_mmio(dsi->dev, regs,
|
||||
&sun6i_dphy_regmap_config);
|
||||
if (IS_ERR(dphy->regs)) {
|
||||
dev_err(dsi->dev, "Couldn't create the DPHY encoder regmap\n");
|
||||
return PTR_ERR(dphy->regs);
|
||||
}
|
||||
|
||||
dphy->reset = of_reset_control_get_shared(node, NULL);
|
||||
if (IS_ERR(dphy->reset)) {
|
||||
dev_err(dsi->dev, "Couldn't get our reset line\n");
|
||||
return PTR_ERR(dphy->reset);
|
||||
}
|
||||
|
||||
dphy->bus_clk = of_clk_get_by_name(node, "bus");
|
||||
if (IS_ERR(dphy->bus_clk)) {
|
||||
dev_err(dsi->dev, "Couldn't get the DPHY bus clock\n");
|
||||
ret = PTR_ERR(dphy->bus_clk);
|
||||
goto err_free_reset;
|
||||
}
|
||||
regmap_mmio_attach_clk(dphy->regs, dphy->bus_clk);
|
||||
|
||||
dphy->mod_clk = of_clk_get_by_name(node, "mod");
|
||||
if (IS_ERR(dphy->mod_clk)) {
|
||||
dev_err(dsi->dev, "Couldn't get the DPHY mod clock\n");
|
||||
ret = PTR_ERR(dphy->mod_clk);
|
||||
goto err_free_bus;
|
||||
}
|
||||
|
||||
dsi->dphy = dphy;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_bus:
|
||||
regmap_mmio_detach_clk(dphy->regs);
|
||||
clk_put(dphy->bus_clk);
|
||||
err_free_reset:
|
||||
reset_control_put(dphy->reset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sun6i_dphy_remove(struct sun6i_dsi *dsi)
|
||||
{
|
||||
struct sun6i_dphy *dphy = dsi->dphy;
|
||||
|
||||
regmap_mmio_detach_clk(dphy->regs);
|
||||
clk_put(dphy->mod_clk);
|
||||
clk_put(dphy->bus_clk);
|
||||
reset_control_put(dphy->reset);
|
||||
|
||||
return 0;
|
||||
}
|
1107
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
Normal file
1107
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user