drm-misc-next for v4.21, part 1:

UAPI Changes:
 - Add syncobj timeline support to drm.
 
 Cross-subsystem Changes:
 - Remove shared fence staging in dma-buf's fence object, and allow
   reserving more than 1 fence and add more paranoia when debugging.
 - Constify infoframe functions in video/hdmi.
 
 Core Changes:
 - Add vkms todo, and a lot of assorted doc fixes.
 - Drop transitional helpers and convert drivers to use drm_atomic_helper_shutdown().
 - Move atomic state helper functions to drm_atomic_state_helper.[ch]
 - Refactor drm selftests, and add new tests.
 - DP MST atomic state cleanups.
 - Drop EXPORT_SYMBOL from drm leases.
 - Lease cleanups and fixes.
 - Create render node for vgem.
 
 Driver Changes:
 - Fix build failure in imx without fbdev emulation.
 - Add rotation quirk for GPD win2 panel.
 - Add support for various CDTech panels, Banana Pi Panel, DLC1010GIG,
   Olimex LCD-O-LinuXino, Samsung S6D16D0, Truly NT35597 WQXGA,
   Himax HX8357D, simulated RTSM AEMv8.
 - Add dw_hdmi support to rockchip driver.
 - Fix YUV support in vc4.
 - Fix resource id handling in virtio.
 - Make rockchip use dw-mipi-dsi bridge driver, and add dual dsi support.
 - Advertise that tinydrm only supports DRM_FORMAT_MOD_LINEAR.
 - Convert many drivers to use atomic helpers, and drm_fbdev_generic_setup().
 - Add Mali linear tiled formats, and enable them in the Mali-DP driver.
 - Add support for H6 DE3 mixer 0, DW HDMI, HDMI PHY and TCON TOP.
 - Assorted driver cleanups and fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAlvi0eAACgkQ/lWMcqZw
 E8PI3g//TIlMmbrUc6BrWCNnz4YpJ+mC2dq7HMYB4yf5QeXTYrRDqK6syakbGcbe
 FGYxt+21REfyQ9IQPdQslmVIUqJZuMBFSvLjXnZvybJSllE23CAXG0hEwHTuPYiF
 yzxmYwlYOxVlW0nnB3fEDM8BfbWyMYR03c4sobPgiAGoBd+CJif/BtKEwranYrRx
 7rZh8PnrCPGAnewmYux6U4zkOpWyjUp5t3kqpNRJDxPfxpa991yvUJX3DxTFFr9b
 nqYNp3F3fkdowYuJj2eH/uBNd17TouzITGQxIZWEsJfFvB+2Awp7KgRCT8nUAt0I
 vELbADsy3QwZlQp1F2FaVwfGbmHr41F+Vsq9coUt4vPaiyT3vCW0KGCeal1dKyYf
 +S8UXIijkoGXm0RqxkbkJsG7AYSIzG+NQm6W+9tnQg6CwpQb2wqU3YCPQWqtFHqM
 Tz/EW+JqG5Wl1aHXVEbnSajtqT2ooskwHfy81iwNqaGwVy+ZSLIZpqC91Hk9SyZ9
 HBDuKWSzqEqXWf7nwbOTm0umQ9mk8+I41k+dyqc2fq9z/gySqKd32eC3aLa0/p3y
 6bngvu1TT6jNhRdduwxgl/Y5cnQp/Zg9wYRKmAjViZtooaWj8p2o45AufGz1rplR
 BdYVUOPofVVD9ShwxayWzuocFW/HbgYc7FHHgKUFgFBO5iC/A2s=
 =UV0m
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2018-11-07' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for v4.21, part 1:

UAPI Changes:
- Add syncobj timeline support to drm.

Cross-subsystem Changes:
- Remove shared fence staging in dma-buf's fence object, and allow
  reserving more than 1 fence and add more paranoia when debugging.
- Constify infoframe functions in video/hdmi.

Core Changes:
- Add vkms todo, and a lot of assorted doc fixes.
- Drop transitional helpers and convert drivers to use drm_atomic_helper_shutdown().
- Move atomic state helper functions to drm_atomic_state_helper.[ch]
- Refactor drm selftests, and add new tests.
- DP MST atomic state cleanups.
- Drop EXPORT_SYMBOL from drm leases.
- Lease cleanups and fixes.
- Create render node for vgem.

Driver Changes:
- Fix build failure in imx without fbdev emulation.
- Add rotation quirk for GPD win2 panel.
- Add support for various CDTech panels, Banana Pi Panel, DLC1010GIG,
  Olimex LCD-O-LinuXino, Samsung S6D16D0, Truly NT35597 WQXGA,
  Himax HX8357D, simulated RTSM AEMv8.
- Add dw_hdmi support to rockchip driver.
- Fix YUV support in vc4.
- Fix resource id handling in virtio.
- Make rockchip use dw-mipi-dsi bridge driver, and add dual dsi support.
- Advertise that tinydrm only supports DRM_FORMAT_MOD_LINEAR.
- Convert many drivers to use atomic helpers, and drm_fbdev_generic_setup().
- Add Mali linear tiled formats, and enable them in the Mali-DP driver.
- Add support for H6 DE3 mixer 0, DW HDMI, HDMI PHY and TCON TOP.
- Assorted driver cleanups and fixes.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/be7ebd91-edd9-8fa4-4286-1c57e3165113@linux.intel.com
This commit is contained in:
Dave Airlie 2018-11-19 10:40:00 +10:00
commit d7563c55ef
221 changed files with 7466 additions and 3698 deletions

View File

@ -1,11 +1,14 @@
Device tree bindings for Allwinner A64 DE2 bus
Device tree bindings for Allwinner DE2/3 bus
The Allwinner A64 DE2 is on a special bus, which needs a SRAM region (SRAM C)
to be claimed for enabling the access.
to be claimed for enabling the access. The DE3 on Allwinner H6 is at the same
situation, and the binding also applies.
Required properties:
- compatible: Should contain "allwinner,sun50i-a64-de2"
- compatible: Should be one of:
- "allwinner,sun50i-a64-de2"
- "allwinner,sun50i-h6-de3", "allwinner,sun50i-a64-de2"
- reg: A resource specifier for the register space
- #address-cells: Must be set to 1
- #size-cells: Must be set to 1

View File

@ -0,0 +1,26 @@
Himax HX8357D display panels
This binding is for display panels using a Himax HX8357D controller in SPI
mode, such as the Adafruit 3.5" TFT for Raspberry Pi.
Required properties:
- compatible: "adafruit,yx350hv15", "himax,hx8357d"
- dc-gpios: D/C pin
- reg: address of the panel on the SPI bus
The node for this driver must be a child node of a SPI controller, hence
all mandatory properties described in ../spi/spi-bus.txt must be specified.
Optional properties:
- rotation: panel rotation in degrees counter clockwise (0,90,180,270)
- backlight: phandle of the backlight device attached to the panel
Example:
display@0{
compatible = "adafruit,yx350hv15", "himax,hx8357d";
reg = <0>;
spi-max-frequency = <32000000>;
dc-gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>;
rotation = <90>;
backlight = <&backlight>;
};

View File

@ -0,0 +1,12 @@
Banana Pi 7" (S070WV20-CT16) TFT LCD Panel
Required properties:
- compatible: should be "bananapi,s070wv20-ct16"
- power-supply: see ./panel-common.txt
Optional properties:
- enable-gpios: see ./simple-panel.txt
- backlight: see ./simple-panel.txt
This binding is compatible with the simple-panel binding, which is specified
in ./simple-panel.txt.

View File

@ -0,0 +1,12 @@
CDTech(H.K.) Electronics Limited 4.3" 480x272 color TFT-LCD panel
Required properties:
- compatible: should be "cdtech,s043wq26h-ct7"
- power-supply: as specified in the base binding
Optional properties:
- backlight: as specified in the base binding
- enable-gpios: as specified in the base binding
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,12 @@
CDTech(H.K.) Electronics Limited 7" 800x480 color TFT-LCD panel
Required properties:
- compatible: should be "cdtech,s070wv95-ct16"
- power-supply: as specified in the base binding
Optional properties:
- backlight: as specified in the base binding
- enable-gpios: as specified in the base binding
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,12 @@
DLC Display Co. DLC1010GIG 10.1" WXGA TFT LCD Panel
Required properties:
- compatible: should be "dlc,dlc1010gig"
- power-supply: See simple-panel.txt
Optional properties:
- enable-gpios: See simple-panel.txt
- backlight: See simple-panel.txt
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.

View File

@ -0,0 +1,42 @@
Binding for Olimex Ltd. LCD-OLinuXino bridge panel.
This device can be used as bridge between a host controller and LCD panels.
Currently supported LCDs are:
- LCD-OLinuXino-4.3TS
- LCD-OLinuXino-5
- LCD-OLinuXino-7
- LCD-OLinuXino-10
The panel itself contains:
- AT24C16C EEPROM holding panel identification and timing requirements
- AR1021 resistive touch screen controller (optional)
- FT5x6 capacitive touch screnn controller (optional)
- GT911/GT928 capacitive touch screen controller (optional)
The above chips share same I2C bus. The EEPROM is factory preprogrammed with
device information (id, serial, etc.) and timing requirements.
Touchscreen bingings can be found in these files:
- input/touchscreen/goodix.txt
- input/touchscreen/edt-ft5x06.txt
- input/touchscreen/ar1021.txt
Required properties:
- compatible: should be "olimex,lcd-olinuxino"
- reg: address of the configuration EEPROM, should be <0x50>
- power-supply: phandle of the regulator that provides the supply voltage
Optional properties:
- enable-gpios: GPIO pin to enable or disable the panel
- backlight: phandle of the backlight device attacked to the panel
Example:
&i2c2 {
panel@50 {
compatible = "olimex,lcd-olinuxino";
reg = <0x50>;
power-supply = <&reg_vcc5v0>;
enable-gpios = <&pio 7 8 GPIO_ACTIVE_HIGH>;
backlight = <&backlight>;
};
};

View File

@ -0,0 +1,30 @@
Samsung S6D16D0 4" 864x480 AMOLED panel
Required properties:
- compatible: should be:
"samsung,s6d16d0",
- reg: the virtual channel number of a DSI peripheral
- vdd1-supply: I/O voltage supply
- reset-gpios: a GPIO spec for the reset pin (active low)
The device node can contain one 'port' child node with one child
'endpoint' node, according to the bindings defined in
media/video-interfaces.txt. This node should describe panel's video bus.
Example:
&dsi {
...
panel@0 {
compatible = "samsung,s6d16d0";
reg = <0>;
vdd1-supply = <&foo>;
reset-gpios = <&foo_gpio 0 GPIO_ACTIVE_LOW>;
port {
panel_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};

View File

@ -13,6 +13,7 @@ Required properties:
- compatible: should be one of the following:
"rockchip,rk3288-dw-hdmi"
"rockchip,rk3328-dw-hdmi"
"rockchip,rk3399-dw-hdmi"
- reg: See dw_hdmi.txt.
- reg-io-width: See dw_hdmi.txt. Shall be 4.
@ -34,6 +35,8 @@ Optional properties
- clock-names: May contain "cec" as defined in dw_hdmi.txt.
- clock-names: May contain "grf", power for grf io.
- clock-names: May contain "vpll", external clock for some hdmi phy.
- phys: from general PHY binding: the phandle for the PHY device.
- phy-names: Should be "hdmi" if phys references an external phy.
Example:

View File

@ -79,6 +79,7 @@ Required properties:
- compatible: value must be one of:
* "allwinner,sun8i-a83t-dw-hdmi"
* "allwinner,sun50i-a64-dw-hdmi", "allwinner,sun8i-a83t-dw-hdmi"
* "allwinner,sun50i-h6-dw-hdmi"
- reg: base address and size of memory-mapped region
- reg-io-width: See dw_hdmi.txt. Shall be 1.
- interrupts: HDMI interrupt number
@ -86,9 +87,14 @@ Required properties:
* iahb: the HDMI bus clock
* isfr: the HDMI register clock
* tmds: TMDS clock
* cec: HDMI CEC clock (H6 only)
* hdcp: HDCP clock (H6 only)
* hdcp-bus: HDCP bus clock (H6 only)
- clock-names: the clock names mentioned above
- resets: phandle to the reset controller
- reset-names: must be "ctrl"
- resets:
* ctrl: HDMI controller reset
* hdcp: HDCP reset (H6 only)
- reset-names: reset names mentioned above
- phys: phandle to the DWC HDMI PHY
- phy-names: must be "phy"
@ -109,6 +115,7 @@ Required properties:
* allwinner,sun8i-h3-hdmi-phy
* allwinner,sun8i-r40-hdmi-phy
* allwinner,sun50i-a64-hdmi-phy
* allwinner,sun50i-h6-hdmi-phy
- reg: base address and size of memory-mapped region
- clocks: phandles to the clocks feeding the HDMI PHY
* bus: the HDMI PHY interface clock
@ -158,6 +165,7 @@ Required properties:
* allwinner,sun9i-a80-tcon-tv
* "allwinner,sun50i-a64-tcon-lcd", "allwinner,sun8i-a83t-tcon-lcd"
* "allwinner,sun50i-a64-tcon-tv", "allwinner,sun8i-a83t-tcon-tv"
* allwinner,sun50i-h6-tcon-tv, allwinner,sun8i-r40-tcon-tv
- reg: base address and size of memory-mapped region
- interrupts: interrupt associated to this IP
- clocks: phandles to the clocks feeding the TCON.
@ -220,24 +228,26 @@ It allows display pipeline to be configured in very different ways:
\ [3] TCON-TV1 [1] - TVE1/RGB
Note that both TCON TOP references same physical unit. Both mixers can be
connected to any TCON.
connected to any TCON. Not all TCON TOP variants support all features.
Required properties:
- compatible: value must be one of:
* allwinner,sun8i-r40-tcon-top
* allwinner,sun50i-h6-tcon-top
- reg: base address and size of the memory-mapped region.
- clocks: phandle to the clocks feeding the TCON TOP
* bus: TCON TOP interface clock
* tcon-tv0: TCON TV0 clock
* tve0: TVE0 clock
* tcon-tv1: TCON TV1 clock
* tve1: TVE0 clock
* dsi: MIPI DSI clock
* tve0: TVE0 clock (R40 only)
* tcon-tv1: TCON TV1 clock (R40 only)
* tve1: TVE0 clock (R40 only)
* dsi: MIPI DSI clock (R40 only)
- clock-names: clock name mentioned above
- resets: phandle to the reset line driving the TCON TOP
- #clock-cells : must contain 1
- clock-output-names: Names of clocks created for TCON TV0 channel clock,
TCON TV1 channel clock and DSI channel clock, in that order.
TCON TV1 channel clock (R40 only) and DSI channel clock (R40 only), in
that order.
- ports: A ports node with endpoint definitions as defined in
Documentation/devicetree/bindings/media/video-interfaces.txt. 6 ports should
@ -381,6 +391,7 @@ Required properties:
* allwinner,sun8i-v3s-de2-mixer
* allwinner,sun50i-a64-de2-mixer-0
* allwinner,sun50i-a64-de2-mixer-1
* allwinner,sun50i-h6-de3-mixer-0
- reg: base address and size of the memory-mapped region.
- clocks: phandles to the clocks feeding the mixer
* bus: the mixer interface clock
@ -415,9 +426,10 @@ Required properties:
* allwinner,sun8i-v3s-display-engine
* allwinner,sun9i-a80-display-engine
* allwinner,sun50i-a64-display-engine
* allwinner,sun50i-h6-display-engine
- allwinner,pipelines: list of phandle to the display engine
frontends (DE 1.0) or mixers (DE 2.0) available.
frontends (DE 1.0) or mixers (DE 2.0/3.0) available.
Example:

View File

@ -0,0 +1,59 @@
Truly model NT35597 DSI display driver
The Truly NT35597 is a generic display driver, currently only configured
for use in the 2K display on the Qualcomm SDM845 MTP board.
Required properties:
- compatible: should be "truly,nt35597-2K-display"
- vdda-supply: phandle of the regulator that provides the supply voltage
Power IC supply
- vdispp-supply: phandle of the regulator that provides the supply voltage
for positive LCD bias
- vdispn-supply: phandle of the regulator that provides the supply voltage
for negative LCD bias
- reset-gpios: phandle of gpio for reset line
This should be 8mA, gpio can be configured using mux, pinctrl, pinctrl-names
(active low)
- mode-gpios: phandle of the gpio for choosing the mode of the display
for single DSI or Dual DSI
This should be low for dual DSI and high for single DSI mode
- ports: This device has two video ports driven by two DSIs. Their connections
are modeled using the OF graph bindings specified in
Documentation/devicetree/bindings/graph.txt.
- port@0: DSI input port driven by master DSI
- port@1: DSI input port driven by secondary DSI
Example:
dsi@ae94000 {
panel@0 {
compatible = "truly,nt35597-2K-display";
reg = <0>;
vdda-supply = <&pm8998_l14>;
vdispp-supply = <&lab_regulator>;
vdispn-supply = <&ibb_regulator>;
pinctrl-names = "default", "suspend";
pinctrl-0 = <&dpu_dsi_active>;
pinctrl-1 = <&dpu_dsi_suspend>;
reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>;
mode-gpios = <&tlmm 52 GPIO_ACTIVE_HIGH>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel0_in: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
port@1 {
reg = <1>;
panel1_in: endpoint {
remote-endpoint = <&dsi1_out>;
};
};
};
};
};

View File

@ -67,6 +67,7 @@ capella Capella Microsystems, Inc
cascoda Cascoda, Ltd.
cavium Cavium, Inc.
cdns Cadence Design Systems Inc.
cdtech CDTech(H.K.) Electronics Limited
ceva Ceva, Inc.
chipidea Chipidea, Inc
chipone ChipOne

View File

@ -59,12 +59,6 @@ Implementing Asynchronous Atomic Commit
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
:doc: implementing nonblocking commit
Atomic State Reset and Initialization
-------------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
:doc: atomic state reset and initialization
Helper Functions Reference
--------------------------
@ -74,6 +68,21 @@ Helper Functions Reference
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
:export:
Atomic State Reset and Initialization
-------------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_atomic_state_helper.c
:doc: atomic state reset and initialization
Atomic State Helper Reference
-----------------------------
.. kernel-doc:: include/drm/drm_atomic_state_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_atomic_state_helper.c
:export:
Simple KMS Helper Reference
===========================

View File

@ -197,6 +197,9 @@ EPERM/EACCESS:
difference between EACCESS and EPERM.
ENODEV:
The device is not (yet) present or fully initialized.
EOPNOTSUPP:
Feature (like PRIME, modesetting, GEM) is not supported by the driver.
ENXIO:

View File

@ -339,6 +339,16 @@ Some of these date from the very introduction of KMS in 2008 ...
leftovers from older (never merged into upstream) KMS designs where modes
where set using their ID, including support to add/remove modes.
- Make ->funcs and ->helper_private vtables optional. There's a bunch of empty
function tables in drivers, but before we can remove them we need to make sure
that all the users in helpers and drivers do correctly check for a NULL
vtable.
- Cleanup up the various ->destroy callbacks. A lot of them just wrapt the
drm_*_cleanup implementations and can be removed. Some tack a kfree() at the
end, for which we could add drm_*_cleanup_kfree(). And then there's the (for
historical reasons) misnamed drm_primary_helper_destroy() function.
Better Testing
==============

View File

@ -10,8 +10,8 @@
TODO
====
CRC API
-------
CRC API Improvements
--------------------
- Optimize CRC computation ``compute_crc()`` and plane blending ``blend()``
@ -22,3 +22,100 @@ CRC API
- Add igt test to check extreme alpha values i.e. fully opaque and fully
transparent (intermediate values are affected by hw-specific rounding modes).
Vblank issues
-------------
Some IGT test cases are failing. Need to analyze why and fix the issues:
- plain-flip-fb-recreate
- plain-flip-ts-check
- flip-vs-blocking-wf-vblank
- plain-flip-fb-recreate-interruptible
- flip-vs-wf_vblank-interruptible
Runtime Configuration
---------------------
We want to be able to reconfigure vkms instance without having to reload the
module. Use/Test-cases:
- Hotplug/hotremove connectors on the fly (to be able to test DP MST handling of
compositors).
- Configure planes/crtcs/connectors (we'd need some code to have more than 1 of
them first).
- Change output configuration: Plug/unplug screens, change EDID, allow changing
the refresh rate.
The currently proposed solution is to expose vkms configuration through
configfs. All existing module options should be supported through configfs too.
Add Plane Features
------------------
There's lots of plane features we could add support for:
- Real overlay planes, not just cursor.
- Full alpha blending on all planes.
- Rotation, scaling.
- Additional buffer formats, especially YUV formats for video like NV12.
Low/high bpp RGB formats would also be interesting.
- Async updates (currently only possible on cursor plane using the legacy cursor
api).
For all of these, we also want to review the igt test coverage and make sure all
relevant igt testcases work on vkms.
Writeback support
-----------------
Currently vkms only computes a CRC for each frame. Once we have additional plane
features, we could write back the entire composited frame, and expose it as:
- Writeback connector. This is useful for testing compositors if you don't have
hardware with writeback support.
- As a v4l device. This is useful for debugging compositors on special vkms
configurations, so that developers see what's really going on.
Prime Buffer Sharing
--------------------
We already have vgem, which is a gem driver for testing rendering, similar to
how vkms is for testing the modeset side. Adding buffer sharing support to vkms
allows us to test them together, to test synchronization and lots of other
features. Also, this allows compositors to test whether they work correctly on
SoC chips, where the display and rendering is very often split between 2
drivers.
Output Features
---------------
- Variable refresh rate/freesync support. This probably needs prime buffer
sharing support, so that we can use vgem fences to simulate rendering in
testing. Also needs support to specify the EDID.
- Add support for link status, so that compositors can validate their runtime
fallbacks when e.g. a Display Port link goes bad.
- All the hotplug handling describe under "Runtime Configuration".
Atomic Check using eBPF
-----------------------
Atomic drivers have lots of restrictions which are not exposed to userspace in
any explicit form through e.g. possible property values. Userspace can only
inquiry about these limits through the atomic IOCTL, possibly using the
TEST_ONLY flag. Trying to add configurable code for all these limits, to allow
compositors to be tested against them, would be rather futile exercise. Instead
we could add support for eBPF to validate any kind of atomic state, and
implement a library of different restrictions.
This needs a bunch of features (plane compositing, multiple outputs, ...)
enabled already to make sense.

View File

@ -4655,6 +4655,13 @@ S: Maintained
F: drivers/gpu/drm/tinydrm/ili9225.c
F: Documentation/devicetree/bindings/display/ilitek,ili9225.txt
DRM DRIVER FOR HX8357D PANELS
M: Eric Anholt <eric@anholt.net>
T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/gpu/drm/tinydrm/hx8357d.c
F: Documentation/devicetree/bindings/display/himax,hx8357d.txt
DRM DRIVER FOR INTEL I810 VIDEO CARDS
S: Orphan / Obsolete
F: drivers/gpu/drm/i810/
@ -4696,6 +4703,12 @@ S: Supported
F: drivers/gpu/drm/nouveau/
F: include/uapi/drm/nouveau_drm.h
DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS
M: Stefan Mavrodiev <stefan@olimex.com>
S: Maintained
F: drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c
F: Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino.txt
DRM DRIVER FOR PERVASIVE DISPLAYS REPAPER PANELS
M: Noralf Trønnes <noralf@tronnes.org>
S: Maintained

View File

@ -56,9 +56,10 @@ const char reservation_seqcount_string[] = "reservation_seqcount";
EXPORT_SYMBOL(reservation_seqcount_string);
/**
* reservation_object_reserve_shared - Reserve space to add a shared
* fence to a reservation_object.
* reservation_object_reserve_shared - Reserve space to add shared fences to
* a reservation_object.
* @obj: reservation object
* @num_fences: number of fences we want to add
*
* Should be called before reservation_object_add_shared_fence(). Must
* be called with obj->lock held.
@ -66,107 +67,27 @@ EXPORT_SYMBOL(reservation_seqcount_string);
* RETURNS
* Zero for success, or -errno
*/
int reservation_object_reserve_shared(struct reservation_object *obj)
int reservation_object_reserve_shared(struct reservation_object *obj,
unsigned int num_fences)
{
struct reservation_object_list *fobj, *old;
u32 max;
struct reservation_object_list *old, *new;
unsigned int i, j, k, max;
old = reservation_object_get_list(obj);
if (old && old->shared_max) {
if (old->shared_count < old->shared_max) {
/* perform an in-place update */
kfree(obj->staged);
obj->staged = NULL;
if ((old->shared_count + num_fences) <= old->shared_max)
return 0;
} else
max = old->shared_max * 2;
} else
max = 4;
/*
* resize obj->staged or allocate if it doesn't exist,
* noop if already correct size
*/
fobj = krealloc(obj->staged, offsetof(typeof(*fobj), shared[max]),
GFP_KERNEL);
if (!fobj)
return -ENOMEM;
obj->staged = fobj;
fobj->shared_max = max;
return 0;
}
EXPORT_SYMBOL(reservation_object_reserve_shared);
static void
reservation_object_add_shared_inplace(struct reservation_object *obj,
struct reservation_object_list *fobj,
struct dma_fence *fence)
{
struct dma_fence *signaled = NULL;
u32 i, signaled_idx;
dma_fence_get(fence);
preempt_disable();
write_seqcount_begin(&obj->seq);
for (i = 0; i < fobj->shared_count; ++i) {
struct dma_fence *old_fence;
old_fence = rcu_dereference_protected(fobj->shared[i],
reservation_object_held(obj));
if (old_fence->context == fence->context) {
/* memory barrier is added by write_seqcount_begin */
RCU_INIT_POINTER(fobj->shared[i], fence);
write_seqcount_end(&obj->seq);
preempt_enable();
dma_fence_put(old_fence);
return;
}
if (!signaled && dma_fence_is_signaled(old_fence)) {
signaled = old_fence;
signaled_idx = i;
}
}
/*
* memory barrier is added by write_seqcount_begin,
* fobj->shared_count is protected by this lock too
*/
if (signaled) {
RCU_INIT_POINTER(fobj->shared[signaled_idx], fence);
else
max = max(old->shared_count + num_fences,
old->shared_max * 2);
} else {
BUG_ON(fobj->shared_count >= fobj->shared_max);
RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence);
fobj->shared_count++;
max = 4;
}
write_seqcount_end(&obj->seq);
preempt_enable();
dma_fence_put(signaled);
}
static void
reservation_object_add_shared_replace(struct reservation_object *obj,
struct reservation_object_list *old,
struct reservation_object_list *fobj,
struct dma_fence *fence)
{
unsigned i, j, k;
dma_fence_get(fence);
if (!old) {
RCU_INIT_POINTER(fobj->shared[0], fence);
fobj->shared_count = 1;
goto done;
}
new = kmalloc(offsetof(typeof(*new), shared[max]), GFP_KERNEL);
if (!new)
return -ENOMEM;
/*
* no need to bump fence refcounts, rcu_read access
@ -174,46 +95,45 @@ reservation_object_add_shared_replace(struct reservation_object *obj,
* references from the old struct are carried over to
* the new.
*/
for (i = 0, j = 0, k = fobj->shared_max; i < old->shared_count; ++i) {
struct dma_fence *check;
for (i = 0, j = 0, k = max; i < (old ? old->shared_count : 0); ++i) {
struct dma_fence *fence;
check = rcu_dereference_protected(old->shared[i],
reservation_object_held(obj));
if (check->context == fence->context ||
dma_fence_is_signaled(check))
RCU_INIT_POINTER(fobj->shared[--k], check);
fence = rcu_dereference_protected(old->shared[i],
reservation_object_held(obj));
if (dma_fence_is_signaled(fence))
RCU_INIT_POINTER(new->shared[--k], fence);
else
RCU_INIT_POINTER(fobj->shared[j++], check);
RCU_INIT_POINTER(new->shared[j++], fence);
}
fobj->shared_count = j;
RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence);
fobj->shared_count++;
new->shared_count = j;
new->shared_max = max;
done:
preempt_disable();
write_seqcount_begin(&obj->seq);
/*
* RCU_INIT_POINTER can be used here,
* seqcount provides the necessary barriers
*/
RCU_INIT_POINTER(obj->fence, fobj);
RCU_INIT_POINTER(obj->fence, new);
write_seqcount_end(&obj->seq);
preempt_enable();
if (!old)
return;
return 0;
/* Drop the references to the signaled fences */
for (i = k; i < fobj->shared_max; ++i) {
struct dma_fence *f;
for (i = k; i < new->shared_max; ++i) {
struct dma_fence *fence;
f = rcu_dereference_protected(fobj->shared[i],
reservation_object_held(obj));
dma_fence_put(f);
fence = rcu_dereference_protected(new->shared[i],
reservation_object_held(obj));
dma_fence_put(fence);
}
kfree_rcu(old, rcu);
return 0;
}
EXPORT_SYMBOL(reservation_object_reserve_shared);
/**
* reservation_object_add_shared_fence - Add a fence to a shared slot
@ -226,15 +146,39 @@ done:
void reservation_object_add_shared_fence(struct reservation_object *obj,
struct dma_fence *fence)
{
struct reservation_object_list *old, *fobj = obj->staged;
struct reservation_object_list *fobj;
unsigned int i, count;
old = reservation_object_get_list(obj);
obj->staged = NULL;
dma_fence_get(fence);
if (!fobj)
reservation_object_add_shared_inplace(obj, old, fence);
else
reservation_object_add_shared_replace(obj, old, fobj, fence);
fobj = reservation_object_get_list(obj);
count = fobj->shared_count;
preempt_disable();
write_seqcount_begin(&obj->seq);
for (i = 0; i < count; ++i) {
struct dma_fence *old_fence;
old_fence = rcu_dereference_protected(fobj->shared[i],
reservation_object_held(obj));
if (old_fence->context == fence->context ||
dma_fence_is_signaled(old_fence)) {
dma_fence_put(old_fence);
goto replace;
}
}
BUG_ON(fobj->shared_count >= fobj->shared_max);
count++;
replace:
RCU_INIT_POINTER(fobj->shared[i], fence);
/* pointer update must be visible before we extend the shared_count */
smp_store_mb(fobj->shared_count, count);
write_seqcount_end(&obj->seq);
preempt_enable();
}
EXPORT_SYMBOL(reservation_object_add_shared_fence);
@ -343,9 +287,6 @@ retry:
new = dma_fence_get_rcu_safe(&src->fence_excl);
rcu_read_unlock();
kfree(dst->staged);
dst->staged = NULL;
src_list = reservation_object_get_list(dst);
old = reservation_object_get_excl(dst);

View File

@ -36,7 +36,8 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
drm_simple_kms_helper.o drm_modeset_helper.o \
drm_scdc_helper.o drm_gem_framebuffer_helper.o
drm_scdc_helper.o drm_gem_framebuffer_helper.o \
drm_atomic_state_helper.o
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o

View File

@ -955,7 +955,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
if (r)
return r;
r = reservation_object_reserve_shared(vm->root.base.bo->tbo.resv);
r = reservation_object_reserve_shared(vm->root.base.bo->tbo.resv, 1);
if (r)
return r;
@ -1104,7 +1104,7 @@ static int amdgpu_syncobj_lookup_and_add_to_sync(struct amdgpu_cs_parser *p,
{
int r;
struct dma_fence *fence;
r = drm_syncobj_find_fence(p->filp, handle, 0, &fence);
r = drm_syncobj_find_fence(p->filp, handle, 0, 0, &fence);
if (r)
return r;

View File

@ -640,7 +640,7 @@ int amdgpu_bo_backup_to_shadow(struct amdgpu_device *adev,
bo_addr = amdgpu_bo_gpu_offset(bo);
shadow_addr = amdgpu_bo_gpu_offset(bo->shadow);
r = reservation_object_reserve_shared(bo->tbo.resv);
r = reservation_object_reserve_shared(bo->tbo.resv, 1);
if (r)
goto err;

View File

@ -339,8 +339,6 @@ static const struct dma_buf_ops amdgpu_dmabuf_ops = {
.unmap_dma_buf = drm_gem_unmap_dma_buf,
.release = drm_gem_dmabuf_release,
.begin_cpu_access = amdgpu_gem_begin_cpu_access,
.map = drm_gem_dmabuf_kmap,
.unmap = drm_gem_dmabuf_kunmap,
.mmap = drm_gem_dmabuf_mmap,
.vmap = drm_gem_dmabuf_vmap,
.vunmap = drm_gem_dmabuf_vunmap,

View File

@ -773,7 +773,7 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
ring = container_of(vm->entity.rq->sched, struct amdgpu_ring, sched);
r = reservation_object_reserve_shared(bo->tbo.resv);
r = reservation_object_reserve_shared(bo->tbo.resv, 1);
if (r)
return r;
@ -1842,7 +1842,7 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
if (r)
goto error_free;
r = reservation_object_reserve_shared(vm->root.base.bo->tbo.resv);
r = reservation_object_reserve_shared(vm->root.base.bo->tbo.resv, 1);
if (r)
goto error_free;

View File

@ -3185,7 +3185,6 @@ amdgpu_dm_connector_helper_funcs = {
*/
.get_modes = get_modes,
.mode_valid = amdgpu_dm_connector_mode_valid,
.best_encoder = drm_atomic_helper_best_encoder
};
static void dm_crtc_helper_disable(struct drm_crtc *crtc)
@ -3588,14 +3587,17 @@ static int to_drm_connector_type(enum signal_type st)
}
}
static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector)
{
return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
}
static void amdgpu_dm_get_native_mode(struct drm_connector *connector)
{
const struct drm_connector_helper_funcs *helper =
connector->helper_private;
struct drm_encoder *encoder;
struct amdgpu_encoder *amdgpu_encoder;
encoder = helper->best_encoder(connector);
encoder = amdgpu_dm_connector_to_encoder(connector);
if (encoder == NULL)
return;
@ -3722,14 +3724,12 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector,
static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
{
const struct drm_connector_helper_funcs *helper =
connector->helper_private;
struct amdgpu_dm_connector *amdgpu_dm_connector =
to_amdgpu_dm_connector(connector);
struct drm_encoder *encoder;
struct edid *edid = amdgpu_dm_connector->edid;
encoder = helper->best_encoder(connector);
encoder = amdgpu_dm_connector_to_encoder(connector);
if (!edid || !drm_edid_is_valid(edid)) {
amdgpu_dm_connector->num_modes =

View File

@ -20,7 +20,6 @@
struct arcpgu_drm_private {
void __iomem *regs;
struct clk *clk;
struct drm_fbdev_cma *fbdev;
struct drm_framebuffer *fb;
struct drm_crtc crtc;
struct drm_plane *plane;
@ -43,8 +42,5 @@ static inline u32 arc_pgu_read(struct arcpgu_drm_private *arcpgu,
int arc_pgu_setup_crtc(struct drm_device *dev);
int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np);
int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np);
struct drm_fbdev_cma *arcpgu_fbdev_cma_init(struct drm_device *dev,
unsigned int preferred_bpp, unsigned int num_crtc,
unsigned int max_conn_count);
#endif

View File

@ -158,8 +158,6 @@ static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc,
static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = {
.mode_valid = arc_pgu_crtc_mode_valid,
.mode_set = drm_helper_crtc_mode_set,
.mode_set_base = drm_helper_crtc_mode_set_base,
.mode_set_nofb = arc_pgu_crtc_mode_set_nofb,
.atomic_begin = arc_pgu_crtc_atomic_begin,
.atomic_enable = arc_pgu_crtc_atomic_enable,
@ -186,7 +184,6 @@ static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = {
static void arc_pgu_plane_destroy(struct drm_plane *plane)
{
drm_plane_helper_disable(plane, NULL);
drm_plane_cleanup(plane);
}

View File

@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_atomic_helper.h>
@ -25,16 +26,8 @@
#include "arcpgu.h"
#include "arcpgu_regs.h"
static void arcpgu_fb_output_poll_changed(struct drm_device *dev)
{
struct arcpgu_drm_private *arcpgu = dev->dev_private;
drm_fbdev_cma_hotplug_event(arcpgu->fbdev);
}
static const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = {
.fb_create = drm_gem_fb_create,
.output_poll_changed = arcpgu_fb_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
@ -51,13 +44,6 @@ static void arcpgu_setup_mode_config(struct drm_device *drm)
DEFINE_DRM_GEM_CMA_FOPS(arcpgu_drm_ops);
static void arcpgu_lastclose(struct drm_device *drm)
{
struct arcpgu_drm_private *arcpgu = drm->dev_private;
drm_fbdev_cma_restore_mode(arcpgu->fbdev);
}
static int arcpgu_load(struct drm_device *drm)
{
struct platform_device *pdev = to_platform_device(drm->dev);
@ -113,27 +99,14 @@ static int arcpgu_load(struct drm_device *drm)
drm_mode_config_reset(drm);
drm_kms_helper_poll_init(drm);
arcpgu->fbdev = drm_fbdev_cma_init(drm, 16,
drm->mode_config.num_connector);
if (IS_ERR(arcpgu->fbdev)) {
ret = PTR_ERR(arcpgu->fbdev);
arcpgu->fbdev = NULL;
return -ENODEV;
}
platform_set_drvdata(pdev, drm);
return 0;
}
static int arcpgu_unload(struct drm_device *drm)
{
struct arcpgu_drm_private *arcpgu = drm->dev_private;
if (arcpgu->fbdev) {
drm_fbdev_cma_fini(arcpgu->fbdev);
arcpgu->fbdev = NULL;
}
drm_kms_helper_poll_fini(drm);
drm_atomic_helper_shutdown(drm);
drm_mode_config_cleanup(drm);
return 0;
@ -167,7 +140,6 @@ static int arcpgu_debugfs_init(struct drm_minor *minor)
static struct drm_driver arcpgu_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
DRIVER_ATOMIC,
.lastclose = arcpgu_lastclose,
.name = "arcpgu",
.desc = "ARC PGU Controller",
.date = "20160219",
@ -210,6 +182,8 @@ static int arcpgu_probe(struct platform_device *pdev)
if (ret)
goto err_unload;
drm_fbdev_generic_setup(drm, 16);
return 0;
err_unload:

View File

@ -77,12 +77,18 @@ static const struct malidp_format_id malidp500_de_formats[] = {
{ DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) }, \
{ DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) }, \
{ DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(5, 6) }, \
{ DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) }
{ DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) }, \
{ DRM_FORMAT_X0L2, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 6)}
static const struct malidp_format_id malidp550_de_formats[] = {
MALIDP_COMMON_FORMATS,
};
static const struct malidp_format_id malidp650_de_formats[] = {
MALIDP_COMMON_FORMATS,
{ DRM_FORMAT_X0L0, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 4)},
};
static const struct malidp_layer malidp500_layers[] = {
/* id, base address, fb pointer address base, stride offset,
* yuv2rgb matrix offset, mmu control register offset, rotation_features
@ -630,6 +636,8 @@ static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16
case DRM_FORMAT_BGR565:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_YUYV:
case DRM_FORMAT_X0L0:
case DRM_FORMAT_X0L2:
bytes_per_col = 32;
break;
/* 16 lines at 1.5 bytes per pixel */
@ -905,8 +913,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
MALIDP550_DC_IRQ_SE,
.vsync_irq = MALIDP550_DC_IRQ_CONF_VALID,
},
.pixel_formats = malidp550_de_formats,
.n_pixel_formats = ARRAY_SIZE(malidp550_de_formats),
.pixel_formats = malidp650_de_formats,
.n_pixel_formats = ARRAY_SIZE(malidp650_de_formats),
.bus_align_bytes = 16,
},
.query_hw = malidp650_query_hw,

View File

@ -398,6 +398,7 @@ static int malidp_de_plane_check(struct drm_plane *plane,
struct drm_framebuffer *fb;
u16 pixel_alpha = state->pixel_blend_mode;
int i, ret;
unsigned int block_w, block_h;
if (!state->crtc || !state->fb)
return 0;
@ -413,13 +414,26 @@ static int malidp_de_plane_check(struct drm_plane *plane,
ms->n_planes = fb->format->num_planes;
for (i = 0; i < ms->n_planes; i++) {
u8 alignment = malidp_hw_get_pitch_align(mp->hwdev, rotated);
if (fb->pitches[i] & (alignment - 1)) {
if ((fb->pitches[i] * drm_format_info_block_height(fb->format, i))
& (alignment - 1)) {
DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
fb->pitches[i], i);
return -EINVAL;
}
}
block_w = drm_format_info_block_width(fb->format, 0);
block_h = drm_format_info_block_height(fb->format, 0);
if (fb->width % block_w || fb->height % block_h) {
DRM_DEBUG_KMS("Buffer width/height needs to be a multiple of tile sizes");
return -EINVAL;
}
if ((state->src_x >> 16) % block_w || (state->src_y >> 16) % block_h) {
DRM_DEBUG_KMS("Plane src_x/src_y needs to be a multiple of tile sizes");
return -EINVAL;
}
if ((state->crtc_w > mp->hwdev->max_line_size) ||
(state->crtc_h > mp->hwdev->max_line_size) ||
(state->crtc_w < mp->hwdev->min_line_size) ||
@ -492,10 +506,18 @@ static void malidp_de_set_plane_pitches(struct malidp_plane *mp,
num_strides = (mp->hwdev->hw->features &
MALIDP_DEVICE_LV_HAS_3_STRIDES) ? 3 : 2;
for (i = 0; i < num_strides; ++i)
malidp_hw_write(mp->hwdev, pitches[i],
/*
* The drm convention for pitch is that it needs to cover width * cpp,
* but our hardware wants the pitch/stride to cover all rows included
* in a tile.
*/
for (i = 0; i < num_strides; ++i) {
unsigned int block_h = drm_format_info_block_height(mp->base.state->fb->format, i);
malidp_hw_write(mp->hwdev, pitches[i] * block_h,
mp->layer->base +
mp->layer->stride_offset + i * 4);
}
}
static const s16

View File

@ -364,9 +364,7 @@ static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc,
static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
.mode_valid = atmel_hlcdc_crtc_mode_valid,
.mode_set = drm_helper_crtc_mode_set,
.mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb,
.mode_set_base = drm_helper_crtc_mode_set_base,
.atomic_check = atmel_hlcdc_crtc_atomic_check,
.atomic_begin = atmel_hlcdc_crtc_atomic_begin,
.atomic_flush = atmel_hlcdc_crtc_atomic_flush,

View File

@ -556,7 +556,6 @@ error:
static const struct drm_mode_config_funcs mode_config_funcs = {
.fb_create = atmel_hlcdc_fb_create,
.output_poll_changed = drm_fb_helper_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = atmel_hlcdc_dc_atomic_commit,
};
@ -658,8 +657,6 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
platform_set_drvdata(pdev, dev);
drm_fb_cma_fbdev_init(dev, 24, 0);
drm_kms_helper_poll_init(dev);
return 0;
@ -678,7 +675,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev)
{
struct atmel_hlcdc_dc *dc = dev->dev_private;
drm_fb_cma_fbdev_fini(dev);
flush_workqueue(dc->wq);
drm_kms_helper_poll_fini(dev);
drm_atomic_helper_shutdown(dev);
@ -727,7 +723,6 @@ static struct drm_driver atmel_hlcdc_dc_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC,
.lastclose = drm_fb_helper_lastclose,
.irq_handler = atmel_hlcdc_dc_irq_handler,
.irq_preinstall = atmel_hlcdc_dc_irq_uninstall,
.irq_postinstall = atmel_hlcdc_dc_irq_postinstall,
@ -763,19 +758,21 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
ret = atmel_hlcdc_dc_load(ddev);
if (ret)
goto err_unref;
goto err_put;
ret = drm_dev_register(ddev, 0);
if (ret)
goto err_unload;
drm_fbdev_generic_setup(ddev, 24);
return 0;
err_unload:
atmel_hlcdc_dc_unload(ddev);
err_unref:
drm_dev_unref(ddev);
err_put:
drm_dev_put(ddev);
return ret;
}
@ -786,7 +783,7 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
drm_dev_unregister(ddev);
atmel_hlcdc_dc_unload(ddev);
drm_dev_unref(ddev);
drm_dev_put(ddev);
return 0;
}

View File

@ -66,6 +66,7 @@ struct bochs_device {
u16 yres_virtual;
u32 stride;
u32 bpp;
struct edid *edid;
/* drm */
struct drm_device *dev;
@ -126,6 +127,7 @@ void bochs_hw_setmode(struct bochs_device *bochs,
const struct drm_format_info *format);
void bochs_hw_setbase(struct bochs_device *bochs,
int x, int y, u64 addr);
int bochs_hw_load_edid(struct bochs_device *bochs);
/* bochs_mm.c */
int bochs_mm_init(struct bochs_device *bochs);

View File

@ -69,6 +69,35 @@ static void bochs_hw_set_little_endian(struct bochs_device *bochs)
#define bochs_hw_set_native_endian(_b) bochs_hw_set_little_endian(_b)
#endif
static int bochs_get_edid_block(void *data, u8 *buf,
unsigned int block, size_t len)
{
struct bochs_device *bochs = data;
size_t i, start = block * EDID_LENGTH;
if (start + len > 0x400 /* vga register offset */)
return -1;
for (i = 0; i < len; i++) {
buf[i] = readb(bochs->mmio + start + i);
}
return 0;
}
int bochs_hw_load_edid(struct bochs_device *bochs)
{
if (!bochs->mmio)
return -1;
kfree(bochs->edid);
bochs->edid = drm_do_get_edid(&bochs->connector,
bochs_get_edid_block, bochs);
if (bochs->edid == NULL)
return -1;
return 0;
}
int bochs_hw_init(struct drm_device *dev)
{
struct bochs_device *bochs = dev->dev_private;
@ -164,6 +193,7 @@ void bochs_hw_fini(struct drm_device *dev)
if (bochs->fb_map)
iounmap(bochs->fb_map);
pci_release_regions(dev->pdev);
kfree(bochs->edid);
}
void bochs_hw_setmode(struct bochs_device *bochs,

View File

@ -213,10 +213,17 @@ static void bochs_encoder_init(struct drm_device *dev)
static int bochs_connector_get_modes(struct drm_connector *connector)
{
int count;
struct bochs_device *bochs =
container_of(connector, struct bochs_device, connector);
int count = 0;
count = drm_add_modes_noedid(connector, 8192, 8192);
drm_set_preferred_mode(connector, defx, defy);
if (bochs->edid)
count = drm_add_edid_modes(connector, bochs->edid);
if (!count) {
count = drm_add_modes_noedid(connector, 8192, 8192);
drm_set_preferred_mode(connector, defx, defy);
}
return count;
}
@ -271,6 +278,13 @@ static void bochs_connector_init(struct drm_device *dev)
drm_connector_helper_add(connector,
&bochs_connector_connector_helper_funcs);
drm_connector_register(connector);
bochs_hw_load_edid(bochs);
if (bochs->edid) {
DRM_INFO("Found EDID data blob.\n");
drm_connector_attach_edid_property(connector);
drm_connector_update_edid_property(connector, bochs->edid);
}
}

View File

@ -414,7 +414,7 @@ int bochs_dumb_create(struct drm_file *file, struct drm_device *dev,
return ret;
ret = drm_gem_handle_create(file, gobj, &handle);
drm_gem_object_unreference_unlocked(gobj);
drm_gem_object_put_unlocked(gobj);
if (ret)
return ret;
@ -454,6 +454,6 @@ int bochs_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
bo = gem_to_bochs_bo(obj);
*offset = bochs_bo_mmap_offset(bo);
drm_gem_object_unreference_unlocked(obj);
drm_gem_object_put_unlocked(obj);
return 0;
}

View File

@ -1219,12 +1219,12 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
* plat_data->attch return, that's why we record the connector
* point after plat attached.
*/
if (dp->plat_data->attach) {
ret = dp->plat_data->attach(dp->plat_data, bridge, connector);
if (ret) {
DRM_ERROR("Failed at platform attch func\n");
return ret;
}
if (dp->plat_data->attach) {
ret = dp->plat_data->attach(dp->plat_data, bridge, connector);
if (ret) {
DRM_ERROR("Failed at platform attach func\n");
return ret;
}
}
if (dp->plat_data->panel) {

View File

@ -1664,6 +1664,7 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
case 0x131a:
case 0x132a:
case 0x201a:
case 0x212a:
count = 1;
break;
default:
@ -1957,7 +1958,6 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
.get_modes = dw_hdmi_connector_get_modes,
.best_encoder = drm_atomic_helper_best_encoder,
};
static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
@ -2205,7 +2205,9 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
unsigned int i;
u8 phy_type;
phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID);
phy_type = hdmi->plat_data->phy_force_vendor ?
DW_HDMI_PHY_VENDOR_PHY :
hdmi_readb(hdmi, HDMI_CONFIG2_ID);
if (phy_type == DW_HDMI_PHY_VENDOR_PHY) {
/* Vendor PHYs require support from the glue layer. */

View File

@ -230,9 +230,20 @@ struct dw_mipi_dsi {
u32 format;
unsigned long mode_flags;
struct dw_mipi_dsi *master; /* dual-dsi master ptr */
struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */
const struct dw_mipi_dsi_plat_data *plat_data;
};
/*
* Check if either a link to a master or slave is present
*/
static inline bool dw_mipi_is_dual_mode(struct dw_mipi_dsi *dsi)
{
return dsi->slave || dsi->master;
}
/*
* The controller should generate 2 frames before
* preparing the peripheral.
@ -270,6 +281,7 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data;
struct drm_bridge *bridge;
struct drm_panel *panel;
int ret;
@ -300,6 +312,12 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
drm_bridge_add(&dsi->bridge);
if (pdata->host_ops && pdata->host_ops->attach) {
ret = pdata->host_ops->attach(pdata->priv_data, device);
if (ret < 0)
return ret;
}
return 0;
}
@ -307,6 +325,14 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data;
int ret;
if (pdata->host_ops && pdata->host_ops->detach) {
ret = pdata->host_ops->detach(pdata->priv_data, device);
if (ret < 0)
return ret;
}
drm_of_panel_bridge_remove(host->dev->of_node, 1, 0);
@ -441,10 +467,17 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
}
dw_mipi_message_config(dsi, msg);
if (dsi->slave)
dw_mipi_message_config(dsi->slave, msg);
ret = dw_mipi_dsi_write(dsi, &packet);
if (ret)
return ret;
if (dsi->slave) {
ret = dw_mipi_dsi_write(dsi->slave, &packet);
if (ret)
return ret;
}
if (msg->rx_buf && msg->rx_len) {
ret = dw_mipi_dsi_read(dsi, msg);
@ -583,7 +616,11 @@ static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
* DSI_VNPCR.NPSIZE... especially because this driver supports
* non-burst video modes, see dw_mipi_dsi_video_mode_config()...
*/
dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
dsi_write(dsi, DSI_VID_PKT_SIZE,
dw_mipi_is_dual_mode(dsi) ?
VID_PKT_SIZE(mode->hdisplay / 2) :
VID_PKT_SIZE(mode->hdisplay));
}
static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
@ -755,24 +792,43 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
*/
dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
if (dsi->slave) {
dw_mipi_dsi_disable(dsi->slave);
clk_disable_unprepare(dsi->slave->pclk);
pm_runtime_put(dsi->slave->dev);
}
dw_mipi_dsi_disable(dsi);
clk_disable_unprepare(dsi->pclk);
pm_runtime_put(dsi->dev);
}
static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi)
{
/* this instance is the slave, so add the master's lanes */
if (dsi->master)
return dsi->master->lanes + dsi->lanes;
/* this instance is the master, so add the slave's lanes */
if (dsi->slave)
return dsi->lanes + dsi->slave->lanes;
/* single-dsi, so no other instance to consider */
return dsi->lanes;
}
static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
struct drm_display_mode *adjusted_mode)
{
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
void *priv_data = dsi->plat_data->priv_data;
int ret;
u32 lanes = dw_mipi_dsi_get_lanes(dsi);
clk_prepare_enable(dsi->pclk);
ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi->mode_flags,
dsi->lanes, dsi->format, &dsi->lane_mbps);
lanes, dsi->format, &dsi->lane_mbps);
if (ret)
DRM_DEBUG_DRIVER("Phy get_lane_mbps() failed\n");
@ -804,12 +860,25 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
dw_mipi_dsi_set_mode(dsi, 0);
}
static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
dw_mipi_dsi_mode_set(dsi, adjusted_mode);
if (dsi->slave)
dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode);
}
static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
{
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
/* Switch to video mode for panel-bridge enable & panel enable */
dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
if (dsi->slave)
dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
}
static enum drm_mode_status
@ -941,9 +1010,25 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
{
mipi_dsi_host_unregister(&dsi->dsi_host);
pm_runtime_disable(dsi->dev);
}
void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave)
{
/* introduce controllers to each other */
dsi->slave = slave;
dsi->slave->master = dsi;
/* migrate settings for already attached displays */
dsi->slave->lanes = dsi->lanes;
dsi->slave->channel = dsi->channel;
dsi->slave->format = dsi->format;
dsi->slave->mode_flags = dsi->mode_flags;
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_set_slave);
/*
* Probe/remove API, used from platforms based on the DRM bridge API.
*/
@ -957,8 +1042,6 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe);
void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
{
mipi_dsi_host_unregister(&dsi->dsi_host);
__dw_mipi_dsi_remove(dsi);
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
@ -966,31 +1049,22 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
/*
* Bind/unbind API, used from platforms based on the component framework.
*/
struct dw_mipi_dsi *
dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
const struct dw_mipi_dsi_plat_data *plat_data)
int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
{
struct dw_mipi_dsi *dsi;
int ret;
dsi = __dw_mipi_dsi_probe(pdev, plat_data);
if (IS_ERR(dsi))
return dsi;
ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
if (ret) {
dw_mipi_dsi_remove(dsi);
DRM_ERROR("Failed to initialize bridge with drm\n");
return ERR_PTR(ret);
return ret;
}
return dsi;
return ret;
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
{
__dw_mipi_dsi_remove(dsi);
}
EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);

View File

@ -92,6 +92,17 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
}
}
/*
* For connectors that support multiple encoders, either the
* .atomic_best_encoder() or .best_encoder() operation must be implemented.
*/
static struct drm_encoder *
pick_single_encoder_for_connector(struct drm_connector *connector)
{
WARN_ON(connector->encoder_ids[1]);
return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
}
static int handle_conflicting_encoders(struct drm_atomic_state *state,
bool disable_conflicting_encoders)
{
@ -119,7 +130,7 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state,
else if (funcs->best_encoder)
new_encoder = funcs->best_encoder(connector);
else
new_encoder = drm_atomic_helper_best_encoder(connector);
new_encoder = pick_single_encoder_for_connector(connector);
if (new_encoder) {
if (encoder_mask & drm_encoder_mask(new_encoder)) {
@ -336,7 +347,7 @@ update_connector_routing(struct drm_atomic_state *state,
else if (funcs->best_encoder)
new_encoder = funcs->best_encoder(connector);
else
new_encoder = drm_atomic_helper_best_encoder(connector);
new_encoder = pick_single_encoder_for_connector(connector);
if (!new_encoder) {
DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
@ -3411,586 +3422,3 @@ fail:
return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_page_flip_target);
/**
* drm_atomic_helper_best_encoder - Helper for
* &drm_connector_helper_funcs.best_encoder callback
* @connector: Connector control structure
*
* This is a &drm_connector_helper_funcs.best_encoder callback helper for
* connectors that support exactly 1 encoder, statically determined at driver
* init time.
*/
struct drm_encoder *
drm_atomic_helper_best_encoder(struct drm_connector *connector)
{
WARN_ON(connector->encoder_ids[1]);
return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
}
EXPORT_SYMBOL(drm_atomic_helper_best_encoder);
/**
* DOC: atomic state reset and initialization
*
* Both the drm core and the atomic helpers assume that there is always the full
* and correct atomic software state for all connectors, CRTCs and planes
* available. Which is a bit a problem on driver load and also after system
* suspend. One way to solve this is to have a hardware state read-out
* infrastructure which reconstructs the full software state (e.g. the i915
* driver).
*
* The simpler solution is to just reset the software state to everything off,
* which is easiest to do by calling drm_mode_config_reset(). To facilitate this
* the atomic helpers provide default reset implementations for all hooks.
*
* On the upside the precise state tracking of atomic simplifies system suspend
* and resume a lot. For drivers using drm_mode_config_reset() a complete recipe
* is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume().
* For other drivers the building blocks are split out, see the documentation
* for these functions.
*/
/**
* drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs
* @crtc: drm CRTC
*
* Resets the atomic state for @crtc by freeing the state pointer (which might
* be NULL, e.g. at driver load time) and allocating a new empty state object.
*/
void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
{
if (crtc->state)
__drm_atomic_helper_crtc_destroy_state(crtc->state);
kfree(crtc->state);
crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
if (crtc->state)
crtc->state->crtc = crtc;
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);
/**
* __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state
* @crtc: CRTC object
* @state: atomic CRTC state
*
* Copies atomic state from a CRTC's current state and resets inferred values.
* This is useful for drivers that subclass the CRTC state.
*/
void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
memcpy(state, crtc->state, sizeof(*state));
if (state->mode_blob)
drm_property_blob_get(state->mode_blob);
if (state->degamma_lut)
drm_property_blob_get(state->degamma_lut);
if (state->ctm)
drm_property_blob_get(state->ctm);
if (state->gamma_lut)
drm_property_blob_get(state->gamma_lut);
state->mode_changed = false;
state->active_changed = false;
state->planes_changed = false;
state->connectors_changed = false;
state->color_mgmt_changed = false;
state->zpos_changed = false;
state->commit = NULL;
state->event = NULL;
state->pageflip_flags = 0;
}
EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);
/**
* drm_atomic_helper_crtc_duplicate_state - default state duplicate hook
* @crtc: drm CRTC
*
* Default CRTC state duplicate hook for drivers which don't have their own
* subclassed CRTC state structure.
*/
struct drm_crtc_state *
drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct drm_crtc_state *state;
if (WARN_ON(!crtc->state))
return NULL;
state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state)
__drm_atomic_helper_crtc_duplicate_state(crtc, state);
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
/**
* __drm_atomic_helper_crtc_destroy_state - release CRTC state
* @state: CRTC state object to release
*
* Releases all resources stored in the CRTC state without actually freeing
* the memory of the CRTC state. This is useful for drivers that subclass the
* CRTC state.
*/
void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
{
if (state->commit) {
/*
* In the event that a non-blocking commit returns
* -ERESTARTSYS before the commit_tail work is queued, we will
* have an extra reference to the commit object. Release it, if
* the event has not been consumed by the worker.
*
* state->event may be freed, so we can't directly look at
* state->event->base.completion.
*/
if (state->event && state->commit->abort_completion)
drm_crtc_commit_put(state->commit);
kfree(state->commit->event);
state->commit->event = NULL;
drm_crtc_commit_put(state->commit);
}
drm_property_blob_put(state->mode_blob);
drm_property_blob_put(state->degamma_lut);
drm_property_blob_put(state->ctm);
drm_property_blob_put(state->gamma_lut);
}
EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);
/**
* drm_atomic_helper_crtc_destroy_state - default state destroy hook
* @crtc: drm CRTC
* @state: CRTC state object to release
*
* Default CRTC state destroy hook for drivers which don't have their own
* subclassed CRTC state structure.
*/
void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
__drm_atomic_helper_crtc_destroy_state(state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
/**
* __drm_atomic_helper_plane_reset - resets planes state to default values
* @plane: plane object, must not be NULL
* @state: atomic plane state, must not be NULL
*
* Initializes plane state to default. This is useful for drivers that subclass
* the plane state.
*/
void __drm_atomic_helper_plane_reset(struct drm_plane *plane,
struct drm_plane_state *state)
{
state->plane = plane;
state->rotation = DRM_MODE_ROTATE_0;
state->alpha = DRM_BLEND_ALPHA_OPAQUE;
state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
plane->state = state;
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_reset);
/**
* drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes
* @plane: drm plane
*
* Resets the atomic state for @plane by freeing the state pointer (which might
* be NULL, e.g. at driver load time) and allocating a new empty state object.
*/
void drm_atomic_helper_plane_reset(struct drm_plane *plane)
{
if (plane->state)
__drm_atomic_helper_plane_destroy_state(plane->state);
kfree(plane->state);
plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL);
if (plane->state)
__drm_atomic_helper_plane_reset(plane, plane->state);
}
EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
/**
* __drm_atomic_helper_plane_duplicate_state - copy atomic plane state
* @plane: plane object
* @state: atomic plane state
*
* Copies atomic state from a plane's current state. This is useful for
* drivers that subclass the plane state.
*/
void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
memcpy(state, plane->state, sizeof(*state));
if (state->fb)
drm_framebuffer_get(state->fb);
state->fence = NULL;
state->commit = NULL;
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
/**
* drm_atomic_helper_plane_duplicate_state - default state duplicate hook
* @plane: drm plane
*
* Default plane state duplicate hook for drivers which don't have their own
* subclassed plane state structure.
*/
struct drm_plane_state *
drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane)
{
struct drm_plane_state *state;
if (WARN_ON(!plane->state))
return NULL;
state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state)
__drm_atomic_helper_plane_duplicate_state(plane, state);
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
/**
* __drm_atomic_helper_plane_destroy_state - release plane state
* @state: plane state object to release
*
* Releases all resources stored in the plane state without actually freeing
* the memory of the plane state. This is useful for drivers that subclass the
* plane state.
*/
void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
{
if (state->fb)
drm_framebuffer_put(state->fb);
if (state->fence)
dma_fence_put(state->fence);
if (state->commit)
drm_crtc_commit_put(state->commit);
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
/**
* drm_atomic_helper_plane_destroy_state - default state destroy hook
* @plane: drm plane
* @state: plane state object to release
*
* Default plane state destroy hook for drivers which don't have their own
* subclassed plane state structure.
*/
void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
__drm_atomic_helper_plane_destroy_state(state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
/**
* __drm_atomic_helper_connector_reset - reset state on connector
* @connector: drm connector
* @conn_state: connector state to assign
*
* Initializes the newly allocated @conn_state and assigns it to
* the &drm_conector->state pointer of @connector, usually required when
* initializing the drivers or when called from the &drm_connector_funcs.reset
* hook.
*
* This is useful for drivers that subclass the connector state.
*/
void
__drm_atomic_helper_connector_reset(struct drm_connector *connector,
struct drm_connector_state *conn_state)
{
if (conn_state)
conn_state->connector = connector;
connector->state = conn_state;
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_reset);
/**
* drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors
* @connector: drm connector
*
* Resets the atomic state for @connector by freeing the state pointer (which
* might be NULL, e.g. at driver load time) and allocating a new empty state
* object.
*/
void drm_atomic_helper_connector_reset(struct drm_connector *connector)
{
struct drm_connector_state *conn_state =
kzalloc(sizeof(*conn_state), GFP_KERNEL);
if (connector->state)
__drm_atomic_helper_connector_destroy_state(connector->state);
kfree(connector->state);
__drm_atomic_helper_connector_reset(connector, conn_state);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
/**
* __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
* @connector: connector object
* @state: atomic connector state
*
* Copies atomic state from a connector's current state. This is useful for
* drivers that subclass the connector state.
*/
void
__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
struct drm_connector_state *state)
{
memcpy(state, connector->state, sizeof(*state));
if (state->crtc)
drm_connector_get(connector);
state->commit = NULL;
/* Don't copy over a writeback job, they are used only once */
state->writeback_job = NULL;
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
/**
* drm_atomic_helper_connector_duplicate_state - default state duplicate hook
* @connector: drm connector
*
* Default connector state duplicate hook for drivers which don't have their own
* subclassed connector state structure.
*/
struct drm_connector_state *
drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
{
struct drm_connector_state *state;
if (WARN_ON(!connector->state))
return NULL;
state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state)
__drm_atomic_helper_connector_duplicate_state(connector, state);
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
/**
* drm_atomic_helper_duplicate_state - duplicate an atomic state object
* @dev: DRM device
* @ctx: lock acquisition context
*
* Makes a copy of the current atomic state by looping over all objects and
* duplicating their respective states. This is used for example by suspend/
* resume support code to save the state prior to suspend such that it can
* be restored upon resume.
*
* Note that this treats atomic state as persistent between save and restore.
* Drivers must make sure that this is possible and won't result in confusion
* or erroneous behaviour.
*
* 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:
* A pointer to the copy of the atomic state object on success or an
* ERR_PTR()-encoded error code on failure.
*
* See also:
* drm_atomic_helper_suspend(), drm_atomic_helper_resume()
*/
struct drm_atomic_state *
drm_atomic_helper_duplicate_state(struct drm_device *dev,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_atomic_state *state;
struct drm_connector *conn;
struct drm_connector_list_iter conn_iter;
struct drm_plane *plane;
struct drm_crtc *crtc;
int err = 0;
state = drm_atomic_state_alloc(dev);
if (!state)
return ERR_PTR(-ENOMEM);
state->acquire_ctx = ctx;
drm_for_each_crtc(crtc, dev) {
struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state)) {
err = PTR_ERR(crtc_state);
goto free;
}
}
drm_for_each_plane(plane, dev) {
struct drm_plane_state *plane_state;
plane_state = drm_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state)) {
err = PTR_ERR(plane_state);
goto free;
}
}
drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(conn, &conn_iter) {
struct drm_connector_state *conn_state;
conn_state = drm_atomic_get_connector_state(state, conn);
if (IS_ERR(conn_state)) {
err = PTR_ERR(conn_state);
drm_connector_list_iter_end(&conn_iter);
goto free;
}
}
drm_connector_list_iter_end(&conn_iter);
/* clear the acquire context so that it isn't accidentally reused */
state->acquire_ctx = NULL;
free:
if (err < 0) {
drm_atomic_state_put(state);
state = ERR_PTR(err);
}
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
/**
* __drm_atomic_helper_connector_destroy_state - release connector state
* @state: connector state object to release
*
* Releases all resources stored in the connector state without actually
* freeing the memory of the connector state. This is useful for drivers that
* subclass the connector state.
*/
void
__drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
{
if (state->crtc)
drm_connector_put(state->connector);
if (state->commit)
drm_crtc_commit_put(state->commit);
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
/**
* drm_atomic_helper_connector_destroy_state - default state destroy hook
* @connector: drm connector
* @state: connector state object to release
*
* Default connector state destroy hook for drivers which don't have their own
* subclassed connector state structure.
*/
void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state)
{
__drm_atomic_helper_connector_destroy_state(state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
/**
* drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table
* @crtc: CRTC object
* @red: red correction table
* @green: green correction table
* @blue: green correction table
* @size: size of the tables
* @ctx: lock acquire context
*
* Implements support for legacy gamma correction table for drivers
* that support color management through the DEGAMMA_LUT/GAMMA_LUT
* properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
* how the atomic color management and gamma tables work.
*/
int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
u16 *red, u16 *green, u16 *blue,
uint32_t size,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_device *dev = crtc->dev;
struct drm_atomic_state *state;
struct drm_crtc_state *crtc_state;
struct drm_property_blob *blob = NULL;
struct drm_color_lut *blob_data;
int i, ret = 0;
bool replaced;
state = drm_atomic_state_alloc(crtc->dev);
if (!state)
return -ENOMEM;
blob = drm_property_create_blob(dev,
sizeof(struct drm_color_lut) * size,
NULL);
if (IS_ERR(blob)) {
ret = PTR_ERR(blob);
blob = NULL;
goto fail;
}
/* Prepare GAMMA_LUT with the legacy values. */
blob_data = blob->data;
for (i = 0; i < size; i++) {
blob_data[i].red = red[i];
blob_data[i].green = green[i];
blob_data[i].blue = blue[i];
}
state->acquire_ctx = ctx;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state)) {
ret = PTR_ERR(crtc_state);
goto fail;
}
/* Reset DEGAMMA_LUT and CTM properties. */
replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL);
replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob);
crtc_state->color_mgmt_changed |= replaced;
ret = drm_atomic_commit(state);
fail:
drm_atomic_state_put(state);
drm_property_blob_put(blob);
return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set);
/**
* __drm_atomic_helper_private_duplicate_state - copy atomic private state
* @obj: CRTC object
* @state: new private object state
*
* Copies atomic state from a private objects's current state and resets inferred values.
* This is useful for drivers that subclass the private state.
*/
void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj,
struct drm_private_state *state)
{
memcpy(state, obj->state, sizeof(*state));
}
EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state);

View File

@ -0,0 +1,601 @@
/*
* Copyright (C) 2018 Intel Corp.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors:
* Rob Clark <robdclark@gmail.com>
* Daniel Vetter <daniel.vetter@ffwll.ch>
*/
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_plane.h>
#include <drm/drm_connector.h>
#include <drm/drm_atomic.h>
#include <drm/drm_device.h>
#include <linux/slab.h>
#include <linux/dma-fence.h>
/**
* DOC: atomic state reset and initialization
*
* Both the drm core and the atomic helpers assume that there is always the full
* and correct atomic software state for all connectors, CRTCs and planes
* available. Which is a bit a problem on driver load and also after system
* suspend. One way to solve this is to have a hardware state read-out
* infrastructure which reconstructs the full software state (e.g. the i915
* driver).
*
* The simpler solution is to just reset the software state to everything off,
* which is easiest to do by calling drm_mode_config_reset(). To facilitate this
* the atomic helpers provide default reset implementations for all hooks.
*
* On the upside the precise state tracking of atomic simplifies system suspend
* and resume a lot. For drivers using drm_mode_config_reset() a complete recipe
* is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume().
* For other drivers the building blocks are split out, see the documentation
* for these functions.
*/
/**
* drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs
* @crtc: drm CRTC
*
* Resets the atomic state for @crtc by freeing the state pointer (which might
* be NULL, e.g. at driver load time) and allocating a new empty state object.
*/
void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
{
if (crtc->state)
__drm_atomic_helper_crtc_destroy_state(crtc->state);
kfree(crtc->state);
crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
if (crtc->state)
crtc->state->crtc = crtc;
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);
/**
* __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state
* @crtc: CRTC object
* @state: atomic CRTC state
*
* Copies atomic state from a CRTC's current state and resets inferred values.
* This is useful for drivers that subclass the CRTC state.
*/
void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
memcpy(state, crtc->state, sizeof(*state));
if (state->mode_blob)
drm_property_blob_get(state->mode_blob);
if (state->degamma_lut)
drm_property_blob_get(state->degamma_lut);
if (state->ctm)
drm_property_blob_get(state->ctm);
if (state->gamma_lut)
drm_property_blob_get(state->gamma_lut);
state->mode_changed = false;
state->active_changed = false;
state->planes_changed = false;
state->connectors_changed = false;
state->color_mgmt_changed = false;
state->zpos_changed = false;
state->commit = NULL;
state->event = NULL;
state->pageflip_flags = 0;
}
EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);
/**
* drm_atomic_helper_crtc_duplicate_state - default state duplicate hook
* @crtc: drm CRTC
*
* Default CRTC state duplicate hook for drivers which don't have their own
* subclassed CRTC state structure.
*/
struct drm_crtc_state *
drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct drm_crtc_state *state;
if (WARN_ON(!crtc->state))
return NULL;
state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state)
__drm_atomic_helper_crtc_duplicate_state(crtc, state);
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
/**
* __drm_atomic_helper_crtc_destroy_state - release CRTC state
* @state: CRTC state object to release
*
* Releases all resources stored in the CRTC state without actually freeing
* the memory of the CRTC state. This is useful for drivers that subclass the
* CRTC state.
*/
void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
{
if (state->commit) {
/*
* In the event that a non-blocking commit returns
* -ERESTARTSYS before the commit_tail work is queued, we will
* have an extra reference to the commit object. Release it, if
* the event has not been consumed by the worker.
*
* state->event may be freed, so we can't directly look at
* state->event->base.completion.
*/
if (state->event && state->commit->abort_completion)
drm_crtc_commit_put(state->commit);
kfree(state->commit->event);
state->commit->event = NULL;
drm_crtc_commit_put(state->commit);
}
drm_property_blob_put(state->mode_blob);
drm_property_blob_put(state->degamma_lut);
drm_property_blob_put(state->ctm);
drm_property_blob_put(state->gamma_lut);
}
EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);
/**
* drm_atomic_helper_crtc_destroy_state - default state destroy hook
* @crtc: drm CRTC
* @state: CRTC state object to release
*
* Default CRTC state destroy hook for drivers which don't have their own
* subclassed CRTC state structure.
*/
void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
__drm_atomic_helper_crtc_destroy_state(state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
/**
* __drm_atomic_helper_plane_reset - resets planes state to default values
* @plane: plane object, must not be NULL
* @state: atomic plane state, must not be NULL
*
* Initializes plane state to default. This is useful for drivers that subclass
* the plane state.
*/
void __drm_atomic_helper_plane_reset(struct drm_plane *plane,
struct drm_plane_state *state)
{
state->plane = plane;
state->rotation = DRM_MODE_ROTATE_0;
state->alpha = DRM_BLEND_ALPHA_OPAQUE;
state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
plane->state = state;
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_reset);
/**
* drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes
* @plane: drm plane
*
* Resets the atomic state for @plane by freeing the state pointer (which might
* be NULL, e.g. at driver load time) and allocating a new empty state object.
*/
void drm_atomic_helper_plane_reset(struct drm_plane *plane)
{
if (plane->state)
__drm_atomic_helper_plane_destroy_state(plane->state);
kfree(plane->state);
plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL);
if (plane->state)
__drm_atomic_helper_plane_reset(plane, plane->state);
}
EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
/**
* __drm_atomic_helper_plane_duplicate_state - copy atomic plane state
* @plane: plane object
* @state: atomic plane state
*
* Copies atomic state from a plane's current state. This is useful for
* drivers that subclass the plane state.
*/
void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
memcpy(state, plane->state, sizeof(*state));
if (state->fb)
drm_framebuffer_get(state->fb);
state->fence = NULL;
state->commit = NULL;
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
/**
* drm_atomic_helper_plane_duplicate_state - default state duplicate hook
* @plane: drm plane
*
* Default plane state duplicate hook for drivers which don't have their own
* subclassed plane state structure.
*/
struct drm_plane_state *
drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane)
{
struct drm_plane_state *state;
if (WARN_ON(!plane->state))
return NULL;
state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state)
__drm_atomic_helper_plane_duplicate_state(plane, state);
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
/**
* __drm_atomic_helper_plane_destroy_state - release plane state
* @state: plane state object to release
*
* Releases all resources stored in the plane state without actually freeing
* the memory of the plane state. This is useful for drivers that subclass the
* plane state.
*/
void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
{
if (state->fb)
drm_framebuffer_put(state->fb);
if (state->fence)
dma_fence_put(state->fence);
if (state->commit)
drm_crtc_commit_put(state->commit);
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
/**
* drm_atomic_helper_plane_destroy_state - default state destroy hook
* @plane: drm plane
* @state: plane state object to release
*
* Default plane state destroy hook for drivers which don't have their own
* subclassed plane state structure.
*/
void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
__drm_atomic_helper_plane_destroy_state(state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
/**
* __drm_atomic_helper_connector_reset - reset state on connector
* @connector: drm connector
* @conn_state: connector state to assign
*
* Initializes the newly allocated @conn_state and assigns it to
* the &drm_conector->state pointer of @connector, usually required when
* initializing the drivers or when called from the &drm_connector_funcs.reset
* hook.
*
* This is useful for drivers that subclass the connector state.
*/
void
__drm_atomic_helper_connector_reset(struct drm_connector *connector,
struct drm_connector_state *conn_state)
{
if (conn_state)
conn_state->connector = connector;
connector->state = conn_state;
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_reset);
/**
* drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors
* @connector: drm connector
*
* Resets the atomic state for @connector by freeing the state pointer (which
* might be NULL, e.g. at driver load time) and allocating a new empty state
* object.
*/
void drm_atomic_helper_connector_reset(struct drm_connector *connector)
{
struct drm_connector_state *conn_state =
kzalloc(sizeof(*conn_state), GFP_KERNEL);
if (connector->state)
__drm_atomic_helper_connector_destroy_state(connector->state);
kfree(connector->state);
__drm_atomic_helper_connector_reset(connector, conn_state);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
/**
* __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
* @connector: connector object
* @state: atomic connector state
*
* Copies atomic state from a connector's current state. This is useful for
* drivers that subclass the connector state.
*/
void
__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
struct drm_connector_state *state)
{
memcpy(state, connector->state, sizeof(*state));
if (state->crtc)
drm_connector_get(connector);
state->commit = NULL;
/* Don't copy over a writeback job, they are used only once */
state->writeback_job = NULL;
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
/**
* drm_atomic_helper_connector_duplicate_state - default state duplicate hook
* @connector: drm connector
*
* Default connector state duplicate hook for drivers which don't have their own
* subclassed connector state structure.
*/
struct drm_connector_state *
drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
{
struct drm_connector_state *state;
if (WARN_ON(!connector->state))
return NULL;
state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state)
__drm_atomic_helper_connector_duplicate_state(connector, state);
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
/**
* drm_atomic_helper_duplicate_state - duplicate an atomic state object
* @dev: DRM device
* @ctx: lock acquisition context
*
* Makes a copy of the current atomic state by looping over all objects and
* duplicating their respective states. This is used for example by suspend/
* resume support code to save the state prior to suspend such that it can
* be restored upon resume.
*
* Note that this treats atomic state as persistent between save and restore.
* Drivers must make sure that this is possible and won't result in confusion
* or erroneous behaviour.
*
* 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:
* A pointer to the copy of the atomic state object on success or an
* ERR_PTR()-encoded error code on failure.
*
* See also:
* drm_atomic_helper_suspend(), drm_atomic_helper_resume()
*/
struct drm_atomic_state *
drm_atomic_helper_duplicate_state(struct drm_device *dev,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_atomic_state *state;
struct drm_connector *conn;
struct drm_connector_list_iter conn_iter;
struct drm_plane *plane;
struct drm_crtc *crtc;
int err = 0;
state = drm_atomic_state_alloc(dev);
if (!state)
return ERR_PTR(-ENOMEM);
state->acquire_ctx = ctx;
drm_for_each_crtc(crtc, dev) {
struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state)) {
err = PTR_ERR(crtc_state);
goto free;
}
}
drm_for_each_plane(plane, dev) {
struct drm_plane_state *plane_state;
plane_state = drm_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state)) {
err = PTR_ERR(plane_state);
goto free;
}
}
drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(conn, &conn_iter) {
struct drm_connector_state *conn_state;
conn_state = drm_atomic_get_connector_state(state, conn);
if (IS_ERR(conn_state)) {
err = PTR_ERR(conn_state);
drm_connector_list_iter_end(&conn_iter);
goto free;
}
}
drm_connector_list_iter_end(&conn_iter);
/* clear the acquire context so that it isn't accidentally reused */
state->acquire_ctx = NULL;
free:
if (err < 0) {
drm_atomic_state_put(state);
state = ERR_PTR(err);
}
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
/**
* __drm_atomic_helper_connector_destroy_state - release connector state
* @state: connector state object to release
*
* Releases all resources stored in the connector state without actually
* freeing the memory of the connector state. This is useful for drivers that
* subclass the connector state.
*/
void
__drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
{
if (state->crtc)
drm_connector_put(state->connector);
if (state->commit)
drm_crtc_commit_put(state->commit);
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
/**
* drm_atomic_helper_connector_destroy_state - default state destroy hook
* @connector: drm connector
* @state: connector state object to release
*
* Default connector state destroy hook for drivers which don't have their own
* subclassed connector state structure.
*/
void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state)
{
__drm_atomic_helper_connector_destroy_state(state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
/**
* drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table
* @crtc: CRTC object
* @red: red correction table
* @green: green correction table
* @blue: green correction table
* @size: size of the tables
* @ctx: lock acquire context
*
* Implements support for legacy gamma correction table for drivers
* that support color management through the DEGAMMA_LUT/GAMMA_LUT
* properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
* how the atomic color management and gamma tables work.
*/
int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
u16 *red, u16 *green, u16 *blue,
uint32_t size,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_device *dev = crtc->dev;
struct drm_atomic_state *state;
struct drm_crtc_state *crtc_state;
struct drm_property_blob *blob = NULL;
struct drm_color_lut *blob_data;
int i, ret = 0;
bool replaced;
state = drm_atomic_state_alloc(crtc->dev);
if (!state)
return -ENOMEM;
blob = drm_property_create_blob(dev,
sizeof(struct drm_color_lut) * size,
NULL);
if (IS_ERR(blob)) {
ret = PTR_ERR(blob);
blob = NULL;
goto fail;
}
/* Prepare GAMMA_LUT with the legacy values. */
blob_data = blob->data;
for (i = 0; i < size; i++) {
blob_data[i].red = red[i];
blob_data[i].green = green[i];
blob_data[i].blue = blue[i];
}
state->acquire_ctx = ctx;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state)) {
ret = PTR_ERR(crtc_state);
goto fail;
}
/* Reset DEGAMMA_LUT and CTM properties. */
replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL);
replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob);
crtc_state->color_mgmt_changed |= replaced;
ret = drm_atomic_commit(state);
fail:
drm_atomic_state_put(state);
drm_property_blob_put(blob);
return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set);
/**
* __drm_atomic_helper_private_duplicate_state - copy atomic private state
* @obj: CRTC object
* @state: new private object state
*
* Copies atomic state from a private objects's current state and resets inferred values.
* This is useful for drivers that subclass the private state.
*/
void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj,
struct drm_private_state *state)
{
memcpy(state, obj->state, sizeof(*state));
}
EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state);

View File

@ -36,6 +36,8 @@
#include <drm/drmP.h>
#include "drm_legacy.h"
#include <linux/nospec.h>
static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
struct drm_local_map *map)
{
@ -1417,6 +1419,7 @@ int drm_legacy_freebufs(struct drm_device *dev, void *data,
idx, dma->buf_count - 1);
return -EINVAL;
}
idx = array_index_nospec(idx, dma->buf_count);
buf = dma->buflist[idx];
if (buf->file_priv != file_priv) {
DRM_ERROR("Process %d freeing buffer not owned\n",

View File

@ -260,9 +260,7 @@ int drm_connector_init(struct drm_device *dev,
if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL &&
connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
drm_object_attach_property(&connector->base,
config->edid_property,
0);
drm_connector_attach_edid_property(connector);
drm_object_attach_property(&connector->base,
config->dpms_property, 0);
@ -294,6 +292,24 @@ out_put:
}
EXPORT_SYMBOL(drm_connector_init);
/**
* drm_connector_attach_edid_property - attach edid property.
* @connector: the connector
*
* Some connector types like DRM_MODE_CONNECTOR_VIRTUAL do not get a
* edid property attached by default. This function can be used to
* explicitly enable the edid property in these cases.
*/
void drm_connector_attach_edid_property(struct drm_connector *connector)
{
struct drm_mode_config *config = &connector->dev->mode_config;
drm_object_attach_property(&connector->base,
config->edid_property,
0);
}
EXPORT_SYMBOL(drm_connector_attach_edid_property);
/**
* drm_connector_attach_encoder - attach a connector to an encoder
* @connector: connector to attach

View File

@ -984,118 +984,3 @@ void drm_helper_resume_force_mode(struct drm_device *dev)
drm_modeset_unlock_all(dev);
}
EXPORT_SYMBOL(drm_helper_resume_force_mode);
/**
* drm_helper_crtc_mode_set - mode_set implementation for atomic plane helpers
* @crtc: DRM CRTC
* @mode: DRM display mode which userspace requested
* @adjusted_mode: DRM display mode adjusted by ->mode_fixup callbacks
* @x: x offset of the CRTC scanout area on the underlying framebuffer
* @y: y offset of the CRTC scanout area on the underlying framebuffer
* @old_fb: previous framebuffer
*
* This function implements a callback useable as the ->mode_set callback
* required by the CRTC helpers. Besides the atomic plane helper functions for
* the primary plane the driver must also provide the ->mode_set_nofb callback
* to set up the CRTC.
*
* This is a transitional helper useful for converting drivers to the atomic
* interfaces.
*/
int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb)
{
struct drm_crtc_state *crtc_state;
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
int ret;
if (crtc->funcs->atomic_duplicate_state)
crtc_state = crtc->funcs->atomic_duplicate_state(crtc);
else {
if (!crtc->state)
drm_atomic_helper_crtc_reset(crtc);
crtc_state = drm_atomic_helper_crtc_duplicate_state(crtc);
}
if (!crtc_state)
return -ENOMEM;
crtc_state->planes_changed = true;
crtc_state->mode_changed = true;
ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
if (ret)
goto out;
drm_mode_copy(&crtc_state->adjusted_mode, adjusted_mode);
if (crtc_funcs->atomic_check) {
ret = crtc_funcs->atomic_check(crtc, crtc_state);
if (ret)
goto out;
}
swap(crtc->state, crtc_state);
crtc_funcs->mode_set_nofb(crtc);
ret = drm_helper_crtc_mode_set_base(crtc, x, y, old_fb);
out:
if (crtc_state) {
if (crtc->funcs->atomic_destroy_state)
crtc->funcs->atomic_destroy_state(crtc, crtc_state);
else
drm_atomic_helper_crtc_destroy_state(crtc, crtc_state);
}
return ret;
}
EXPORT_SYMBOL(drm_helper_crtc_mode_set);
/**
* drm_helper_crtc_mode_set_base - mode_set_base implementation for atomic plane helpers
* @crtc: DRM CRTC
* @x: x offset of the CRTC scanout area on the underlying framebuffer
* @y: y offset of the CRTC scanout area on the underlying framebuffer
* @old_fb: previous framebuffer
*
* This function implements a callback useable as the ->mode_set_base used
* required by the CRTC helpers. The driver must provide the atomic plane helper
* functions for the primary plane.
*
* This is a transitional helper useful for converting drivers to the atomic
* interfaces.
*/
int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
struct drm_plane_state *plane_state;
struct drm_plane *plane = crtc->primary;
if (plane->funcs->atomic_duplicate_state)
plane_state = plane->funcs->atomic_duplicate_state(plane);
else {
if (!plane->state)
drm_atomic_helper_plane_reset(plane);
plane_state = drm_atomic_helper_plane_duplicate_state(plane);
}
if (!plane_state)
return -ENOMEM;
plane_state->plane = plane;
plane_state->crtc = crtc;
drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb);
plane_state->crtc_x = 0;
plane_state->crtc_y = 0;
plane_state->crtc_h = crtc->mode.vdisplay;
plane_state->crtc_w = crtc->mode.hdisplay;
plane_state->src_x = x << 16;
plane_state->src_y = y << 16;
plane_state->src_h = crtc->mode.vdisplay << 16;
plane_state->src_w = crtc->mode.hdisplay << 16;
return drm_plane_helper_commit(plane, plane_state, old_fb);
}
EXPORT_SYMBOL(drm_helper_crtc_mode_set_base);

View File

@ -424,8 +424,6 @@ void drm_dp_cec_register_connector(struct drm_dp_aux *aux, const char *name,
aux->cec.parent = parent;
INIT_DELAYED_WORK(&aux->cec.unregister_work,
drm_dp_cec_unregister_work);
drm_dp_cec_set_edid(aux, NULL);
}
EXPORT_SYMBOL(drm_dp_cec_register_connector);

View File

@ -2572,9 +2572,16 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_
EXPORT_SYMBOL(drm_dp_mst_get_edid);
/**
* drm_dp_find_vcpi_slots() - find slots for this PBN value
* drm_dp_find_vcpi_slots() - Find VCPI slots for this PBN value
* @mgr: manager to use
* @pbn: payload bandwidth to convert into slots.
*
* Calculate the number of VCPI slots that will be required for the given PBN
* value. This function is deprecated, and should not be used in atomic
* drivers.
*
* RETURNS:
* The total slots required for this port, or error.
*/
int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
int pbn)

View File

@ -476,8 +476,6 @@ static void drm_fs_inode_free(struct inode *inode)
* The initial ref-count of the object is 1. Use drm_dev_get() and
* drm_dev_put() to take and drop further ref-counts.
*
* Note that for purely virtual devices @parent can be NULL.
*
* Drivers that do not want to allocate their own device struct
* embedding &struct drm_device can call drm_dev_alloc() instead. For drivers
* that do embed &struct drm_device it must be placed first in the overall
@ -502,6 +500,8 @@ int drm_dev_init(struct drm_device *dev,
return -ENODEV;
}
BUG_ON(!parent);
kref_init(&dev->ref);
dev->dev = parent;
dev->driver = driver;
@ -556,9 +556,7 @@ int drm_dev_init(struct drm_device *dev,
}
}
/* Use the parent device name as DRM device unique identifier, but fall
* back to the driver name for virtual devices like vgem. */
ret = drm_dev_set_unique(dev, parent ? dev_name(parent) : driver->name);
ret = drm_dev_set_unique(dev, dev_name(parent));
if (ret)
goto err_setunique;

View File

@ -72,7 +72,9 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
/**
* drm_fb_cma_get_gem_addr() - Get physical address for framebuffer
* drm_fb_cma_get_gem_addr() - Get physical address for framebuffer, for pixel
* formats where values are grouped in blocks this will get you the beginning of
* the block
* @fb: The framebuffer
* @state: Which state of drm plane
* @plane: Which plane
@ -87,6 +89,13 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
struct drm_gem_cma_object *obj;
dma_addr_t paddr;
u8 h_div = 1, v_div = 1;
u32 block_w = drm_format_info_block_width(fb->format, plane);
u32 block_h = drm_format_info_block_height(fb->format, plane);
u32 block_size = fb->format->char_per_block[plane];
u32 sample_x;
u32 sample_y;
u32 block_start_y;
u32 num_hblocks;
obj = drm_fb_cma_get_gem_obj(fb, plane);
if (!obj)
@ -99,8 +108,13 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
v_div = fb->format->vsub;
}
paddr += (fb->format->cpp[plane] * (state->src_x >> 16)) / h_div;
paddr += (fb->pitches[plane] * (state->src_y >> 16)) / v_div;
sample_x = (state->src_x >> 16) / h_div;
sample_y = (state->src_y >> 16) / v_div;
block_start_y = (sample_y / block_h) * block_h;
num_hblocks = sample_x / block_w;
paddr += fb->pitches[plane] * block_start_y;
paddr += block_size * num_hblocks;
return paddr;
}
@ -124,10 +138,7 @@ int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp,
/* dev->fb_helper will indirectly point to fbdev_cma after this call */
fbdev_cma = drm_fbdev_cma_init(dev, preferred_bpp, max_conn_count);
if (IS_ERR(fbdev_cma))
return PTR_ERR(fbdev_cma);
return 0;
return PTR_ERR_OR_ZERO(fbdev_cma);
}
EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init);
@ -226,21 +237,3 @@ void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma)
drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper);
}
EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event);
/**
* drm_fbdev_cma_set_suspend_unlocked - wrapper around
* drm_fb_helper_set_suspend_unlocked
* @fbdev_cma: The drm_fbdev_cma struct, may be NULL
* @state: desired state, zero to resume, non-zero to suspend
*
* Calls drm_fb_helper_set_suspend, which is a wrapper around
* fb_set_suspend implemented by fbdev core.
*/
void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma *fbdev_cma,
bool state)
{
if (fbdev_cma)
drm_fb_helper_set_suspend_unlocked(&fbdev_cma->fb_helper,
state);
}
EXPORT_SYMBOL(drm_fbdev_cma_set_suspend_unlocked);

View File

@ -1632,6 +1632,10 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
if (var->pixclock != 0 || in_dbg_master())
return -EINVAL;
if ((drm_format_info_block_width(fb->format, 0) > 1) ||
(drm_format_info_block_height(fb->format, 0) > 1))
return -EINVAL;
/*
* Changes struct fb_var_screeninfo are currently not pushed back
* to KMS, hence fail if different settings are requested.
@ -1949,6 +1953,8 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe
{
struct drm_framebuffer *fb = fb_helper->fb;
WARN_ON((drm_format_info_block_width(fb->format, 0) > 1) ||
(drm_format_info_block_height(fb->format, 0) > 1));
info->pseudo_palette = fb_helper->pseudo_palette;
info->var.xres_virtual = fb->width;
info->var.yres_virtual = fb->height;

View File

@ -103,8 +103,8 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format);
*
* Computes a drm fourcc pixel format code for the given @bpp/@depth values.
* Unlike drm_mode_legacy_fb_format() this looks at the drivers mode_config,
* and depending on the quirk_addfb_prefer_host_byte_order flag it returns
* little endian byte order or host byte order framebuffer formats.
* and depending on the &drm_mode_config.quirk_addfb_prefer_host_byte_order flag
* it returns little endian byte order or host byte order framebuffer formats.
*/
uint32_t drm_driver_legacy_fb_format(struct drm_device *dev,
uint32_t bpp, uint32_t depth)
@ -225,6 +225,18 @@ const struct drm_format_info *__drm_format_info(u32 format)
{ .format = DRM_FORMAT_UYVY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
{ .format = DRM_FORMAT_Y0L0, .depth = 0, .num_planes = 1,
.char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 },
.hsub = 2, .vsub = 2, .has_alpha = true, .is_yuv = true },
{ .format = DRM_FORMAT_X0L0, .depth = 0, .num_planes = 1,
.char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 },
.hsub = 2, .vsub = 2, .is_yuv = true },
{ .format = DRM_FORMAT_Y0L2, .depth = 0, .num_planes = 1,
.char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 },
.hsub = 2, .vsub = 2, .has_alpha = true, .is_yuv = true },
{ .format = DRM_FORMAT_X0L2, .depth = 0, .num_planes = 1,
.char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 },
.hsub = 2, .vsub = 2, .is_yuv = true },
};
unsigned int i;
@ -400,3 +412,65 @@ int drm_format_plane_height(int height, uint32_t format, int plane)
return height / info->vsub;
}
EXPORT_SYMBOL(drm_format_plane_height);
/**
* drm_format_info_block_width - width in pixels of block.
* @info: pixel format info
* @plane: plane index
*
* Returns:
* The width in pixels of a block, depending on the plane index.
*/
unsigned int drm_format_info_block_width(const struct drm_format_info *info,
int plane)
{
if (!info || plane < 0 || plane >= info->num_planes)
return 0;
if (!info->block_w[plane])
return 1;
return info->block_w[plane];
}
EXPORT_SYMBOL(drm_format_info_block_width);
/**
* drm_format_info_block_height - height in pixels of a block
* @info: pixel format info
* @plane: plane index
*
* Returns:
* The height in pixels of a block, depending on the plane index.
*/
unsigned int drm_format_info_block_height(const struct drm_format_info *info,
int plane)
{
if (!info || plane < 0 || plane >= info->num_planes)
return 0;
if (!info->block_h[plane])
return 1;
return info->block_h[plane];
}
EXPORT_SYMBOL(drm_format_info_block_height);
/**
* drm_format_info_min_pitch - computes the minimum required pitch in bytes
* @info: pixel format info
* @plane: plane index
* @buffer_width: buffer width in pixels
*
* Returns:
* The minimum required pitch in bytes for a buffer by taking into consideration
* the pixel format information and the buffer width.
*/
uint64_t drm_format_info_min_pitch(const struct drm_format_info *info,
int plane, unsigned int buffer_width)
{
if (!info || plane < 0 || plane >= info->num_planes)
return 0;
return DIV_ROUND_UP_ULL((u64)buffer_width * info->char_per_block[plane],
drm_format_info_block_width(info, plane) *
drm_format_info_block_height(info, plane));
}
EXPORT_SYMBOL(drm_format_info_min_pitch);

View File

@ -195,20 +195,26 @@ static int framebuffer_check(struct drm_device *dev,
for (i = 0; i < info->num_planes; i++) {
unsigned int width = fb_plane_width(r->width, info, i);
unsigned int height = fb_plane_height(r->height, info, i);
unsigned int cpp = info->cpp[i];
unsigned int block_size = info->char_per_block[i];
u64 min_pitch = drm_format_info_min_pitch(info, i, width);
if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) {
DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i);
return -EINVAL;
}
if (!r->handles[i]) {
DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
return -EINVAL;
}
if ((uint64_t) width * cpp > UINT_MAX)
if (min_pitch > UINT_MAX)
return -ERANGE;
if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
return -ERANGE;
if (r->pitches[i] < width * cpp) {
if (block_size && r->pitches[i] < min_pitch) {
DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
return -EINVAL;
}
@ -317,6 +323,7 @@ drm_internal_framebuffer_create(struct drm_device *dev,
return fb;
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create);
/**
* drm_mode_addfb2 - add an FB to the graphics configuration

View File

@ -171,7 +171,7 @@ drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
}
min_size = (height - 1) * mode_cmd->pitches[i]
+ width * info->cpp[i]
+ drm_format_info_min_pitch(info, i, width)
+ mode_cmd->offsets[i];
if (objs[i]->size < min_size) {

View File

@ -39,7 +39,6 @@ struct drm_master *drm_lease_owner(struct drm_master *master)
master = master->lessor;
return master;
}
EXPORT_SYMBOL(drm_lease_owner);
/**
* _drm_find_lessee - find lessee by id (idr_mutex held)
@ -117,7 +116,6 @@ bool _drm_lease_held(struct drm_file *file_priv, int id)
return _drm_lease_held_master(file_priv->master, id);
}
EXPORT_SYMBOL(_drm_lease_held);
/**
* drm_lease_held - check drm_mode_object lease status (idr_mutex not held)
@ -144,7 +142,6 @@ bool drm_lease_held(struct drm_file *file_priv, int id)
mutex_unlock(&master->dev->mode_config.idr_mutex);
return ret;
}
EXPORT_SYMBOL(drm_lease_held);
/**
* drm_lease_filter_crtcs - restricted crtc set to leased values (idr_mutex not held)
@ -184,7 +181,6 @@ uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs_in)
mutex_unlock(&master->dev->mode_config.idr_mutex);
return crtcs_out;
}
EXPORT_SYMBOL(drm_lease_filter_crtcs);
/*
* drm_lease_create - create a new drm_master with leased objects (idr_mutex not held)
@ -195,7 +191,7 @@ EXPORT_SYMBOL(drm_lease_filter_crtcs);
* make sure all of the desired objects can be leased, atomically
* leasing them to the new drmmaster.
*
* ERR_PTR(-EACCESS) some other master holds the title to any object
* ERR_PTR(-EACCES) some other master holds the title to any object
* ERR_PTR(-ENOENT) some object is not a valid DRM object for this device
* ERR_PTR(-EBUSY) some other lessee holds title to this object
* ERR_PTR(-EEXIST) same object specified more than once in the provided list
@ -357,9 +353,9 @@ void drm_lease_revoke(struct drm_master *top)
}
static int validate_lease(struct drm_device *dev,
struct drm_file *lessor_priv,
int object_count,
struct drm_mode_object **objects)
struct drm_mode_object **objects,
bool universal_planes)
{
int o;
int has_crtc = -1;
@ -376,14 +372,14 @@ static int validate_lease(struct drm_device *dev,
if (objects[o]->type == DRM_MODE_OBJECT_CONNECTOR && has_connector == -1)
has_connector = o;
if (lessor_priv->universal_planes) {
if (universal_planes) {
if (objects[o]->type == DRM_MODE_OBJECT_PLANE && has_plane == -1)
has_plane = o;
}
}
if (has_crtc == -1 || has_connector == -1)
return -EINVAL;
if (lessor_priv->universal_planes && has_plane == -1)
if (universal_planes && has_plane == -1)
return -EINVAL;
return 0;
}
@ -397,6 +393,8 @@ static int fill_object_idr(struct drm_device *dev,
struct drm_mode_object **objects;
u32 o;
int ret;
bool universal_planes = READ_ONCE(lessor_priv->universal_planes);
objects = kcalloc(object_count, sizeof(struct drm_mode_object *),
GFP_KERNEL);
if (!objects)
@ -419,14 +417,17 @@ static int fill_object_idr(struct drm_device *dev,
}
if (!drm_mode_object_lease_required(objects[o]->type)) {
DRM_DEBUG_KMS("invalid object for lease\n");
ret = -EINVAL;
goto out_free_objects;
}
}
ret = validate_lease(dev, lessor_priv, object_count, objects);
if (ret)
ret = validate_lease(dev, object_count, objects, universal_planes);
if (ret) {
DRM_DEBUG_LEASE("lease validation failed\n");
goto out_free_objects;
}
/* add their IDs to the lease request - taking into account
universal planes */
@ -449,7 +450,7 @@ static int fill_object_idr(struct drm_device *dev,
object_id, ret);
goto out_free_objects;
}
if (obj->type == DRM_MODE_OBJECT_CRTC && !lessor_priv->universal_planes) {
if (obj->type == DRM_MODE_OBJECT_CRTC && !universal_planes) {
struct drm_crtc *crtc = obj_to_crtc(obj);
ret = idr_alloc(leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL);
if (ret < 0) {
@ -509,15 +510,21 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
return -EOPNOTSUPP;
/* Do not allow sub-leases */
if (lessor->lessor)
if (lessor->lessor) {
DRM_DEBUG_LEASE("recursive leasing not allowed\n");
return -EINVAL;
}
/* need some objects */
if (cl->object_count == 0)
if (cl->object_count == 0) {
DRM_DEBUG_LEASE("no objects in lease\n");
return -EINVAL;
}
if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK)))
if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) {
DRM_DEBUG_LEASE("invalid flags\n");
return -EINVAL;
}
object_count = cl->object_count;
@ -532,6 +539,7 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
object_count, object_ids);
kfree(object_ids);
if (ret) {
DRM_DEBUG_LEASE("lease object lookup failed: %i\n", ret);
idr_destroy(&leases);
return ret;
}

View File

@ -51,7 +51,7 @@
#endif
static void *agp_remap(unsigned long offset, unsigned long size,
struct drm_device * dev)
struct drm_device *dev)
{
unsigned long i, num_pages =
PAGE_ALIGN(size) / PAGE_SIZE;
@ -94,26 +94,26 @@ static void *agp_remap(unsigned long offset, unsigned long size,
}
/** Wrapper around agp_free_memory() */
void drm_free_agp(struct agp_memory * handle, int pages)
void drm_free_agp(struct agp_memory *handle, int pages)
{
agp_free_memory(handle);
}
/** Wrapper around agp_bind_memory() */
int drm_bind_agp(struct agp_memory * handle, unsigned int start)
int drm_bind_agp(struct agp_memory *handle, unsigned int start)
{
return agp_bind_memory(handle, start);
}
/** Wrapper around agp_unbind_memory() */
int drm_unbind_agp(struct agp_memory * handle)
int drm_unbind_agp(struct agp_memory *handle)
{
return agp_unbind_memory(handle);
}
#else /* CONFIG_AGP */
static inline void *agp_remap(unsigned long offset, unsigned long size,
struct drm_device * dev)
struct drm_device *dev)
{
return NULL;
}

View File

@ -38,7 +38,8 @@ int __drm_mode_object_add(struct drm_device *dev, struct drm_mode_object *obj,
int ret;
mutex_lock(&dev->mode_config.idr_mutex);
ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL);
ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL,
1, 0, GFP_KERNEL);
if (ret >= 0) {
/*
* Set up the object linking under the protection of the idr

View File

@ -716,8 +716,8 @@ int of_get_drm_display_mode(struct device_node *np,
if (bus_flags)
drm_bus_flags_from_videomode(&vm, bus_flags);
pr_debug("%pOF: got %dx%d display mode from %s\n",
np, vm.hactive, vm.vactive, np->name);
pr_debug("%pOF: got %dx%d display mode\n",
np, vm.hactive, vm.vactive);
drm_mode_debug_printmodeline(dmode);
return 0;

View File

@ -146,6 +146,21 @@ static struct drm_plane *create_primary_plane(struct drm_device *dev)
* Initialize a CRTC object with a default helper-provided primary plane and no
* cursor plane.
*
* Note that we make some assumptions about hardware limitations that may not be
* true for all hardware:
*
* 1. Primary plane cannot be repositioned.
* 2. Primary plane cannot be scaled.
* 3. Primary plane must cover the entire CRTC.
* 4. Subpixel positioning is not supported.
* 5. The primary plane must always be on if the CRTC is enabled.
*
* This is purely a backwards compatibility helper for old drivers. Drivers
* should instead implement their own primary plane. Atomic drivers must do so.
* Drivers with the above hardware restriction can look into using &struct
* drm_simple_display_pipe, which encapsulates the above limitations into a nice
* interface.
*
* Returns:
* Zero on success, error code on failure.
*/

View File

@ -59,6 +59,14 @@ static const struct drm_dmi_panel_orientation_data gpd_win = {
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
};
static const struct drm_dmi_panel_orientation_data gpd_win2 = {
.width = 720,
.height = 1280,
.bios_dates = (const char * const []){
"12/07/2017", "05/24/2018", NULL },
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
};
static const struct drm_dmi_panel_orientation_data itworks_tw891 = {
.width = 800,
.height = 1280,
@ -106,6 +114,14 @@ static const struct dmi_system_id orientation_data[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
},
.driver_data = (void *)&gpd_win,
}, { /* GPD Win 2 (too generic strings, also match on bios date) */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
},
.driver_data = (void *)&gpd_win2,
}, { /* I.T.Works TW891 */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),

View File

@ -61,15 +61,14 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali
return NULL;
dmah->size = size;
dmah->vaddr = dma_alloc_coherent(&dev->pdev->dev, size, &dmah->busaddr, GFP_KERNEL | __GFP_COMP);
dmah->vaddr = dma_zalloc_coherent(&dev->pdev->dev, size, &dmah->busaddr,
GFP_KERNEL | __GFP_COMP);
if (dmah->vaddr == NULL) {
kfree(dmah);
return NULL;
}
memset(dmah->vaddr, 0, size);
/* XXX - Is virt_to_page() legal for consistent mem? */
/* Reserve */
for (addr = (unsigned long)dmah->vaddr, sz = size;

View File

@ -636,6 +636,29 @@ static int __setplane_check(struct drm_plane *plane,
return 0;
}
/**
* drm_any_plane_has_format - Check whether any plane supports this format and modifier combination
* @dev: DRM device
* @format: pixel format (DRM_FORMAT_*)
* @modifier: data layout modifier
*
* Returns:
* Whether at least one plane supports the specified format and modifier combination.
*/
bool drm_any_plane_has_format(struct drm_device *dev,
u32 format, u64 modifier)
{
struct drm_plane *plane;
drm_for_each_plane(plane, dev) {
if (drm_plane_check_pixel_format(plane, format, modifier) == 0)
return true;
}
return false;
}
EXPORT_SYMBOL(drm_any_plane_has_format);
/*
* __setplane_internal - setplane handler for internal callers
*

View File

@ -42,11 +42,8 @@
* primary plane support on top of the normal CRTC configuration interface.
* Since the legacy &drm_mode_config_funcs.set_config interface ties the primary
* plane together with the CRTC state this does not allow userspace to disable
* the primary plane itself. To avoid too much duplicated code use
* drm_plane_helper_check_update() which can be used to enforce the same
* restrictions as primary planes had thus. The default primary plane only
* expose XRBG8888 and ARGB8888 as valid pixel formats for the attached
* framebuffer.
* the primary plane itself. The default primary plane only expose XRBG8888 and
* ARGB8888 as valid pixel formats for the attached framebuffer.
*
* Drivers are highly recommended to implement proper support for primary
* planes, and newly merged drivers must not rely upon these transitional
@ -100,43 +97,17 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
return count;
}
/**
* drm_plane_helper_check_update() - Check plane update for validity
* @plane: plane object to update
* @crtc: owning CRTC of owning plane
* @fb: framebuffer to flip onto plane
* @src: source coordinates in 16.16 fixed point
* @dst: integer destination coordinates
* @rotation: plane rotation
* @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
* @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
* @can_position: is it legal to position the plane such that it
* doesn't cover the entire crtc? This will generally
* only be false for primary planes.
* @can_update_disabled: can the plane be updated while the crtc
* is disabled?
* @visible: output parameter indicating whether plane is still visible after
* clipping
*
* Checks that a desired plane update is valid. Drivers that provide
* their own plane handling rather than helper-provided implementations may
* still wish to call this function to avoid duplication of error checking
* code.
*
* RETURNS:
* Zero if update appears valid, error code on failure
*/
int drm_plane_helper_check_update(struct drm_plane *plane,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_rect *src,
struct drm_rect *dst,
unsigned int rotation,
int min_scale,
int max_scale,
bool can_position,
bool can_update_disabled,
bool *visible)
static int drm_plane_helper_check_update(struct drm_plane *plane,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_rect *src,
struct drm_rect *dst,
unsigned int rotation,
int min_scale,
int max_scale,
bool can_position,
bool can_update_disabled,
bool *visible)
{
struct drm_plane_state plane_state = {
.plane = plane,
@ -173,52 +144,14 @@ int drm_plane_helper_check_update(struct drm_plane *plane,
return 0;
}
EXPORT_SYMBOL(drm_plane_helper_check_update);
/**
* drm_primary_helper_update() - Helper for primary plane update
* @plane: plane object to update
* @crtc: owning CRTC of owning plane
* @fb: framebuffer to flip onto plane
* @crtc_x: x offset of primary plane on crtc
* @crtc_y: y offset of primary plane on crtc
* @crtc_w: width of primary plane rectangle on crtc
* @crtc_h: height of primary plane rectangle on crtc
* @src_x: x offset of @fb for panning
* @src_y: y offset of @fb for panning
* @src_w: width of source rectangle in @fb
* @src_h: height of source rectangle in @fb
* @ctx: lock acquire context, not used here
*
* Provides a default plane update handler for primary planes. This is handler
* is called in response to a userspace SetPlane operation on the plane with a
* non-NULL framebuffer. We call the driver's modeset handler to update the
* framebuffer.
*
* SetPlane() on a primary plane of a disabled CRTC is not supported, and will
* return an error.
*
* Note that we make some assumptions about hardware limitations that may not be
* true for all hardware --
*
* 1. Primary plane cannot be repositioned.
* 2. Primary plane cannot be scaled.
* 3. Primary plane must cover the entire CRTC.
* 4. Subpixel positioning is not supported.
*
* Drivers for hardware that don't have these restrictions can provide their
* own implementation rather than using this helper.
*
* RETURNS:
* Zero on success, error code on failure
*/
int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h,
struct drm_modeset_acquire_ctx *ctx)
static int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_mode_set set = {
.crtc = crtc,
@ -285,35 +218,12 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
kfree(connector_list);
return ret;
}
EXPORT_SYMBOL(drm_primary_helper_update);
/**
* drm_primary_helper_disable() - Helper for primary plane disable
* @plane: plane to disable
* @ctx: lock acquire context, not used here
*
* Provides a default plane disable handler for primary planes. This is handler
* is called in response to a userspace SetPlane operation on the plane with a
* NULL framebuffer parameter. It unconditionally fails the disable call with
* -EINVAL the only way to disable the primary plane without driver support is
* to disable the entire CRTC. Which does not match the plane
* &drm_plane_funcs.disable_plane hook.
*
* Note that some hardware may be able to disable the primary plane without
* disabling the whole CRTC. Drivers for such hardware should provide their
* own disable handler that disables just the primary plane (and they'll likely
* need to provide their own update handler as well to properly re-enable a
* disabled primary plane).
*
* RETURNS:
* Unconditionally returns -EINVAL.
*/
int drm_primary_helper_disable(struct drm_plane *plane,
struct drm_modeset_acquire_ctx *ctx)
static int drm_primary_helper_disable(struct drm_plane *plane,
struct drm_modeset_acquire_ctx *ctx)
{
return -EINVAL;
}
EXPORT_SYMBOL(drm_primary_helper_disable);
/**
* drm_primary_helper_destroy() - Helper for primary plane destruction
@ -336,200 +246,3 @@ const struct drm_plane_funcs drm_primary_helper_funcs = {
.destroy = drm_primary_helper_destroy,
};
EXPORT_SYMBOL(drm_primary_helper_funcs);
int drm_plane_helper_commit(struct drm_plane *plane,
struct drm_plane_state *plane_state,
struct drm_framebuffer *old_fb)
{
const struct drm_plane_helper_funcs *plane_funcs;
struct drm_crtc *crtc[2];
const struct drm_crtc_helper_funcs *crtc_funcs[2];
int i, ret = 0;
plane_funcs = plane->helper_private;
/* Since this is a transitional helper we can't assume that plane->state
* is always valid. Hence we need to use plane->crtc instead of
* plane->state->crtc as the old crtc. */
crtc[0] = plane->crtc;
crtc[1] = crtc[0] != plane_state->crtc ? plane_state->crtc : NULL;
for (i = 0; i < 2; i++)
crtc_funcs[i] = crtc[i] ? crtc[i]->helper_private : NULL;
if (plane_funcs->atomic_check) {
ret = plane_funcs->atomic_check(plane, plane_state);
if (ret)
goto out;
}
if (plane_funcs->prepare_fb && plane_state->fb != old_fb) {
ret = plane_funcs->prepare_fb(plane,
plane_state);
if (ret)
goto out;
}
/* Point of no return, commit sw state. */
swap(plane->state, plane_state);
for (i = 0; i < 2; i++) {
if (crtc_funcs[i] && crtc_funcs[i]->atomic_begin)
crtc_funcs[i]->atomic_begin(crtc[i], crtc[i]->state);
}
/*
* Drivers may optionally implement the ->atomic_disable callback, so
* special-case that here.
*/
if (drm_atomic_plane_disabling(plane_state, plane->state) &&
plane_funcs->atomic_disable)
plane_funcs->atomic_disable(plane, plane_state);
else
plane_funcs->atomic_update(plane, plane_state);
for (i = 0; i < 2; i++) {
if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
crtc_funcs[i]->atomic_flush(crtc[i], crtc[i]->state);
}
/*
* If we only moved the plane and didn't change fb's, there's no need to
* wait for vblank.
*/
if (plane->state->fb == old_fb)
goto out;
for (i = 0; i < 2; i++) {
if (!crtc[i])
continue;
if (crtc[i]->cursor == plane)
continue;
/* There's no other way to figure out whether the crtc is running. */
ret = drm_crtc_vblank_get(crtc[i]);
if (ret == 0) {
drm_crtc_wait_one_vblank(crtc[i]);
drm_crtc_vblank_put(crtc[i]);
}
ret = 0;
}
if (plane_funcs->cleanup_fb)
plane_funcs->cleanup_fb(plane, plane_state);
out:
if (plane->funcs->atomic_destroy_state)
plane->funcs->atomic_destroy_state(plane, plane_state);
else
drm_atomic_helper_plane_destroy_state(plane, plane_state);
return ret;
}
/**
* drm_plane_helper_update() - Transitional helper for plane update
* @plane: plane object to update
* @crtc: owning CRTC of owning plane
* @fb: framebuffer to flip onto plane
* @crtc_x: x offset of primary plane on crtc
* @crtc_y: y offset of primary plane on crtc
* @crtc_w: width of primary plane rectangle on crtc
* @crtc_h: height of primary plane rectangle on crtc
* @src_x: x offset of @fb for panning
* @src_y: y offset of @fb for panning
* @src_w: width of source rectangle in @fb
* @src_h: height of source rectangle in @fb
* @ctx: lock acquire context, not used here
*
* Provides a default plane update handler using the atomic plane update
* functions. It is fully left to the driver to check plane constraints and
* handle corner-cases like a fully occluded or otherwise invisible plane.
*
* This is useful for piecewise transitioning of a driver to the atomic helpers.
*
* RETURNS:
* Zero on success, error code on failure
*/
int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_plane_state *plane_state;
if (plane->funcs->atomic_duplicate_state)
plane_state = plane->funcs->atomic_duplicate_state(plane);
else {
if (!plane->state)
drm_atomic_helper_plane_reset(plane);
plane_state = drm_atomic_helper_plane_duplicate_state(plane);
}
if (!plane_state)
return -ENOMEM;
plane_state->plane = plane;
plane_state->crtc = crtc;
drm_atomic_set_fb_for_plane(plane_state, fb);
plane_state->crtc_x = crtc_x;
plane_state->crtc_y = crtc_y;
plane_state->crtc_h = crtc_h;
plane_state->crtc_w = crtc_w;
plane_state->src_x = src_x;
plane_state->src_y = src_y;
plane_state->src_h = src_h;
plane_state->src_w = src_w;
return drm_plane_helper_commit(plane, plane_state, plane->fb);
}
EXPORT_SYMBOL(drm_plane_helper_update);
/**
* drm_plane_helper_disable() - Transitional helper for plane disable
* @plane: plane to disable
* @ctx: lock acquire context, not used here
*
* Provides a default plane disable handler using the atomic plane update
* functions. It is fully left to the driver to check plane constraints and
* handle corner-cases like a fully occluded or otherwise invisible plane.
*
* This is useful for piecewise transitioning of a driver to the atomic helpers.
*
* RETURNS:
* Zero on success, error code on failure
*/
int drm_plane_helper_disable(struct drm_plane *plane,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_plane_state *plane_state;
struct drm_framebuffer *old_fb;
/* crtc helpers love to call disable functions for already disabled hw
* functions. So cope with that. */
if (!plane->crtc)
return 0;
if (plane->funcs->atomic_duplicate_state)
plane_state = plane->funcs->atomic_duplicate_state(plane);
else {
if (!plane->state)
drm_atomic_helper_plane_reset(plane);
plane_state = drm_atomic_helper_plane_duplicate_state(plane);
}
if (!plane_state)
return -ENOMEM;
plane_state->plane = plane;
plane_state->crtc = NULL;
old_fb = plane_state->fb;
drm_atomic_set_fb_for_plane(plane_state, NULL);
return drm_plane_helper_commit(plane, plane_state, old_fb);
}
EXPORT_SYMBOL(drm_plane_helper_disable);

View File

@ -433,34 +433,6 @@ void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
}
EXPORT_SYMBOL(drm_gem_dmabuf_vunmap);
/**
* 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.
*/
void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num)
{
return NULL;
}
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.
*/
void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_num,
void *addr)
{
}
EXPORT_SYMBOL(drm_gem_dmabuf_kunmap);
/**
* drm_gem_dmabuf_mmap - dma_buf mmap implementation for GEM
* @dma_buf: buffer to be mapped
@ -489,8 +461,6 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
.map_dma_buf = drm_gem_map_dma_buf,
.unmap_dma_buf = drm_gem_unmap_dma_buf,
.release = drm_gem_dmabuf_release,
.map = drm_gem_dmabuf_kmap,
.unmap = drm_gem_dmabuf_kunmap,
.mmap = drm_gem_dmabuf_mmap,
.vmap = drm_gem_dmabuf_vmap,
.vunmap = drm_gem_dmabuf_vunmap,

View File

@ -190,6 +190,13 @@ static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
pipe->funcs->cleanup_fb(pipe, state);
}
static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
uint32_t format,
uint64_t modifier)
{
return modifier == DRM_FORMAT_MOD_LINEAR;
}
static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
.prepare_fb = drm_simple_kms_plane_prepare_fb,
.cleanup_fb = drm_simple_kms_plane_cleanup_fb,
@ -204,6 +211,7 @@ static const struct drm_plane_funcs drm_simple_kms_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,
.format_mod_supported = drm_simple_kms_format_mod_supported,
};
/**

View File

@ -56,6 +56,9 @@
#include "drm_internal.h"
#include <drm/drm_syncobj.h>
/* merge normal syncobj to timeline syncobj, the point interval is 1 */
#define DRM_SYNCOBJ_BINARY_POINT 1
struct drm_syncobj_stub_fence {
struct dma_fence base;
spinlock_t lock;
@ -71,7 +74,29 @@ static const struct dma_fence_ops drm_syncobj_stub_fence_ops = {
.get_timeline_name = drm_syncobj_stub_fence_get_name,
};
struct drm_syncobj_signal_pt {
struct dma_fence_array *fence_array;
u64 value;
struct list_head list;
};
static DEFINE_SPINLOCK(signaled_fence_lock);
static struct dma_fence signaled_fence;
static struct dma_fence *drm_syncobj_get_stub_fence(void)
{
spin_lock(&signaled_fence_lock);
if (!signaled_fence.ops) {
dma_fence_init(&signaled_fence,
&drm_syncobj_stub_fence_ops,
&signaled_fence_lock,
0, 0);
dma_fence_signal_locked(&signaled_fence);
}
spin_unlock(&signaled_fence_lock);
return dma_fence_get(&signaled_fence);
}
/**
* drm_syncobj_find - lookup and reference a sync object.
* @file_private: drm file private pointer
@ -98,6 +123,27 @@ struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
}
EXPORT_SYMBOL(drm_syncobj_find);
static struct dma_fence *
drm_syncobj_find_signal_pt_for_point(struct drm_syncobj *syncobj,
uint64_t point)
{
struct drm_syncobj_signal_pt *signal_pt;
if ((syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) &&
(point <= syncobj->timeline))
return drm_syncobj_get_stub_fence();
list_for_each_entry(signal_pt, &syncobj->signal_pt_list, list) {
if (point > signal_pt->value)
continue;
if ((syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) &&
(point != signal_pt->value))
continue;
return dma_fence_get(&signal_pt->fence_array->base);
}
return NULL;
}
static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj,
struct drm_syncobj_cb *cb,
drm_syncobj_func_t func)
@ -106,55 +152,158 @@ static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj,
list_add_tail(&cb->node, &syncobj->cb_list);
}
static int drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj,
struct dma_fence **fence,
struct drm_syncobj_cb *cb,
drm_syncobj_func_t func)
static void drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj,
struct dma_fence **fence,
struct drm_syncobj_cb *cb,
drm_syncobj_func_t func)
{
int ret;
u64 pt_value = 0;
WARN_ON(*fence);
*fence = drm_syncobj_fence_get(syncobj);
if (*fence)
return 1;
if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) {
/*BINARY syncobj always wait on last pt */
pt_value = syncobj->signal_point;
spin_lock(&syncobj->lock);
/* We've already tried once to get a fence and failed. Now that we
* have the lock, try one more time just to be sure we don't add a
* callback when a fence has already been set.
*/
if (syncobj->fence) {
*fence = dma_fence_get(rcu_dereference_protected(syncobj->fence,
lockdep_is_held(&syncobj->lock)));
ret = 1;
} else {
*fence = NULL;
drm_syncobj_add_callback_locked(syncobj, cb, func);
ret = 0;
if (pt_value == 0)
pt_value += DRM_SYNCOBJ_BINARY_POINT;
}
spin_unlock(&syncobj->lock);
mutex_lock(&syncobj->cb_mutex);
spin_lock(&syncobj->pt_lock);
*fence = drm_syncobj_find_signal_pt_for_point(syncobj, pt_value);
spin_unlock(&syncobj->pt_lock);
if (!*fence)
drm_syncobj_add_callback_locked(syncobj, cb, func);
mutex_unlock(&syncobj->cb_mutex);
}
static void drm_syncobj_remove_callback(struct drm_syncobj *syncobj,
struct drm_syncobj_cb *cb)
{
mutex_lock(&syncobj->cb_mutex);
list_del_init(&cb->node);
mutex_unlock(&syncobj->cb_mutex);
}
static void drm_syncobj_init(struct drm_syncobj *syncobj)
{
spin_lock(&syncobj->pt_lock);
syncobj->timeline_context = dma_fence_context_alloc(1);
syncobj->timeline = 0;
syncobj->signal_point = 0;
init_waitqueue_head(&syncobj->wq);
INIT_LIST_HEAD(&syncobj->signal_pt_list);
spin_unlock(&syncobj->pt_lock);
}
static void drm_syncobj_fini(struct drm_syncobj *syncobj)
{
struct drm_syncobj_signal_pt *signal_pt = NULL, *tmp;
spin_lock(&syncobj->pt_lock);
list_for_each_entry_safe(signal_pt, tmp,
&syncobj->signal_pt_list, list) {
list_del(&signal_pt->list);
dma_fence_put(&signal_pt->fence_array->base);
kfree(signal_pt);
}
spin_unlock(&syncobj->pt_lock);
}
static int drm_syncobj_create_signal_pt(struct drm_syncobj *syncobj,
struct dma_fence *fence,
u64 point)
{
struct drm_syncobj_signal_pt *signal_pt =
kzalloc(sizeof(struct drm_syncobj_signal_pt), GFP_KERNEL);
struct drm_syncobj_signal_pt *tail_pt;
struct dma_fence **fences;
int num_fences = 0;
int ret = 0, i;
if (!signal_pt)
return -ENOMEM;
if (!fence)
goto out;
fences = kmalloc_array(sizeof(void *), 2, GFP_KERNEL);
if (!fences) {
ret = -ENOMEM;
goto out;
}
fences[num_fences++] = dma_fence_get(fence);
/* timeline syncobj must take this dependency */
if (syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) {
spin_lock(&syncobj->pt_lock);
if (!list_empty(&syncobj->signal_pt_list)) {
tail_pt = list_last_entry(&syncobj->signal_pt_list,
struct drm_syncobj_signal_pt, list);
fences[num_fences++] =
dma_fence_get(&tail_pt->fence_array->base);
}
spin_unlock(&syncobj->pt_lock);
}
signal_pt->fence_array = dma_fence_array_create(num_fences, fences,
syncobj->timeline_context,
point, false);
if (!signal_pt->fence_array) {
ret = -ENOMEM;
goto fail;
}
spin_lock(&syncobj->pt_lock);
if (syncobj->signal_point >= point) {
DRM_WARN("A later signal is ready!");
spin_unlock(&syncobj->pt_lock);
goto exist;
}
signal_pt->value = point;
list_add_tail(&signal_pt->list, &syncobj->signal_pt_list);
syncobj->signal_point = point;
spin_unlock(&syncobj->pt_lock);
wake_up_all(&syncobj->wq);
return 0;
exist:
dma_fence_put(&signal_pt->fence_array->base);
fail:
for (i = 0; i < num_fences; i++)
dma_fence_put(fences[i]);
kfree(fences);
out:
kfree(signal_pt);
return ret;
}
void drm_syncobj_add_callback(struct drm_syncobj *syncobj,
struct drm_syncobj_cb *cb,
drm_syncobj_func_t func)
static void drm_syncobj_garbage_collection(struct drm_syncobj *syncobj)
{
spin_lock(&syncobj->lock);
drm_syncobj_add_callback_locked(syncobj, cb, func);
spin_unlock(&syncobj->lock);
}
struct drm_syncobj_signal_pt *signal_pt, *tmp, *tail_pt;
void drm_syncobj_remove_callback(struct drm_syncobj *syncobj,
struct drm_syncobj_cb *cb)
{
spin_lock(&syncobj->lock);
list_del_init(&cb->node);
spin_unlock(&syncobj->lock);
}
spin_lock(&syncobj->pt_lock);
tail_pt = list_last_entry(&syncobj->signal_pt_list,
struct drm_syncobj_signal_pt,
list);
list_for_each_entry_safe(signal_pt, tmp,
&syncobj->signal_pt_list, list) {
if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY &&
signal_pt == tail_pt)
continue;
if (dma_fence_is_signaled(&signal_pt->fence_array->base)) {
syncobj->timeline = signal_pt->value;
list_del(&signal_pt->list);
dma_fence_put(&signal_pt->fence_array->base);
kfree(signal_pt);
} else {
/*signal_pt is in order in list, from small to big, so
* the later must not be signal either */
break;
}
}
spin_unlock(&syncobj->pt_lock);
}
/**
* drm_syncobj_replace_fence - replace fence in a sync object.
* @syncobj: Sync object to replace fence in
@ -167,28 +316,30 @@ void drm_syncobj_replace_fence(struct drm_syncobj *syncobj,
u64 point,
struct dma_fence *fence)
{
struct dma_fence *old_fence;
struct drm_syncobj_cb *cur, *tmp;
u64 pt_value = point;
if (fence)
dma_fence_get(fence);
drm_syncobj_garbage_collection(syncobj);
if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) {
if (!fence) {
drm_syncobj_fini(syncobj);
drm_syncobj_init(syncobj);
return;
}
pt_value = syncobj->signal_point +
DRM_SYNCOBJ_BINARY_POINT;
}
drm_syncobj_create_signal_pt(syncobj, fence, pt_value);
if (fence) {
struct drm_syncobj_cb *cur, *tmp;
LIST_HEAD(cb_list);
spin_lock(&syncobj->lock);
old_fence = rcu_dereference_protected(syncobj->fence,
lockdep_is_held(&syncobj->lock));
rcu_assign_pointer(syncobj->fence, fence);
if (fence != old_fence) {
mutex_lock(&syncobj->cb_mutex);
list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) {
list_del_init(&cur->node);
cur->func(syncobj, cur);
}
mutex_unlock(&syncobj->cb_mutex);
}
spin_unlock(&syncobj->lock);
dma_fence_put(old_fence);
}
EXPORT_SYMBOL(drm_syncobj_replace_fence);
@ -211,35 +362,89 @@ static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
return 0;
}
static int
drm_syncobj_point_get(struct drm_syncobj *syncobj, u64 point, u64 flags,
struct dma_fence **fence)
{
int ret = 0;
if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
ret = wait_event_interruptible(syncobj->wq,
point <= syncobj->signal_point);
if (ret < 0)
return ret;
}
spin_lock(&syncobj->pt_lock);
*fence = drm_syncobj_find_signal_pt_for_point(syncobj, point);
if (!*fence)
ret = -EINVAL;
spin_unlock(&syncobj->pt_lock);
return ret;
}
/**
* drm_syncobj_search_fence - lookup and reference the fence in a sync object or
* in a timeline point
* @syncobj: sync object pointer
* @point: timeline point
* @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
* @fence: out parameter for the fence
*
* if flags is DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, the function will block
* here until specific timeline points is reached.
* if not, you need a submit thread and block in userspace until all future
* timeline points have materialized, only then you can submit to the kernel,
* otherwise, function will fail to return fence.
*
* Returns 0 on success or a negative error value on failure. On success @fence
* contains a reference to the fence, which must be released by calling
* dma_fence_put().
*/
int drm_syncobj_search_fence(struct drm_syncobj *syncobj, u64 point,
u64 flags, struct dma_fence **fence)
{
u64 pt_value = point;
if (!syncobj)
return -ENOENT;
drm_syncobj_garbage_collection(syncobj);
if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) {
/*BINARY syncobj always wait on last pt */
pt_value = syncobj->signal_point;
if (pt_value == 0)
pt_value += DRM_SYNCOBJ_BINARY_POINT;
}
return drm_syncobj_point_get(syncobj, pt_value, flags, fence);
}
EXPORT_SYMBOL(drm_syncobj_search_fence);
/**
* drm_syncobj_find_fence - lookup and reference the fence in a sync object
* @file_private: drm file private pointer
* @handle: sync object handle to lookup.
* @point: timeline point
* @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
* @fence: out parameter for the fence
*
* This is just a convenience function that combines drm_syncobj_find() and
* drm_syncobj_fence_get().
* drm_syncobj_lookup_fence().
*
* Returns 0 on success or a negative error value on failure. On success @fence
* contains a reference to the fence, which must be released by calling
* dma_fence_put().
*/
int drm_syncobj_find_fence(struct drm_file *file_private,
u32 handle, u64 point,
u32 handle, u64 point, u64 flags,
struct dma_fence **fence)
{
struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
int ret = 0;
int ret;
if (!syncobj)
return -ENOENT;
*fence = drm_syncobj_fence_get(syncobj);
if (!*fence) {
ret = -EINVAL;
}
drm_syncobj_put(syncobj);
ret = drm_syncobj_search_fence(syncobj, point, flags, fence);
if (syncobj)
drm_syncobj_put(syncobj);
return ret;
}
EXPORT_SYMBOL(drm_syncobj_find_fence);
@ -255,7 +460,7 @@ void drm_syncobj_free(struct kref *kref)
struct drm_syncobj *syncobj = container_of(kref,
struct drm_syncobj,
refcount);
drm_syncobj_replace_fence(syncobj, 0, NULL);
drm_syncobj_fini(syncobj);
kfree(syncobj);
}
EXPORT_SYMBOL(drm_syncobj_free);
@ -284,7 +489,13 @@ int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
kref_init(&syncobj->refcount);
INIT_LIST_HEAD(&syncobj->cb_list);
spin_lock_init(&syncobj->lock);
spin_lock_init(&syncobj->pt_lock);
mutex_init(&syncobj->cb_mutex);
if (flags & DRM_SYNCOBJ_CREATE_TYPE_TIMELINE)
syncobj->type = DRM_SYNCOBJ_TYPE_TIMELINE;
else
syncobj->type = DRM_SYNCOBJ_TYPE_BINARY;
drm_syncobj_init(syncobj);
if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) {
ret = drm_syncobj_assign_null_handle(syncobj);
@ -497,7 +708,7 @@ static int drm_syncobj_export_sync_file(struct drm_file *file_private,
if (fd < 0)
return fd;
ret = drm_syncobj_find_fence(file_private, handle, 0, &fence);
ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
if (ret)
goto err_put_fd;
@ -567,7 +778,8 @@ drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
return -EOPNOTSUPP;
/* no valid flags yet */
if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED)
if (args->flags & ~(DRM_SYNCOBJ_CREATE_SIGNALED |
DRM_SYNCOBJ_CREATE_TYPE_TIMELINE))
return -EINVAL;
return drm_syncobj_create_as_handle(file_private,
@ -660,9 +872,8 @@ static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
struct syncobj_wait_entry *wait =
container_of(cb, struct syncobj_wait_entry, syncobj_cb);
/* This happens inside the syncobj lock */
wait->fence = dma_fence_get(rcu_dereference_protected(syncobj->fence,
lockdep_is_held(&syncobj->lock)));
drm_syncobj_search_fence(syncobj, 0, 0, &wait->fence);
wake_up_process(wait->task);
}
@ -688,7 +899,8 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
signaled_count = 0;
for (i = 0; i < count; ++i) {
entries[i].task = current;
entries[i].fence = drm_syncobj_fence_get(syncobjs[i]);
drm_syncobj_search_fence(syncobjs[i], 0, 0,
&entries[i].fence);
if (!entries[i].fence) {
if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
continue;
@ -953,12 +1165,13 @@ drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
if (ret < 0)
return ret;
for (i = 0; i < args->count_handles; i++)
drm_syncobj_replace_fence(syncobjs[i], 0, NULL);
for (i = 0; i < args->count_handles; i++) {
drm_syncobj_fini(syncobjs[i]);
drm_syncobj_init(syncobjs[i]);
}
drm_syncobj_array_free(syncobjs, args->count_handles);
return 0;
return ret;
}
int

View File

@ -179,7 +179,7 @@ static int submit_fence_sync(struct etnaviv_gem_submit *submit)
struct reservation_object *robj = bo->obj->resv;
if (!(bo->flags & ETNA_SUBMIT_BO_WRITE)) {
ret = reservation_object_reserve_shared(robj);
ret = reservation_object_reserve_shared(robj, 1);
if (ret)
return ret;
}

View File

@ -17,6 +17,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <video/videomode.h>
#include "fsl_dcu_drm_crtc.h"
#include "fsl_dcu_drm_drv.h"
@ -85,40 +86,34 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
struct drm_connector *con = &fsl_dev->connector.base;
struct drm_display_mode *mode = &crtc->state->mode;
unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0;
unsigned int pol = 0;
struct videomode vm;
index = drm_crtc_index(crtc);
clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000);
/* Configure timings: */
hbp = mode->htotal - mode->hsync_end;
hfp = mode->hsync_start - mode->hdisplay;
hsw = mode->hsync_end - mode->hsync_start;
vbp = mode->vtotal - mode->vsync_end;
vfp = mode->vsync_start - mode->vdisplay;
vsw = mode->vsync_end - mode->vsync_start;
drm_display_mode_to_videomode(mode, &vm);
/* INV_PXCK as default (most display sample data on rising edge) */
if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE))
pol |= DCU_SYN_POL_INV_PXCK;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW)
pol |= DCU_SYN_POL_INV_HS_LOW;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW)
pol |= DCU_SYN_POL_INV_VS_LOW;
regmap_write(fsl_dev->regmap, DCU_HSYN_PARA,
DCU_HSYN_PARA_BP(hbp) |
DCU_HSYN_PARA_PW(hsw) |
DCU_HSYN_PARA_FP(hfp));
DCU_HSYN_PARA_BP(vm.hback_porch) |
DCU_HSYN_PARA_PW(vm.hsync_len) |
DCU_HSYN_PARA_FP(vm.hfront_porch));
regmap_write(fsl_dev->regmap, DCU_VSYN_PARA,
DCU_VSYN_PARA_BP(vbp) |
DCU_VSYN_PARA_PW(vsw) |
DCU_VSYN_PARA_FP(vfp));
DCU_VSYN_PARA_BP(vm.vback_porch) |
DCU_VSYN_PARA_PW(vm.vsync_len) |
DCU_VSYN_PARA_FP(vm.vfront_porch));
regmap_write(fsl_dev->regmap, DCU_DISP_SIZE,
DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) |
DCU_DISP_SIZE_DELTA_X(mode->hdisplay));
DCU_DISP_SIZE_DELTA_Y(vm.vactive) |
DCU_DISP_SIZE_DELTA_X(vm.hactive));
regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol);
regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) |
DCU_BGND_G(0) | DCU_BGND_B(0));

View File

@ -26,6 +26,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_modeset_helper.h>
@ -89,20 +90,11 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
"Invalid legacyfb_depth. Defaulting to 24bpp\n");
legacyfb_depth = 24;
}
fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1);
if (IS_ERR(fsl_dev->fbdev)) {
ret = PTR_ERR(fsl_dev->fbdev);
fsl_dev->fbdev = NULL;
goto done;
}
return 0;
done:
drm_kms_helper_poll_fini(dev);
if (fsl_dev->fbdev)
drm_fbdev_cma_fini(fsl_dev->fbdev);
drm_mode_config_cleanup(dev);
drm_irq_uninstall(dev);
dev->dev_private = NULL;
@ -112,14 +104,9 @@ done:
static void fsl_dcu_unload(struct drm_device *dev)
{
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
drm_atomic_helper_shutdown(dev);
drm_kms_helper_poll_fini(dev);
if (fsl_dev->fbdev)
drm_fbdev_cma_fini(fsl_dev->fbdev);
drm_mode_config_cleanup(dev);
drm_irq_uninstall(dev);
@ -147,19 +134,11 @@ static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg)
return IRQ_HANDLED;
}
static void fsl_dcu_drm_lastclose(struct drm_device *dev)
{
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
}
DEFINE_DRM_GEM_CMA_FOPS(fsl_dcu_drm_fops);
static struct drm_driver fsl_dcu_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
| DRIVER_PRIME | DRIVER_ATOMIC,
.lastclose = fsl_dcu_drm_lastclose,
.load = fsl_dcu_load,
.unload = fsl_dcu_unload,
.irq_handler = fsl_dcu_drm_irq,
@ -355,6 +334,8 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
if (ret < 0)
goto put;
drm_fbdev_generic_setup(drm, legacyfb_depth);
return 0;
put:

View File

@ -191,7 +191,6 @@ struct fsl_dcu_drm_device {
/*protects hardware register*/
spinlock_t irq_lock;
struct drm_device *drm;
struct drm_fbdev_cma *fbdev;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct fsl_dcu_drm_connector connector;

View File

@ -2157,7 +2157,7 @@ await_fence_array(struct i915_execbuffer *eb,
if (!(flags & I915_EXEC_FENCE_WAIT))
continue;
fence = drm_syncobj_fence_get(syncobj);
drm_syncobj_search_fence(syncobj, 0, 0, &fence);
if (!fence)
return -EINVAL;

View File

@ -892,7 +892,7 @@ static void export_fence(struct i915_vma *vma,
reservation_object_lock(resv, NULL);
if (flags & EXEC_OBJECT_WRITE)
reservation_object_add_excl_fence(resv, &rq->fence);
else if (reservation_object_reserve_shared(resv) == 0)
else if (reservation_object_reserve_shared(resv, 1) == 0)
reservation_object_add_shared_fence(resv, &rq->fence);
reservation_object_unlock(resv);
}

View File

@ -474,7 +474,8 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
const struct drm_display_mode *adjusted_mode =
&crtc_state->base.adjusted_mode;
struct drm_connector *connector = &intel_hdmi->attached_connector->base;
bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported;
bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported ||
connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB420;
union hdmi_infoframe frame;
int ret;

View File

@ -68,15 +68,7 @@
* - Powering Up HDMI controller and PHY
*/
static void meson_fb_output_poll_changed(struct drm_device *dev)
{
struct meson_drm *priv = dev->dev_private;
drm_fbdev_cma_hotplug_event(priv->fbdev);
}
static const struct drm_mode_config_funcs meson_mode_config_funcs = {
.output_poll_changed = meson_fb_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
.fb_create = drm_gem_fb_create,
@ -282,13 +274,6 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
drm_mode_config_reset(drm);
priv->fbdev = drm_fbdev_cma_init(drm, 32,
drm->mode_config.num_connector);
if (IS_ERR(priv->fbdev)) {
ret = PTR_ERR(priv->fbdev);
goto free_drm;
}
drm_kms_helper_poll_init(drm);
platform_set_drvdata(pdev, priv);
@ -297,6 +282,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (ret)
goto free_drm;
drm_fbdev_generic_setup(drm, 32);
return 0;
free_drm:
@ -313,11 +300,9 @@ static int meson_drv_bind(struct device *dev)
static void meson_drv_unbind(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct meson_drm *priv = drm->dev_private;
drm_dev_unregister(drm);
drm_kms_helper_poll_fini(drm);
drm_fbdev_cma_fini(priv->fbdev);
drm_mode_config_cleanup(drm);
drm_dev_put(drm);

View File

@ -33,7 +33,6 @@ struct meson_drm {
struct drm_device *drm;
struct drm_crtc *crtc;
struct drm_fbdev_cma *fbdev;
struct drm_plane *primary_plane;
/* Components Data */

View File

@ -1222,10 +1222,7 @@ static int a5xx_crashdumper_init(struct msm_gpu *gpu,
SZ_1M, MSM_BO_UNCACHED, gpu->aspace,
&dumper->bo, &dumper->iova);
if (IS_ERR(dumper->ptr))
return PTR_ERR(dumper->ptr);
return 0;
return PTR_ERR_OR_ZERO(dumper->ptr);
}
static void a5xx_crashdumper_free(struct msm_gpu *gpu,

View File

@ -1179,8 +1179,6 @@ static void dpu_plane_destroy(struct drm_plane *plane)
mutex_destroy(&pdpu->lock);
drm_plane_helper_disable(plane, NULL);
/* this will destroy the states as well */
drm_plane_cleanup(plane);

View File

@ -68,7 +68,6 @@ static void mdp4_plane_destroy(struct drm_plane *plane)
{
struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
drm_plane_helper_disable(plane, NULL);
drm_plane_cleanup(plane);
kfree(mdp4_plane);

View File

@ -46,7 +46,6 @@ static void mdp5_plane_destroy(struct drm_plane *plane)
{
struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
drm_plane_helper_disable(plane, NULL);
drm_plane_cleanup(plane);
kfree(mdp5_plane);

View File

@ -579,7 +579,7 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
hdmi_cfg = (struct hdmi_platform_config *)
of_device_get_match_data(dev);
if (!hdmi_cfg) {
dev_err(dev, "unknown hdmi_cfg: %s\n", of_node->name);
dev_err(dev, "unknown hdmi_cfg: %pOFn\n", of_node);
return -ENXIO;
}

View File

@ -312,6 +312,7 @@ static int msm_drm_uninit(struct device *dev)
if (fbdev && priv->fbdev)
msm_fbdev_free(ddev);
#endif
drm_atomic_helper_shutdown(ddev);
drm_mode_config_cleanup(ddev);
pm_runtime_get_sync(dev);

View File

@ -241,7 +241,8 @@ static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
* strange place to call it. OTOH this is a
* convenient can-fail point to hook it in.
*/
ret = reservation_object_reserve_shared(msm_obj->resv);
ret = reservation_object_reserve_shared(msm_obj->resv,
1);
if (ret)
return ret;
}

View File

@ -341,7 +341,7 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e
int ret = 0, i;
if (!exclusive) {
ret = reservation_object_reserve_shared(resv);
ret = reservation_object_reserve_shared(resv, 1);
if (ret)
return ret;

View File

@ -90,6 +90,18 @@ config DRM_PANEL_LG_LG4573
Say Y here if you want to enable support for LG4573 RGB panel.
To compile this driver as a module, choose M here.
config DRM_PANEL_OLIMEX_LCD_OLINUXINO
tristate "Olimex LCD-OLinuXino panel"
depends on OF
depends on I2C
depends on BACKLIGHT_CLASS_DEVICE
help
The panel is used with different sizes LCDs, from 480x272 to
1280x800, and 24 bit per pixel.
Say Y here if you want to enable support for Olimex Ltd.
LCD-OLinuXino panel.
config DRM_PANEL_ORISETECH_OTM8009A
tristate "Orise Technology otm8009a 480x800 dsi 2dl panel"
depends on OF
@ -126,6 +138,12 @@ config DRM_PANEL_RAYDIUM_RM68200
Say Y here if you want to enable support for Raydium RM68200
720x1280 DSI video mode panel.
config DRM_PANEL_SAMSUNG_S6D16D0
tristate "Samsung S6D16D0 DSI video mode panel"
depends on OF
depends on DRM_MIPI_DSI
select VIDEOMODE_HELPERS
config DRM_PANEL_SAMSUNG_S6E3HA2
tristate "Samsung S6E3HA2 DSI video mode panel"
depends on OF
@ -186,4 +204,11 @@ config DRM_PANEL_SITRONIX_ST7789V
Say Y here if you want to enable support for the Sitronix
ST7789V controller for 240x320 LCD panels
config DRM_PANEL_TRULY_NT35597_WQXGA
tristate "Truly WQXGA"
depends on OF
depends on DRM_MIPI_DSI
help
Say Y here if you want to enable support for Truly NT35597 WQXGA Dual DSI
Video Mode panel
endmenu

View File

@ -7,11 +7,13 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o
obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
@ -19,3 +21,4 @@ obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o

View File

@ -506,8 +506,7 @@ static int innolux_panel_add(struct mipi_dsi_device *dsi,
static void innolux_panel_del(struct innolux_panel *innolux)
{
if (innolux->base.dev)
drm_panel_remove(&innolux->base);
drm_panel_remove(&innolux->base);
}
static int innolux_panel_probe(struct mipi_dsi_device *dsi)

View File

@ -0,0 +1,330 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* LCD-OLinuXino support for panel driver
*
* Copyright (C) 2018 Olimex Ltd.
* Author: Stefan Mavrodiev <stefan@olimex.com>
*/
#include <linux/backlight.h>
#include <linux/crc32.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#include <drm/drmP.h>
#include <video/videomode.h>
#include <video/display_timing.h>
#define LCD_OLINUXINO_HEADER_MAGIC 0x4F4CB727
#define LCD_OLINUXINO_DATA_LEN 256
struct lcd_olinuxino_mode {
u32 pixelclock;
u32 hactive;
u32 hfp;
u32 hbp;
u32 hpw;
u32 vactive;
u32 vfp;
u32 vbp;
u32 vpw;
u32 refresh;
u32 flags;
};
struct lcd_olinuxino_info {
char name[32];
u32 width_mm;
u32 height_mm;
u32 bpc;
u32 bus_format;
u32 bus_flag;
} __attribute__((__packed__));
struct lcd_olinuxino_eeprom {
u32 header;
u32 id;
char revision[4];
u32 serial;
struct lcd_olinuxino_info info;
u32 num_modes;
u8 reserved[180];
u32 checksum;
} __attribute__((__packed__));
struct lcd_olinuxino {
struct drm_panel panel;
struct device *dev;
struct i2c_client *client;
struct mutex mutex;
bool prepared;
bool enabled;
struct backlight_device *backlight;
struct regulator *supply;
struct gpio_desc *enable_gpio;
struct lcd_olinuxino_eeprom eeprom;
};
static inline struct lcd_olinuxino *to_lcd_olinuxino(struct drm_panel *panel)
{
return container_of(panel, struct lcd_olinuxino, panel);
}
static int lcd_olinuxino_disable(struct drm_panel *panel)
{
struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
if (!lcd->enabled)
return 0;
backlight_disable(lcd->backlight);
lcd->enabled = false;
return 0;
}
static int lcd_olinuxino_unprepare(struct drm_panel *panel)
{
struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
if (!lcd->prepared)
return 0;
gpiod_set_value_cansleep(lcd->enable_gpio, 0);
regulator_disable(lcd->supply);
lcd->prepared = false;
return 0;
}
static int lcd_olinuxino_prepare(struct drm_panel *panel)
{
struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
int ret;
if (lcd->prepared)
return 0;
ret = regulator_enable(lcd->supply);
if (ret < 0)
return ret;
gpiod_set_value_cansleep(lcd->enable_gpio, 1);
lcd->prepared = true;
return 0;
}
static int lcd_olinuxino_enable(struct drm_panel *panel)
{
struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
if (lcd->enabled)
return 0;
backlight_enable(lcd->backlight);
lcd->enabled = true;
return 0;
}
static int lcd_olinuxino_get_modes(struct drm_panel *panel)
{
struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
struct drm_connector *connector = lcd->panel.connector;
struct lcd_olinuxino_info *lcd_info = &lcd->eeprom.info;
struct drm_device *drm = lcd->panel.drm;
struct lcd_olinuxino_mode *lcd_mode;
struct drm_display_mode *mode;
u32 i, num = 0;
for (i = 0; i < lcd->eeprom.num_modes; i++) {
lcd_mode = (struct lcd_olinuxino_mode *)
&lcd->eeprom.reserved[i * sizeof(*lcd_mode)];
mode = drm_mode_create(drm);
if (!mode) {
dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
lcd_mode->hactive,
lcd_mode->vactive,
lcd_mode->refresh);
continue;
}
mode->clock = lcd_mode->pixelclock;
mode->hdisplay = lcd_mode->hactive;
mode->hsync_start = lcd_mode->hactive + lcd_mode->hfp;
mode->hsync_end = lcd_mode->hactive + lcd_mode->hfp +
lcd_mode->hpw;
mode->htotal = lcd_mode->hactive + lcd_mode->hfp +
lcd_mode->hpw + lcd_mode->hbp;
mode->vdisplay = lcd_mode->vactive;
mode->vsync_start = lcd_mode->vactive + lcd_mode->vfp;
mode->vsync_end = lcd_mode->vactive + lcd_mode->vfp +
lcd_mode->vpw;
mode->vtotal = lcd_mode->vactive + lcd_mode->vfp +
lcd_mode->vpw + lcd_mode->vbp;
mode->vrefresh = lcd_mode->refresh;
/* Always make the first mode preferred */
if (i == 0)
mode->type |= DRM_MODE_TYPE_PREFERRED;
mode->type |= DRM_MODE_TYPE_DRIVER;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
num++;
}
memcpy(connector->display_info.name, lcd_info->name, 32);
connector->display_info.width_mm = lcd_info->width_mm;
connector->display_info.height_mm = lcd_info->height_mm;
connector->display_info.bpc = lcd_info->bpc;
if (lcd_info->bus_format)
drm_display_info_set_bus_formats(&connector->display_info,
&lcd_info->bus_format, 1);
connector->display_info.bus_flags = lcd_info->bus_flag;
return num;
}
static const struct drm_panel_funcs lcd_olinuxino_funcs = {
.disable = lcd_olinuxino_disable,
.unprepare = lcd_olinuxino_unprepare,
.prepare = lcd_olinuxino_prepare,
.enable = lcd_olinuxino_enable,
.get_modes = lcd_olinuxino_get_modes,
};
static int lcd_olinuxino_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct lcd_olinuxino *lcd;
u32 checksum, i;
int ret = 0;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_READ_I2C_BLOCK))
return -ENODEV;
lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
if (!lcd)
return -ENOMEM;
i2c_set_clientdata(client, lcd);
lcd->dev = dev;
lcd->client = client;
mutex_init(&lcd->mutex);
/* Copy data into buffer */
for (i = 0; i < LCD_OLINUXINO_DATA_LEN; i += I2C_SMBUS_BLOCK_MAX) {
mutex_lock(&lcd->mutex);
ret = i2c_smbus_read_i2c_block_data(client,
i,
I2C_SMBUS_BLOCK_MAX,
(u8 *)&lcd->eeprom + i);
mutex_unlock(&lcd->mutex);
if (ret < 0) {
dev_err(dev, "error reading from device at %02x\n", i);
return ret;
}
}
/* Check configuration checksum */
checksum = ~crc32(~0, (u8 *)&lcd->eeprom, 252);
if (checksum != lcd->eeprom.checksum) {
dev_err(dev, "configuration checksum does not match!\n");
return -EINVAL;
}
/* Check magic header */
if (lcd->eeprom.header != LCD_OLINUXINO_HEADER_MAGIC) {
dev_err(dev, "magic header does not match\n");
return -EINVAL;
}
dev_info(dev, "Detected %s, Rev. %s, Serial: %08x\n",
lcd->eeprom.info.name,
lcd->eeprom.revision,
lcd->eeprom.serial);
/*
* The eeprom can hold up to 4 modes.
* If the stored value is bigger, overwrite it.
*/
if (lcd->eeprom.num_modes > 4) {
dev_warn(dev, "invalid number of modes, falling back to 4\n");
lcd->eeprom.num_modes = 4;
}
lcd->enabled = false;
lcd->prepared = false;
lcd->supply = devm_regulator_get(dev, "power");
if (IS_ERR(lcd->supply))
return PTR_ERR(lcd->supply);
lcd->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(lcd->enable_gpio))
return PTR_ERR(lcd->enable_gpio);
lcd->backlight = devm_of_find_backlight(dev);
if (IS_ERR(lcd->backlight))
return PTR_ERR(lcd->backlight);
drm_panel_init(&lcd->panel);
lcd->panel.dev = dev;
lcd->panel.funcs = &lcd_olinuxino_funcs;
return drm_panel_add(&lcd->panel);
}
static int lcd_olinuxino_remove(struct i2c_client *client)
{
struct lcd_olinuxino *panel = i2c_get_clientdata(client);
drm_panel_remove(&panel->panel);
lcd_olinuxino_disable(&panel->panel);
lcd_olinuxino_unprepare(&panel->panel);
return 0;
}
static const struct of_device_id lcd_olinuxino_of_ids[] = {
{ .compatible = "olimex,lcd-olinuxino" },
{ }
};
MODULE_DEVICE_TABLE(of, lcd_olinuxino_of_ids);
static struct i2c_driver lcd_olinuxino_driver = {
.driver = {
.name = "lcd_olinuxino",
.of_match_table = lcd_olinuxino_of_ids,
},
.probe = lcd_olinuxino_probe,
.remove = lcd_olinuxino_remove,
};
module_i2c_driver(lcd_olinuxino_driver);
MODULE_AUTHOR("Stefan Mavrodiev <stefan@olimex.com>");
MODULE_DESCRIPTION("LCD-OLinuXino driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,264 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* MIPI-DSI Samsung s6d16d0 panel driver. This is a 864x480
* AMOLED panel with a command-only DSI interface.
*/
#include <drm/drm_modes.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/module.h>
struct s6d16d0 {
struct device *dev;
struct drm_panel panel;
struct regulator *supply;
struct gpio_desc *reset_gpio;
};
/*
* The timings are not very helpful as the display is used in
* command mode.
*/
static const struct drm_display_mode samsung_s6d16d0_mode = {
/* HS clock, (htotal*vtotal*vrefresh)/1000 */
.clock = 420160,
.hdisplay = 864,
.hsync_start = 864 + 154,
.hsync_end = 864 + 154 + 16,
.htotal = 864 + 154 + 16 + 32,
.vdisplay = 480,
.vsync_start = 480 + 1,
.vsync_end = 480 + 1 + 1,
.vtotal = 480 + 1 + 1 + 1,
/*
* This depends on the clocking HS vs LP rate, this value
* is calculated as:
* vrefresh = (clock * 1000) / (htotal*vtotal)
*/
.vrefresh = 816,
.width_mm = 84,
.height_mm = 48,
};
static inline struct s6d16d0 *panel_to_s6d16d0(struct drm_panel *panel)
{
return container_of(panel, struct s6d16d0, panel);
}
static int s6d16d0_unprepare(struct drm_panel *panel)
{
struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
int ret;
/* Enter sleep mode */
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
if (ret) {
DRM_DEV_ERROR(s6->dev, "failed to enter sleep mode (%d)\n",
ret);
return ret;
}
/* Assert RESET */
gpiod_set_value_cansleep(s6->reset_gpio, 1);
regulator_disable(s6->supply);
return 0;
}
static int s6d16d0_prepare(struct drm_panel *panel)
{
struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
int ret;
ret = regulator_enable(s6->supply);
if (ret) {
DRM_DEV_ERROR(s6->dev, "failed to enable supply (%d)\n", ret);
return ret;
}
/* Assert RESET */
gpiod_set_value_cansleep(s6->reset_gpio, 1);
udelay(10);
/* De-assert RESET */
gpiod_set_value_cansleep(s6->reset_gpio, 0);
msleep(120);
/* Enabe tearing mode: send TE (tearing effect) at VBLANK */
ret = mipi_dsi_dcs_set_tear_on(dsi,
MIPI_DSI_DCS_TEAR_MODE_VBLANK);
if (ret) {
DRM_DEV_ERROR(s6->dev, "failed to enble vblank TE (%d)\n",
ret);
return ret;
}
/* Exit sleep mode and power on */
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret) {
DRM_DEV_ERROR(s6->dev, "failed to exit sleep mode (%d)\n",
ret);
return ret;
}
return 0;
}
static int s6d16d0_enable(struct drm_panel *panel)
{
struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
int ret;
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret) {
DRM_DEV_ERROR(s6->dev, "failed to turn display on (%d)\n",
ret);
return ret;
}
return 0;
}
static int s6d16d0_disable(struct drm_panel *panel)
{
struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
int ret;
ret = mipi_dsi_dcs_set_display_off(dsi);
if (ret) {
DRM_DEV_ERROR(s6->dev, "failed to turn display off (%d)\n",
ret);
return ret;
}
return 0;
}
static int s6d16d0_get_modes(struct drm_panel *panel)
{
struct drm_connector *connector = panel->connector;
struct drm_display_mode *mode;
strncpy(connector->display_info.name, "Samsung S6D16D0\0",
DRM_DISPLAY_INFO_LEN);
mode = drm_mode_duplicate(panel->drm, &samsung_s6d16d0_mode);
if (!mode) {
DRM_ERROR("bad mode or failed to add mode\n");
return -EINVAL;
}
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
drm_mode_probed_add(connector, mode);
return 1; /* Number of modes */
}
static const struct drm_panel_funcs s6d16d0_drm_funcs = {
.disable = s6d16d0_disable,
.unprepare = s6d16d0_unprepare,
.prepare = s6d16d0_prepare,
.enable = s6d16d0_enable,
.get_modes = s6d16d0_get_modes,
};
static int s6d16d0_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct s6d16d0 *s6;
int ret;
s6 = devm_kzalloc(dev, sizeof(struct s6d16d0), GFP_KERNEL);
if (!s6)
return -ENOMEM;
mipi_dsi_set_drvdata(dsi, s6);
s6->dev = dev;
dsi->lanes = 2;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->hs_rate = 420160000;
dsi->lp_rate = 19200000;
/*
* This display uses command mode so no MIPI_DSI_MODE_VIDEO
* or MIPI_DSI_MODE_VIDEO_SYNC_PULSE
*
* As we only send commands we do not need to be continuously
* clocked.
*/
dsi->mode_flags =
MIPI_DSI_CLOCK_NON_CONTINUOUS |
MIPI_DSI_MODE_EOT_PACKET;
s6->supply = devm_regulator_get(dev, "vdd1");
if (IS_ERR(s6->supply))
return PTR_ERR(s6->supply);
/* This asserts RESET by default */
s6->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(s6->reset_gpio)) {
ret = PTR_ERR(s6->reset_gpio);
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "failed to request GPIO (%d)\n",
ret);
return ret;
}
drm_panel_init(&s6->panel);
s6->panel.dev = dev;
s6->panel.funcs = &s6d16d0_drm_funcs;
ret = drm_panel_add(&s6->panel);
if (ret < 0)
return ret;
ret = mipi_dsi_attach(dsi);
if (ret < 0)
drm_panel_remove(&s6->panel);
return ret;
}
static int s6d16d0_remove(struct mipi_dsi_device *dsi)
{
struct s6d16d0 *s6 = mipi_dsi_get_drvdata(dsi);
mipi_dsi_detach(dsi);
drm_panel_remove(&s6->panel);
return 0;
}
static const struct of_device_id s6d16d0_of_match[] = {
{ .compatible = "samsung,s6d16d0" },
{ }
};
MODULE_DEVICE_TABLE(of, s6d16d0_of_match);
static struct mipi_dsi_driver s6d16d0_driver = {
.probe = s6d16d0_probe,
.remove = s6d16d0_remove,
.driver = {
.name = "panel-samsung-s6d16d0",
.of_match_table = s6d16d0_of_match,
},
};
module_mipi_dsi_driver(s6d16d0_driver);
MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("MIPI-DSI s6d16d0 Panel Driver");
MODULE_LICENSE("GPL v2");

View File

@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 NXP Semiconductors.
* Author: Marco Franchi <marco.franchi@nxp.com>
*
* Based on Panel Simple driver by Thierry Reding <treding@nvidia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/backlight.h>
@ -366,6 +363,6 @@ static struct platform_driver seiko_panel_platform_driver = {
};
module_platform_driver(seiko_panel_platform_driver);
MODULE_AUTHOR("Marco Franchi <marco.franchi@nxp.com");
MODULE_AUTHOR("Marco Franchi <marco.franchi@nxp.com>");
MODULE_DESCRIPTION("Seiko 43WVF1G panel driver");
MODULE_LICENSE("GPL v2");

View File

@ -782,16 +782,38 @@ static const struct panel_desc avic_tm070ddh03 = {
},
};
static const struct drm_display_mode bananapi_s070wv20_ct16_mode = {
.clock = 30000,
.hdisplay = 800,
.hsync_start = 800 + 40,
.hsync_end = 800 + 40 + 48,
.htotal = 800 + 40 + 48 + 40,
.vdisplay = 480,
.vsync_start = 480 + 13,
.vsync_end = 480 + 13 + 3,
.vtotal = 480 + 13 + 3 + 29,
};
static const struct panel_desc bananapi_s070wv20_ct16 = {
.modes = &bananapi_s070wv20_ct16_mode,
.num_modes = 1,
.bpc = 6,
.size = {
.width = 154,
.height = 86,
},
};
static const struct drm_display_mode boe_hv070wsa_mode = {
.clock = 40800,
.clock = 42105,
.hdisplay = 1024,
.hsync_start = 1024 + 90,
.hsync_end = 1024 + 90 + 90,
.htotal = 1024 + 90 + 90 + 90,
.hsync_start = 1024 + 30,
.hsync_end = 1024 + 30 + 30,
.htotal = 1024 + 30 + 30 + 30,
.vdisplay = 600,
.vsync_start = 600 + 3,
.vsync_end = 600 + 3 + 4,
.vtotal = 600 + 3 + 4 + 3,
.vsync_start = 600 + 10,
.vsync_end = 600 + 10 + 10,
.vtotal = 600 + 10 + 10 + 10,
.vrefresh = 60,
};
@ -846,6 +868,55 @@ static const struct panel_desc boe_nv101wxmn51 = {
},
};
static const struct drm_display_mode cdtech_s043wq26h_ct7_mode = {
.clock = 9000,
.hdisplay = 480,
.hsync_start = 480 + 5,
.hsync_end = 480 + 5 + 5,
.htotal = 480 + 5 + 5 + 40,
.vdisplay = 272,
.vsync_start = 272 + 8,
.vsync_end = 272 + 8 + 8,
.vtotal = 272 + 8 + 8 + 8,
.vrefresh = 60,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
};
static const struct panel_desc cdtech_s043wq26h_ct7 = {
.modes = &cdtech_s043wq26h_ct7_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 95,
.height = 54,
},
.bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE,
};
static const struct drm_display_mode cdtech_s070wv95_ct16_mode = {
.clock = 35000,
.hdisplay = 800,
.hsync_start = 800 + 40,
.hsync_end = 800 + 40 + 40,
.htotal = 800 + 40 + 40 + 48,
.vdisplay = 480,
.vsync_start = 480 + 29,
.vsync_end = 480 + 29 + 13,
.vtotal = 480 + 29 + 13 + 3,
.vrefresh = 60,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
};
static const struct panel_desc cdtech_s070wv95_ct16 = {
.modes = &cdtech_s070wv95_ct16_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 154,
.height = 85,
},
};
static const struct drm_display_mode chunghwa_claa070wp03xg_mode = {
.clock = 66770,
.hdisplay = 800,
@ -971,6 +1042,36 @@ static const struct panel_desc dlc_dlc0700yzg_1 = {
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
};
static const struct display_timing dlc_dlc1010gig_timing = {
.pixelclock = { 68900000, 71100000, 73400000 },
.hactive = { 1280, 1280, 1280 },
.hfront_porch = { 43, 53, 63 },
.hback_porch = { 43, 53, 63 },
.hsync_len = { 44, 54, 64 },
.vactive = { 800, 800, 800 },
.vfront_porch = { 5, 8, 11 },
.vback_porch = { 5, 8, 11 },
.vsync_len = { 5, 7, 11 },
.flags = DISPLAY_FLAGS_DE_HIGH,
};
static const struct panel_desc dlc_dlc1010gig = {
.timings = &dlc_dlc1010gig_timing,
.num_timings = 1,
.bpc = 8,
.size = {
.width = 216,
.height = 135,
},
.delay = {
.prepare = 60,
.enable = 150,
.disable = 100,
.unprepare = 60,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
};
static const struct drm_display_mode edt_et057090dhu_mode = {
.clock = 25175,
.hdisplay = 640,
@ -2334,6 +2435,33 @@ static const struct panel_desc winstar_wf35ltiacd = {
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
static const struct drm_display_mode arm_rtsm_mode[] = {
{
.clock = 65000,
.hdisplay = 1024,
.hsync_start = 1024 + 24,
.hsync_end = 1024 + 24 + 136,
.htotal = 1024 + 24 + 136 + 160,
.vdisplay = 768,
.vsync_start = 768 + 3,
.vsync_end = 768 + 3 + 6,
.vtotal = 768 + 3 + 6 + 29,
.vrefresh = 60,
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
},
};
static const struct panel_desc arm_rtsm = {
.modes = arm_rtsm_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 400,
.height = 300,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
static const struct of_device_id platform_of_match[] = {
{
.compatible = "ampire,am-480272h3tmqw-t01h",
@ -2341,6 +2469,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "ampire,am800480r3tmqwa1h",
.data = &ampire_am800480r3tmqwa1h,
}, {
.compatible = "arm,rtsm-display",
.data = &arm_rtsm,
}, {
.compatible = "auo,b101aw03",
.data = &auo_b101aw03,
@ -2380,12 +2511,21 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "avic,tm070ddh03",
.data = &avic_tm070ddh03,
}, {
.compatible = "bananapi,s070wv20-ct16",
.data = &bananapi_s070wv20_ct16,
}, {
.compatible = "boe,hv070wsa-100",
.data = &boe_hv070wsa
}, {
.compatible = "boe,nv101wxmn51",
.data = &boe_nv101wxmn51,
}, {
.compatible = "cdtech,s043wq26h-ct7",
.data = &cdtech_s043wq26h_ct7,
}, {
.compatible = "cdtech,s070wv95-ct16",
.data = &cdtech_s070wv95_ct16,
}, {
.compatible = "chunghwa,claa070wp03xg",
.data = &chunghwa_claa070wp03xg,
@ -2401,6 +2541,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "dlc,dlc0700yzg-1",
.data = &dlc_dlc0700yzg_1,
}, {
.compatible = "dlc,dlc1010gig",
.data = &dlc_dlc1010gig,
}, {
.compatible = "edt,et057090dhu",
.data = &edt_et057090dhu,

View File

@ -0,0 +1,675 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
#include <drm/drmP.h>
#include <drm/drm_panel.h>
#include <drm/drm_mipi_dsi.h>
#include <linux/gpio/consumer.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>
static const char * const regulator_names[] = {
"vdda",
"vdispp",
"vdispn",
};
static unsigned long const regulator_enable_loads[] = {
62000,
100000,
100000,
};
static unsigned long const regulator_disable_loads[] = {
80,
100,
100,
};
struct cmd_set {
u8 commands[4];
u8 size;
};
struct nt35597_config {
u32 width_mm;
u32 height_mm;
const char *panel_name;
const struct cmd_set *panel_on_cmds;
u32 num_on_cmds;
const struct drm_display_mode *dm;
};
struct truly_nt35597 {
struct device *dev;
struct drm_panel panel;
struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
struct gpio_desc *reset_gpio;
struct gpio_desc *mode_gpio;
struct backlight_device *backlight;
struct mipi_dsi_device *dsi[2];
const struct nt35597_config *config;
bool prepared;
bool enabled;
};
static inline struct truly_nt35597 *panel_to_ctx(struct drm_panel *panel)
{
return container_of(panel, struct truly_nt35597, panel);
}
static const struct cmd_set qcom_2k_panel_magic_cmds[] = {
/* CMD2_P0 */
{ { 0xff, 0x20 }, 2 },
{ { 0xfb, 0x01 }, 2 },
{ { 0x00, 0x01 }, 2 },
{ { 0x01, 0x55 }, 2 },
{ { 0x02, 0x45 }, 2 },
{ { 0x05, 0x40 }, 2 },
{ { 0x06, 0x19 }, 2 },
{ { 0x07, 0x1e }, 2 },
{ { 0x0b, 0x73 }, 2 },
{ { 0x0c, 0x73 }, 2 },
{ { 0x0e, 0xb0 }, 2 },
{ { 0x0f, 0xae }, 2 },
{ { 0x11, 0xb8 }, 2 },
{ { 0x13, 0x00 }, 2 },
{ { 0x58, 0x80 }, 2 },
{ { 0x59, 0x01 }, 2 },
{ { 0x5a, 0x00 }, 2 },
{ { 0x5b, 0x01 }, 2 },
{ { 0x5c, 0x80 }, 2 },
{ { 0x5d, 0x81 }, 2 },
{ { 0x5e, 0x00 }, 2 },
{ { 0x5f, 0x01 }, 2 },
{ { 0x72, 0x11 }, 2 },
{ { 0x68, 0x03 }, 2 },
/* CMD2_P4 */
{ { 0xFF, 0x24 }, 2 },
{ { 0xFB, 0x01 }, 2 },
{ { 0x00, 0x1C }, 2 },
{ { 0x01, 0x0B }, 2 },
{ { 0x02, 0x0C }, 2 },
{ { 0x03, 0x01 }, 2 },
{ { 0x04, 0x0F }, 2 },
{ { 0x05, 0x10 }, 2 },
{ { 0x06, 0x10 }, 2 },
{ { 0x07, 0x10 }, 2 },
{ { 0x08, 0x89 }, 2 },
{ { 0x09, 0x8A }, 2 },
{ { 0x0A, 0x13 }, 2 },
{ { 0x0B, 0x13 }, 2 },
{ { 0x0C, 0x15 }, 2 },
{ { 0x0D, 0x15 }, 2 },
{ { 0x0E, 0x17 }, 2 },
{ { 0x0F, 0x17 }, 2 },
{ { 0x10, 0x1C }, 2 },
{ { 0x11, 0x0B }, 2 },
{ { 0x12, 0x0C }, 2 },
{ { 0x13, 0x01 }, 2 },
{ { 0x14, 0x0F }, 2 },
{ { 0x15, 0x10 }, 2 },
{ { 0x16, 0x10 }, 2 },
{ { 0x17, 0x10 }, 2 },
{ { 0x18, 0x89 }, 2 },
{ { 0x19, 0x8A }, 2 },
{ { 0x1A, 0x13 }, 2 },
{ { 0x1B, 0x13 }, 2 },
{ { 0x1C, 0x15 }, 2 },
{ { 0x1D, 0x15 }, 2 },
{ { 0x1E, 0x17 }, 2 },
{ { 0x1F, 0x17 }, 2 },
/* STV */
{ { 0x20, 0x40 }, 2 },
{ { 0x21, 0x01 }, 2 },
{ { 0x22, 0x00 }, 2 },
{ { 0x23, 0x40 }, 2 },
{ { 0x24, 0x40 }, 2 },
{ { 0x25, 0x6D }, 2 },
{ { 0x26, 0x40 }, 2 },
{ { 0x27, 0x40 }, 2 },
/* Vend */
{ { 0xE0, 0x00 }, 2 },
{ { 0xDC, 0x21 }, 2 },
{ { 0xDD, 0x22 }, 2 },
{ { 0xDE, 0x07 }, 2 },
{ { 0xDF, 0x07 }, 2 },
{ { 0xE3, 0x6D }, 2 },
{ { 0xE1, 0x07 }, 2 },
{ { 0xE2, 0x07 }, 2 },
/* UD */
{ { 0x29, 0xD8 }, 2 },
{ { 0x2A, 0x2A }, 2 },
/* CLK */
{ { 0x4B, 0x03 }, 2 },
{ { 0x4C, 0x11 }, 2 },
{ { 0x4D, 0x10 }, 2 },
{ { 0x4E, 0x01 }, 2 },
{ { 0x4F, 0x01 }, 2 },
{ { 0x50, 0x10 }, 2 },
{ { 0x51, 0x00 }, 2 },
{ { 0x52, 0x80 }, 2 },
{ { 0x53, 0x00 }, 2 },
{ { 0x56, 0x00 }, 2 },
{ { 0x54, 0x07 }, 2 },
{ { 0x58, 0x07 }, 2 },
{ { 0x55, 0x25 }, 2 },
/* Reset XDONB */
{ { 0x5B, 0x43 }, 2 },
{ { 0x5C, 0x00 }, 2 },
{ { 0x5F, 0x73 }, 2 },
{ { 0x60, 0x73 }, 2 },
{ { 0x63, 0x22 }, 2 },
{ { 0x64, 0x00 }, 2 },
{ { 0x67, 0x08 }, 2 },
{ { 0x68, 0x04 }, 2 },
/* Resolution:1440x2560 */
{ { 0x72, 0x02 }, 2 },
/* mux */
{ { 0x7A, 0x80 }, 2 },
{ { 0x7B, 0x91 }, 2 },
{ { 0x7C, 0xD8 }, 2 },
{ { 0x7D, 0x60 }, 2 },
{ { 0x7F, 0x15 }, 2 },
{ { 0x75, 0x15 }, 2 },
/* ABOFF */
{ { 0xB3, 0xC0 }, 2 },
{ { 0xB4, 0x00 }, 2 },
{ { 0xB5, 0x00 }, 2 },
/* Source EQ */
{ { 0x78, 0x00 }, 2 },
{ { 0x79, 0x00 }, 2 },
{ { 0x80, 0x00 }, 2 },
{ { 0x83, 0x00 }, 2 },
/* FP BP */
{ { 0x93, 0x0A }, 2 },
{ { 0x94, 0x0A }, 2 },
/* Inversion Type */
{ { 0x8A, 0x00 }, 2 },
{ { 0x9B, 0xFF }, 2 },
/* IMGSWAP =1 @PortSwap=1 */
{ { 0x9D, 0xB0 }, 2 },
{ { 0x9F, 0x63 }, 2 },
{ { 0x98, 0x10 }, 2 },
/* FRM */
{ { 0xEC, 0x00 }, 2 },
/* CMD1 */
{ { 0xFF, 0x10 }, 2 },
/* VBP+VSA=,VFP = 10H */
{ { 0x3B, 0x03, 0x0A, 0x0A }, 4 },
/* FTE on */
{ { 0x35, 0x00 }, 2 },
/* EN_BK =1(auto black) */
{ { 0xE5, 0x01 }, 2 },
/* CMD mode(10) VDO mode(03) */
{ { 0xBB, 0x03 }, 2 },
/* Non Reload MTP */
{ { 0xFB, 0x01 }, 2 },
};
static int truly_dcs_write(struct drm_panel *panel, u32 command)
{
struct truly_nt35597 *ctx = panel_to_ctx(panel);
int i, ret;
for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
ret = mipi_dsi_dcs_write(ctx->dsi[i], command, NULL, 0);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev,
"cmd 0x%x failed for dsi = %d\n",
command, i);
}
}
return ret;
}
static int truly_dcs_write_buf(struct drm_panel *panel,
u32 size, const u8 *buf)
{
struct truly_nt35597 *ctx = panel_to_ctx(panel);
int ret = 0;
int i;
for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
ret = mipi_dsi_dcs_write_buffer(ctx->dsi[i], buf, size);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev,
"failed to tx cmd [%d], err: %d\n", i, ret);
return ret;
}
}
return ret;
}
static int truly_35597_power_on(struct truly_nt35597 *ctx)
{
int ret, i;
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
ret = regulator_set_load(ctx->supplies[i].consumer,
regulator_enable_loads[i]);
if (ret)
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret < 0)
return ret;
/*
* Reset sequence of truly panel requires the panel to be
* out of reset for 10ms, followed by being held in reset
* for 10ms and then out again
*/
gpiod_set_value(ctx->reset_gpio, 0);
usleep_range(10000, 20000);
gpiod_set_value(ctx->reset_gpio, 1);
usleep_range(10000, 20000);
gpiod_set_value(ctx->reset_gpio, 0);
return 0;
}
static int truly_nt35597_power_off(struct truly_nt35597 *ctx)
{
int ret = 0;
int i;
gpiod_set_value(ctx->reset_gpio, 1);
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
ret = regulator_set_load(ctx->supplies[i].consumer,
regulator_disable_loads[i]);
if (ret) {
DRM_DEV_ERROR(ctx->dev,
"regulator_set_load failed %d\n", ret);
return ret;
}
}
ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret) {
DRM_DEV_ERROR(ctx->dev,
"regulator_bulk_disable failed %d\n", ret);
}
return ret;
}
static int truly_nt35597_disable(struct drm_panel *panel)
{
struct truly_nt35597 *ctx = panel_to_ctx(panel);
int ret;
if (!ctx->enabled)
return 0;
if (ctx->backlight) {
ret = backlight_disable(ctx->backlight);
if (ret < 0)
DRM_DEV_ERROR(ctx->dev, "backlight disable failed %d\n",
ret);
}
ctx->enabled = false;
return 0;
}
static int truly_nt35597_unprepare(struct drm_panel *panel)
{
struct truly_nt35597 *ctx = panel_to_ctx(panel);
int ret = 0;
if (!ctx->prepared)
return 0;
ctx->dsi[0]->mode_flags = 0;
ctx->dsi[1]->mode_flags = 0;
ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_OFF);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev,
"set_display_off cmd failed ret = %d\n",
ret);
}
/* 120ms delay required here as per DCS spec */
msleep(120);
ret = truly_dcs_write(panel, MIPI_DCS_ENTER_SLEEP_MODE);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev,
"enter_sleep cmd failed ret = %d\n", ret);
}
ret = truly_nt35597_power_off(ctx);
if (ret < 0)
DRM_DEV_ERROR(ctx->dev, "power_off failed ret = %d\n", ret);
ctx->prepared = false;
return ret;
}
static int truly_nt35597_prepare(struct drm_panel *panel)
{
struct truly_nt35597 *ctx = panel_to_ctx(panel);
int ret;
int i;
const struct cmd_set *panel_on_cmds;
const struct nt35597_config *config;
u32 num_cmds;
if (ctx->prepared)
return 0;
ret = truly_35597_power_on(ctx);
if (ret < 0)
return ret;
ctx->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
config = ctx->config;
panel_on_cmds = config->panel_on_cmds;
num_cmds = config->num_on_cmds;
for (i = 0; i < num_cmds; i++) {
ret = truly_dcs_write_buf(panel,
panel_on_cmds[i].size,
panel_on_cmds[i].commands);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev,
"cmd set tx failed i = %d ret = %d\n",
i, ret);
goto power_off;
}
}
ret = truly_dcs_write(panel, MIPI_DCS_EXIT_SLEEP_MODE);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev,
"exit_sleep_mode cmd failed ret = %d\n",
ret);
goto power_off;
}
/* Per DSI spec wait 120ms after sending exit sleep DCS command */
msleep(120);
ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_ON);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev,
"set_display_on cmd failed ret = %d\n", ret);
goto power_off;
}
/* Per DSI spec wait 120ms after sending set_display_on DCS command */
msleep(120);
ctx->prepared = true;
return 0;
power_off:
if (truly_nt35597_power_off(ctx))
DRM_DEV_ERROR(ctx->dev, "power_off failed\n");
return ret;
}
static int truly_nt35597_enable(struct drm_panel *panel)
{
struct truly_nt35597 *ctx = panel_to_ctx(panel);
int ret;
if (ctx->enabled)
return 0;
if (ctx->backlight) {
ret = backlight_enable(ctx->backlight);
if (ret < 0)
DRM_DEV_ERROR(ctx->dev, "backlight enable failed %d\n",
ret);
}
ctx->enabled = true;
return 0;
}
static int truly_nt35597_get_modes(struct drm_panel *panel)
{
struct drm_connector *connector = panel->connector;
struct truly_nt35597 *ctx = panel_to_ctx(panel);
struct drm_display_mode *mode;
const struct nt35597_config *config;
config = ctx->config;
mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_DEV_ERROR(ctx->dev,
"failed to create a new display mode\n");
return 0;
}
connector->display_info.width_mm = config->width_mm;
connector->display_info.height_mm = config->height_mm;
drm_mode_copy(mode, config->dm);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
return 1;
}
static const struct drm_panel_funcs truly_nt35597_drm_funcs = {
.disable = truly_nt35597_disable,
.unprepare = truly_nt35597_unprepare,
.prepare = truly_nt35597_prepare,
.enable = truly_nt35597_enable,
.get_modes = truly_nt35597_get_modes,
};
static int truly_nt35597_panel_add(struct truly_nt35597 *ctx)
{
struct device *dev = ctx->dev;
int ret, i;
const struct nt35597_config *config;
config = ctx->config;
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
ctx->supplies[i].supply = regulator_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
ctx->supplies);
if (ret < 0)
return ret;
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset_gpio)) {
DRM_DEV_ERROR(dev, "cannot get reset gpio %ld\n",
PTR_ERR(ctx->reset_gpio));
return PTR_ERR(ctx->reset_gpio);
}
ctx->mode_gpio = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW);
if (IS_ERR(ctx->mode_gpio)) {
DRM_DEV_ERROR(dev, "cannot get mode gpio %ld\n",
PTR_ERR(ctx->mode_gpio));
return PTR_ERR(ctx->mode_gpio);
}
/* dual port */
gpiod_set_value(ctx->mode_gpio, 0);
drm_panel_init(&ctx->panel);
ctx->panel.dev = dev;
ctx->panel.funcs = &truly_nt35597_drm_funcs;
drm_panel_add(&ctx->panel);
return 0;
}
static const struct drm_display_mode qcom_sdm845_mtp_2k_mode = {
.name = "1440x2560",
.clock = 268316,
.hdisplay = 1440,
.hsync_start = 1440 + 200,
.hsync_end = 1440 + 200 + 32,
.htotal = 1440 + 200 + 32 + 64,
.vdisplay = 2560,
.vsync_start = 2560 + 8,
.vsync_end = 2560 + 8 + 1,
.vtotal = 2560 + 8 + 1 + 7,
.vrefresh = 60,
.flags = 0,
};
static const struct nt35597_config nt35597_dir = {
.width_mm = 74,
.height_mm = 131,
.panel_name = "qcom_sdm845_mtp_2k_panel",
.dm = &qcom_sdm845_mtp_2k_mode,
.panel_on_cmds = qcom_2k_panel_magic_cmds,
.num_on_cmds = ARRAY_SIZE(qcom_2k_panel_magic_cmds),
};
static int truly_nt35597_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct truly_nt35597 *ctx;
struct mipi_dsi_device *dsi1_device;
struct device_node *dsi1;
struct mipi_dsi_host *dsi1_host;
struct mipi_dsi_device *dsi_dev;
int ret = 0;
int i;
const struct mipi_dsi_device_info info = {
.type = "trulynt35597",
.channel = 0,
.node = NULL,
};
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
/*
* This device represents itself as one with two input ports which are
* fed by the output ports of the two DSI controllers . The DSI0 is
* the master controller and has most of the panel related info in its
* child node.
*/
ctx->config = of_device_get_match_data(dev);
if (!ctx->config) {
dev_err(dev, "missing device configuration\n");
return -ENODEV;
}
dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
if (!dsi1) {
DRM_DEV_ERROR(dev,
"failed to get remote node for dsi1_device\n");
return -ENODEV;
}
dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);
of_node_put(dsi1);
if (!dsi1_host) {
DRM_DEV_ERROR(dev, "failed to find dsi host\n");
return -EPROBE_DEFER;
}
/* register the second DSI device */
dsi1_device = mipi_dsi_device_register_full(dsi1_host, &info);
if (IS_ERR(dsi1_device)) {
DRM_DEV_ERROR(dev, "failed to create dsi device\n");
return PTR_ERR(dsi1_device);
}
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dev = dev;
ctx->dsi[0] = dsi;
ctx->dsi[1] = dsi1_device;
ret = truly_nt35597_panel_add(ctx);
if (ret) {
DRM_DEV_ERROR(dev, "failed to add panel\n");
goto err_panel_add;
}
for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
dsi_dev = ctx->dsi[i];
dsi_dev->lanes = 4;
dsi_dev->format = MIPI_DSI_FMT_RGB888;
dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM |
MIPI_DSI_CLOCK_NON_CONTINUOUS;
ret = mipi_dsi_attach(dsi_dev);
if (ret < 0) {
DRM_DEV_ERROR(dev,
"dsi attach failed i = %d\n", i);
goto err_dsi_attach;
}
}
return 0;
err_dsi_attach:
drm_panel_remove(&ctx->panel);
err_panel_add:
mipi_dsi_device_unregister(dsi1_device);
return ret;
}
static int truly_nt35597_remove(struct mipi_dsi_device *dsi)
{
struct truly_nt35597 *ctx = mipi_dsi_get_drvdata(dsi);
if (ctx->dsi[0])
mipi_dsi_detach(ctx->dsi[0]);
if (ctx->dsi[1]) {
mipi_dsi_detach(ctx->dsi[1]);
mipi_dsi_device_unregister(ctx->dsi[1]);
}
drm_panel_remove(&ctx->panel);
return 0;
}
static const struct of_device_id truly_nt35597_of_match[] = {
{
.compatible = "truly,nt35597-2K-display",
.data = &nt35597_dir,
},
{ }
};
MODULE_DEVICE_TABLE(of, truly_nt35597_of_match);
static struct mipi_dsi_driver truly_nt35597_driver = {
.driver = {
.name = "panel-truly-nt35597",
.of_match_table = truly_nt35597_of_match,
},
.probe = truly_nt35597_probe,
.remove = truly_nt35597_remove,
};
module_mipi_dsi_driver(truly_nt35597_driver);
MODULE_DESCRIPTION("Truly NT35597 DSI Panel Driver");
MODULE_LICENSE("GPL v2");

View File

@ -84,6 +84,7 @@ static int qxl_check_header(struct qxl_ring *ring)
int ret;
struct qxl_ring_header *header = &(ring->ring->header);
unsigned long flags;
spin_lock_irqsave(&ring->lock, flags);
ret = header->prod - header->cons < header->num_items;
if (ret == 0)
@ -97,6 +98,7 @@ int qxl_check_idle(struct qxl_ring *ring)
int ret;
struct qxl_ring_header *header = &(ring->ring->header);
unsigned long flags;
spin_lock_irqsave(&ring->lock, flags);
ret = header->prod == header->cons;
spin_unlock_irqrestore(&ring->lock, flags);
@ -110,6 +112,7 @@ int qxl_ring_push(struct qxl_ring *ring,
uint8_t *elt;
int idx, ret;
unsigned long flags;
spin_lock_irqsave(&ring->lock, flags);
if (header->prod - header->cons == header->num_items) {
header->notify_on_cons = header->cons + 1;
@ -156,6 +159,7 @@ static bool qxl_ring_pop(struct qxl_ring *ring,
volatile uint8_t *ring_elt;
int idx;
unsigned long flags;
spin_lock_irqsave(&ring->lock, flags);
if (header->cons == header->prod) {
header->notify_on_prod = header->cons + 1;
@ -365,7 +369,6 @@ void qxl_io_flush_surfaces(struct qxl_device *qdev)
wait_for_io_cmd(qdev, 0, QXL_IO_FLUSH_SURFACES_ASYNC);
}
void qxl_io_destroy_primary(struct qxl_device *qdev)
{
wait_for_io_cmd(qdev, 0, QXL_IO_DESTROY_PRIMARY_ASYNC);
@ -373,7 +376,7 @@ void qxl_io_destroy_primary(struct qxl_device *qdev)
}
void qxl_io_create_primary(struct qxl_device *qdev,
unsigned offset, struct qxl_bo *bo)
unsigned int offset, struct qxl_bo *bo)
{
struct qxl_surface_create *create;

View File

@ -34,7 +34,6 @@
#include "qxl_drv.h"
#include "qxl_object.h"
#if defined(CONFIG_DEBUG_FS)
static int
qxl_debugfs_irq_received(struct seq_file *m, void *data)
@ -102,9 +101,9 @@ qxl_debugfs_init(struct drm_minor *minor)
int qxl_debugfs_add_files(struct qxl_device *qdev,
struct drm_info_list *files,
unsigned nfiles)
unsigned int nfiles)
{
unsigned i;
unsigned int i;
for (i = 0; i < qdev->debugfs_count; i++) {
if (qdev->debugfs[i].files == files) {

View File

@ -28,7 +28,6 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef H_QXL_DEV
#define H_QXL_DEV

View File

@ -253,12 +253,13 @@ static struct mode_size {
};
static int qxl_add_common_modes(struct drm_connector *connector,
unsigned pwidth,
unsigned pheight)
unsigned int pwidth,
unsigned int pheight)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode = NULL;
int i;
for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h,
60, false, false, false);
@ -315,6 +316,7 @@ static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc,
oldcount = qdev->monitors_config->count;
if (crtc->state->active) {
struct drm_display_mode *mode = &crtc->mode;
head.width = mode->hdisplay;
head.height = mode->vdisplay;
head.x = crtc->x;
@ -391,9 +393,9 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned flags, unsigned color,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips,
unsigned num_clips)
unsigned int num_clips)
{
/* TODO: vmwgfx where this was cribbed from had locking. Why? */
struct qxl_device *qdev = fb->dev->dev_private;
@ -917,8 +919,8 @@ free_mem:
static int qxl_conn_get_modes(struct drm_connector *connector)
{
unsigned pwidth = 1024;
unsigned pheight = 768;
unsigned int pwidth = 1024;
unsigned int pheight = 768;
int ret = 0;
ret = qxl_add_monitors_config_modes(connector, &pwidth, &pheight);
@ -938,8 +940,8 @@ static enum drm_mode_status qxl_conn_mode_valid(struct drm_connector *connector,
/* TODO: is this called for user defined modes? (xrandr --add-mode)
* TODO: check that the mode fits in the framebuffer */
if(qdev->monitors_config_width == mode->hdisplay &&
qdev->monitors_config_height == mode->vdisplay)
if (qdev->monitors_config_width == mode->hdisplay &&
qdev->monitors_config_height == mode->vdisplay)
return MODE_OK;
for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
@ -958,7 +960,6 @@ static struct drm_encoder *qxl_best_encoder(struct drm_connector *connector)
return &qxl_output->enc;
}
static const struct drm_encoder_helper_funcs qxl_enc_helper_funcs = {
};

View File

@ -25,7 +25,7 @@
static int alloc_clips(struct qxl_device *qdev,
struct qxl_release *release,
unsigned num_clips,
unsigned int num_clips,
struct qxl_bo **clips_bo)
{
int size = sizeof(struct qxl_clip_rects) + sizeof(struct qxl_rect) * num_clips;
@ -37,7 +37,7 @@ static int alloc_clips(struct qxl_device *qdev,
* the qxl_clip_rects. This is *not* the same as the memory allocated
* on the device, it is offset to qxl_clip_rects.chunk.data */
static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev,
unsigned num_clips,
unsigned int num_clips,
struct qxl_bo *clips_bo)
{
struct qxl_clip_rects *dev_clips;
@ -168,6 +168,7 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image,
int ret;
struct qxl_drm_image *dimage;
struct qxl_bo *palette_bo = NULL;
if (stride == 0)
stride = depth * width / 8;
@ -214,6 +215,7 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image,
if (depth == 1) {
void *ptr;
ret = qxl_palette_create_1bit(palette_bo, release, qxl_fb_image);
ptr = qxl_bo_kmap_atomic_page(qdev, dimage->bo, 0);
@ -264,9 +266,9 @@ out_free_drawable:
void qxl_draw_dirty_fb(struct qxl_device *qdev,
struct drm_framebuffer *fb,
struct qxl_bo *bo,
unsigned flags, unsigned color,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips,
unsigned num_clips, int inc)
unsigned int num_clips, int inc)
{
/*
* TODO: if flags & DRM_MODE_FB_DIRTY_ANNOTATE_FILL then we should
@ -340,7 +342,6 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
if (ret)
goto out_release_backoff;
ret = qxl_image_init(qdev, release, dimage, surface_base,
left, top, width, height, depth, stride);
qxl_bo_kunmap(bo);

View File

@ -23,7 +23,6 @@
* Alon Levy
*/
#ifndef QXL_DRV_H
#define QXL_DRV_H
@ -83,16 +82,16 @@ struct qxl_bo {
struct ttm_placement placement;
struct ttm_buffer_object tbo;
struct ttm_bo_kmap_obj kmap;
unsigned pin_count;
unsigned int pin_count;
void *kptr;
int type;
/* Constant after initialization */
struct drm_gem_object gem_base;
bool is_primary; /* is this now a primary surface */
bool is_dumb;
unsigned int is_primary:1; /* is this now a primary surface */
unsigned int is_dumb:1;
struct qxl_bo *shadow;
bool hw_surf_alloc;
unsigned int hw_surf_alloc:1;
struct qxl_surface surf;
uint32_t surface_id;
struct qxl_release *surf_create;
@ -129,11 +128,10 @@ struct qxl_output {
struct qxl_mman {
struct ttm_bo_global_ref bo_global_ref;
struct drm_global_reference mem_global_ref;
bool mem_global_referenced;
unsigned int mem_global_referenced:1;
struct ttm_bo_device bdev;
};
struct qxl_memslot {
uint8_t generation;
uint64_t start_phys_addr;
@ -191,12 +189,12 @@ struct qxl_draw_fill {
*/
struct qxl_debugfs {
struct drm_info_list *files;
unsigned num_files;
unsigned int num_files;
};
int qxl_debugfs_add_files(struct qxl_device *rdev,
struct drm_info_list *files,
unsigned nfiles);
unsigned int nfiles);
int qxl_debugfs_fence_init(struct qxl_device *rdev);
struct qxl_device;
@ -231,7 +229,7 @@ struct qxl_device {
struct qxl_ram_header *ram_header;
bool primary_created;
unsigned int primary_created:1;
struct qxl_memslot *mem_slots;
uint8_t n_mem_slots;
@ -254,7 +252,7 @@ struct qxl_device {
atomic_t irq_received_display;
atomic_t irq_received_cursor;
atomic_t irq_received_io_cmd;
unsigned irq_received_error;
unsigned int irq_received_error;
wait_queue_head_t display_event;
wait_queue_head_t cursor_event;
wait_queue_head_t io_cmd_event;
@ -262,7 +260,7 @@ struct qxl_device {
/* debugfs */
struct qxl_debugfs debugfs[QXL_DEBUGFS_MAX_COMPONENTS];
unsigned debugfs_count;
unsigned int debugfs_count;
struct mutex update_area_mutex;
@ -372,7 +370,6 @@ int qxl_mode_dumb_mmap(struct drm_file *filp,
struct drm_device *dev,
uint32_t handle, uint64_t *offset_p);
/* qxl ttm */
int qxl_ttm_init(struct qxl_device *qdev);
void qxl_ttm_fini(struct qxl_device *qdev);
@ -398,7 +395,7 @@ void qxl_update_screen(struct qxl_device *qxl);
/* qxl io operations (qxl_cmd.c) */
void qxl_io_create_primary(struct qxl_device *qdev,
unsigned offset,
unsigned int offset,
struct qxl_bo *bo);
void qxl_io_destroy_primary(struct qxl_device *qdev);
void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id);
@ -449,9 +446,9 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image,
void qxl_draw_dirty_fb(struct qxl_device *qdev,
struct drm_framebuffer *fb,
struct qxl_bo *bo,
unsigned flags, unsigned color,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips,
unsigned num_clips, int inc);
unsigned int num_clips, int inc);
void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec);
@ -496,7 +493,7 @@ bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj);
int qxl_debugfs_add_files(struct qxl_device *qdev,
struct drm_info_list *files,
unsigned nfiles);
unsigned int nfiles);
int qxl_surface_id_alloc(struct qxl_device *qdev,
struct qxl_bo *surf);

View File

@ -38,6 +38,7 @@ int qxl_mode_dumb_create(struct drm_file *file_priv,
int r;
struct qxl_surface surf;
uint32_t pitch, format;
pitch = args->width * ((args->bpp + 1) / 8);
args->size = pitch * args->height;
args->size = ALIGN(args->size, PAGE_SIZE);
@ -52,7 +53,7 @@ int qxl_mode_dumb_create(struct drm_file *file_priv,
default:
return -EINVAL;
}
surf.width = args->width;
surf.height = args->height;
surf.stride = pitch;

View File

@ -134,9 +134,9 @@ out_unref:
*/
static int qxlfb_framebuffer_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned flags, unsigned color,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips,
unsigned num_clips)
unsigned int num_clips)
{
struct qxl_device *qdev = fb->dev->dev_private;
struct fb_info *info = qdev->fb_helper.fbdev;

View File

@ -136,6 +136,7 @@ qxl_image_init_helper(struct qxl_device *qdev,
int remain;
int page;
int size;
if (stride == linesize && chunk_stride == stride) {
remain = linesize * height;
page = 0;
@ -162,7 +163,8 @@ qxl_image_init_helper(struct qxl_device *qdev,
page++;
}
} else {
unsigned page_base, page_offset, out_offset;
unsigned int page_base, page_offset, out_offset;
for (i = 0 ; i < height ; ++i) {
i_data = (void *)data + i * stride;
remain = linesize;

View File

@ -85,6 +85,7 @@ static void
apply_reloc(struct qxl_device *qdev, struct qxl_reloc_info *info)
{
void *reloc_page;
reloc_page = qxl_bo_kmap_atomic_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK);
*(uint64_t *)(reloc_page + (info->dst_offset & ~PAGE_MASK)) = qxl_bo_physical_address(qdev,
info->src_bo,
@ -189,6 +190,7 @@ static int qxl_process_single_command(struct qxl_device *qdev,
{
struct qxl_drawable *draw = fb_cmd;
draw->mm_time = qdev->rom->mm_clock;
}

View File

@ -92,6 +92,7 @@ void qxl_reinit_memslots(struct qxl_device *qdev)
static void qxl_gc_work(struct work_struct *work)
{
struct qxl_device *qdev = container_of(work, struct qxl_device, gc_work);
qxl_garbage_collect(qdev);
}
@ -284,7 +285,6 @@ int qxl_device_init(struct qxl_device *qdev,
(unsigned long)qdev->surfaceram_base,
(unsigned long)qdev->surfaceram_size);
INIT_WORK(&qdev->gc_work, qxl_gc_work);
return 0;

View File

@ -54,7 +54,7 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned)
{
u32 c = 0;
u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0;
unsigned i;
unsigned int i;
qbo->placement.placement = qbo->placements;
qbo->placement.busy_placement = qbo->placements;
@ -74,7 +74,6 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned)
}
}
int qxl_bo_create(struct qxl_device *qdev,
unsigned long size, bool kernel, bool pinned, u32 domain,
struct qxl_surface *surf,
@ -266,7 +265,6 @@ static int __qxl_bo_unpin(struct qxl_bo *bo)
return r;
}
/*
* Reserve the BO before pinning the object. If the BO was reserved
* beforehand, use the internal version directly __qxl_bo_pin.
@ -335,6 +333,7 @@ void qxl_bo_fini(struct qxl_device *qdev)
int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo)
{
int ret;
if (bo->type == QXL_GEM_DOMAIN_SURFACE && bo->surface_id == 0) {
/* allocate a surface id for this surface now */
ret = qxl_surface_id_alloc(qdev, bo);

Some files were not shown because too many files have changed in this diff Show More