forked from Minki/linux
drm-misc-next for 4.19:
Cross-subsystem Changes: - many dt-bindings Doc changes Core Changes: - Encoder clean ups (Ville Syrjälä) - Connector Writeback improvements(Boris Brezillon) - Fake vblank support (Boris Brezillon) - API for in-kernel clients (Noralf Trønnes) - improvements to the path of finding panels(Boris Brezillon) Driver Changes: - initial support for the virtual display driver - vkms(Haneen Mohammed and Rodrigo Siqueira) - panel: add Rocktech RK070ER9427 LCD support (Jagan Teki) - panel: add support for the EDT ETM0700G0EDH6 and EDT ETM0700G0BDH6(Jan Tuerk) - panel: add DLC DLC0700YZG-1 (Philipp Zabel) - panel: add support for BOE HV070WSA-100 (Andrzej Hajda) - panel: add newhaven, nhd-4.3-480272ef-atxl LCD (Tomi Valkeinen) - panel: add support for Innolux G070Y2-L01 (Christoph Fritz) - panel: add support for DataImage SCF0700C48GGU18 (Michal Vokáč) - panel: add support for Sharp LQ035Q7DB03 (Vladimir Zapolskiy) - panel: p079zca: Refactor panel driver to support multiple panels (Lin Huang) - sun4i: Add R40 display engine compatible(Jernej Skrabec) -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJbRqmlAAoJEEN0HIUfOBk0I2wQALu/Kk5y7gVGZWFMP/uImqB5 vbkNXX5rdzFfvXHbNE9GHVLtMLIJS705pou+wzuUzil3C/xjUUrP7mUdWEVgNl4s hj6norNP2Jhd6v+Jua5dwhamDi6RHcroWc6c3SYt//JjcZKtOb/oqwt+7Bu0I+yi I+62iMpOVjxOIaygthZiJD89Q0mRsFBELRC1iYm1SxXF0l1/9Hf5Xt9PbUvoMscb /74e0MqdqKNTopWz7TLIZT6mHDWLM8IklLkMBDEClZQUMQAfIvPSKhe7KGXnOB0T PzBd1MJeQ/rPyyJJtWY5BTng6C38XW5xmq+I4YUSIfRJZjJHf4vBEcJO3ErIxBGj LE957F/rT8IYxN0Z31wH0DZhP8KpL1KIXy3AOb7Yqqbu+SemKT/WaepAdFvKfFbO 5BxBgeFSQg9soHv7ndjOWDQal1AmuIhK/UP18ytYO9T54fPBJ1TaaHUWacYnvo/0 oQdCTghvFuZgpP3Tl3IisFO1xDpLq4Y4VqvP4xwSj2Id1MUp6MGh9W/A39bc8FA0 JJcvQYYCcnf4GVQQem+QFk+bpXmzLZW3+/cMpU72zrP+8OKrxD5PwxLmhuqdRzLR 2lMT5gXEuPl8K8wyOLJDiWbXfZB9jn7Mm+5lySKGKu6NYzLN+cbLPA7+tVsqrWMu XLd++4ntNy5hdvS/TO7j =Bepe -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2018-07-11' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for 4.19: Cross-subsystem Changes: - many dt-bindings Doc changes Core Changes: - Encoder clean ups (Ville Syrjälä) - Connector Writeback improvements(Boris Brezillon) - Fake vblank support (Boris Brezillon) - API for in-kernel clients (Noralf Trønnes) - improvements to the path of finding panels(Boris Brezillon) Driver Changes: - initial support for the virtual display driver - vkms(Haneen Mohammed and Rodrigo Siqueira) - panel: add Rocktech RK070ER9427 LCD support (Jagan Teki) - panel: add support for the EDT ETM0700G0EDH6 and EDT ETM0700G0BDH6(Jan Tuerk) - panel: add DLC DLC0700YZG-1 (Philipp Zabel) - panel: add support for BOE HV070WSA-100 (Andrzej Hajda) - panel: add newhaven, nhd-4.3-480272ef-atxl LCD (Tomi Valkeinen) - panel: add support for Innolux G070Y2-L01 (Christoph Fritz) - panel: add support for DataImage SCF0700C48GGU18 (Michal Vokáč) - panel: add support for Sharp LQ035Q7DB03 (Vladimir Zapolskiy) - panel: p079zca: Refactor panel driver to support multiple panels (Lin Huang) - sun4i: Add R40 display engine compatible(Jernej Skrabec) Signed-off-by: Dave Airlie <airlied@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20180712011137.GA26620@juma
This commit is contained in:
commit
54c88a029a
@ -74,6 +74,12 @@ Required properties for DSI:
|
||||
The 3 clocks output from the DSI analog PHY: dsi[01]_byte,
|
||||
dsi[01]_ddr2, and dsi[01]_ddr
|
||||
|
||||
Required properties for the TXP (writeback) block:
|
||||
- compatible: Should be "brcm,bcm2835-txp"
|
||||
- reg: Physical base address and length of the TXP block's registers
|
||||
- interrupts: The interrupt number
|
||||
See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
|
||||
|
||||
[1] Documentation/devicetree/bindings/media/video-interfaces.txt
|
||||
|
||||
Example:
|
||||
|
@ -0,0 +1,28 @@
|
||||
BOE HV070WSA-100 7.01" WSVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "boe,hv070wsa-100"
|
||||
- power-supply: regulator to provide the VCC supply voltage (3.3 volts)
|
||||
- enable-gpios: GPIO pin to enable and disable panel (active high)
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
||||
|
||||
The device node can contain one 'port' child node with one child
|
||||
'endpoint' node, according to the bindings defined in [1]. This
|
||||
node should describe panel's video bus.
|
||||
|
||||
[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
|
||||
|
||||
Example:
|
||||
|
||||
panel: panel {
|
||||
compatible = "boe,hv070wsa-100";
|
||||
power-supply = <&vcc_3v3_reg>;
|
||||
enable-gpios = <&gpd1 3 GPIO_ACTIVE_HIGH>;
|
||||
port {
|
||||
panel_ep: endpoint {
|
||||
remote-endpoint = <&bridge_out_ep>;
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
DataImage, Inc. 7" WVGA (800x480) TFT LCD panel with 24-bit parallel interface.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "dataimage,scf0700c48ggu18"
|
||||
- power-supply: as specified in the base binding
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,13 @@
|
||||
DLC Display Co. DLC0700YZG-1 7.0" WSVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "dlc,dlc0700yzg-1"
|
||||
- power-supply: See simple-panel.txt
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios: See panel-common.txt
|
||||
- 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.
|
@ -0,0 +1,39 @@
|
||||
Emerging Display Technology Corp. Displays
|
||||
==========================================
|
||||
|
||||
|
||||
Display bindings for EDT Display Technology Corp. Displays which are
|
||||
compatible with the simple-panel binding, which is specified in
|
||||
simple-panel.txt
|
||||
|
||||
|
||||
5,7" WVGA TFT Panels
|
||||
--------------------
|
||||
|
||||
+-----------------+---------------------+-------------------------------------+
|
||||
| Identifier | compatbile | description |
|
||||
+=================+=====================+=====================================+
|
||||
| ET057090DHU | edt,et057090dhu | 5.7" VGA TFT LCD panel |
|
||||
+-----------------+---------------------+-------------------------------------+
|
||||
|
||||
|
||||
7,0" WVGA TFT Panels
|
||||
--------------------
|
||||
|
||||
+-----------------+---------------------+-------------------------------------+
|
||||
| Identifier | compatbile | description |
|
||||
+=================+=====================+=====================================+
|
||||
| ETM0700G0DH6 | edt,etm070080dh6 | WVGA TFT Display with capacitive |
|
||||
| | | Touchscreen |
|
||||
+-----------------+---------------------+-------------------------------------+
|
||||
| ETM0700G0BDH6 | edt,etm070080bdh6 | Same as ETM0700G0DH6 but with |
|
||||
| | | inverted pixel clock. |
|
||||
+-----------------+---------------------+-------------------------------------+
|
||||
| ETM0700G0EDH6 | edt,etm070080edh6 | Same display as the ETM0700G0BDH6, |
|
||||
| | | but with changed Hardware for the |
|
||||
| | | backlight and the touch interface |
|
||||
+-----------------+---------------------+-------------------------------------+
|
||||
| ET070080DH6 | edt,etm070080dh6 | Same timings as the ETM0700G0DH6, |
|
||||
| | | but with resistive touch. |
|
||||
+-----------------+---------------------+-------------------------------------+
|
||||
|
@ -1,10 +0,0 @@
|
||||
Emerging Display Technology Corp. ET070080DH6 7.0" WVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "edt,et070080dh6"
|
||||
|
||||
This panel is the same as ETM0700G0DH6 except for the touchscreen.
|
||||
ET070080DH6 is the model with resistive touch.
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -1,10 +0,0 @@
|
||||
Emerging Display Technology Corp. ETM0700G0DH6 7.0" WVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "edt,etm0700g0dh6"
|
||||
|
||||
This panel is the same as ET070080DH6 except for the touchscreen.
|
||||
ETM0700G0DH6 is the model with capacitive multitouch.
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,12 @@
|
||||
Innolux G070Y2-L01 7" WVGA (800x480) TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "innolux,g070y2-l01"
|
||||
- power-supply: as specified in the base binding
|
||||
|
||||
Optional properties:
|
||||
- backlight: as specified in the base binding
|
||||
- enable-gpios: as specified in the base binding
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,24 @@
|
||||
Innolux P097PFG 9.7" 1536x2048 TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "innolux,p097pfg"
|
||||
- reg: DSI virtual channel of the peripheral
|
||||
- avdd-supply: phandle of the regulator that provides positive voltage
|
||||
- avee-supply: phandle of the regulator that provides negative voltage
|
||||
- enable-gpios: panel enable gpio
|
||||
|
||||
Optional properties:
|
||||
- backlight: phandle of the backlight device attached to the panel
|
||||
|
||||
Example:
|
||||
|
||||
&mipi_dsi {
|
||||
panel {
|
||||
compatible = "innolux,p079zca";
|
||||
reg = <0>;
|
||||
avdd-supply = <...>;
|
||||
avee-supply = <...>;
|
||||
backlight = <&backlight>;
|
||||
enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
Kingdisplay KD097D04 9.7" 1536x2048 TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "kingdisplay,kd097d04"
|
||||
- reg: DSI virtual channel of the peripheral
|
||||
- power-supply: phandle of the regulator that provides the supply voltage
|
||||
- enable-gpios: panel enable gpio
|
||||
|
||||
Optional properties:
|
||||
- backlight: phandle of the backlight device attached to the panel
|
||||
|
||||
Example:
|
||||
|
||||
&mipi_dsi {
|
||||
panel {
|
||||
compatible = "kingdisplay,kd097d04";
|
||||
reg = <0>;
|
||||
power-supply = <...>;
|
||||
backlight = <&backlight>;
|
||||
enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
Emerging Display Technology Corp. 5.7" VGA TFT LCD panel
|
||||
Newhaven Display International 480 x 272 TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "edt,et057090dhu"
|
||||
- compatible: should be "newhaven,nhd-4.3-480272ef-atxl"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,25 @@
|
||||
Rocktech Display Ltd. RK070ER9427 800(RGB)x480 TFT LCD panel
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "rocktech,rk070er9427"
|
||||
|
||||
Optional properties:
|
||||
- backlight: phandle of the backlight device attached to the panel
|
||||
|
||||
Optional nodes:
|
||||
- Video port for LCD panel input.
|
||||
|
||||
Example:
|
||||
panel {
|
||||
compatible = "rocktech,rk070er9427";
|
||||
backlight = <&backlight_lcd>;
|
||||
|
||||
port {
|
||||
lcd_panel_in: endpoint {
|
||||
remote-endpoint = <&lcd_display_out>;
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
Sharp LQ035Q7DB03 3.5" QVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "sharp,lq035q7db03"
|
||||
- power-supply: phandle of the regulator that provides the supply voltage
|
||||
|
||||
Optional properties:
|
||||
- enable-gpios: GPIO pin to enable or disable the panel
|
||||
- backlight: phandle of the backlight device attached to the panel
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -101,9 +101,9 @@ DWC HDMI PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: value must be one of:
|
||||
* allwinner,sun50i-a64-hdmi-phy
|
||||
* allwinner,sun8i-a83t-hdmi-phy
|
||||
* allwinner,sun8i-h3-hdmi-phy
|
||||
* allwinner,sun50i-a64-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
|
||||
@ -147,6 +147,7 @@ Required properties:
|
||||
* allwinner,sun8i-a33-tcon
|
||||
* allwinner,sun8i-a83t-tcon-lcd
|
||||
* allwinner,sun8i-a83t-tcon-tv
|
||||
* allwinner,sun8i-r40-tcon-tv
|
||||
* allwinner,sun8i-v3s-tcon
|
||||
* allwinner,sun9i-a80-tcon-lcd
|
||||
* allwinner,sun9i-a80-tcon-tv
|
||||
@ -181,7 +182,7 @@ For TCONs with channel 0, there is one more clock required:
|
||||
For TCONs with channel 1, there is one more clock required:
|
||||
- 'tcon-ch1': The clock driving the TCON channel 1
|
||||
|
||||
When TCON support LVDS (all TCONs except TV TCON on A83T and those found
|
||||
When TCON support LVDS (all TCONs except TV TCONs on A83T, R40 and those found
|
||||
in A13, H3, H5 and V3s SoCs), you need one more reset line:
|
||||
- 'lvds': The reset line driving the LVDS logic
|
||||
|
||||
@ -399,6 +400,7 @@ Required properties:
|
||||
* allwinner,sun8i-a33-display-engine
|
||||
* allwinner,sun8i-a83t-display-engine
|
||||
* allwinner,sun8i-h3-display-engine
|
||||
* allwinner,sun8i-r40-display-engine
|
||||
* allwinner,sun8i-v3s-display-engine
|
||||
* allwinner,sun9i-a80-display-engine
|
||||
|
||||
|
@ -86,6 +86,7 @@ cubietech Cubietech, Ltd.
|
||||
cypress Cypress Semiconductor Corporation
|
||||
cznic CZ.NIC, z.s.p.o.
|
||||
dallas Maxim Integrated Products (formerly Dallas Semiconductor)
|
||||
dataimage DataImage, Inc.
|
||||
davicom DAVICOM Semiconductor, Inc.
|
||||
delta Delta Electronics, Inc.
|
||||
denx Denx Software Engineering
|
||||
@ -94,6 +95,7 @@ dh DH electronics GmbH
|
||||
digi Digi International Inc.
|
||||
digilent Diglent, Inc.
|
||||
dioo Dioo Microcircuit Co., Ltd
|
||||
dlc DLC Display Co., Ltd.
|
||||
dlg Dialog Semiconductor
|
||||
dlink D-Link Corporation
|
||||
dmo Data Modul AG
|
||||
@ -189,6 +191,7 @@ keymile Keymile GmbH
|
||||
khadas Khadas
|
||||
kiebackpeter Kieback & Peter GmbH
|
||||
kinetic Kinetic Technologies
|
||||
kingdisplay King & Display Technology Co., Ltd.
|
||||
kingnovel Kingnovel Technology Co., Ltd.
|
||||
koe Kaohsiung Opto-Electronics Inc.
|
||||
kosagi Sutajio Ko-Usagi PTE Ltd.
|
||||
|
12
Documentation/gpu/drm-client.rst
Normal file
12
Documentation/gpu/drm-client.rst
Normal file
@ -0,0 +1,12 @@
|
||||
=================
|
||||
Kernel clients
|
||||
=================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_client.c
|
||||
:doc: overview
|
||||
|
||||
.. kernel-doc:: include/drm/drm_client.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_client.c
|
||||
:export:
|
@ -10,6 +10,7 @@ Linux GPU Driver Developer's Guide
|
||||
drm-kms
|
||||
drm-kms-helpers
|
||||
drm-uapi
|
||||
drm-client
|
||||
drivers
|
||||
vga-switcheroo
|
||||
vgaarbiter
|
||||
|
28
Documentation/gpu/v3d.rst
Normal file
28
Documentation/gpu/v3d.rst
Normal file
@ -0,0 +1,28 @@
|
||||
=====================================
|
||||
drm/v3d Broadcom V3D Graphics Driver
|
||||
=====================================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/v3d/v3d_drv.c
|
||||
:doc: Broadcom V3D Graphics Driver
|
||||
|
||||
GPU buffer object (BO) management
|
||||
---------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/v3d/v3d_bo.c
|
||||
:doc: V3D GEM BO management support
|
||||
|
||||
Address space management
|
||||
===========================================
|
||||
.. kernel-doc:: drivers/gpu/drm/v3d/v3d_mmu.c
|
||||
:doc: Broadcom V3D MMU
|
||||
|
||||
GPU Scheduling
|
||||
===========================================
|
||||
.. kernel-doc:: drivers/gpu/drm/v3d/v3d_sched.c
|
||||
:doc: Broadcom V3D scheduling
|
||||
|
||||
Interrupts
|
||||
--------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/v3d/v3d_irq.c
|
||||
:doc: Interrupt management for the V3D engine
|
@ -213,6 +213,17 @@ config DRM_VGEM
|
||||
as used by Mesa's software renderer for enhanced performance.
|
||||
If M is selected the module will be called vgem.
|
||||
|
||||
config DRM_VKMS
|
||||
tristate "Virtual KMS (EXPERIMENTAL)"
|
||||
depends on DRM
|
||||
select DRM_KMS_HELPER
|
||||
default n
|
||||
help
|
||||
Virtual Kernel Mode-Setting (VKMS) is used for testing or for
|
||||
running GPU in a headless machines. Choose this option to get
|
||||
a VKMS.
|
||||
|
||||
If M is selected the module will be called vkms.
|
||||
|
||||
source "drivers/gpu/drm/exynos/Kconfig"
|
||||
|
||||
|
@ -18,7 +18,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
|
||||
drm_encoder.o drm_mode_object.o drm_property.o \
|
||||
drm_plane.o drm_color_mgmt.o drm_print.o \
|
||||
drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \
|
||||
drm_syncobj.o drm_lease.o drm_writeback.o
|
||||
drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o
|
||||
|
||||
drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
|
||||
drm-$(CONFIG_DRM_VM) += drm_vm.o
|
||||
@ -69,6 +69,7 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/
|
||||
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
|
||||
obj-$(CONFIG_DRM_VIA) +=via/
|
||||
obj-$(CONFIG_DRM_VGEM) += vgem/
|
||||
obj-$(CONFIG_DRM_VKMS) += vkms/
|
||||
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
||||
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
|
||||
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
|
||||
|
@ -212,30 +212,21 @@ static void
|
||||
amdgpu_connector_update_scratch_regs(struct drm_connector *connector,
|
||||
enum drm_connector_status status)
|
||||
{
|
||||
struct drm_encoder *best_encoder = NULL;
|
||||
struct drm_encoder *encoder = NULL;
|
||||
struct drm_encoder *best_encoder;
|
||||
struct drm_encoder *encoder;
|
||||
const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private;
|
||||
bool connected;
|
||||
int i;
|
||||
|
||||
best_encoder = connector_funcs->best_encoder(connector);
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if ((encoder == best_encoder) && (status == connector_status_connected))
|
||||
connected = true;
|
||||
else
|
||||
connected = false;
|
||||
|
||||
amdgpu_atombios_encoder_set_bios_scratch_regs(connector, encoder, connected);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,17 +237,11 @@ amdgpu_connector_find_encoder(struct drm_connector *connector,
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (encoder->encoder_type == encoder_type)
|
||||
return encoder;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -360,11 +345,13 @@ static int amdgpu_connector_ddc_get_modes(struct drm_connector *connector)
|
||||
static struct drm_encoder *
|
||||
amdgpu_connector_best_single_encoder(struct drm_connector *connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
|
||||
/* pick the first one */
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i)
|
||||
return encoder;
|
||||
|
||||
/* pick the encoder ids */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -985,9 +972,8 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct amdgpu_device *adev = dev->dev_private;
|
||||
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
|
||||
struct drm_encoder *encoder = NULL;
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
int i, r;
|
||||
int r;
|
||||
enum drm_connector_status ret = connector_status_disconnected;
|
||||
bool dret = false, broken_edid = false;
|
||||
|
||||
@ -1077,14 +1063,10 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
/* find analog encoder */
|
||||
if (amdgpu_connector->dac_load_detect) {
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (encoder->encoder_type != DRM_MODE_ENCODER_DAC &&
|
||||
encoder->encoder_type != DRM_MODE_ENCODER_TVDAC)
|
||||
continue;
|
||||
@ -1132,18 +1114,11 @@ exit:
|
||||
static struct drm_encoder *
|
||||
amdgpu_connector_dvi_encoder(struct drm_connector *connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (amdgpu_connector->use_digital == true) {
|
||||
if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
|
||||
return encoder;
|
||||
@ -1158,8 +1133,9 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector)
|
||||
|
||||
/* then check use digitial */
|
||||
/* pick the first one */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i)
|
||||
return encoder;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1296,15 +1272,7 @@ u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn
|
||||
struct amdgpu_encoder *amdgpu_encoder;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
amdgpu_encoder = to_amdgpu_encoder(encoder);
|
||||
|
||||
switch (amdgpu_encoder->encoder_id) {
|
||||
@ -1326,14 +1294,7 @@ static bool amdgpu_connector_encoder_is_hbr2(struct drm_connector *connector)
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
amdgpu_encoder = to_amdgpu_encoder(encoder);
|
||||
if (amdgpu_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2)
|
||||
found = true;
|
||||
|
@ -269,25 +269,18 @@ static int dce_virtual_early_init(void *handle)
|
||||
static struct drm_encoder *
|
||||
dce_virtual_encoder(struct drm_connector *connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
|
||||
return encoder;
|
||||
}
|
||||
|
||||
/* pick the first one */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i)
|
||||
return encoder;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1152,7 +1152,7 @@ static int cdns_dsi_attach(struct mipi_dsi_host *host,
|
||||
np = of_node_get(dev->dev.of_node);
|
||||
|
||||
panel = of_drm_find_panel(np);
|
||||
if (panel) {
|
||||
if (!IS_ERR(panel)) {
|
||||
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
|
||||
} else {
|
||||
bridge = of_drm_find_bridge(dev->dev.of_node);
|
||||
|
@ -68,9 +68,9 @@ static int lvds_encoder_probe(struct platform_device *pdev)
|
||||
|
||||
panel = of_drm_find_panel(panel_node);
|
||||
of_node_put(panel_node);
|
||||
if (!panel) {
|
||||
if (IS_ERR(panel)) {
|
||||
dev_dbg(&pdev->dev, "panel not found, deferring probe\n");
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(panel);
|
||||
}
|
||||
|
||||
lvds_encoder->panel_bridge =
|
||||
|
@ -1111,6 +1111,7 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
|
||||
drm_printf(p, "\tcrtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest));
|
||||
drm_printf(p, "\tsrc-pos=" DRM_RECT_FP_FMT "\n", DRM_RECT_FP_ARG(&src));
|
||||
drm_printf(p, "\trotation=%x\n", state->rotation);
|
||||
drm_printf(p, "\tnormalized-zpos=%x\n", state->normalized_zpos);
|
||||
drm_printf(p, "\tcolor-encoding=%s\n",
|
||||
drm_get_color_encoding_name(state->color_encoding));
|
||||
drm_printf(p, "\tcolor-range=%s\n",
|
||||
@ -2427,6 +2428,7 @@ static int prepare_signaling(struct drm_device *dev,
|
||||
}
|
||||
|
||||
for_each_new_connector_in_state(state, conn, conn_state, i) {
|
||||
struct drm_writeback_connector *wb_conn;
|
||||
struct drm_writeback_job *job;
|
||||
struct drm_out_fence_state *f;
|
||||
struct dma_fence *fence;
|
||||
@ -2450,7 +2452,8 @@ static int prepare_signaling(struct drm_device *dev,
|
||||
f[*num_fences].out_fence_ptr = fence_ptr;
|
||||
*fence_state = f;
|
||||
|
||||
fence = drm_writeback_get_out_fence((struct drm_writeback_connector *)conn);
|
||||
wb_conn = drm_connector_to_writeback(conn);
|
||||
fence = drm_writeback_get_out_fence(wb_conn);
|
||||
if (!fence)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -645,7 +645,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
connectors_mask += BIT(i);
|
||||
connectors_mask |= BIT(i);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1184,10 +1184,12 @@ static void drm_atomic_helper_commit_writebacks(struct drm_device *dev,
|
||||
const struct drm_connector_helper_funcs *funcs;
|
||||
|
||||
funcs = connector->helper_private;
|
||||
if (!funcs->atomic_commit)
|
||||
continue;
|
||||
|
||||
if (new_conn_state->writeback_job && new_conn_state->writeback_job->fb) {
|
||||
WARN_ON(connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
|
||||
funcs->atomic_commit(connector, new_conn_state->writeback_job);
|
||||
funcs->atomic_commit(connector, new_conn_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1448,6 +1450,8 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state)
|
||||
|
||||
drm_atomic_helper_commit_modeset_enables(dev, old_state);
|
||||
|
||||
drm_atomic_helper_fake_vblank(old_state);
|
||||
|
||||
drm_atomic_helper_commit_hw_done(old_state);
|
||||
|
||||
drm_atomic_helper_wait_for_vblanks(dev, old_state);
|
||||
@ -1477,6 +1481,8 @@ void drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state)
|
||||
drm_atomic_helper_commit_planes(dev, old_state,
|
||||
DRM_PLANE_COMMIT_ACTIVE_ONLY);
|
||||
|
||||
drm_atomic_helper_fake_vblank(old_state);
|
||||
|
||||
drm_atomic_helper_commit_hw_done(old_state);
|
||||
|
||||
drm_atomic_helper_wait_for_vblanks(dev, old_state);
|
||||
@ -2051,6 +2057,45 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_fake_vblank - fake VBLANK events if needed
|
||||
* @old_state: atomic state object with old state structures
|
||||
*
|
||||
* This function walks all CRTCs and fake VBLANK events on those with
|
||||
* &drm_crtc_state.no_vblank set to true and &drm_crtc_state.event != NULL.
|
||||
* The primary use of this function is writeback connectors working in oneshot
|
||||
* mode and faking VBLANK events. In this case they only fake the VBLANK event
|
||||
* when a job is queued, and any change to the pipeline that does not touch the
|
||||
* connector is leading to timeouts when calling
|
||||
* drm_atomic_helper_wait_for_vblanks() or
|
||||
* drm_atomic_helper_wait_for_flip_done().
|
||||
*
|
||||
* This is part of the atomic helper support for nonblocking commits, see
|
||||
* drm_atomic_helper_setup_commit() for an overview.
|
||||
*/
|
||||
void drm_atomic_helper_fake_vblank(struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
struct drm_crtc *crtc;
|
||||
int i;
|
||||
|
||||
for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
|
||||
unsigned long flags;
|
||||
|
||||
if (!new_crtc_state->no_vblank)
|
||||
continue;
|
||||
|
||||
spin_lock_irqsave(&old_state->dev->event_lock, flags);
|
||||
if (new_crtc_state->event) {
|
||||
drm_crtc_send_vblank_event(crtc,
|
||||
new_crtc_state->event);
|
||||
new_crtc_state->event = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&old_state->dev->event_lock, flags);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_fake_vblank);
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_commit_hw_done - setup possible nonblocking commit
|
||||
* @old_state: atomic state object with old state structures
|
||||
|
406
drivers/gpu/drm/drm_client.c
Normal file
406
drivers/gpu/drm/drm_client.c
Normal file
@ -0,0 +1,406 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2018 Noralf Trønnes
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_debugfs.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_mode.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "drm_crtc_internal.h"
|
||||
#include "drm_internal.h"
|
||||
|
||||
/**
|
||||
* DOC: overview
|
||||
*
|
||||
* This library provides support for clients running in the kernel like fbdev and bootsplash.
|
||||
* Currently it's only partially implemented, just enough to support fbdev.
|
||||
*
|
||||
* GEM drivers which provide a GEM based dumb buffer with a virtual address are supported.
|
||||
*/
|
||||
|
||||
static int drm_client_open(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_device *dev = client->dev;
|
||||
struct drm_file *file;
|
||||
|
||||
file = drm_file_alloc(dev->primary);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
mutex_lock(&dev->filelist_mutex);
|
||||
list_add(&file->lhead, &dev->filelist_internal);
|
||||
mutex_unlock(&dev->filelist_mutex);
|
||||
|
||||
client->file = file;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drm_client_close(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_device *dev = client->dev;
|
||||
|
||||
mutex_lock(&dev->filelist_mutex);
|
||||
list_del(&client->file->lhead);
|
||||
mutex_unlock(&dev->filelist_mutex);
|
||||
|
||||
drm_file_free(client->file);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_client_close);
|
||||
|
||||
/**
|
||||
* drm_client_new - Create a DRM client
|
||||
* @dev: DRM device
|
||||
* @client: DRM client
|
||||
* @name: Client name
|
||||
* @funcs: DRM client functions (optional)
|
||||
*
|
||||
* The caller needs to hold a reference on @dev before calling this function.
|
||||
* The client is freed when the &drm_device is unregistered. See drm_client_release().
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success or negative error code on failure.
|
||||
*/
|
||||
int drm_client_new(struct drm_device *dev, struct drm_client_dev *client,
|
||||
const char *name, const struct drm_client_funcs *funcs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET) ||
|
||||
!dev->driver->dumb_create || !dev->driver->gem_prime_vmap)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (funcs && !try_module_get(funcs->owner))
|
||||
return -ENODEV;
|
||||
|
||||
client->dev = dev;
|
||||
client->name = name;
|
||||
client->funcs = funcs;
|
||||
|
||||
ret = drm_client_open(client);
|
||||
if (ret)
|
||||
goto err_put_module;
|
||||
|
||||
mutex_lock(&dev->clientlist_mutex);
|
||||
list_add(&client->list, &dev->clientlist);
|
||||
mutex_unlock(&dev->clientlist_mutex);
|
||||
|
||||
drm_dev_get(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_module:
|
||||
if (funcs)
|
||||
module_put(funcs->owner);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_client_new);
|
||||
|
||||
/**
|
||||
* drm_client_release - Release DRM client resources
|
||||
* @client: DRM client
|
||||
*
|
||||
* Releases resources by closing the &drm_file that was opened by drm_client_new().
|
||||
* It is called automatically if the &drm_client_funcs.unregister callback is _not_ set.
|
||||
*
|
||||
* This function should only be called from the unregister callback. An exception
|
||||
* is fbdev which cannot free the buffer if userspace has open file descriptors.
|
||||
*
|
||||
* Note:
|
||||
* Clients cannot initiate a release by themselves. This is done to keep the code simple.
|
||||
* The driver has to be unloaded before the client can be unloaded.
|
||||
*/
|
||||
void drm_client_release(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_device *dev = client->dev;
|
||||
|
||||
DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name);
|
||||
|
||||
drm_client_close(client);
|
||||
drm_dev_put(dev);
|
||||
if (client->funcs)
|
||||
module_put(client->funcs->owner);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_client_release);
|
||||
|
||||
void drm_client_dev_unregister(struct drm_device *dev)
|
||||
{
|
||||
struct drm_client_dev *client, *tmp;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return;
|
||||
|
||||
mutex_lock(&dev->clientlist_mutex);
|
||||
list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
|
||||
list_del(&client->list);
|
||||
if (client->funcs && client->funcs->unregister) {
|
||||
client->funcs->unregister(client);
|
||||
} else {
|
||||
drm_client_release(client);
|
||||
kfree(client);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dev->clientlist_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_client_dev_hotplug - Send hotplug event to clients
|
||||
* @dev: DRM device
|
||||
*
|
||||
* This function calls the &drm_client_funcs.hotplug callback on the attached clients.
|
||||
*
|
||||
* drm_kms_helper_hotplug_event() calls this function, so drivers that use it
|
||||
* don't need to call this function themselves.
|
||||
*/
|
||||
void drm_client_dev_hotplug(struct drm_device *dev)
|
||||
{
|
||||
struct drm_client_dev *client;
|
||||
int ret;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return;
|
||||
|
||||
mutex_lock(&dev->clientlist_mutex);
|
||||
list_for_each_entry(client, &dev->clientlist, list) {
|
||||
if (!client->funcs || !client->funcs->hotplug)
|
||||
continue;
|
||||
|
||||
ret = client->funcs->hotplug(client);
|
||||
DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret);
|
||||
}
|
||||
mutex_unlock(&dev->clientlist_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_client_dev_hotplug);
|
||||
|
||||
void drm_client_dev_restore(struct drm_device *dev)
|
||||
{
|
||||
struct drm_client_dev *client;
|
||||
int ret;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return;
|
||||
|
||||
mutex_lock(&dev->clientlist_mutex);
|
||||
list_for_each_entry(client, &dev->clientlist, list) {
|
||||
if (!client->funcs || !client->funcs->restore)
|
||||
continue;
|
||||
|
||||
ret = client->funcs->restore(client);
|
||||
DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret);
|
||||
if (!ret) /* The first one to return zero gets the privilege to restore */
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&dev->clientlist_mutex);
|
||||
}
|
||||
|
||||
static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
|
||||
{
|
||||
struct drm_device *dev = buffer->client->dev;
|
||||
|
||||
if (buffer->vaddr && dev->driver->gem_prime_vunmap)
|
||||
dev->driver->gem_prime_vunmap(buffer->gem, buffer->vaddr);
|
||||
|
||||
if (buffer->gem)
|
||||
drm_gem_object_put_unlocked(buffer->gem);
|
||||
|
||||
drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file);
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
static struct drm_client_buffer *
|
||||
drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
|
||||
{
|
||||
struct drm_mode_create_dumb dumb_args = { };
|
||||
struct drm_device *dev = client->dev;
|
||||
struct drm_client_buffer *buffer;
|
||||
struct drm_gem_object *obj;
|
||||
void *vaddr;
|
||||
int ret;
|
||||
|
||||
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
buffer->client = client;
|
||||
|
||||
dumb_args.width = width;
|
||||
dumb_args.height = height;
|
||||
dumb_args.bpp = drm_format_plane_cpp(format, 0) * 8;
|
||||
ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
buffer->handle = dumb_args.handle;
|
||||
buffer->pitch = dumb_args.pitch;
|
||||
|
||||
obj = drm_gem_object_lookup(client->file, dumb_args.handle);
|
||||
if (!obj) {
|
||||
ret = -ENOENT;
|
||||
goto err_delete;
|
||||
}
|
||||
|
||||
buffer->gem = obj;
|
||||
|
||||
/*
|
||||
* FIXME: The dependency on GEM here isn't required, we could
|
||||
* convert the driver handle to a dma-buf instead and use the
|
||||
* backend-agnostic dma-buf vmap support instead. This would
|
||||
* require that the handle2fd prime ioctl is reworked to pull the
|
||||
* fd_install step out of the driver backend hooks, to make that
|
||||
* final step optional for internal users.
|
||||
*/
|
||||
vaddr = dev->driver->gem_prime_vmap(obj);
|
||||
if (!vaddr) {
|
||||
ret = -ENOMEM;
|
||||
goto err_delete;
|
||||
}
|
||||
|
||||
buffer->vaddr = vaddr;
|
||||
|
||||
return buffer;
|
||||
|
||||
err_delete:
|
||||
drm_client_buffer_delete(buffer);
|
||||
err_free:
|
||||
kfree(buffer);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!buffer->fb)
|
||||
return;
|
||||
|
||||
ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
|
||||
if (ret)
|
||||
DRM_DEV_ERROR(buffer->client->dev->dev,
|
||||
"Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
|
||||
|
||||
buffer->fb = NULL;
|
||||
}
|
||||
|
||||
static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
|
||||
u32 width, u32 height, u32 format)
|
||||
{
|
||||
struct drm_client_dev *client = buffer->client;
|
||||
struct drm_mode_fb_cmd fb_req = { };
|
||||
const struct drm_format_info *info;
|
||||
int ret;
|
||||
|
||||
info = drm_format_info(format);
|
||||
fb_req.bpp = info->cpp[0] * 8;
|
||||
fb_req.depth = info->depth;
|
||||
fb_req.width = width;
|
||||
fb_req.height = height;
|
||||
fb_req.handle = buffer->handle;
|
||||
fb_req.pitch = buffer->pitch;
|
||||
|
||||
ret = drm_mode_addfb(client->dev, &fb_req, client->file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
|
||||
if (WARN_ON(!buffer->fb))
|
||||
return -ENOENT;
|
||||
|
||||
/* drop the reference we picked up in framebuffer lookup */
|
||||
drm_framebuffer_put(buffer->fb);
|
||||
|
||||
strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_client_framebuffer_create - Create a client framebuffer
|
||||
* @client: DRM client
|
||||
* @width: Framebuffer width
|
||||
* @height: Framebuffer height
|
||||
* @format: Buffer format
|
||||
*
|
||||
* This function creates a &drm_client_buffer which consists of a
|
||||
* &drm_framebuffer backed by a dumb buffer.
|
||||
* Call drm_client_framebuffer_delete() to free the buffer.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to a client buffer or an error pointer on failure.
|
||||
*/
|
||||
struct drm_client_buffer *
|
||||
drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
|
||||
{
|
||||
struct drm_client_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
buffer = drm_client_buffer_create(client, width, height, format);
|
||||
if (IS_ERR(buffer))
|
||||
return buffer;
|
||||
|
||||
ret = drm_client_buffer_addfb(buffer, width, height, format);
|
||||
if (ret) {
|
||||
drm_client_buffer_delete(buffer);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_client_framebuffer_create);
|
||||
|
||||
/**
|
||||
* drm_client_framebuffer_delete - Delete a client framebuffer
|
||||
* @buffer: DRM client buffer (can be NULL)
|
||||
*/
|
||||
void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
|
||||
{
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
drm_client_buffer_rmfb(buffer);
|
||||
drm_client_buffer_delete(buffer);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_client_framebuffer_delete);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_printer p = drm_seq_file_printer(m);
|
||||
struct drm_client_dev *client;
|
||||
|
||||
mutex_lock(&dev->clientlist_mutex);
|
||||
list_for_each_entry(client, &dev->clientlist, list)
|
||||
drm_printf(&p, "%s\n", client->name);
|
||||
mutex_unlock(&dev->clientlist_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_info_list drm_client_debugfs_list[] = {
|
||||
{ "internal_clients", drm_client_debugfs_internal_clients, 0 },
|
||||
};
|
||||
|
||||
int drm_client_debugfs_init(struct drm_minor *minor)
|
||||
{
|
||||
return drm_debugfs_create_files(drm_client_debugfs_list,
|
||||
ARRAY_SIZE(drm_client_debugfs_list),
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
#endif
|
@ -321,7 +321,7 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector,
|
||||
if (WARN_ON(connector->encoder))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(connector->encoder_ids); i++) {
|
||||
if (connector->encoder_ids[i] == 0) {
|
||||
connector->encoder_ids[i] = encoder->base.id;
|
||||
return 0;
|
||||
@ -331,6 +331,29 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
|
||||
|
||||
/**
|
||||
* drm_connector_has_possible_encoder - check if the connector and encoder are assosicated with each other
|
||||
* @connector: the connector
|
||||
* @encoder: the encoder
|
||||
*
|
||||
* Returns:
|
||||
* True if @encoder is one of the possible encoders for @connector.
|
||||
*/
|
||||
bool drm_connector_has_possible_encoder(struct drm_connector *connector,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_encoder *enc;
|
||||
int i;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, enc, i) {
|
||||
if (enc == encoder)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_has_possible_encoder);
|
||||
|
||||
static void drm_mode_remove(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
@ -1706,22 +1729,19 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
|
||||
if (!connector)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
|
||||
if (connector->encoder_ids[i] != 0)
|
||||
encoders_count++;
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i)
|
||||
encoders_count++;
|
||||
|
||||
if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
|
||||
copied = 0;
|
||||
encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] != 0) {
|
||||
if (put_user(connector->encoder_ids[i],
|
||||
encoder_ptr + copied)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
copied++;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (put_user(encoder->base.id, encoder_ptr + copied)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
copied++;
|
||||
}
|
||||
}
|
||||
out_resp->count_encoders = encoders_count;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_debugfs.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
@ -164,6 +165,12 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
|
||||
DRM_ERROR("Failed to create framebuffer debugfs file\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_client_debugfs_init(minor);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to create client debugfs file\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->driver->debugfs_init) {
|
||||
|
@ -139,6 +139,7 @@ static int crtc_crc_data_count(struct drm_crtc_crc *crc)
|
||||
static void crtc_crc_cleanup(struct drm_crtc_crc *crc)
|
||||
{
|
||||
kfree(crc->entries);
|
||||
crc->overflow = false;
|
||||
crc->entries = NULL;
|
||||
crc->head = 0;
|
||||
crc->tail = 0;
|
||||
@ -391,8 +392,14 @@ int drm_crtc_add_crc_entry(struct drm_crtc *crtc, bool has_frame,
|
||||
tail = crc->tail;
|
||||
|
||||
if (CIRC_SPACE(head, tail, DRM_CRC_ENTRIES_NR) < 1) {
|
||||
bool was_overflow = crc->overflow;
|
||||
|
||||
crc->overflow = true;
|
||||
spin_unlock(&crc->lock);
|
||||
DRM_ERROR("Overflow of CRC buffer, userspace reads too slow.\n");
|
||||
|
||||
if (!was_overflow)
|
||||
DRM_ERROR("Overflow of CRC buffer, userspace reads too slow.\n");
|
||||
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/srcu.h>
|
||||
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
@ -505,6 +506,8 @@ int drm_dev_init(struct drm_device *dev,
|
||||
dev->driver = driver;
|
||||
|
||||
INIT_LIST_HEAD(&dev->filelist);
|
||||
INIT_LIST_HEAD(&dev->filelist_internal);
|
||||
INIT_LIST_HEAD(&dev->clientlist);
|
||||
INIT_LIST_HEAD(&dev->ctxlist);
|
||||
INIT_LIST_HEAD(&dev->vmalist);
|
||||
INIT_LIST_HEAD(&dev->maplist);
|
||||
@ -514,6 +517,7 @@ int drm_dev_init(struct drm_device *dev,
|
||||
spin_lock_init(&dev->event_lock);
|
||||
mutex_init(&dev->struct_mutex);
|
||||
mutex_init(&dev->filelist_mutex);
|
||||
mutex_init(&dev->clientlist_mutex);
|
||||
mutex_init(&dev->ctxlist_mutex);
|
||||
mutex_init(&dev->master_mutex);
|
||||
|
||||
@ -569,6 +573,7 @@ err_minors:
|
||||
err_free:
|
||||
mutex_destroy(&dev->master_mutex);
|
||||
mutex_destroy(&dev->ctxlist_mutex);
|
||||
mutex_destroy(&dev->clientlist_mutex);
|
||||
mutex_destroy(&dev->filelist_mutex);
|
||||
mutex_destroy(&dev->struct_mutex);
|
||||
return ret;
|
||||
@ -603,6 +608,7 @@ void drm_dev_fini(struct drm_device *dev)
|
||||
|
||||
mutex_destroy(&dev->master_mutex);
|
||||
mutex_destroy(&dev->ctxlist_mutex);
|
||||
mutex_destroy(&dev->clientlist_mutex);
|
||||
mutex_destroy(&dev->filelist_mutex);
|
||||
mutex_destroy(&dev->struct_mutex);
|
||||
kfree(dev->unique);
|
||||
@ -858,6 +864,8 @@ void drm_dev_unregister(struct drm_device *dev)
|
||||
|
||||
dev->registered = false;
|
||||
|
||||
drm_client_dev_unregister(dev);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
drm_modeset_unregister_all(dev);
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
@ -26,11 +27,8 @@
|
||||
#include <drm/drm_print.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define DEFAULT_FBDEFIO_DELAY_MS 50
|
||||
|
||||
struct drm_fbdev_cma {
|
||||
struct drm_fb_helper fb_helper;
|
||||
const struct drm_framebuffer_funcs *fb_funcs;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -44,36 +42,6 @@ struct drm_fbdev_cma {
|
||||
*
|
||||
* An fbdev framebuffer backed by cma is also available by calling
|
||||
* drm_fb_cma_fbdev_init(). drm_fb_cma_fbdev_fini() tears it down.
|
||||
* If the &drm_framebuffer_funcs.dirty callback is set, fb_deferred_io will be
|
||||
* set up automatically. &drm_framebuffer_funcs.dirty is called by
|
||||
* drm_fb_helper_deferred_io() in process context (&struct delayed_work).
|
||||
*
|
||||
* Example fbdev deferred io code::
|
||||
*
|
||||
* static int driver_fb_dirty(struct drm_framebuffer *fb,
|
||||
* struct drm_file *file_priv,
|
||||
* unsigned flags, unsigned color,
|
||||
* struct drm_clip_rect *clips,
|
||||
* unsigned num_clips)
|
||||
* {
|
||||
* struct drm_gem_cma_object *cma = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
* ... push changes ...
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* static struct drm_framebuffer_funcs driver_fb_funcs = {
|
||||
* .destroy = drm_gem_fb_destroy,
|
||||
* .create_handle = drm_gem_fb_create_handle,
|
||||
* .dirty = driver_fb_dirty,
|
||||
* };
|
||||
*
|
||||
* Initialize::
|
||||
*
|
||||
* fbdev = drm_fb_cma_fbdev_init_with_funcs(dev, 16,
|
||||
* dev->mode_config.num_crtc,
|
||||
* dev->mode_config.num_connector,
|
||||
* &driver_fb_funcs);
|
||||
*
|
||||
*/
|
||||
|
||||
static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper)
|
||||
@ -131,220 +99,6 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr);
|
||||
|
||||
static int drm_fb_cma_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
return dma_mmap_writecombine(info->device, vma, info->screen_base,
|
||||
info->fix.smem_start, info->fix.smem_len);
|
||||
}
|
||||
|
||||
static struct fb_ops drm_fbdev_cma_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_fillrect = drm_fb_helper_sys_fillrect,
|
||||
.fb_copyarea = drm_fb_helper_sys_copyarea,
|
||||
.fb_imageblit = drm_fb_helper_sys_imageblit,
|
||||
.fb_mmap = drm_fb_cma_mmap,
|
||||
};
|
||||
|
||||
static int drm_fbdev_cma_deferred_io_mmap(struct fb_info *info,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
fb_deferred_io_mmap(info, vma);
|
||||
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_fbdev_cma_defio_init(struct fb_info *fbi,
|
||||
struct drm_gem_cma_object *cma_obj)
|
||||
{
|
||||
struct fb_deferred_io *fbdefio;
|
||||
struct fb_ops *fbops;
|
||||
|
||||
/*
|
||||
* Per device structures are needed because:
|
||||
* fbops: fb_deferred_io_cleanup() clears fbops.fb_mmap
|
||||
* fbdefio: individual delays
|
||||
*/
|
||||
fbdefio = kzalloc(sizeof(*fbdefio), GFP_KERNEL);
|
||||
fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
|
||||
if (!fbdefio || !fbops) {
|
||||
kfree(fbdefio);
|
||||
kfree(fbops);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* can't be offset from vaddr since dirty() uses cma_obj */
|
||||
fbi->screen_buffer = cma_obj->vaddr;
|
||||
/* fb_deferred_io_fault() needs a physical address */
|
||||
fbi->fix.smem_start = page_to_phys(virt_to_page(fbi->screen_buffer));
|
||||
|
||||
*fbops = *fbi->fbops;
|
||||
fbi->fbops = fbops;
|
||||
|
||||
fbdefio->delay = msecs_to_jiffies(DEFAULT_FBDEFIO_DELAY_MS);
|
||||
fbdefio->deferred_io = drm_fb_helper_deferred_io;
|
||||
fbi->fbdefio = fbdefio;
|
||||
fb_deferred_io_init(fbi);
|
||||
fbi->fbops->fb_mmap = drm_fbdev_cma_deferred_io_mmap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drm_fbdev_cma_defio_fini(struct fb_info *fbi)
|
||||
{
|
||||
if (!fbi->fbdefio)
|
||||
return;
|
||||
|
||||
fb_deferred_io_cleanup(fbi);
|
||||
kfree(fbi->fbdefio);
|
||||
kfree(fbi->fbops);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_fbdev_cma_create(struct drm_fb_helper *helper,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper);
|
||||
struct drm_device *dev = helper->dev;
|
||||
struct drm_gem_cma_object *obj;
|
||||
struct drm_framebuffer *fb;
|
||||
unsigned int bytes_per_pixel;
|
||||
unsigned long offset;
|
||||
struct fb_info *fbi;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
|
||||
sizes->surface_width, sizes->surface_height,
|
||||
sizes->surface_bpp);
|
||||
|
||||
bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
|
||||
size = sizes->surface_width * sizes->surface_height * bytes_per_pixel;
|
||||
obj = drm_gem_cma_create(dev, size);
|
||||
if (IS_ERR(obj))
|
||||
return -ENOMEM;
|
||||
|
||||
fbi = drm_fb_helper_alloc_fbi(helper);
|
||||
if (IS_ERR(fbi)) {
|
||||
ret = PTR_ERR(fbi);
|
||||
goto err_gem_free_object;
|
||||
}
|
||||
|
||||
fb = drm_gem_fbdev_fb_create(dev, sizes, 0, &obj->base,
|
||||
fbdev_cma->fb_funcs);
|
||||
if (IS_ERR(fb)) {
|
||||
dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
|
||||
ret = PTR_ERR(fb);
|
||||
goto err_fb_info_destroy;
|
||||
}
|
||||
|
||||
helper->fb = fb;
|
||||
|
||||
fbi->par = helper;
|
||||
fbi->flags = FBINFO_FLAG_DEFAULT;
|
||||
fbi->fbops = &drm_fbdev_cma_ops;
|
||||
|
||||
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
|
||||
drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
|
||||
|
||||
offset = fbi->var.xoffset * bytes_per_pixel;
|
||||
offset += fbi->var.yoffset * fb->pitches[0];
|
||||
|
||||
dev->mode_config.fb_base = (resource_size_t)obj->paddr;
|
||||
fbi->screen_base = obj->vaddr + offset;
|
||||
fbi->fix.smem_start = (unsigned long)(obj->paddr + offset);
|
||||
fbi->screen_size = size;
|
||||
fbi->fix.smem_len = size;
|
||||
|
||||
if (fb->funcs->dirty) {
|
||||
ret = drm_fbdev_cma_defio_init(fbi, obj);
|
||||
if (ret)
|
||||
goto err_cma_destroy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_cma_destroy:
|
||||
drm_framebuffer_remove(fb);
|
||||
err_fb_info_destroy:
|
||||
drm_fb_helper_fini(helper);
|
||||
err_gem_free_object:
|
||||
drm_gem_object_put_unlocked(&obj->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
|
||||
.fb_probe = drm_fbdev_cma_create,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_fb_cma_fbdev_init_with_funcs() - Allocate and initialize fbdev emulation
|
||||
* @dev: DRM device
|
||||
* @preferred_bpp: Preferred bits per pixel for the device.
|
||||
* @dev->mode_config.preferred_depth is used if this is zero.
|
||||
* @max_conn_count: Maximum number of connectors.
|
||||
* @dev->mode_config.num_connector is used if this is zero.
|
||||
* @funcs: Framebuffer functions, in particular a custom dirty() callback.
|
||||
* Can be NULL.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success or negative error code on failure.
|
||||
*/
|
||||
int drm_fb_cma_fbdev_init_with_funcs(struct drm_device *dev,
|
||||
unsigned int preferred_bpp, unsigned int max_conn_count,
|
||||
const struct drm_framebuffer_funcs *funcs)
|
||||
{
|
||||
struct drm_fbdev_cma *fbdev_cma;
|
||||
struct drm_fb_helper *fb_helper;
|
||||
int ret;
|
||||
|
||||
if (!preferred_bpp)
|
||||
preferred_bpp = dev->mode_config.preferred_depth;
|
||||
if (!preferred_bpp)
|
||||
preferred_bpp = 32;
|
||||
|
||||
if (!max_conn_count)
|
||||
max_conn_count = dev->mode_config.num_connector;
|
||||
|
||||
fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
|
||||
if (!fbdev_cma)
|
||||
return -ENOMEM;
|
||||
|
||||
fbdev_cma->fb_funcs = funcs;
|
||||
fb_helper = &fbdev_cma->fb_helper;
|
||||
|
||||
drm_fb_helper_prepare(dev, fb_helper, &drm_fb_cma_helper_funcs);
|
||||
|
||||
ret = drm_fb_helper_init(dev, fb_helper, max_conn_count);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(dev->dev, "Failed to initialize fbdev helper.\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = drm_fb_helper_single_add_all_connectors(fb_helper);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(dev->dev, "Failed to add connectors.\n");
|
||||
goto err_drm_fb_helper_fini;
|
||||
}
|
||||
|
||||
ret = drm_fb_helper_initial_config(fb_helper, preferred_bpp);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(dev->dev, "Failed to set fbdev configuration.\n");
|
||||
goto err_drm_fb_helper_fini;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_drm_fb_helper_fini:
|
||||
drm_fb_helper_fini(fb_helper);
|
||||
err_free:
|
||||
kfree(fbdev_cma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init_with_funcs);
|
||||
|
||||
/**
|
||||
* drm_fb_cma_fbdev_init() - Allocate and initialize fbdev emulation
|
||||
* @dev: DRM device
|
||||
@ -359,8 +113,14 @@ EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init_with_funcs);
|
||||
int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp,
|
||||
unsigned int max_conn_count)
|
||||
{
|
||||
return drm_fb_cma_fbdev_init_with_funcs(dev, preferred_bpp,
|
||||
max_conn_count, NULL);
|
||||
struct drm_fbdev_cma *fbdev_cma;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init);
|
||||
|
||||
@ -370,87 +130,13 @@ EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init);
|
||||
*/
|
||||
void drm_fb_cma_fbdev_fini(struct drm_device *dev)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = dev->fb_helper;
|
||||
|
||||
if (!fb_helper)
|
||||
return;
|
||||
|
||||
/* Unregister if it hasn't been done already */
|
||||
if (fb_helper->fbdev && fb_helper->fbdev->dev)
|
||||
drm_fb_helper_unregister_fbi(fb_helper);
|
||||
|
||||
if (fb_helper->fbdev)
|
||||
drm_fbdev_cma_defio_fini(fb_helper->fbdev);
|
||||
|
||||
if (fb_helper->fb)
|
||||
drm_framebuffer_remove(fb_helper->fb);
|
||||
|
||||
drm_fb_helper_fini(fb_helper);
|
||||
kfree(to_fbdev_cma(fb_helper));
|
||||
if (dev->fb_helper)
|
||||
drm_fbdev_cma_fini(to_fbdev_cma(dev->fb_helper));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_fini);
|
||||
|
||||
/**
|
||||
* drm_fbdev_cma_init_with_funcs() - Allocate and initializes a drm_fbdev_cma struct
|
||||
* @dev: DRM device
|
||||
* @preferred_bpp: Preferred bits per pixel for the device
|
||||
* @max_conn_count: Maximum number of connectors
|
||||
* @funcs: fb helper functions, in particular a custom dirty() callback
|
||||
*
|
||||
* Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR.
|
||||
*/
|
||||
struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev,
|
||||
unsigned int preferred_bpp, unsigned int max_conn_count,
|
||||
const struct drm_framebuffer_funcs *funcs)
|
||||
{
|
||||
struct drm_fbdev_cma *fbdev_cma;
|
||||
struct drm_fb_helper *helper;
|
||||
int ret;
|
||||
|
||||
fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
|
||||
if (!fbdev_cma) {
|
||||
dev_err(dev->dev, "Failed to allocate drm fbdev.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
fbdev_cma->fb_funcs = funcs;
|
||||
|
||||
helper = &fbdev_cma->fb_helper;
|
||||
|
||||
drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
|
||||
|
||||
ret = drm_fb_helper_init(dev, helper, max_conn_count);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = drm_fb_helper_single_add_all_connectors(helper);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to add connectors.\n");
|
||||
goto err_drm_fb_helper_fini;
|
||||
|
||||
}
|
||||
|
||||
ret = drm_fb_helper_initial_config(helper, preferred_bpp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to set initial hw configuration.\n");
|
||||
goto err_drm_fb_helper_fini;
|
||||
}
|
||||
|
||||
return fbdev_cma;
|
||||
|
||||
err_drm_fb_helper_fini:
|
||||
drm_fb_helper_fini(helper);
|
||||
err_free:
|
||||
kfree(fbdev_cma);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fbdev_cma_init_with_funcs);
|
||||
|
||||
static const struct drm_framebuffer_funcs drm_fb_cma_funcs = {
|
||||
.destroy = drm_gem_fb_destroy,
|
||||
.create_handle = drm_gem_fb_create_handle,
|
||||
static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
|
||||
.fb_probe = drm_fb_helper_generic_probe,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -464,9 +150,33 @@ static const struct drm_framebuffer_funcs drm_fb_cma_funcs = {
|
||||
struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
|
||||
unsigned int preferred_bpp, unsigned int max_conn_count)
|
||||
{
|
||||
return drm_fbdev_cma_init_with_funcs(dev, preferred_bpp,
|
||||
max_conn_count,
|
||||
&drm_fb_cma_funcs);
|
||||
struct drm_fbdev_cma *fbdev_cma;
|
||||
struct drm_fb_helper *fb_helper;
|
||||
int ret;
|
||||
|
||||
fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
|
||||
if (!fbdev_cma)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fb_helper = &fbdev_cma->fb_helper;
|
||||
|
||||
ret = drm_client_new(dev, &fb_helper->client, "fbdev", NULL);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_cma_helper_funcs,
|
||||
preferred_bpp, max_conn_count);
|
||||
if (ret)
|
||||
goto err_client_put;
|
||||
|
||||
return fbdev_cma;
|
||||
|
||||
err_client_put:
|
||||
drm_client_release(&fb_helper->client);
|
||||
err_free:
|
||||
kfree(fbdev_cma);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fbdev_cma_init);
|
||||
|
||||
@ -477,14 +187,7 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_init);
|
||||
void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)
|
||||
{
|
||||
drm_fb_helper_unregister_fbi(&fbdev_cma->fb_helper);
|
||||
if (fbdev_cma->fb_helper.fbdev)
|
||||
drm_fbdev_cma_defio_fini(fbdev_cma->fb_helper.fbdev);
|
||||
|
||||
if (fbdev_cma->fb_helper.fb)
|
||||
drm_framebuffer_remove(fbdev_cma->fb_helper.fb);
|
||||
|
||||
drm_fb_helper_fini(&fbdev_cma->fb_helper);
|
||||
kfree(fbdev_cma);
|
||||
/* All resources have now been freed by drm_fbdev_fb_destroy() */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/slab.h>
|
||||
@ -66,6 +67,9 @@ static DEFINE_MUTEX(kernel_fb_helper_lock);
|
||||
* helper functions used by many drivers to implement the kernel mode setting
|
||||
* interfaces.
|
||||
*
|
||||
* Drivers that support a dumb buffer with a virtual address and mmap support,
|
||||
* should try out the generic fbdev emulation using drm_fbdev_generic_setup().
|
||||
*
|
||||
* Setup fbdev emulation by calling drm_fb_helper_fbdev_setup() and tear it
|
||||
* down by calling drm_fb_helper_fbdev_teardown().
|
||||
*
|
||||
@ -738,6 +742,24 @@ static void drm_fb_helper_resume_worker(struct work_struct *work)
|
||||
console_unlock();
|
||||
}
|
||||
|
||||
static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper,
|
||||
struct drm_clip_rect *clip)
|
||||
{
|
||||
struct drm_framebuffer *fb = fb_helper->fb;
|
||||
unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
|
||||
size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp;
|
||||
void *src = fb_helper->fbdev->screen_buffer + offset;
|
||||
void *dst = fb_helper->buffer->vaddr + offset;
|
||||
size_t len = (clip->x2 - clip->x1) * cpp;
|
||||
unsigned int y;
|
||||
|
||||
for (y = clip->y1; y < clip->y2; y++) {
|
||||
memcpy(dst, src, len);
|
||||
src += fb->pitches[0];
|
||||
dst += fb->pitches[0];
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_fb_helper_dirty_work(struct work_struct *work)
|
||||
{
|
||||
struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
|
||||
@ -753,8 +775,12 @@ static void drm_fb_helper_dirty_work(struct work_struct *work)
|
||||
spin_unlock_irqrestore(&helper->dirty_lock, flags);
|
||||
|
||||
/* call dirty callback only when it has been really touched */
|
||||
if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2)
|
||||
if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) {
|
||||
/* Generic fbdev uses a shadow buffer */
|
||||
if (helper->buffer)
|
||||
drm_fb_helper_dirty_blit_real(helper, &clip_copy);
|
||||
helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2323,6 +2349,20 @@ retry:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool connector_has_possible_crtc(struct drm_connector *connector,
|
||||
struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (encoder->possible_crtcs & drm_crtc_mask(crtc))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
|
||||
struct drm_fb_helper_crtc **best_crtcs,
|
||||
struct drm_display_mode **modes,
|
||||
@ -2331,7 +2371,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
|
||||
int c, o;
|
||||
struct drm_connector *connector;
|
||||
const struct drm_connector_helper_funcs *connector_funcs;
|
||||
struct drm_encoder *encoder;
|
||||
int my_score, best_score, score;
|
||||
struct drm_fb_helper_crtc **crtcs, *crtc;
|
||||
struct drm_fb_helper_connector *fb_helper_conn;
|
||||
@ -2362,20 +2401,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
|
||||
|
||||
connector_funcs = connector->helper_private;
|
||||
|
||||
/*
|
||||
* If the DRM device implements atomic hooks and ->best_encoder() is
|
||||
* NULL we fallback to the default drm_atomic_helper_best_encoder()
|
||||
* helper.
|
||||
*/
|
||||
if (drm_drv_uses_atomic_modeset(fb_helper->dev) &&
|
||||
!connector_funcs->best_encoder)
|
||||
encoder = drm_atomic_helper_best_encoder(connector);
|
||||
else
|
||||
encoder = connector_funcs->best_encoder(connector);
|
||||
|
||||
if (!encoder)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* select a crtc for this connector and then attempt to configure
|
||||
* remaining connectors
|
||||
@ -2383,7 +2408,8 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
|
||||
for (c = 0; c < fb_helper->crtc_count; c++) {
|
||||
crtc = &fb_helper->crtc_info[c];
|
||||
|
||||
if ((encoder->possible_crtcs & (1 << c)) == 0)
|
||||
if (!connector_has_possible_crtc(connector,
|
||||
crtc->mode_set.crtc))
|
||||
continue;
|
||||
|
||||
for (o = 0; o < n; o++)
|
||||
@ -2410,7 +2436,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
|
||||
sizeof(struct drm_fb_helper_crtc *));
|
||||
}
|
||||
}
|
||||
out:
|
||||
|
||||
kfree(crtcs);
|
||||
return best_score;
|
||||
}
|
||||
@ -2921,6 +2947,294 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
|
||||
|
||||
/* @user: 1=userspace, 0=fbcon */
|
||||
static int drm_fbdev_fb_open(struct fb_info *info, int user)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = info->par;
|
||||
|
||||
if (!try_module_get(fb_helper->dev->driver->fops->owner))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_fbdev_fb_release(struct fb_info *info, int user)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = info->par;
|
||||
|
||||
module_put(fb_helper->dev->driver->fops->owner);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end of
|
||||
* unregister_framebuffer() or fb_release().
|
||||
*/
|
||||
static void drm_fbdev_fb_destroy(struct fb_info *info)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = info->par;
|
||||
struct fb_info *fbi = fb_helper->fbdev;
|
||||
struct fb_ops *fbops = NULL;
|
||||
void *shadow = NULL;
|
||||
|
||||
if (fbi->fbdefio) {
|
||||
fb_deferred_io_cleanup(fbi);
|
||||
shadow = fbi->screen_buffer;
|
||||
fbops = fbi->fbops;
|
||||
}
|
||||
|
||||
drm_fb_helper_fini(fb_helper);
|
||||
|
||||
if (shadow) {
|
||||
vfree(shadow);
|
||||
kfree(fbops);
|
||||
}
|
||||
|
||||
drm_client_framebuffer_delete(fb_helper->buffer);
|
||||
/*
|
||||
* FIXME:
|
||||
* Remove conditional when all CMA drivers have been moved over to using
|
||||
* drm_fbdev_generic_setup().
|
||||
*/
|
||||
if (fb_helper->client.funcs) {
|
||||
drm_client_release(&fb_helper->client);
|
||||
kfree(fb_helper);
|
||||
}
|
||||
}
|
||||
|
||||
static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = info->par;
|
||||
|
||||
if (fb_helper->dev->driver->gem_prime_mmap)
|
||||
return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct fb_ops drm_fbdev_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_open = drm_fbdev_fb_open,
|
||||
.fb_release = drm_fbdev_fb_release,
|
||||
.fb_destroy = drm_fbdev_fb_destroy,
|
||||
.fb_mmap = drm_fbdev_fb_mmap,
|
||||
.fb_read = drm_fb_helper_sys_read,
|
||||
.fb_write = drm_fb_helper_sys_write,
|
||||
.fb_fillrect = drm_fb_helper_sys_fillrect,
|
||||
.fb_copyarea = drm_fb_helper_sys_copyarea,
|
||||
.fb_imageblit = drm_fb_helper_sys_imageblit,
|
||||
};
|
||||
|
||||
static struct fb_deferred_io drm_fbdev_defio = {
|
||||
.delay = HZ / 20,
|
||||
.deferred_io = drm_fb_helper_deferred_io,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_fb_helper_generic_probe - Generic fbdev emulation probe helper
|
||||
* @fb_helper: fbdev helper structure
|
||||
* @sizes: describes fbdev size and scanout surface size
|
||||
*
|
||||
* This function uses the client API to crate a framebuffer backed by a dumb buffer.
|
||||
*
|
||||
* The _sys_ versions are used for &fb_ops.fb_read, fb_write, fb_fillrect,
|
||||
* fb_copyarea, fb_imageblit.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success or negative error code on failure.
|
||||
*/
|
||||
int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct drm_client_dev *client = &fb_helper->client;
|
||||
struct drm_client_buffer *buffer;
|
||||
struct drm_framebuffer *fb;
|
||||
struct fb_info *fbi;
|
||||
u32 format;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
|
||||
sizes->surface_width, sizes->surface_height,
|
||||
sizes->surface_bpp);
|
||||
|
||||
format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
|
||||
buffer = drm_client_framebuffer_create(client, sizes->surface_width,
|
||||
sizes->surface_height, format);
|
||||
if (IS_ERR(buffer))
|
||||
return PTR_ERR(buffer);
|
||||
|
||||
fb_helper->buffer = buffer;
|
||||
fb_helper->fb = buffer->fb;
|
||||
fb = buffer->fb;
|
||||
|
||||
fbi = drm_fb_helper_alloc_fbi(fb_helper);
|
||||
if (IS_ERR(fbi)) {
|
||||
ret = PTR_ERR(fbi);
|
||||
goto err_free_buffer;
|
||||
}
|
||||
|
||||
fbi->par = fb_helper;
|
||||
fbi->fbops = &drm_fbdev_fb_ops;
|
||||
fbi->screen_size = fb->height * fb->pitches[0];
|
||||
fbi->fix.smem_len = fbi->screen_size;
|
||||
fbi->screen_buffer = buffer->vaddr;
|
||||
strcpy(fbi->fix.id, "DRM emulated");
|
||||
|
||||
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
|
||||
drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height);
|
||||
|
||||
if (fb->funcs->dirty) {
|
||||
struct fb_ops *fbops;
|
||||
void *shadow;
|
||||
|
||||
/*
|
||||
* fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per
|
||||
* instance version is necessary.
|
||||
*/
|
||||
fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
|
||||
shadow = vzalloc(fbi->screen_size);
|
||||
if (!fbops || !shadow) {
|
||||
kfree(fbops);
|
||||
vfree(shadow);
|
||||
ret = -ENOMEM;
|
||||
goto err_fb_info_destroy;
|
||||
}
|
||||
|
||||
*fbops = *fbi->fbops;
|
||||
fbi->fbops = fbops;
|
||||
fbi->screen_buffer = shadow;
|
||||
fbi->fbdefio = &drm_fbdev_defio;
|
||||
|
||||
fb_deferred_io_init(fbi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_fb_info_destroy:
|
||||
drm_fb_helper_fini(fb_helper);
|
||||
err_free_buffer:
|
||||
drm_client_framebuffer_delete(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_generic_probe);
|
||||
|
||||
static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = {
|
||||
.fb_probe = drm_fb_helper_generic_probe,
|
||||
};
|
||||
|
||||
static void drm_fbdev_client_unregister(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
|
||||
|
||||
if (fb_helper->fbdev) {
|
||||
drm_fb_helper_unregister_fbi(fb_helper);
|
||||
/* drm_fbdev_fb_destroy() takes care of cleanup */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Did drm_fb_helper_fbdev_setup() run? */
|
||||
if (fb_helper->dev)
|
||||
drm_fb_helper_fini(fb_helper);
|
||||
|
||||
drm_client_release(client);
|
||||
kfree(fb_helper);
|
||||
}
|
||||
|
||||
static int drm_fbdev_client_restore(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
|
||||
|
||||
drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
|
||||
struct drm_device *dev = client->dev;
|
||||
int ret;
|
||||
|
||||
/* If drm_fb_helper_fbdev_setup() failed, we only try once */
|
||||
if (!fb_helper->dev && fb_helper->funcs)
|
||||
return 0;
|
||||
|
||||
if (dev->fb_helper)
|
||||
return drm_fb_helper_hotplug_event(dev->fb_helper);
|
||||
|
||||
if (!dev->mode_config.num_connector)
|
||||
return 0;
|
||||
|
||||
ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_helper_generic_funcs,
|
||||
fb_helper->preferred_bpp, 0);
|
||||
if (ret) {
|
||||
fb_helper->dev = NULL;
|
||||
fb_helper->fbdev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_client_funcs drm_fbdev_client_funcs = {
|
||||
.owner = THIS_MODULE,
|
||||
.unregister = drm_fbdev_client_unregister,
|
||||
.restore = drm_fbdev_client_restore,
|
||||
.hotplug = drm_fbdev_client_hotplug,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_fb_helper_generic_fbdev_setup() - Setup generic fbdev emulation
|
||||
* @dev: DRM device
|
||||
* @preferred_bpp: Preferred bits per pixel for the device.
|
||||
* @dev->mode_config.preferred_depth is used if this is zero.
|
||||
*
|
||||
* This function sets up generic fbdev emulation for drivers that supports
|
||||
* dumb buffers with a virtual address and that can be mmap'ed.
|
||||
*
|
||||
* Restore, hotplug events and teardown are all taken care of. Drivers that do
|
||||
* suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
|
||||
* Simple drivers might use drm_mode_config_helper_suspend().
|
||||
*
|
||||
* Drivers that set the dirty callback on their framebuffer will get a shadow
|
||||
* fbdev buffer that is blitted onto the real buffer. This is done in order to
|
||||
* make deferred I/O work with all kinds of buffers.
|
||||
*
|
||||
* This function is safe to call even when there are no connectors present.
|
||||
* Setup will be retried on the next hotplug event.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success or negative error code on failure.
|
||||
*/
|
||||
int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper;
|
||||
int ret;
|
||||
|
||||
if (!drm_fbdev_emulation)
|
||||
return 0;
|
||||
|
||||
fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
|
||||
if (!fb_helper)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = drm_client_new(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs);
|
||||
if (ret) {
|
||||
kfree(fb_helper);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fb_helper->preferred_bpp = preferred_bpp;
|
||||
|
||||
drm_fbdev_client_hotplug(&fb_helper->client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fbdev_generic_setup);
|
||||
|
||||
/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
|
||||
* but the module doesn't depend on any fb console symbols. At least
|
||||
* attempt to load fbcon to avoid leaving the system without a usable console.
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
@ -444,6 +445,8 @@ void drm_lastclose(struct drm_device * dev)
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_LEGACY))
|
||||
drm_legacy_dev_reinit(dev);
|
||||
|
||||
drm_client_dev_restore(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -659,10 +659,12 @@ EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode);
|
||||
* drm_bus_flags_from_videomode - extract information about pixelclk and
|
||||
* DE polarity from videomode and store it in a separate variable
|
||||
* @vm: videomode structure to use
|
||||
* @bus_flags: information about pixelclk and DE polarity will be stored here
|
||||
* @bus_flags: information about pixelclk, sync and DE polarity will be stored
|
||||
* here
|
||||
*
|
||||
* Sets DRM_BUS_FLAG_DE_(LOW|HIGH) and DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE
|
||||
* in @bus_flags according to DISPLAY_FLAGS found in @vm
|
||||
* Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE and
|
||||
* DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS
|
||||
* found in @vm
|
||||
*/
|
||||
void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags)
|
||||
{
|
||||
@ -672,6 +674,11 @@ void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags)
|
||||
if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
|
||||
*bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
|
||||
|
||||
if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE)
|
||||
*bus_flags |= DRM_BUS_FLAG_SYNC_POSEDGE;
|
||||
if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE)
|
||||
*bus_flags |= DRM_BUS_FLAG_SYNC_NEGEDGE;
|
||||
|
||||
if (vm->flags & DISPLAY_FLAGS_DE_LOW)
|
||||
*bus_flags |= DRM_BUS_FLAG_DE_LOW;
|
||||
if (vm->flags & DISPLAY_FLAGS_DE_HIGH)
|
||||
@ -684,7 +691,7 @@ EXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode);
|
||||
* of_get_drm_display_mode - get a drm_display_mode from devicetree
|
||||
* @np: device_node with the timing specification
|
||||
* @dmode: will be set to the return value
|
||||
* @bus_flags: information about pixelclk and DE polarity
|
||||
* @bus_flags: information about pixelclk, sync and DE polarity
|
||||
* @index: index into the list of display timings in devicetree
|
||||
*
|
||||
* This function is expensive and should only be used, if only one mode is to be
|
||||
|
@ -239,10 +239,17 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
|
||||
if (!remote)
|
||||
return -ENODEV;
|
||||
|
||||
if (!of_device_is_available(remote)) {
|
||||
of_node_put(remote);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (panel) {
|
||||
*panel = of_drm_find_panel(remote);
|
||||
if (*panel)
|
||||
if (!IS_ERR(*panel))
|
||||
ret = 0;
|
||||
else
|
||||
*panel = NULL;
|
||||
}
|
||||
|
||||
/* No panel found yet, check for a bridge next. */
|
||||
|
@ -151,12 +151,19 @@ EXPORT_SYMBOL(drm_panel_detach);
|
||||
* tree node. If a matching panel is found, return a pointer to it.
|
||||
*
|
||||
* Return: A pointer to the panel registered for the specified device tree
|
||||
* node or NULL if no panel matching the device tree node can be found.
|
||||
* node or an ERR_PTR() if no panel matching the device tree node can be found.
|
||||
* Possible error codes returned by this function:
|
||||
* - EPROBE_DEFER: the panel device has not been probed yet, and the caller
|
||||
* should retry later
|
||||
* - ENODEV: the device is not available (status != "okay" or "ok")
|
||||
*/
|
||||
struct drm_panel *of_drm_find_panel(const struct device_node *np)
|
||||
{
|
||||
struct drm_panel *panel;
|
||||
|
||||
if (!of_device_is_available(np))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
mutex_lock(&panel_lock);
|
||||
|
||||
list_for_each_entry(panel, &panel_list, list) {
|
||||
@ -167,7 +174,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
|
||||
}
|
||||
|
||||
mutex_unlock(&panel_lock);
|
||||
return NULL;
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
EXPORT_SYMBOL(of_drm_find_panel);
|
||||
#endif
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
@ -88,9 +89,9 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
uint32_t *ids = connector->encoder_ids;
|
||||
enum drm_mode_status ret = MODE_OK;
|
||||
unsigned int i;
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
|
||||
/* Step 1: Validate against connector */
|
||||
ret = drm_connector_mode_valid(connector, mode);
|
||||
@ -98,13 +99,9 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
|
||||
return ret;
|
||||
|
||||
/* Step 2: Validate against encoders and crtcs */
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
struct drm_encoder *encoder = drm_encoder_find(dev, NULL, ids[i]);
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
ret = drm_encoder_mode_valid(encoder, mode);
|
||||
if (ret != MODE_OK) {
|
||||
/* No point in continuing for crtc check as this encoder
|
||||
@ -563,6 +560,8 @@ void drm_kms_helper_hotplug_event(struct drm_device *dev)
|
||||
drm_sysfs_hotplug_event(dev);
|
||||
if (dev->mode_config.funcs->output_poll_changed)
|
||||
dev->mode_config.funcs->output_poll_changed(dev);
|
||||
|
||||
drm_client_dev_hotplug(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_hotplug_event);
|
||||
|
||||
|
@ -22,10 +22,13 @@
|
||||
* Writeback connectors are used to expose hardware which can write the output
|
||||
* from a CRTC to a memory buffer. They are used and act similarly to other
|
||||
* types of connectors, with some important differences:
|
||||
* - Writeback connectors don't provide a way to output visually to the user.
|
||||
* - Writeback connectors should always report as "disconnected" (so that
|
||||
* clients which don't understand them will ignore them).
|
||||
* - Writeback connectors don't have EDID.
|
||||
*
|
||||
* * Writeback connectors don't provide a way to output visually to the user.
|
||||
*
|
||||
* * Writeback connectors should always report as "disconnected" (so that
|
||||
* clients which don't understand them will ignore them).
|
||||
*
|
||||
* * Writeback connectors don't have EDID.
|
||||
*
|
||||
* A framebuffer may only be attached to a writeback connector when the
|
||||
* connector is attached to a CRTC. The WRITEBACK_FB_ID property which sets the
|
||||
|
@ -232,9 +232,11 @@ static int exynos_dp_probe(struct platform_device *pdev)
|
||||
np = of_parse_phandle(dev->of_node, "panel", 0);
|
||||
if (np) {
|
||||
dp->plat_data.panel = of_drm_find_panel(np);
|
||||
|
||||
of_node_put(np);
|
||||
if (!dp->plat_data.panel)
|
||||
return -EPROBE_DEFER;
|
||||
if (IS_ERR(dp->plat_data.panel))
|
||||
return PTR_ERR(dp->plat_data.panel);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -240,8 +240,8 @@ struct drm_encoder *exynos_dpi_probe(struct device *dev)
|
||||
|
||||
if (ctx->panel_node) {
|
||||
ctx->panel = of_drm_find_panel(ctx->panel_node);
|
||||
if (!ctx->panel)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
if (IS_ERR(ctx->panel))
|
||||
return ERR_CAST(ctx->panel);
|
||||
}
|
||||
|
||||
return &ctx->encoder;
|
||||
|
@ -1519,6 +1519,9 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
dsi->format = device->format;
|
||||
dsi->mode_flags = device->mode_flags;
|
||||
dsi->panel = of_drm_find_panel(device->dev.of_node);
|
||||
if (IS_ERR(dsi->panel))
|
||||
dsi->panel = NULL;
|
||||
|
||||
if (dsi->panel) {
|
||||
drm_panel_attach(dsi->panel, &dsi->connector);
|
||||
dsi->connector.status = connector_status_connected;
|
||||
|
@ -148,8 +148,9 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
|
||||
if (panel_node) {
|
||||
fsl_dev->connector.panel = of_drm_find_panel(panel_node);
|
||||
of_node_put(panel_node);
|
||||
if (!fsl_dev->connector.panel)
|
||||
return -EPROBE_DEFER;
|
||||
if (IS_ERR(fsl_dev->connector.panel))
|
||||
return PTR_ERR(fsl_dev->connector.panel);
|
||||
|
||||
return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel);
|
||||
}
|
||||
|
||||
|
@ -403,20 +403,10 @@ static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *c
|
||||
return &intel_dp->mst_encoders[crtc->pipe]->base.base;
|
||||
}
|
||||
|
||||
static struct drm_encoder *intel_mst_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
struct intel_dp *intel_dp = intel_connector->mst_port;
|
||||
if (!intel_dp)
|
||||
return NULL;
|
||||
return &intel_dp->mst_encoders[0]->base.base;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
|
||||
.get_modes = intel_dp_mst_get_modes,
|
||||
.mode_valid = intel_dp_mst_mode_valid,
|
||||
.atomic_best_encoder = intel_mst_atomic_best_encoder,
|
||||
.best_encoder = intel_mst_best_encoder,
|
||||
.atomic_check = intel_dp_mst_atomic_check,
|
||||
};
|
||||
|
||||
|
@ -341,7 +341,7 @@ static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder)
|
||||
mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);
|
||||
|
||||
panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node);
|
||||
if (panel) {
|
||||
if (!IS_ERR(panel)) {
|
||||
drm_panel_disable(panel);
|
||||
drm_panel_unprepare(panel);
|
||||
}
|
||||
@ -410,7 +410,7 @@ static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder)
|
||||
dev_err(dev->dev, "failed to enable lcdc_clk: %d\n", ret);
|
||||
|
||||
panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node);
|
||||
if (panel) {
|
||||
if (!IS_ERR(panel)) {
|
||||
drm_panel_prepare(panel);
|
||||
drm_panel_enable(panel);
|
||||
}
|
||||
|
@ -34,9 +34,12 @@ static enum drm_connector_status mdp4_lvds_connector_detect(
|
||||
struct mdp4_lvds_connector *mdp4_lvds_connector =
|
||||
to_mdp4_lvds_connector(connector);
|
||||
|
||||
if (!mdp4_lvds_connector->panel)
|
||||
if (!mdp4_lvds_connector->panel) {
|
||||
mdp4_lvds_connector->panel =
|
||||
of_drm_find_panel(mdp4_lvds_connector->panel_node);
|
||||
if (IS_ERR(mdp4_lvds_connector->panel))
|
||||
mdp4_lvds_connector->panel = NULL;
|
||||
}
|
||||
|
||||
return mdp4_lvds_connector->panel ?
|
||||
connector_status_connected :
|
||||
|
@ -1898,7 +1898,7 @@ int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer)
|
||||
* output
|
||||
*/
|
||||
if (check_defer && msm_host->device_node) {
|
||||
if (!of_drm_find_panel(msm_host->device_node))
|
||||
if (IS_ERR(of_drm_find_panel(msm_host->device_node)))
|
||||
if (!of_drm_find_bridge(msm_host->device_node))
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
@ -751,12 +751,8 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id)
|
||||
connector_list = &dev->mode_config.connector_list;
|
||||
|
||||
list_for_each_entry(connector, connector_list, head) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == encoder->base.id)
|
||||
return connector;
|
||||
}
|
||||
if (drm_connector_has_possible_encoder(connector, encoder))
|
||||
return connector;
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
@ -363,19 +363,11 @@ module_param_named(hdmimhz, nouveau_hdmimhz, int, 0400);
|
||||
struct nouveau_encoder *
|
||||
find_encoder(struct drm_connector *connector, int type)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct drm_encoder *enc;
|
||||
int i, id;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
id = connector->encoder_ids[i];
|
||||
if (!id)
|
||||
break;
|
||||
|
||||
enc = drm_encoder_find(dev, NULL, id);
|
||||
if (!enc)
|
||||
continue;
|
||||
drm_connector_for_each_possible_encoder(connector, enc, i) {
|
||||
nv_encoder = nouveau_encoder(enc);
|
||||
|
||||
if (type == DCB_OUTPUT_ANY ||
|
||||
@ -420,7 +412,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
|
||||
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device);
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct nouveau_encoder *nv_encoder = NULL;
|
||||
struct drm_encoder *encoder;
|
||||
int i, panel = -ENODEV;
|
||||
|
||||
@ -436,14 +428,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; nv_encoder = NULL, i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
int id = connector->encoder_ids[i];
|
||||
if (id == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(dev, NULL, id);
|
||||
if (!encoder)
|
||||
continue;
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
nv_encoder = nouveau_encoder(encoder);
|
||||
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
|
||||
|
@ -334,7 +334,7 @@ static int ili9881c_prepare(struct drm_panel *panel)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
@ -20,12 +21,41 @@
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
struct panel_init_cmd {
|
||||
size_t len;
|
||||
const char *data;
|
||||
};
|
||||
|
||||
#define _INIT_CMD(...) { \
|
||||
.len = sizeof((char[]){__VA_ARGS__}), \
|
||||
.data = (char[]){__VA_ARGS__} }
|
||||
|
||||
struct panel_desc {
|
||||
const struct drm_display_mode *mode;
|
||||
unsigned int bpc;
|
||||
struct {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
} size;
|
||||
|
||||
unsigned long flags;
|
||||
enum mipi_dsi_pixel_format format;
|
||||
const struct panel_init_cmd *init_cmds;
|
||||
unsigned int lanes;
|
||||
const char * const *supply_names;
|
||||
unsigned int num_supplies;
|
||||
unsigned int sleep_mode_delay;
|
||||
unsigned int power_down_delay;
|
||||
};
|
||||
|
||||
struct innolux_panel {
|
||||
struct drm_panel base;
|
||||
struct mipi_dsi_device *link;
|
||||
const struct panel_desc *desc;
|
||||
|
||||
struct backlight_device *backlight;
|
||||
struct regulator *supply;
|
||||
struct regulator_bulk_data *supplies;
|
||||
unsigned int num_supplies;
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
||||
bool prepared;
|
||||
@ -72,12 +102,16 @@ static int innolux_panel_unprepare(struct drm_panel *panel)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (innolux->desc->sleep_mode_delay)
|
||||
msleep(innolux->desc->sleep_mode_delay);
|
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0);
|
||||
|
||||
/* T8: 80ms - 1000ms */
|
||||
msleep(80);
|
||||
if (innolux->desc->power_down_delay)
|
||||
msleep(innolux->desc->power_down_delay);
|
||||
|
||||
err = regulator_disable(innolux->supply);
|
||||
err = regulator_bulk_disable(innolux->desc->num_supplies,
|
||||
innolux->supplies);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -89,24 +123,55 @@ static int innolux_panel_unprepare(struct drm_panel *panel)
|
||||
static int innolux_panel_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct innolux_panel *innolux = to_innolux_panel(panel);
|
||||
int err, regulator_err;
|
||||
int err;
|
||||
|
||||
if (innolux->prepared)
|
||||
return 0;
|
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0);
|
||||
|
||||
err = regulator_enable(innolux->supply);
|
||||
err = regulator_bulk_enable(innolux->desc->num_supplies,
|
||||
innolux->supplies);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* T2: 15ms - 1000ms */
|
||||
usleep_range(15000, 16000);
|
||||
/* p079zca: t2 (20ms), p097pfg: t4 (15ms) */
|
||||
usleep_range(20000, 21000);
|
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 1);
|
||||
|
||||
/* T4: 15ms - 1000ms */
|
||||
usleep_range(15000, 16000);
|
||||
/* p079zca: t4, p097pfg: t5 */
|
||||
usleep_range(20000, 21000);
|
||||
|
||||
if (innolux->desc->init_cmds) {
|
||||
const struct panel_init_cmd *cmds =
|
||||
innolux->desc->init_cmds;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; cmds[i].len != 0; i++) {
|
||||
const struct panel_init_cmd *cmd = &cmds[i];
|
||||
|
||||
err = mipi_dsi_generic_write(innolux->link, cmd->data,
|
||||
cmd->len);
|
||||
if (err < 0) {
|
||||
dev_err(panel->dev,
|
||||
"failed to write command %u\n", i);
|
||||
goto poweroff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Included by random guessing, because without this
|
||||
* (or at least, some delay), the panel sometimes
|
||||
* didn't appear to pick up the command sequence.
|
||||
*/
|
||||
err = mipi_dsi_dcs_nop(innolux->link);
|
||||
if (err < 0) {
|
||||
dev_err(panel->dev,
|
||||
"failed to send DCS nop: %d\n", err);
|
||||
goto poweroff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
|
||||
if (err < 0) {
|
||||
@ -133,12 +198,9 @@ static int innolux_panel_prepare(struct drm_panel *panel)
|
||||
return 0;
|
||||
|
||||
poweroff:
|
||||
regulator_err = regulator_disable(innolux->supply);
|
||||
if (regulator_err)
|
||||
DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n",
|
||||
regulator_err);
|
||||
|
||||
gpiod_set_value_cansleep(innolux->enable_gpio, 0);
|
||||
regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -162,7 +224,11 @@ static int innolux_panel_enable(struct drm_panel *panel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode default_mode = {
|
||||
static const char * const innolux_p079zca_supply_names[] = {
|
||||
"power",
|
||||
};
|
||||
|
||||
static const struct drm_display_mode innolux_p079zca_mode = {
|
||||
.clock = 56900,
|
||||
.hdisplay = 768,
|
||||
.hsync_start = 768 + 40,
|
||||
@ -175,15 +241,181 @@ static const struct drm_display_mode default_mode = {
|
||||
.vrefresh = 60,
|
||||
};
|
||||
|
||||
static const struct panel_desc innolux_p079zca_panel_desc = {
|
||||
.mode = &innolux_p079zca_mode,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 120,
|
||||
.height = 160,
|
||||
},
|
||||
.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
|
||||
MIPI_DSI_MODE_LPM,
|
||||
.format = MIPI_DSI_FMT_RGB888,
|
||||
.lanes = 4,
|
||||
.supply_names = innolux_p079zca_supply_names,
|
||||
.num_supplies = ARRAY_SIZE(innolux_p079zca_supply_names),
|
||||
.power_down_delay = 80, /* T8: 80ms - 1000ms */
|
||||
};
|
||||
|
||||
static const char * const innolux_p097pfg_supply_names[] = {
|
||||
"avdd",
|
||||
"avee",
|
||||
};
|
||||
|
||||
static const struct drm_display_mode innolux_p097pfg_mode = {
|
||||
.clock = 229000,
|
||||
.hdisplay = 1536,
|
||||
.hsync_start = 1536 + 100,
|
||||
.hsync_end = 1536 + 100 + 24,
|
||||
.htotal = 1536 + 100 + 24 + 100,
|
||||
.vdisplay = 2048,
|
||||
.vsync_start = 2048 + 100,
|
||||
.vsync_end = 2048 + 100 + 2,
|
||||
.vtotal = 2048 + 100 + 2 + 18,
|
||||
.vrefresh = 60,
|
||||
};
|
||||
|
||||
/*
|
||||
* Display manufacturer failed to provide init sequencing according to
|
||||
* https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/892065/
|
||||
* so the init sequence stems from a register dump of a working panel.
|
||||
*/
|
||||
static const struct panel_init_cmd innolux_p097pfg_init_cmds[] = {
|
||||
/* page 0 */
|
||||
_INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00),
|
||||
_INIT_CMD(0xB1, 0xE8, 0x11),
|
||||
_INIT_CMD(0xB2, 0x25, 0x02),
|
||||
_INIT_CMD(0xB5, 0x08, 0x00),
|
||||
_INIT_CMD(0xBC, 0x0F, 0x00),
|
||||
_INIT_CMD(0xB8, 0x03, 0x06, 0x00, 0x00),
|
||||
_INIT_CMD(0xBD, 0x01, 0x90, 0x14, 0x14),
|
||||
_INIT_CMD(0x6F, 0x01),
|
||||
_INIT_CMD(0xC0, 0x03),
|
||||
_INIT_CMD(0x6F, 0x02),
|
||||
_INIT_CMD(0xC1, 0x0D),
|
||||
_INIT_CMD(0xD9, 0x01, 0x09, 0x70),
|
||||
_INIT_CMD(0xC5, 0x12, 0x21, 0x00),
|
||||
_INIT_CMD(0xBB, 0x93, 0x93),
|
||||
|
||||
/* page 1 */
|
||||
_INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01),
|
||||
_INIT_CMD(0xB3, 0x3C, 0x3C),
|
||||
_INIT_CMD(0xB4, 0x0F, 0x0F),
|
||||
_INIT_CMD(0xB9, 0x45, 0x45),
|
||||
_INIT_CMD(0xBA, 0x14, 0x14),
|
||||
_INIT_CMD(0xCA, 0x02),
|
||||
_INIT_CMD(0xCE, 0x04),
|
||||
_INIT_CMD(0xC3, 0x9B, 0x9B),
|
||||
_INIT_CMD(0xD8, 0xC0, 0x03),
|
||||
_INIT_CMD(0xBC, 0x82, 0x01),
|
||||
_INIT_CMD(0xBD, 0x9E, 0x01),
|
||||
|
||||
/* page 2 */
|
||||
_INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x02),
|
||||
_INIT_CMD(0xB0, 0x82),
|
||||
_INIT_CMD(0xD1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x82, 0x00, 0xA5,
|
||||
0x00, 0xC1, 0x00, 0xEA, 0x01, 0x0D, 0x01, 0x40),
|
||||
_INIT_CMD(0xD2, 0x01, 0x6A, 0x01, 0xA8, 0x01, 0xDC, 0x02, 0x29,
|
||||
0x02, 0x67, 0x02, 0x68, 0x02, 0xA8, 0x02, 0xF0),
|
||||
_INIT_CMD(0xD3, 0x03, 0x19, 0x03, 0x49, 0x03, 0x67, 0x03, 0x8C,
|
||||
0x03, 0xA6, 0x03, 0xC7, 0x03, 0xDE, 0x03, 0xEC),
|
||||
_INIT_CMD(0xD4, 0x03, 0xFF, 0x03, 0xFF),
|
||||
_INIT_CMD(0xE0, 0x00, 0x00, 0x00, 0x86, 0x00, 0xC5, 0x00, 0xE5,
|
||||
0x00, 0xFF, 0x01, 0x26, 0x01, 0x45, 0x01, 0x75),
|
||||
_INIT_CMD(0xE1, 0x01, 0x9C, 0x01, 0xD5, 0x02, 0x05, 0x02, 0x4D,
|
||||
0x02, 0x86, 0x02, 0x87, 0x02, 0xC3, 0x03, 0x03),
|
||||
_INIT_CMD(0xE2, 0x03, 0x2A, 0x03, 0x56, 0x03, 0x72, 0x03, 0x94,
|
||||
0x03, 0xAC, 0x03, 0xCB, 0x03, 0xE0, 0x03, 0xED),
|
||||
_INIT_CMD(0xE3, 0x03, 0xFF, 0x03, 0xFF),
|
||||
|
||||
/* page 3 */
|
||||
_INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x03),
|
||||
_INIT_CMD(0xB0, 0x00, 0x00, 0x00, 0x00),
|
||||
_INIT_CMD(0xB1, 0x00, 0x00, 0x00, 0x00),
|
||||
_INIT_CMD(0xB2, 0x00, 0x00, 0x06, 0x04, 0x01, 0x40, 0x85),
|
||||
_INIT_CMD(0xB3, 0x10, 0x07, 0xFC, 0x04, 0x01, 0x40, 0x80),
|
||||
_INIT_CMD(0xB6, 0xF0, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
|
||||
0x40, 0x80),
|
||||
_INIT_CMD(0xBA, 0xC5, 0x07, 0x00, 0x04, 0x11, 0x25, 0x8C),
|
||||
_INIT_CMD(0xBB, 0xC5, 0x07, 0x00, 0x03, 0x11, 0x25, 0x8C),
|
||||
_INIT_CMD(0xC0, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x80, 0x80),
|
||||
_INIT_CMD(0xC1, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x80, 0x80),
|
||||
_INIT_CMD(0xC4, 0x00, 0x00),
|
||||
_INIT_CMD(0xEF, 0x41),
|
||||
|
||||
/* page 4 */
|
||||
_INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x04),
|
||||
_INIT_CMD(0xEC, 0x4C),
|
||||
|
||||
/* page 5 */
|
||||
_INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x05),
|
||||
_INIT_CMD(0xB0, 0x13, 0x03, 0x03, 0x01),
|
||||
_INIT_CMD(0xB1, 0x30, 0x00),
|
||||
_INIT_CMD(0xB2, 0x02, 0x02, 0x00),
|
||||
_INIT_CMD(0xB3, 0x82, 0x23, 0x82, 0x9D),
|
||||
_INIT_CMD(0xB4, 0xC5, 0x75, 0x24, 0x57),
|
||||
_INIT_CMD(0xB5, 0x00, 0xD4, 0x72, 0x11, 0x11, 0xAB, 0x0A),
|
||||
_INIT_CMD(0xB6, 0x00, 0x00, 0xD5, 0x72, 0x24, 0x56),
|
||||
_INIT_CMD(0xB7, 0x5C, 0xDC, 0x5C, 0x5C),
|
||||
_INIT_CMD(0xB9, 0x0C, 0x00, 0x00, 0x01, 0x00),
|
||||
_INIT_CMD(0xC0, 0x75, 0x11, 0x11, 0x54, 0x05),
|
||||
_INIT_CMD(0xC6, 0x00, 0x00, 0x00, 0x00),
|
||||
_INIT_CMD(0xD0, 0x00, 0x48, 0x08, 0x00, 0x00),
|
||||
_INIT_CMD(0xD1, 0x00, 0x48, 0x09, 0x00, 0x00),
|
||||
|
||||
/* page 6 */
|
||||
_INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x06),
|
||||
_INIT_CMD(0xB0, 0x02, 0x32, 0x32, 0x08, 0x2F),
|
||||
_INIT_CMD(0xB1, 0x2E, 0x15, 0x14, 0x13, 0x12),
|
||||
_INIT_CMD(0xB2, 0x11, 0x10, 0x00, 0x3D, 0x3D),
|
||||
_INIT_CMD(0xB3, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D),
|
||||
_INIT_CMD(0xB4, 0x3D, 0x32),
|
||||
_INIT_CMD(0xB5, 0x03, 0x32, 0x32, 0x09, 0x2F),
|
||||
_INIT_CMD(0xB6, 0x2E, 0x1B, 0x1A, 0x19, 0x18),
|
||||
_INIT_CMD(0xB7, 0x17, 0x16, 0x01, 0x3D, 0x3D),
|
||||
_INIT_CMD(0xB8, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D),
|
||||
_INIT_CMD(0xB9, 0x3D, 0x32),
|
||||
_INIT_CMD(0xC0, 0x01, 0x32, 0x32, 0x09, 0x2F),
|
||||
_INIT_CMD(0xC1, 0x2E, 0x1A, 0x1B, 0x16, 0x17),
|
||||
_INIT_CMD(0xC2, 0x18, 0x19, 0x03, 0x3D, 0x3D),
|
||||
_INIT_CMD(0xC3, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D),
|
||||
_INIT_CMD(0xC4, 0x3D, 0x32),
|
||||
_INIT_CMD(0xC5, 0x00, 0x32, 0x32, 0x08, 0x2F),
|
||||
_INIT_CMD(0xC6, 0x2E, 0x14, 0x15, 0x10, 0x11),
|
||||
_INIT_CMD(0xC7, 0x12, 0x13, 0x02, 0x3D, 0x3D),
|
||||
_INIT_CMD(0xC8, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D),
|
||||
_INIT_CMD(0xC9, 0x3D, 0x32),
|
||||
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct panel_desc innolux_p097pfg_panel_desc = {
|
||||
.mode = &innolux_p097pfg_mode,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 147,
|
||||
.height = 196,
|
||||
},
|
||||
.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
|
||||
MIPI_DSI_MODE_LPM,
|
||||
.format = MIPI_DSI_FMT_RGB888,
|
||||
.init_cmds = innolux_p097pfg_init_cmds,
|
||||
.lanes = 4,
|
||||
.supply_names = innolux_p097pfg_supply_names,
|
||||
.num_supplies = ARRAY_SIZE(innolux_p097pfg_supply_names),
|
||||
.sleep_mode_delay = 100, /* T15 */
|
||||
};
|
||||
|
||||
static int innolux_panel_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct innolux_panel *innolux = to_innolux_panel(panel);
|
||||
const struct drm_display_mode *m = innolux->desc->mode;
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(panel->drm, &default_mode);
|
||||
mode = drm_mode_duplicate(panel->drm, m);
|
||||
if (!mode) {
|
||||
DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
|
||||
default_mode.hdisplay, default_mode.vdisplay,
|
||||
default_mode.vrefresh);
|
||||
m->hdisplay, m->vdisplay, m->vrefresh);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -191,9 +423,11 @@ static int innolux_panel_get_modes(struct drm_panel *panel)
|
||||
|
||||
drm_mode_probed_add(panel->connector, mode);
|
||||
|
||||
panel->connector->display_info.width_mm = 120;
|
||||
panel->connector->display_info.height_mm = 160;
|
||||
panel->connector->display_info.bpc = 8;
|
||||
panel->connector->display_info.width_mm =
|
||||
innolux->desc->size.width;
|
||||
panel->connector->display_info.height_mm =
|
||||
innolux->desc->size.height;
|
||||
panel->connector->display_info.bpc = innolux->desc->bpc;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -207,19 +441,42 @@ static const struct drm_panel_funcs innolux_panel_funcs = {
|
||||
};
|
||||
|
||||
static const struct of_device_id innolux_of_match[] = {
|
||||
{ .compatible = "innolux,p079zca", },
|
||||
{ .compatible = "innolux,p079zca",
|
||||
.data = &innolux_p079zca_panel_desc
|
||||
},
|
||||
{ .compatible = "innolux,p097pfg",
|
||||
.data = &innolux_p097pfg_panel_desc
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, innolux_of_match);
|
||||
|
||||
static int innolux_panel_add(struct innolux_panel *innolux)
|
||||
static int innolux_panel_add(struct mipi_dsi_device *dsi,
|
||||
const struct panel_desc *desc)
|
||||
{
|
||||
struct device *dev = &innolux->link->dev;
|
||||
int err;
|
||||
struct innolux_panel *innolux;
|
||||
struct device *dev = &dsi->dev;
|
||||
int err, i;
|
||||
|
||||
innolux->supply = devm_regulator_get(dev, "power");
|
||||
if (IS_ERR(innolux->supply))
|
||||
return PTR_ERR(innolux->supply);
|
||||
innolux = devm_kzalloc(dev, sizeof(*innolux), GFP_KERNEL);
|
||||
if (!innolux)
|
||||
return -ENOMEM;
|
||||
|
||||
innolux->desc = desc;
|
||||
|
||||
innolux->supplies = devm_kcalloc(dev, desc->num_supplies,
|
||||
sizeof(*innolux->supplies),
|
||||
GFP_KERNEL);
|
||||
if (!innolux->supplies)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < desc->num_supplies; i++)
|
||||
innolux->supplies[i].supply = desc->supply_names[i];
|
||||
|
||||
err = devm_regulator_bulk_get(dev, desc->num_supplies,
|
||||
innolux->supplies);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
|
||||
GPIOD_OUT_HIGH);
|
||||
@ -230,15 +487,21 @@ static int innolux_panel_add(struct innolux_panel *innolux)
|
||||
}
|
||||
|
||||
innolux->backlight = devm_of_find_backlight(dev);
|
||||
|
||||
if (IS_ERR(innolux->backlight))
|
||||
return PTR_ERR(innolux->backlight);
|
||||
|
||||
drm_panel_init(&innolux->base);
|
||||
innolux->base.funcs = &innolux_panel_funcs;
|
||||
innolux->base.dev = &innolux->link->dev;
|
||||
innolux->base.dev = dev;
|
||||
|
||||
return drm_panel_add(&innolux->base);
|
||||
err = drm_panel_add(&innolux->base);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mipi_dsi_set_drvdata(dsi, innolux);
|
||||
innolux->link = dsi;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void innolux_panel_del(struct innolux_panel *innolux)
|
||||
@ -249,28 +512,19 @@ static void innolux_panel_del(struct innolux_panel *innolux)
|
||||
|
||||
static int innolux_panel_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct innolux_panel *innolux;
|
||||
const struct panel_desc *desc;
|
||||
int err;
|
||||
|
||||
dsi->lanes = 4;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
|
||||
MIPI_DSI_MODE_LPM;
|
||||
desc = of_device_get_match_data(&dsi->dev);
|
||||
dsi->mode_flags = desc->flags;
|
||||
dsi->format = desc->format;
|
||||
dsi->lanes = desc->lanes;
|
||||
|
||||
innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL);
|
||||
if (!innolux)
|
||||
return -ENOMEM;
|
||||
|
||||
mipi_dsi_set_drvdata(dsi, innolux);
|
||||
|
||||
innolux->link = dsi;
|
||||
|
||||
err = innolux_panel_add(innolux);
|
||||
err = innolux_panel_add(dsi, desc);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = mipi_dsi_attach(dsi);
|
||||
return err;
|
||||
return mipi_dsi_attach(dsi);
|
||||
}
|
||||
|
||||
static int innolux_panel_remove(struct mipi_dsi_device *dsi)
|
||||
@ -317,5 +571,6 @@ static struct mipi_dsi_driver innolux_panel_driver = {
|
||||
module_mipi_dsi_driver(innolux_panel_driver);
|
||||
|
||||
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
|
||||
MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -823,7 +823,7 @@ static void s6e8aa0_read_mtp_id(struct s6e8aa0 *ctx)
|
||||
int ret, i;
|
||||
|
||||
ret = s6e8aa0_dcs_read(ctx, 0xd1, id, ARRAY_SIZE(id));
|
||||
if (ret < ARRAY_SIZE(id) || id[0] == 0x00) {
|
||||
if (ret < 0 || ret < ARRAY_SIZE(id) || id[0] == 0x00) {
|
||||
dev_err(ctx->dev, "read id failed\n");
|
||||
ctx->error = -EIO;
|
||||
return;
|
||||
|
@ -772,6 +772,28 @@ static const struct panel_desc avic_tm070ddh03 = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct drm_display_mode boe_hv070wsa_mode = {
|
||||
.clock = 40800,
|
||||
.hdisplay = 1024,
|
||||
.hsync_start = 1024 + 90,
|
||||
.hsync_end = 1024 + 90 + 90,
|
||||
.htotal = 1024 + 90 + 90 + 90,
|
||||
.vdisplay = 600,
|
||||
.vsync_start = 600 + 3,
|
||||
.vsync_end = 600 + 3 + 4,
|
||||
.vtotal = 600 + 3 + 4 + 3,
|
||||
.vrefresh = 60,
|
||||
};
|
||||
|
||||
static const struct panel_desc boe_hv070wsa = {
|
||||
.modes = &boe_hv070wsa_mode,
|
||||
.num_modes = 1,
|
||||
.size = {
|
||||
.width = 154,
|
||||
.height = 90,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct drm_display_mode boe_nv101wxmn51_modes[] = {
|
||||
{
|
||||
.clock = 71900,
|
||||
@ -884,6 +906,61 @@ static const struct panel_desc chunghwa_claa101wb01 = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct drm_display_mode dataimage_scf0700c48ggu18_mode = {
|
||||
.clock = 33260,
|
||||
.hdisplay = 800,
|
||||
.hsync_start = 800 + 40,
|
||||
.hsync_end = 800 + 40 + 128,
|
||||
.htotal = 800 + 40 + 128 + 88,
|
||||
.vdisplay = 480,
|
||||
.vsync_start = 480 + 10,
|
||||
.vsync_end = 480 + 10 + 2,
|
||||
.vtotal = 480 + 10 + 2 + 33,
|
||||
.vrefresh = 60,
|
||||
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
|
||||
};
|
||||
|
||||
static const struct panel_desc dataimage_scf0700c48ggu18 = {
|
||||
.modes = &dataimage_scf0700c48ggu18_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 152,
|
||||
.height = 91,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE,
|
||||
};
|
||||
|
||||
static const struct display_timing dlc_dlc0700yzg_1_timing = {
|
||||
.pixelclock = { 45000000, 51200000, 57000000 },
|
||||
.hactive = { 1024, 1024, 1024 },
|
||||
.hfront_porch = { 100, 106, 113 },
|
||||
.hback_porch = { 100, 106, 113 },
|
||||
.hsync_len = { 100, 108, 114 },
|
||||
.vactive = { 600, 600, 600 },
|
||||
.vfront_porch = { 8, 11, 15 },
|
||||
.vback_porch = { 8, 11, 15 },
|
||||
.vsync_len = { 9, 13, 15 },
|
||||
.flags = DISPLAY_FLAGS_DE_HIGH,
|
||||
};
|
||||
|
||||
static const struct panel_desc dlc_dlc0700yzg_1 = {
|
||||
.timings = &dlc_dlc0700yzg_1_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 154,
|
||||
.height = 86,
|
||||
},
|
||||
.delay = {
|
||||
.prepare = 30,
|
||||
.enable = 200,
|
||||
.disable = 200,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode edt_et057090dhu_mode = {
|
||||
.clock = 25175,
|
||||
.hdisplay = 640,
|
||||
@ -936,6 +1013,18 @@ static const struct panel_desc edt_etm0700g0dh6 = {
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE,
|
||||
};
|
||||
|
||||
static const struct panel_desc edt_etm0700g0bdh6 = {
|
||||
.modes = &edt_etm0700g0dh6_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 152,
|
||||
.height = 91,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = {
|
||||
.clock = 32260,
|
||||
.hdisplay = 800,
|
||||
@ -1113,6 +1202,36 @@ static const struct panel_desc innolux_at070tn92 = {
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
};
|
||||
|
||||
static const struct display_timing innolux_g070y2_l01_timing = {
|
||||
.pixelclock = { 28000000, 29500000, 32000000 },
|
||||
.hactive = { 800, 800, 800 },
|
||||
.hfront_porch = { 61, 91, 141 },
|
||||
.hback_porch = { 60, 90, 140 },
|
||||
.hsync_len = { 12, 12, 12 },
|
||||
.vactive = { 480, 480, 480 },
|
||||
.vfront_porch = { 4, 9, 30 },
|
||||
.vback_porch = { 4, 8, 28 },
|
||||
.vsync_len = { 2, 2, 2 },
|
||||
.flags = DISPLAY_FLAGS_DE_HIGH,
|
||||
};
|
||||
|
||||
static const struct panel_desc innolux_g070y2_l01 = {
|
||||
.timings = &innolux_g070y2_l01_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 152,
|
||||
.height = 91,
|
||||
},
|
||||
.delay = {
|
||||
.prepare = 10,
|
||||
.enable = 100,
|
||||
.disable = 100,
|
||||
.unprepare = 800,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
};
|
||||
|
||||
static const struct display_timing innolux_g101ice_l01_timing = {
|
||||
.pixelclock = { 60400000, 71100000, 74700000 },
|
||||
.hactive = { 1280, 1280, 1280 },
|
||||
@ -1562,6 +1681,33 @@ static const struct panel_desc netron_dy_e231732 = {
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode newhaven_nhd_43_480272ef_atxl_mode = {
|
||||
.clock = 9000,
|
||||
.hdisplay = 480,
|
||||
.hsync_start = 480 + 2,
|
||||
.hsync_end = 480 + 2 + 41,
|
||||
.htotal = 480 + 2 + 41 + 2,
|
||||
.vdisplay = 272,
|
||||
.vsync_start = 272 + 2,
|
||||
.vsync_end = 272 + 2 + 10,
|
||||
.vtotal = 272 + 2 + 10 + 2,
|
||||
.vrefresh = 60,
|
||||
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
|
||||
};
|
||||
|
||||
static const struct panel_desc newhaven_nhd_43_480272ef_atxl = {
|
||||
.modes = &newhaven_nhd_43_480272ef_atxl_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 95,
|
||||
.height = 54,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE |
|
||||
DRM_BUS_FLAG_SYNC_POSEDGE,
|
||||
};
|
||||
|
||||
static const struct display_timing nlt_nl192108ac18_02d_timing = {
|
||||
.pixelclock = { 130000000, 148350000, 163000000 },
|
||||
.hactive = { 1920, 1920, 1920 },
|
||||
@ -1747,6 +1893,36 @@ static const struct panel_desc qd43003c0_40 = {
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
};
|
||||
|
||||
static const struct display_timing rocktech_rk070er9427_timing = {
|
||||
.pixelclock = { 26400000, 33300000, 46800000 },
|
||||
.hactive = { 800, 800, 800 },
|
||||
.hfront_porch = { 16, 210, 354 },
|
||||
.hback_porch = { 46, 46, 46 },
|
||||
.hsync_len = { 1, 1, 1 },
|
||||
.vactive = { 480, 480, 480 },
|
||||
.vfront_porch = { 7, 22, 147 },
|
||||
.vback_porch = { 23, 23, 23 },
|
||||
.vsync_len = { 1, 1, 1 },
|
||||
.flags = DISPLAY_FLAGS_DE_HIGH,
|
||||
};
|
||||
|
||||
static const struct panel_desc rocktech_rk070er9427 = {
|
||||
.timings = &rocktech_rk070er9427_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 154,
|
||||
.height = 86,
|
||||
},
|
||||
.delay = {
|
||||
.prepare = 41,
|
||||
.enable = 50,
|
||||
.unprepare = 41,
|
||||
.disable = 50,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode samsung_lsn122dl01_c01_mode = {
|
||||
.clock = 271560,
|
||||
.hdisplay = 2560,
|
||||
@ -1815,6 +1991,30 @@ static const struct panel_desc samsung_ltn140at29_301 = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct drm_display_mode sharp_lq035q7db03_mode = {
|
||||
.clock = 5500,
|
||||
.hdisplay = 240,
|
||||
.hsync_start = 240 + 16,
|
||||
.hsync_end = 240 + 16 + 7,
|
||||
.htotal = 240 + 16 + 7 + 5,
|
||||
.vdisplay = 320,
|
||||
.vsync_start = 320 + 9,
|
||||
.vsync_end = 320 + 9 + 1,
|
||||
.vtotal = 320 + 9 + 1 + 7,
|
||||
.vrefresh = 60,
|
||||
};
|
||||
|
||||
static const struct panel_desc sharp_lq035q7db03 = {
|
||||
.modes = &sharp_lq035q7db03_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 54,
|
||||
.height = 72,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
|
||||
};
|
||||
|
||||
static const struct display_timing sharp_lq101k1ly04_timing = {
|
||||
.pixelclock = { 60000000, 65000000, 80000000 },
|
||||
.hactive = { 1280, 1280, 1280 },
|
||||
@ -2166,6 +2366,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "avic,tm070ddh03",
|
||||
.data = &avic_tm070ddh03,
|
||||
}, {
|
||||
.compatible = "boe,hv070wsa-100",
|
||||
.data = &boe_hv070wsa
|
||||
}, {
|
||||
.compatible = "boe,nv101wxmn51",
|
||||
.data = &boe_nv101wxmn51,
|
||||
@ -2178,6 +2381,12 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "chunghwa,claa101wb01",
|
||||
.data = &chunghwa_claa101wb01
|
||||
}, {
|
||||
.compatible = "dataimage,scf0700c48ggu18",
|
||||
.data = &dataimage_scf0700c48ggu18,
|
||||
}, {
|
||||
.compatible = "dlc,dlc0700yzg-1",
|
||||
.data = &dlc_dlc0700yzg_1,
|
||||
}, {
|
||||
.compatible = "edt,et057090dhu",
|
||||
.data = &edt_et057090dhu,
|
||||
@ -2187,6 +2396,12 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "edt,etm0700g0dh6",
|
||||
.data = &edt_etm0700g0dh6,
|
||||
}, {
|
||||
.compatible = "edt,etm0700g0bdh6",
|
||||
.data = &edt_etm0700g0bdh6,
|
||||
}, {
|
||||
.compatible = "edt,etm0700g0edh6",
|
||||
.data = &edt_etm0700g0bdh6,
|
||||
}, {
|
||||
.compatible = "foxlink,fl500wvr00-a0t",
|
||||
.data = &foxlink_fl500wvr00_a0t,
|
||||
@ -2209,10 +2424,13 @@ static const struct of_device_id platform_of_match[] = {
|
||||
.compatible = "innolux,at070tn92",
|
||||
.data = &innolux_at070tn92,
|
||||
}, {
|
||||
.compatible ="innolux,g101ice-l01",
|
||||
.compatible = "innolux,g070y2-l01",
|
||||
.data = &innolux_g070y2_l01,
|
||||
}, {
|
||||
.compatible = "innolux,g101ice-l01",
|
||||
.data = &innolux_g101ice_l01
|
||||
}, {
|
||||
.compatible ="innolux,g121i1-l01",
|
||||
.compatible = "innolux,g121i1-l01",
|
||||
.data = &innolux_g121i1_l01
|
||||
}, {
|
||||
.compatible = "innolux,g121x1-l03",
|
||||
@ -2262,6 +2480,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "netron-dy,e231732",
|
||||
.data = &netron_dy_e231732,
|
||||
}, {
|
||||
.compatible = "newhaven,nhd-4.3-480272ef-atxl",
|
||||
.data = &newhaven_nhd_43_480272ef_atxl,
|
||||
}, {
|
||||
.compatible = "nlt,nl192108ac18-02d",
|
||||
.data = &nlt_nl192108ac18_02d,
|
||||
@ -2283,6 +2504,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "qiaodian,qd43003c0-40",
|
||||
.data = &qd43003c0_40,
|
||||
}, {
|
||||
.compatible = "rocktech,rk070er9427",
|
||||
.data = &rocktech_rk070er9427,
|
||||
}, {
|
||||
.compatible = "samsung,lsn122dl01-c01",
|
||||
.data = &samsung_lsn122dl01_c01,
|
||||
@ -2292,6 +2516,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,ltn140at29-301",
|
||||
.data = &samsung_ltn140at29_301,
|
||||
}, {
|
||||
.compatible = "sharp,lq035q7db03",
|
||||
.data = &sharp_lq035q7db03,
|
||||
}, {
|
||||
.compatible = "sharp,lq101k1ly04",
|
||||
.data = &sharp_lq101k1ly04,
|
||||
|
@ -250,6 +250,8 @@ static struct drm_driver pl111_drm_driver = {
|
||||
.gem_prime_import_sg_table = pl111_gem_import_sg_table,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
.debugfs_init = pl111_debugfs_init,
|
||||
|
@ -244,23 +244,15 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct radeon_device *rdev = dev->dev_private;
|
||||
struct drm_encoder *best_encoder = NULL;
|
||||
struct drm_encoder *encoder = NULL;
|
||||
struct drm_encoder *best_encoder;
|
||||
struct drm_encoder *encoder;
|
||||
const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private;
|
||||
bool connected;
|
||||
int i;
|
||||
|
||||
best_encoder = connector_funcs->best_encoder(connector);
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if ((encoder == best_encoder) && (status == connector_status_connected))
|
||||
connected = true;
|
||||
else
|
||||
@ -270,7 +262,6 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c
|
||||
radeon_atombios_connected_scratch_regs(connector, encoder, connected);
|
||||
else
|
||||
radeon_combios_connected_scratch_regs(connector, encoder, connected);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,17 +270,11 @@ static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector,
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (encoder->encoder_type == encoder_type)
|
||||
return encoder;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -393,10 +378,13 @@ static int radeon_ddc_get_modes(struct drm_connector *connector)
|
||||
|
||||
static struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
/* pick the encoder ids */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
|
||||
/* pick the first one */
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i)
|
||||
return encoder;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -436,19 +424,19 @@ radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector,
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_connector *conflict;
|
||||
struct radeon_connector *radeon_conflict;
|
||||
int i;
|
||||
|
||||
list_for_each_entry(conflict, &dev->mode_config.connector_list, head) {
|
||||
struct drm_encoder *enc;
|
||||
int i;
|
||||
|
||||
if (conflict == connector)
|
||||
continue;
|
||||
|
||||
radeon_conflict = to_radeon_connector(conflict);
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (conflict->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
drm_connector_for_each_possible_encoder(conflict, enc, i) {
|
||||
/* if the IDs match */
|
||||
if (conflict->encoder_ids[i] == encoder->base.id) {
|
||||
if (enc == encoder) {
|
||||
if (conflict->status != connector_status_connected)
|
||||
continue;
|
||||
|
||||
@ -1256,7 +1244,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
|
||||
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
||||
struct drm_encoder *encoder = NULL;
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
int i, r;
|
||||
int r;
|
||||
enum drm_connector_status ret = connector_status_disconnected;
|
||||
bool dret = false, broken_edid = false;
|
||||
|
||||
@ -1374,15 +1362,9 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
/* find analog encoder */
|
||||
if (radeon_connector->dac_load_detect) {
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL,
|
||||
connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
int i;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (encoder->encoder_type != DRM_MODE_ENCODER_DAC &&
|
||||
encoder->encoder_type != DRM_MODE_ENCODER_TVDAC)
|
||||
continue;
|
||||
@ -1458,18 +1440,11 @@ exit:
|
||||
/* okay need to be smart in here about which encoder to pick */
|
||||
static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
||||
struct drm_encoder *encoder;
|
||||
int i;
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
if (radeon_connector->use_digital == true) {
|
||||
if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
|
||||
return encoder;
|
||||
@ -1484,8 +1459,9 @@ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector)
|
||||
|
||||
/* then check use digitial */
|
||||
/* pick the first one */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i)
|
||||
return encoder;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1628,14 +1604,7 @@ u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn
|
||||
struct radeon_encoder *radeon_encoder;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
radeon_encoder = to_radeon_encoder(encoder);
|
||||
|
||||
switch (radeon_encoder->encoder_id) {
|
||||
@ -1657,14 +1626,7 @@ static bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector)
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] == 0)
|
||||
break;
|
||||
|
||||
encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
|
||||
if (!encoder)
|
||||
continue;
|
||||
|
||||
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
||||
radeon_encoder = to_radeon_encoder(encoder);
|
||||
if (radeon_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2)
|
||||
found = true;
|
||||
|
@ -434,8 +434,8 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
|
||||
ret = -EPROBE_DEFER;
|
||||
} else {
|
||||
lvds->panel = of_drm_find_panel(remote);
|
||||
if (!lvds->panel)
|
||||
ret = -EPROBE_DEFER;
|
||||
if (IS_ERR(lvds->panel))
|
||||
ret = PTR_ERR(lvds->panel);
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -595,7 +595,7 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
dsi->format = device->format;
|
||||
dsi->mode_flags = device->mode_flags;
|
||||
dsi->panel = of_drm_find_panel(device->dev.of_node);
|
||||
if (dsi->panel)
|
||||
if (!IS_ERR(dsi->panel))
|
||||
return drm_panel_attach(dsi->panel, &dsi->connector);
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -387,7 +387,9 @@ sti_dvo_connector_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
if (!dvo->panel) {
|
||||
dvo->panel = of_drm_find_panel(dvo->panel_node);
|
||||
if (dvo->panel)
|
||||
if (IS_ERR(dvo->panel))
|
||||
dvo->panel = NULL;
|
||||
else
|
||||
drm_panel_attach(dvo->panel, connector);
|
||||
}
|
||||
|
||||
|
@ -457,6 +457,14 @@ ltdc_crtc_mode_valid(struct drm_crtc *crtc,
|
||||
int target_max = target + CLK_TOLERANCE_HZ;
|
||||
int result;
|
||||
|
||||
result = clk_round_rate(ldev->pixel_clk, target);
|
||||
|
||||
DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result);
|
||||
|
||||
/* Filter modes according to the max frequency supported by the pads */
|
||||
if (result > ldev->caps.pad_max_freq_hz)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
/*
|
||||
* Accept all "preferred" modes:
|
||||
* - this is important for panels because panel clock tolerances are
|
||||
@ -468,10 +476,6 @@ ltdc_crtc_mode_valid(struct drm_crtc *crtc,
|
||||
if (mode->type & DRM_MODE_TYPE_PREFERRED)
|
||||
return MODE_OK;
|
||||
|
||||
result = clk_round_rate(ldev->pixel_clk, target);
|
||||
|
||||
DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result);
|
||||
|
||||
/*
|
||||
* Filter modes according to the clock value, particularly useful for
|
||||
* hdmi modes that require precise pixel clocks.
|
||||
@ -991,11 +995,15 @@ static int ltdc_get_caps(struct drm_device *ddev)
|
||||
* does not work on 2nd layer.
|
||||
*/
|
||||
ldev->caps.non_alpha_only_l1 = true;
|
||||
ldev->caps.pad_max_freq_hz = 90000000;
|
||||
if (ldev->caps.hw_version == HWVER_10200)
|
||||
ldev->caps.pad_max_freq_hz = 65000000;
|
||||
break;
|
||||
case HWVER_20101:
|
||||
ldev->caps.reg_ofs = REG_OFS_4;
|
||||
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
|
||||
ldev->caps.non_alpha_only_l1 = false;
|
||||
ldev->caps.pad_max_freq_hz = 150000000;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
@ -1074,8 +1082,11 @@ int ltdc_load(struct drm_device *ddev)
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_ERR(rstc))
|
||||
if (!IS_ERR(rstc)) {
|
||||
reset_control_assert(rstc);
|
||||
usleep_range(10, 20);
|
||||
reset_control_deassert(rstc);
|
||||
}
|
||||
|
||||
/* Disable interrupts */
|
||||
reg_clear(ldev->regs, LTDC_IER,
|
||||
|
@ -18,6 +18,7 @@ struct ltdc_caps {
|
||||
u32 bus_width; /* bus width (32 or 64 bits) */
|
||||
const u32 *pix_fmt_hw; /* supported pixel formats */
|
||||
bool non_alpha_only_l1; /* non-native no-alpha formats on layer 1 */
|
||||
int pad_max_freq_hz; /* max frequency supported by pad */
|
||||
};
|
||||
|
||||
#define LTDC_MAX_LAYER 4
|
||||
|
@ -417,6 +417,7 @@ static const struct of_device_id sun4i_drv_of_table[] = {
|
||||
{ .compatible = "allwinner,sun8i-a33-display-engine" },
|
||||
{ .compatible = "allwinner,sun8i-a83t-display-engine" },
|
||||
{ .compatible = "allwinner,sun8i-h3-display-engine" },
|
||||
{ .compatible = "allwinner,sun8i-r40-display-engine" },
|
||||
{ .compatible = "allwinner,sun8i-v3s-display-engine" },
|
||||
{ .compatible = "allwinner,sun9i-a80-display-engine" },
|
||||
{ }
|
||||
|
@ -811,6 +811,7 @@ sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv,
|
||||
* remote output id. If this for some reason can't be done, 0
|
||||
* is used as input port id.
|
||||
*/
|
||||
of_node_put(port);
|
||||
port = of_graph_get_remote_port(ep);
|
||||
if (!of_property_read_u32(port, "reg", ®) && reg > 0)
|
||||
reg -= 1;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
@ -247,10 +248,8 @@ static u16 sun6i_dsi_crc_compute(u8 const *buffer, size_t len)
|
||||
return crc_ccitt(0xffff, buffer, len);
|
||||
}
|
||||
|
||||
static u16 sun6i_dsi_crc_repeat_compute(u8 pd, size_t len)
|
||||
static u16 sun6i_dsi_crc_repeat(u8 pd, u8 *buffer, size_t len)
|
||||
{
|
||||
u8 buffer[len];
|
||||
|
||||
memset(buffer, pd, len);
|
||||
|
||||
return sun6i_dsi_crc_compute(buffer, len);
|
||||
@ -274,11 +273,11 @@ static u32 sun6i_dsi_build_blk0_pkt(u8 vc, u16 wc)
|
||||
wc & 0xff, wc >> 8);
|
||||
}
|
||||
|
||||
static u32 sun6i_dsi_build_blk1_pkt(u16 pd, size_t len)
|
||||
static u32 sun6i_dsi_build_blk1_pkt(u16 pd, u8 *buffer, size_t len)
|
||||
{
|
||||
u32 val = SUN6I_DSI_BLK_PD(pd);
|
||||
|
||||
return val | SUN6I_DSI_BLK_PF(sun6i_dsi_crc_repeat_compute(pd, len));
|
||||
return val | SUN6I_DSI_BLK_PF(sun6i_dsi_crc_repeat(pd, buffer, len));
|
||||
}
|
||||
|
||||
static void sun6i_dsi_inst_abort(struct sun6i_dsi *dsi)
|
||||
@ -452,6 +451,54 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
|
||||
u16 hbp, hfp, hsa, hblk, vblk;
|
||||
size_t bytes;
|
||||
u8 *buffer;
|
||||
|
||||
/* Do all timing calculations up front to allocate buffer space */
|
||||
|
||||
/*
|
||||
* A sync period is composed of a blanking packet (4 bytes +
|
||||
* payload + 2 bytes) and a sync event packet (4 bytes). Its
|
||||
* minimal size is therefore 10 bytes
|
||||
*/
|
||||
#define HSA_PACKET_OVERHEAD 10
|
||||
hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
|
||||
(mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* The backporch is set using a blanking packet (4 bytes +
|
||||
* payload + 2 bytes). Its minimal size is therefore 6 bytes
|
||||
*/
|
||||
#define HBP_PACKET_OVERHEAD 6
|
||||
hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
|
||||
(mode->hsync_start - mode->hdisplay) * Bpp - HBP_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* The frontporch is set using a blanking packet (4 bytes +
|
||||
* payload + 2 bytes). Its minimal size is therefore 6 bytes
|
||||
*/
|
||||
#define HFP_PACKET_OVERHEAD 6
|
||||
hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
|
||||
(mode->htotal - mode->hsync_end) * Bpp - HFP_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* hblk seems to be the line + porches length.
|
||||
*/
|
||||
hblk = mode->htotal * Bpp - hsa;
|
||||
|
||||
/*
|
||||
* And I'm not entirely sure what vblk is about. The driver in
|
||||
* Allwinner BSP is using a rather convoluted calculation
|
||||
* there only for 4 lanes. However, using 0 (the !4 lanes
|
||||
* case) even with a 4 lanes screen seems to work...
|
||||
*/
|
||||
vblk = 0;
|
||||
|
||||
/* How many bytes do we need to send all payloads? */
|
||||
bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk);
|
||||
buffer = kmalloc(bytes, GFP_KERNEL);
|
||||
if (WARN_ON(!buffer))
|
||||
return;
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0);
|
||||
|
||||
@ -485,63 +532,37 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
|
||||
SUN6I_DSI_BASIC_SIZE1_VACT(mode->vdisplay) |
|
||||
SUN6I_DSI_BASIC_SIZE1_VT(mode->vtotal));
|
||||
|
||||
/*
|
||||
* A sync period is composed of a blanking packet (4 bytes +
|
||||
* payload + 2 bytes) and a sync event packet (4 bytes). Its
|
||||
* minimal size is therefore 10 bytes
|
||||
*/
|
||||
#define HSA_PACKET_OVERHEAD 10
|
||||
hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
|
||||
(mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
|
||||
/* sync */
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_HSA0_REG,
|
||||
sun6i_dsi_build_blk0_pkt(device->channel, hsa));
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_HSA1_REG,
|
||||
sun6i_dsi_build_blk1_pkt(0, hsa));
|
||||
sun6i_dsi_build_blk1_pkt(0, buffer, hsa));
|
||||
|
||||
/*
|
||||
* The backporch is set using a blanking packet (4 bytes +
|
||||
* payload + 2 bytes). Its minimal size is therefore 6 bytes
|
||||
*/
|
||||
#define HBP_PACKET_OVERHEAD 6
|
||||
hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
|
||||
(mode->hsync_start - mode->hdisplay) * Bpp - HBP_PACKET_OVERHEAD);
|
||||
/* backporch */
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_HBP0_REG,
|
||||
sun6i_dsi_build_blk0_pkt(device->channel, hbp));
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_HBP1_REG,
|
||||
sun6i_dsi_build_blk1_pkt(0, hbp));
|
||||
sun6i_dsi_build_blk1_pkt(0, buffer, hbp));
|
||||
|
||||
/*
|
||||
* The frontporch is set using a blanking packet (4 bytes +
|
||||
* payload + 2 bytes). Its minimal size is therefore 6 bytes
|
||||
*/
|
||||
#define HFP_PACKET_OVERHEAD 6
|
||||
hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
|
||||
(mode->htotal - mode->hsync_end) * Bpp - HFP_PACKET_OVERHEAD);
|
||||
/* frontporch */
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_HFP0_REG,
|
||||
sun6i_dsi_build_blk0_pkt(device->channel, hfp));
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_HFP1_REG,
|
||||
sun6i_dsi_build_blk1_pkt(0, hfp));
|
||||
sun6i_dsi_build_blk1_pkt(0, buffer, hfp));
|
||||
|
||||
/*
|
||||
* hblk seems to be the line + porches length.
|
||||
*/
|
||||
hblk = mode->htotal * Bpp - hsa;
|
||||
/* hblk */
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_HBLK0_REG,
|
||||
sun6i_dsi_build_blk0_pkt(device->channel, hblk));
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_HBLK1_REG,
|
||||
sun6i_dsi_build_blk1_pkt(0, hblk));
|
||||
sun6i_dsi_build_blk1_pkt(0, buffer, hblk));
|
||||
|
||||
/*
|
||||
* And I'm not entirely sure what vblk is about. The driver in
|
||||
* Allwinner BSP is using a rather convoluted calculation
|
||||
* there only for 4 lanes. However, using 0 (the !4 lanes
|
||||
* case) even with a 4 lanes screen seems to work...
|
||||
*/
|
||||
vblk = 0;
|
||||
/* vblk */
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_VBLK0_REG,
|
||||
sun6i_dsi_build_blk0_pkt(device->channel, vblk));
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BLK_VBLK1_REG,
|
||||
sun6i_dsi_build_blk1_pkt(0, vblk));
|
||||
sun6i_dsi_build_blk1_pkt(0, buffer, vblk));
|
||||
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
static int sun6i_dsi_start(struct sun6i_dsi *dsi,
|
||||
@ -812,8 +833,8 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
|
||||
|
||||
dsi->device = device;
|
||||
dsi->panel = of_drm_find_panel(device->dev.of_node);
|
||||
if (!dsi->panel)
|
||||
return -EINVAL;
|
||||
if (IS_ERR(dsi->panel))
|
||||
return PTR_ERR(dsi->panel);
|
||||
|
||||
dev_info(host->dev, "Attached device %s\n", device->name);
|
||||
|
||||
|
@ -53,22 +53,14 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm,
|
||||
struct device_node *port, *ep, *remote, *remote_port;
|
||||
u32 crtcs = 0;
|
||||
|
||||
port = of_graph_get_port_by_id(node, 0);
|
||||
if (!port)
|
||||
return 0;
|
||||
|
||||
ep = of_get_next_available_child(port, NULL);
|
||||
if (!ep)
|
||||
return 0;
|
||||
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
remote = of_graph_get_remote_node(node, 0, -1);
|
||||
if (!remote)
|
||||
return 0;
|
||||
|
||||
if (sun8i_dw_hdmi_node_is_tcon_top(remote)) {
|
||||
port = of_graph_get_port_by_id(remote, 4);
|
||||
if (!port)
|
||||
return 0;
|
||||
goto crtcs_exit;
|
||||
|
||||
for_each_child_of_node(port, ep) {
|
||||
remote_port = of_graph_get_remote_port(ep);
|
||||
@ -81,6 +73,9 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm,
|
||||
crtcs = drm_of_find_possible_crtcs(drm, node);
|
||||
}
|
||||
|
||||
crtcs_exit:
|
||||
of_node_put(remote);
|
||||
|
||||
return crtcs;
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,8 @@
|
||||
|
||||
#include <linux/component.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun8i_mixer.h"
|
||||
|
@ -14,45 +14,95 @@
|
||||
|
||||
#include "sun8i_tcon_top.h"
|
||||
|
||||
static int sun8i_tcon_top_get_connected_ep_id(struct device_node *node,
|
||||
int port_id)
|
||||
static bool sun8i_tcon_top_node_is_tcon_top(struct device_node *node)
|
||||
{
|
||||
struct device_node *ep, *remote, *port;
|
||||
struct of_endpoint endpoint;
|
||||
|
||||
port = of_graph_get_port_by_id(node, port_id);
|
||||
if (!port)
|
||||
return -ENOENT;
|
||||
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
|
||||
if (of_device_is_available(remote)) {
|
||||
of_graph_parse_endpoint(ep, &endpoint);
|
||||
|
||||
of_node_put(remote);
|
||||
|
||||
return endpoint.id;
|
||||
}
|
||||
|
||||
of_node_put(remote);
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
return !!of_match_node(sun8i_tcon_top_of_table, node);
|
||||
}
|
||||
|
||||
int sun8i_tcon_top_set_hdmi_src(struct device *dev, int tcon)
|
||||
{
|
||||
struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (!sun8i_tcon_top_node_is_tcon_top(dev->of_node)) {
|
||||
dev_err(dev, "Device is not TCON TOP!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tcon < 2 || tcon > 3) {
|
||||
dev_err(dev, "TCON index must be 2 or 3!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tcon_top->reg_lock, flags);
|
||||
|
||||
val = readl(tcon_top->regs + TCON_TOP_GATE_SRC_REG);
|
||||
val &= ~TCON_TOP_HDMI_SRC_MSK;
|
||||
val |= FIELD_PREP(TCON_TOP_HDMI_SRC_MSK, tcon - 1);
|
||||
writel(val, tcon_top->regs + TCON_TOP_GATE_SRC_REG);
|
||||
|
||||
spin_unlock_irqrestore(&tcon_top->reg_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sun8i_tcon_top_set_hdmi_src);
|
||||
|
||||
int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon)
|
||||
{
|
||||
struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
if (!sun8i_tcon_top_node_is_tcon_top(dev->of_node)) {
|
||||
dev_err(dev, "Device is not TCON TOP!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mixer > 1) {
|
||||
dev_err(dev, "Mixer index is too high!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tcon > 3) {
|
||||
dev_err(dev, "TCON index is too high!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tcon_top->reg_lock, flags);
|
||||
|
||||
reg = readl(tcon_top->regs + TCON_TOP_PORT_SEL_REG);
|
||||
if (mixer == 0) {
|
||||
reg &= ~TCON_TOP_PORT_DE0_MSK;
|
||||
reg |= FIELD_PREP(TCON_TOP_PORT_DE0_MSK, tcon);
|
||||
} else {
|
||||
reg &= ~TCON_TOP_PORT_DE1_MSK;
|
||||
reg |= FIELD_PREP(TCON_TOP_PORT_DE1_MSK, tcon);
|
||||
}
|
||||
writel(reg, tcon_top->regs + TCON_TOP_PORT_SEL_REG);
|
||||
|
||||
spin_unlock_irqrestore(&tcon_top->reg_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sun8i_tcon_top_de_config);
|
||||
|
||||
|
||||
static struct clk_hw *sun8i_tcon_top_register_gate(struct device *dev,
|
||||
struct clk *parent,
|
||||
const char *parent,
|
||||
void __iomem *regs,
|
||||
spinlock_t *lock,
|
||||
u8 bit, int name_index)
|
||||
{
|
||||
const char *clk_name, *parent_name;
|
||||
int ret;
|
||||
int ret, index;
|
||||
|
||||
index = of_property_match_string(dev->of_node, "clock-names", parent);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
parent_name = of_clk_get_parent_name(dev->of_node, index);
|
||||
|
||||
parent_name = __clk_get_name(parent);
|
||||
ret = of_property_read_string_index(dev->of_node,
|
||||
"clock-output-names", name_index,
|
||||
&clk_name);
|
||||
@ -69,14 +119,11 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct clk *dsi, *tcon_tv0, *tcon_tv1, *tve0, *tve1;
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
struct sun8i_tcon_top *tcon_top;
|
||||
bool mixer0_unused = false;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int ret, i, id;
|
||||
u32 val;
|
||||
int ret, i;
|
||||
|
||||
tcon_top = devm_kzalloc(dev, sizeof(*tcon_top), GFP_KERNEL);
|
||||
if (!tcon_top)
|
||||
@ -103,38 +150,9 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master,
|
||||
return PTR_ERR(tcon_top->bus);
|
||||
}
|
||||
|
||||
dsi = devm_clk_get(dev, "dsi");
|
||||
if (IS_ERR(dsi)) {
|
||||
dev_err(dev, "Couldn't get the dsi clock\n");
|
||||
return PTR_ERR(dsi);
|
||||
}
|
||||
|
||||
tcon_tv0 = devm_clk_get(dev, "tcon-tv0");
|
||||
if (IS_ERR(tcon_tv0)) {
|
||||
dev_err(dev, "Couldn't get the tcon-tv0 clock\n");
|
||||
return PTR_ERR(tcon_tv0);
|
||||
}
|
||||
|
||||
tcon_tv1 = devm_clk_get(dev, "tcon-tv1");
|
||||
if (IS_ERR(tcon_tv1)) {
|
||||
dev_err(dev, "Couldn't get the tcon-tv1 clock\n");
|
||||
return PTR_ERR(tcon_tv1);
|
||||
}
|
||||
|
||||
tve0 = devm_clk_get(dev, "tve0");
|
||||
if (IS_ERR(tve0)) {
|
||||
dev_err(dev, "Couldn't get the tve0 clock\n");
|
||||
return PTR_ERR(tve0);
|
||||
}
|
||||
|
||||
tve1 = devm_clk_get(dev, "tve1");
|
||||
if (IS_ERR(tve1)) {
|
||||
dev_err(dev, "Couldn't get the tve1 clock\n");
|
||||
return PTR_ERR(tve1);
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
tcon_top->regs = regs;
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
@ -150,50 +168,6 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master,
|
||||
goto err_assert_reset;
|
||||
}
|
||||
|
||||
val = 0;
|
||||
|
||||
/* check if HDMI mux output is connected */
|
||||
if (sun8i_tcon_top_get_connected_ep_id(dev->of_node, 5) >= 0) {
|
||||
/* find HDMI input endpoint id, if it is connected at all*/
|
||||
id = sun8i_tcon_top_get_connected_ep_id(dev->of_node, 4);
|
||||
if (id >= 0)
|
||||
val = FIELD_PREP(TCON_TOP_HDMI_SRC_MSK, id + 1);
|
||||
else
|
||||
DRM_DEBUG_DRIVER("TCON TOP HDMI input is not connected\n");
|
||||
} else {
|
||||
DRM_DEBUG_DRIVER("TCON TOP HDMI output is not connected\n");
|
||||
}
|
||||
|
||||
writel(val, regs + TCON_TOP_GATE_SRC_REG);
|
||||
|
||||
val = 0;
|
||||
|
||||
/* process mixer0 mux output */
|
||||
id = sun8i_tcon_top_get_connected_ep_id(dev->of_node, 1);
|
||||
if (id >= 0) {
|
||||
val = FIELD_PREP(TCON_TOP_PORT_DE0_MSK, id);
|
||||
} else {
|
||||
DRM_DEBUG_DRIVER("TCON TOP mixer0 output is not connected\n");
|
||||
mixer0_unused = true;
|
||||
}
|
||||
|
||||
/* process mixer1 mux output */
|
||||
id = sun8i_tcon_top_get_connected_ep_id(dev->of_node, 3);
|
||||
if (id >= 0) {
|
||||
val |= FIELD_PREP(TCON_TOP_PORT_DE1_MSK, id);
|
||||
|
||||
/*
|
||||
* mixer0 mux has priority over mixer1 mux. We have to
|
||||
* make sure mixer0 doesn't overtake TCON from mixer1.
|
||||
*/
|
||||
if (mixer0_unused && id == 0)
|
||||
val |= FIELD_PREP(TCON_TOP_PORT_DE0_MSK, 1);
|
||||
} else {
|
||||
DRM_DEBUG_DRIVER("TCON TOP mixer1 output is not connected\n");
|
||||
}
|
||||
|
||||
writel(val, regs + TCON_TOP_PORT_SEL_REG);
|
||||
|
||||
/*
|
||||
* TCON TOP has two muxes, which select parent clock for each TCON TV
|
||||
* channel clock. Parent could be either TCON TV or TVE clock. For now
|
||||
@ -203,17 +177,17 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master,
|
||||
* to TVE clock parent.
|
||||
*/
|
||||
clk_data->hws[CLK_TCON_TOP_TV0] =
|
||||
sun8i_tcon_top_register_gate(dev, tcon_tv0, regs,
|
||||
sun8i_tcon_top_register_gate(dev, "tcon-tv0", regs,
|
||||
&tcon_top->reg_lock,
|
||||
TCON_TOP_TCON_TV0_GATE, 0);
|
||||
|
||||
clk_data->hws[CLK_TCON_TOP_TV1] =
|
||||
sun8i_tcon_top_register_gate(dev, tcon_tv1, regs,
|
||||
sun8i_tcon_top_register_gate(dev, "tcon-tv1", regs,
|
||||
&tcon_top->reg_lock,
|
||||
TCON_TOP_TCON_TV1_GATE, 1);
|
||||
|
||||
clk_data->hws[CLK_TCON_TOP_DSI] =
|
||||
sun8i_tcon_top_register_gate(dev, dsi, regs,
|
||||
sun8i_tcon_top_register_gate(dev, "dsi", regs,
|
||||
&tcon_top->reg_lock,
|
||||
TCON_TOP_TCON_DSI_GATE, 2);
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
struct sun8i_tcon_top {
|
||||
struct clk *bus;
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
void __iomem *regs;
|
||||
struct reset_control *rst;
|
||||
|
||||
/*
|
||||
@ -37,4 +38,7 @@ struct sun8i_tcon_top {
|
||||
|
||||
extern const struct of_device_id sun8i_tcon_top_of_table[];
|
||||
|
||||
int sun8i_tcon_top_set_hdmi_src(struct device *dev, int tcon);
|
||||
int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon);
|
||||
|
||||
#endif /* _SUN8I_TCON_TOP_H_ */
|
||||
|
@ -1411,6 +1411,9 @@ static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
struct tegra_output *output = &dsi->output;
|
||||
|
||||
output->panel = of_drm_find_panel(device->dev.of_node);
|
||||
if (IS_ERR(output->panel))
|
||||
output->panel = NULL;
|
||||
|
||||
if (output->panel && output->connector.dev) {
|
||||
drm_panel_attach(output->panel, &output->connector);
|
||||
drm_helper_hpd_irq_event(output->connector.dev);
|
||||
|
@ -110,8 +110,8 @@ int tegra_output_probe(struct tegra_output *output)
|
||||
panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
|
||||
if (panel) {
|
||||
output->panel = of_drm_find_panel(panel);
|
||||
if (!output->panel)
|
||||
return -EPROBE_DEFER;
|
||||
if (IS_ERR(output->panel))
|
||||
return PTR_ERR(output->panel);
|
||||
|
||||
of_node_put(panel);
|
||||
}
|
||||
|
@ -103,12 +103,11 @@ struct drm_connector *tilcdc_encoder_find_connector(struct drm_device *ddev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
int i;
|
||||
|
||||
list_for_each_entry(connector, &ddev->mode_config.connector_list, head)
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
|
||||
if (connector->encoder_ids[i] == encoder->base.id)
|
||||
return connector;
|
||||
list_for_each_entry(connector, &ddev->mode_config.connector_list, head) {
|
||||
if (drm_connector_has_possible_encoder(connector, encoder))
|
||||
return connector;
|
||||
}
|
||||
|
||||
dev_err(ddev->dev, "No connector found for %s encoder (id %d)\n",
|
||||
encoder->name, encoder->base.id);
|
||||
|
@ -204,7 +204,7 @@ static int tinydrm_register(struct tinydrm_device *tdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_fb_cma_fbdev_init_with_funcs(drm, 0, 0, tdev->fb_funcs);
|
||||
ret = drm_fbdev_generic_setup(drm, 0);
|
||||
if (ret)
|
||||
DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
|
||||
|
||||
@ -214,7 +214,6 @@ static int tinydrm_register(struct tinydrm_device *tdev)
|
||||
static void tinydrm_unregister(struct tinydrm_device *tdev)
|
||||
{
|
||||
drm_atomic_helper_shutdown(tdev->drm);
|
||||
drm_fb_cma_fbdev_fini(tdev->drm);
|
||||
drm_dev_unregister(tdev->drm);
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,6 @@ static struct drm_driver ili9225_driver = {
|
||||
DRIVER_ATOMIC,
|
||||
.fops = &ili9225_fops,
|
||||
TINYDRM_GEM_DRIVER_OPS,
|
||||
.lastclose = drm_fb_helper_lastclose,
|
||||
.name = "ili9225",
|
||||
.desc = "Ilitek ILI9225",
|
||||
.date = "20171106",
|
||||
|
@ -145,7 +145,6 @@ static struct drm_driver ili9341_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
|
||||
.fops = &ili9341_fops,
|
||||
TINYDRM_GEM_DRIVER_OPS,
|
||||
.lastclose = drm_fb_helper_lastclose,
|
||||
.debugfs_init = mipi_dbi_debugfs_init,
|
||||
.name = "ili9341",
|
||||
.desc = "Ilitek ILI9341",
|
||||
|
@ -154,7 +154,6 @@ static struct drm_driver mi0283qt_driver = {
|
||||
DRIVER_ATOMIC,
|
||||
.fops = &mi0283qt_fops,
|
||||
TINYDRM_GEM_DRIVER_OPS,
|
||||
.lastclose = drm_fb_helper_lastclose,
|
||||
.debugfs_init = mipi_dbi_debugfs_init,
|
||||
.name = "mi0283qt",
|
||||
.desc = "Multi-Inno MI0283QT",
|
||||
|
@ -260,6 +260,8 @@ static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = {
|
||||
/**
|
||||
* mipi_dbi_enable_flush - MIPI DBI enable helper
|
||||
* @mipi: MIPI DBI structure
|
||||
* @crtc_state: CRTC state
|
||||
* @plane_state: Plane state
|
||||
*
|
||||
* This function sets &mipi_dbi->enabled, flushes the whole framebuffer and
|
||||
* enables the backlight. Drivers can use this in their
|
||||
|
@ -304,7 +304,6 @@ static struct drm_driver st7586_driver = {
|
||||
DRIVER_ATOMIC,
|
||||
.fops = &st7586_fops,
|
||||
TINYDRM_GEM_DRIVER_OPS,
|
||||
.lastclose = drm_fb_helper_lastclose,
|
||||
.debugfs_init = mipi_dbi_debugfs_init,
|
||||
.name = "st7586",
|
||||
.desc = "Sitronix ST7586",
|
||||
|
@ -120,7 +120,6 @@ static struct drm_driver st7735r_driver = {
|
||||
DRIVER_ATOMIC,
|
||||
.fops = &st7735r_fops,
|
||||
TINYDRM_GEM_DRIVER_OPS,
|
||||
.lastclose = drm_fb_helper_lastclose,
|
||||
.debugfs_init = mipi_dbi_debugfs_init,
|
||||
.name = "st7735r",
|
||||
.desc = "Sitronix ST7735R",
|
||||
|
@ -227,37 +227,19 @@ v3d_set_mmap_vma_flags(struct vm_area_struct *vma)
|
||||
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
|
||||
}
|
||||
|
||||
int v3d_gem_fault(struct vm_fault *vmf)
|
||||
vm_fault_t v3d_gem_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct drm_gem_object *obj = vma->vm_private_data;
|
||||
struct v3d_bo *bo = to_v3d_bo(obj);
|
||||
unsigned long pfn;
|
||||
pfn_t pfn;
|
||||
pgoff_t pgoff;
|
||||
int ret;
|
||||
|
||||
/* We don't use vmf->pgoff since that has the fake offset: */
|
||||
pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
|
||||
pfn = page_to_pfn(bo->pages[pgoff]);
|
||||
pfn = __pfn_to_pfn_t(page_to_pfn(bo->pages[pgoff]), PFN_DEV);
|
||||
|
||||
ret = vm_insert_mixed(vma, vmf->address, __pfn_to_pfn_t(pfn, PFN_DEV));
|
||||
|
||||
switch (ret) {
|
||||
case -EAGAIN:
|
||||
case 0:
|
||||
case -ERESTARTSYS:
|
||||
case -EINTR:
|
||||
case -EBUSY:
|
||||
/*
|
||||
* EBUSY is ok: this just means that another thread
|
||||
* already did the job.
|
||||
*/
|
||||
return VM_FAULT_NOPAGE;
|
||||
case -ENOMEM:
|
||||
return VM_FAULT_OOM;
|
||||
default:
|
||||
return VM_FAULT_SIGBUS;
|
||||
}
|
||||
return vmf_insert_mixed(vma, vmf->address, pfn);
|
||||
}
|
||||
|
||||
int v3d_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
|
@ -2,6 +2,7 @@
|
||||
/* Copyright (C) 2015-2018 Broadcom */
|
||||
|
||||
#include <linux/reservation.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_gem.h>
|
||||
@ -183,6 +184,8 @@ struct v3d_job {
|
||||
|
||||
/* GPU virtual addresses of the start/end of the CL job. */
|
||||
u32 start, end;
|
||||
|
||||
u32 timedout_ctca, timedout_ctra;
|
||||
};
|
||||
|
||||
struct v3d_exec_info {
|
||||
@ -252,7 +255,7 @@ int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
int v3d_gem_fault(struct vm_fault *vmf);
|
||||
vm_fault_t v3d_gem_fault(struct vm_fault *vmf);
|
||||
int v3d_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
struct reservation_object *v3d_prime_res_obj(struct drm_gem_object *obj);
|
||||
int v3d_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
|
||||
|
@ -35,19 +35,7 @@ static const char *v3d_fence_get_timeline_name(struct dma_fence *fence)
|
||||
return "v3d-render";
|
||||
}
|
||||
|
||||
static bool v3d_fence_enable_signaling(struct dma_fence *fence)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct dma_fence_ops v3d_fence_ops = {
|
||||
.get_driver_name = v3d_fence_get_driver_name,
|
||||
.get_timeline_name = v3d_fence_get_timeline_name,
|
||||
.enable_signaling = v3d_fence_enable_signaling,
|
||||
/* Each of our fences gets signaled as complete by the IRQ
|
||||
* handler, so we rely on the core's tracking of signaling.
|
||||
*/
|
||||
.signaled = NULL,
|
||||
.wait = dma_fence_default_wait,
|
||||
.release = dma_fence_free,
|
||||
};
|
||||
|
@ -222,6 +222,7 @@
|
||||
#define V3D_CLE_CTNCA(n) (V3D_CLE_CT0CA + 4 * n)
|
||||
#define V3D_CLE_CT0RA 0x00118
|
||||
#define V3D_CLE_CT1RA 0x0011c
|
||||
#define V3D_CLE_CTNRA(n) (V3D_CLE_CT0RA + 4 * n)
|
||||
#define V3D_CLE_CT0LC 0x00120
|
||||
#define V3D_CLE_CT1LC 0x00124
|
||||
#define V3D_CLE_CT0PC 0x00128
|
||||
|
@ -14,8 +14,8 @@
|
||||
* to the HW only when it has completed the last one, instead of
|
||||
* filling up the CT[01]Q FIFOs with jobs. Similarly, we use
|
||||
* v3d_job_dependency() to manage the dependency between bin and
|
||||
* render, instead of having the clients submit jobs with using the
|
||||
* HW's semaphores to interlock between them.
|
||||
* render, instead of having the clients submit jobs using the HW's
|
||||
* semaphores to interlock between them.
|
||||
*/
|
||||
|
||||
#include <linux/kthread.h>
|
||||
@ -153,7 +153,25 @@ v3d_job_timedout(struct drm_sched_job *sched_job)
|
||||
struct v3d_job *job = to_v3d_job(sched_job);
|
||||
struct v3d_exec_info *exec = job->exec;
|
||||
struct v3d_dev *v3d = exec->v3d;
|
||||
enum v3d_queue job_q = job == &exec->bin ? V3D_BIN : V3D_RENDER;
|
||||
enum v3d_queue q;
|
||||
u32 ctca = V3D_CORE_READ(0, V3D_CLE_CTNCA(job_q));
|
||||
u32 ctra = V3D_CORE_READ(0, V3D_CLE_CTNRA(job_q));
|
||||
|
||||
/* If the current address or return address have changed, then
|
||||
* the GPU has probably made progress and we should delay the
|
||||
* reset. This could fail if the GPU got in an infinite loop
|
||||
* in the CL, but that is pretty unlikely outside of an i-g-t
|
||||
* testcase.
|
||||
*/
|
||||
if (job->timedout_ctca != ctca || job->timedout_ctra != ctra) {
|
||||
job->timedout_ctca = ctca;
|
||||
job->timedout_ctra = ctra;
|
||||
|
||||
schedule_delayed_work(&job->base.work_tdr,
|
||||
job->base.sched->timeout);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&v3d->reset_lock);
|
||||
|
||||
|
@ -19,6 +19,7 @@ vc4-y := \
|
||||
vc4_plane.o \
|
||||
vc4_render_cl.o \
|
||||
vc4_trace_points.o \
|
||||
vc4_txp.o \
|
||||
vc4_v3d.o \
|
||||
vc4_validate.o \
|
||||
vc4_validate_shaders.o
|
||||
|
@ -46,6 +46,8 @@ struct vc4_crtc_state {
|
||||
struct drm_crtc_state base;
|
||||
/* Dlist area for this CRTC configuration. */
|
||||
struct drm_mm_node mm;
|
||||
bool feed_txp;
|
||||
bool txp_armed;
|
||||
};
|
||||
|
||||
static inline struct vc4_crtc_state *
|
||||
@ -324,10 +326,8 @@ static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
static void vc4_crtc_config_pv(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc);
|
||||
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
|
||||
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||||
@ -338,12 +338,6 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 ||
|
||||
vc4_encoder->type == VC4_ENCODER_TYPE_DSI1);
|
||||
u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
|
||||
bool debug_dump_regs = false;
|
||||
|
||||
if (debug_dump_regs) {
|
||||
DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
|
||||
vc4_crtc_dump_regs(vc4_crtc);
|
||||
}
|
||||
|
||||
/* Reset the PV fifo. */
|
||||
CRTC_WRITE(PV_CONTROL, 0);
|
||||
@ -419,6 +413,49 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
PV_CONTROL_CLK_SELECT) |
|
||||
PV_CONTROL_FIFO_CLR |
|
||||
PV_CONTROL_EN);
|
||||
}
|
||||
|
||||
static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||||
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
|
||||
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
||||
bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
|
||||
bool debug_dump_regs = false;
|
||||
|
||||
if (debug_dump_regs) {
|
||||
DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
|
||||
vc4_crtc_dump_regs(vc4_crtc);
|
||||
}
|
||||
|
||||
if (vc4_crtc->channel == 2) {
|
||||
u32 dispctrl;
|
||||
u32 dsp3_mux;
|
||||
|
||||
/*
|
||||
* SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to
|
||||
* FIFO X'.
|
||||
* SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'.
|
||||
*
|
||||
* DSP3 is connected to FIFO2 unless the transposer is
|
||||
* enabled. In this case, FIFO 2 is directly accessed by the
|
||||
* TXP IP, and we need to disable the FIFO2 -> pixelvalve1
|
||||
* route.
|
||||
*/
|
||||
if (vc4_state->feed_txp)
|
||||
dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX);
|
||||
else
|
||||
dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
|
||||
|
||||
dispctrl = HVS_READ(SCALER_DISPCTRL) &
|
||||
~SCALER_DISPCTRL_DSP3_MUX_MASK;
|
||||
HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux);
|
||||
}
|
||||
|
||||
if (!vc4_state->feed_txp)
|
||||
vc4_crtc_config_pv(crtc);
|
||||
|
||||
HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel),
|
||||
SCALER_DISPBKGND_AUTOHS |
|
||||
@ -499,6 +536,13 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
}
|
||||
}
|
||||
|
||||
void vc4_crtc_txp_armed(struct drm_crtc_state *state)
|
||||
{
|
||||
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
|
||||
|
||||
vc4_state->txp_armed = true;
|
||||
}
|
||||
|
||||
static void vc4_crtc_update_dlist(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
@ -514,8 +558,11 @@ static void vc4_crtc_update_dlist(struct drm_crtc *crtc)
|
||||
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
vc4_crtc->event = crtc->state->event;
|
||||
crtc->state->event = NULL;
|
||||
|
||||
if (!vc4_state->feed_txp || vc4_state->txp_armed) {
|
||||
vc4_crtc->event = crtc->state->event;
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
|
||||
HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
|
||||
vc4_state->mm.start);
|
||||
@ -533,8 +580,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||||
struct drm_crtc_state *state = crtc->state;
|
||||
struct drm_display_mode *mode = &state->adjusted_mode;
|
||||
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
|
||||
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
||||
|
||||
require_hvs_enabled(dev);
|
||||
|
||||
@ -546,15 +593,21 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
|
||||
/* Turn on the scaler, which will wait for vstart to start
|
||||
* compositing.
|
||||
* When feeding the transposer, we should operate in oneshot
|
||||
* mode.
|
||||
*/
|
||||
HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
|
||||
VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) |
|
||||
VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) |
|
||||
SCALER_DISPCTRLX_ENABLE);
|
||||
SCALER_DISPCTRLX_ENABLE |
|
||||
(vc4_state->feed_txp ? SCALER_DISPCTRLX_ONESHOT : 0));
|
||||
|
||||
/* Turn on the pixel valve, which will emit the vstart signal. */
|
||||
CRTC_WRITE(PV_V_CONTROL,
|
||||
CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
|
||||
/* When feeding the transposer block the pixelvalve is unneeded and
|
||||
* should not be enabled.
|
||||
*/
|
||||
if (!vc4_state->feed_txp)
|
||||
CRTC_WRITE(PV_V_CONTROL,
|
||||
CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
|
||||
}
|
||||
|
||||
static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc,
|
||||
@ -579,8 +632,10 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_plane *plane;
|
||||
unsigned long flags;
|
||||
const struct drm_plane_state *plane_state;
|
||||
struct drm_connector *conn;
|
||||
struct drm_connector_state *conn_state;
|
||||
u32 dlist_count = 0;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
/* The pixelvalve can only feed one encoder (and encoders are
|
||||
* 1:1 with connectors.)
|
||||
@ -600,6 +655,24 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for_each_new_connector_in_state(state->state, conn, conn_state, i) {
|
||||
if (conn_state->crtc != crtc)
|
||||
continue;
|
||||
|
||||
/* The writeback connector is implemented using the transposer
|
||||
* block which is directly taking its data from the HVS FIFO.
|
||||
*/
|
||||
if (conn->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) {
|
||||
state->no_vblank = true;
|
||||
vc4_state->feed_txp = true;
|
||||
} else {
|
||||
state->no_vblank = false;
|
||||
vc4_state->feed_txp = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -713,7 +786,8 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
if (vc4_crtc->event &&
|
||||
(vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)))) {
|
||||
(vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)) ||
|
||||
vc4_state->feed_txp)) {
|
||||
drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
|
||||
vc4_crtc->event = NULL;
|
||||
drm_crtc_vblank_put(crtc);
|
||||
@ -721,6 +795,13 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
void vc4_crtc_handle_vblank(struct vc4_crtc *crtc)
|
||||
{
|
||||
crtc->t_vblank = ktime_get();
|
||||
drm_crtc_handle_vblank(&crtc->base);
|
||||
vc4_crtc_handle_page_flip(crtc);
|
||||
}
|
||||
|
||||
static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct vc4_crtc *vc4_crtc = data;
|
||||
@ -728,10 +809,8 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
if (stat & PV_INT_VFP_START) {
|
||||
vc4_crtc->t_vblank = ktime_get();
|
||||
CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
|
||||
drm_crtc_handle_vblank(&vc4_crtc->base);
|
||||
vc4_crtc_handle_page_flip(vc4_crtc);
|
||||
vc4_crtc_handle_vblank(vc4_crtc);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -884,12 +963,15 @@ static int vc4_page_flip(struct drm_crtc *crtc,
|
||||
|
||||
static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc)
|
||||
{
|
||||
struct vc4_crtc_state *vc4_state;
|
||||
struct vc4_crtc_state *vc4_state, *old_vc4_state;
|
||||
|
||||
vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
|
||||
if (!vc4_state)
|
||||
return NULL;
|
||||
|
||||
old_vc4_state = to_vc4_crtc_state(crtc->state);
|
||||
vc4_state->feed_txp = old_vc4_state->feed_txp;
|
||||
|
||||
__drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
|
||||
return &vc4_state->base;
|
||||
}
|
||||
@ -987,9 +1069,17 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm,
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
drm_for_each_encoder(encoder, drm) {
|
||||
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
|
||||
struct vc4_encoder *vc4_encoder;
|
||||
int i;
|
||||
|
||||
/* HVS FIFO2 can feed the TXP IP. */
|
||||
if (crtc_data->hvs_channel == 2 &&
|
||||
encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) {
|
||||
encoder->possible_crtcs |= drm_crtc_mask(crtc);
|
||||
continue;
|
||||
}
|
||||
|
||||
vc4_encoder = to_vc4_encoder(encoder);
|
||||
for (i = 0; i < ARRAY_SIZE(crtc_data->encoder_types); i++) {
|
||||
if (vc4_encoder->type == encoder_types[i]) {
|
||||
vc4_encoder->clock_select = i;
|
||||
|
@ -21,6 +21,7 @@ static const struct drm_info_list vc4_debugfs_list[] = {
|
||||
{"dsi1_regs", vc4_dsi_debugfs_regs, 0, (void *)(uintptr_t)1},
|
||||
{"hdmi_regs", vc4_hdmi_debugfs_regs, 0},
|
||||
{"vec_regs", vc4_vec_debugfs_regs, 0},
|
||||
{"txp_regs", vc4_txp_debugfs_regs, 0},
|
||||
{"hvs_regs", vc4_hvs_debugfs_regs, 0},
|
||||
{"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
|
||||
{"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1},
|
||||
|
@ -344,6 +344,7 @@ static struct platform_driver *const component_drivers[] = {
|
||||
&vc4_vec_driver,
|
||||
&vc4_dpi_driver,
|
||||
&vc4_dsi_driver,
|
||||
&vc4_txp_driver,
|
||||
&vc4_hvs_driver,
|
||||
&vc4_crtc_driver,
|
||||
&vc4_v3d_driver,
|
||||
|
@ -73,6 +73,7 @@ struct vc4_dev {
|
||||
struct vc4_dpi *dpi;
|
||||
struct vc4_dsi *dsi1;
|
||||
struct vc4_vec *vec;
|
||||
struct vc4_txp *txp;
|
||||
|
||||
struct vc4_hang_state *hang_state;
|
||||
|
||||
@ -698,6 +699,8 @@ bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
|
||||
bool in_vblank_irq, int *vpos, int *hpos,
|
||||
ktime_t *stime, ktime_t *etime,
|
||||
const struct drm_display_mode *mode);
|
||||
void vc4_crtc_handle_vblank(struct vc4_crtc *crtc);
|
||||
void vc4_crtc_txp_armed(struct drm_crtc_state *state);
|
||||
|
||||
/* vc4_debugfs.c */
|
||||
int vc4_debugfs_init(struct drm_minor *minor);
|
||||
@ -745,6 +748,10 @@ int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused);
|
||||
extern struct platform_driver vc4_vec_driver;
|
||||
int vc4_vec_debugfs_regs(struct seq_file *m, void *unused);
|
||||
|
||||
/* vc4_txp.c */
|
||||
extern struct platform_driver vc4_txp_driver;
|
||||
int vc4_txp_debugfs_regs(struct seq_file *m, void *unused);
|
||||
|
||||
/* vc4_irq.c */
|
||||
irqreturn_t vc4_irq(int irq, void *arg);
|
||||
void vc4_irq_preinstall(struct drm_device *dev);
|
||||
|
@ -1612,8 +1612,18 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
|
||||
&panel, &dsi->bridge);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
/* If the bridge or panel pointed by dev->of_node is not
|
||||
* enabled, just return 0 here so that we don't prevent the DRM
|
||||
* dev from being registered. Of course that means the DSI
|
||||
* encoder won't be exposed, but that's not a problem since
|
||||
* nothing is connected to it.
|
||||
*/
|
||||
if (ret == -ENODEV)
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (panel) {
|
||||
dsi->bridge = devm_drm_panel_bridge_add(dev, panel,
|
||||
@ -1664,7 +1674,8 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
struct vc4_dsi *dsi = dev_get_drvdata(dev);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
if (dsi->bridge)
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
vc4_dsi_encoder_destroy(dsi->encoder);
|
||||
|
||||
|
@ -153,18 +153,11 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state)
|
||||
|
||||
drm_atomic_helper_commit_modeset_enables(dev, state);
|
||||
|
||||
/* Make sure that drm_atomic_helper_wait_for_vblanks()
|
||||
* actually waits for vblank. If we're doing a full atomic
|
||||
* modeset (as opposed to a vc4_update_plane() short circuit),
|
||||
* then we need to wait for scanout to be done with our
|
||||
* display lists before we free it and potentially reallocate
|
||||
* and overwrite the dlist memory with a new modeset.
|
||||
*/
|
||||
state->legacy_cursor_update = false;
|
||||
drm_atomic_helper_fake_vblank(state);
|
||||
|
||||
drm_atomic_helper_commit_hw_done(state);
|
||||
|
||||
drm_atomic_helper_wait_for_vblanks(dev, state);
|
||||
drm_atomic_helper_wait_for_flip_done(dev, state);
|
||||
|
||||
drm_atomic_helper_cleanup_planes(dev, state);
|
||||
|
||||
|
477
drivers/gpu/drm/vc4/vc4_txp.c
Normal file
477
drivers/gpu/drm/vc4/vc4_txp.c
Normal file
@ -0,0 +1,477 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright © 2018 Broadcom
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Boris Brezillon <boris.brezillon@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_writeback.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "vc4_drv.h"
|
||||
#include "vc4_regs.h"
|
||||
|
||||
/* Base address of the output. Raster formats must be 4-byte aligned,
|
||||
* T and LT must be 16-byte aligned or maybe utile-aligned (docs are
|
||||
* inconsistent, but probably utile).
|
||||
*/
|
||||
#define TXP_DST_PTR 0x00
|
||||
|
||||
/* Pitch in bytes for raster images, 16-byte aligned. For tiled, it's
|
||||
* the width in tiles.
|
||||
*/
|
||||
#define TXP_DST_PITCH 0x04
|
||||
/* For T-tiled imgaes, DST_PITCH should be the number of tiles wide,
|
||||
* shifted up.
|
||||
*/
|
||||
# define TXP_T_TILE_WIDTH_SHIFT 7
|
||||
/* For LT-tiled images, DST_PITCH should be the number of utiles wide,
|
||||
* shifted up.
|
||||
*/
|
||||
# define TXP_LT_TILE_WIDTH_SHIFT 4
|
||||
|
||||
/* Pre-rotation width/height of the image. Must match HVS config.
|
||||
*
|
||||
* If TFORMAT and 32-bit, limit is 1920 for 32-bit and 3840 to 16-bit
|
||||
* and width/height must be tile or utile-aligned as appropriate. If
|
||||
* transposing (rotating), width is limited to 1920.
|
||||
*
|
||||
* Height is limited to various numbers between 4088 and 4095. I'd
|
||||
* just use 4088 to be safe.
|
||||
*/
|
||||
#define TXP_DIM 0x08
|
||||
# define TXP_HEIGHT_SHIFT 16
|
||||
# define TXP_HEIGHT_MASK GENMASK(31, 16)
|
||||
# define TXP_WIDTH_SHIFT 0
|
||||
# define TXP_WIDTH_MASK GENMASK(15, 0)
|
||||
|
||||
#define TXP_DST_CTRL 0x0c
|
||||
/* These bits are set to 0x54 */
|
||||
#define TXP_PILOT_SHIFT 24
|
||||
#define TXP_PILOT_MASK GENMASK(31, 24)
|
||||
/* Bits 22-23 are set to 0x01 */
|
||||
#define TXP_VERSION_SHIFT 22
|
||||
#define TXP_VERSION_MASK GENMASK(23, 22)
|
||||
|
||||
/* Powers down the internal memory. */
|
||||
# define TXP_POWERDOWN BIT(21)
|
||||
|
||||
/* Enables storing the alpha component in 8888/4444, instead of
|
||||
* filling with ~ALPHA_INVERT.
|
||||
*/
|
||||
# define TXP_ALPHA_ENABLE BIT(20)
|
||||
|
||||
/* 4 bits, each enables stores for a channel in each set of 4 bytes.
|
||||
* Set to 0xf for normal operation.
|
||||
*/
|
||||
# define TXP_BYTE_ENABLE_SHIFT 16
|
||||
# define TXP_BYTE_ENABLE_MASK GENMASK(19, 16)
|
||||
|
||||
/* Debug: Generate VSTART again at EOF. */
|
||||
# define TXP_VSTART_AT_EOF BIT(15)
|
||||
|
||||
/* Debug: Terminate the current frame immediately. Stops AXI
|
||||
* writes.
|
||||
*/
|
||||
# define TXP_ABORT BIT(14)
|
||||
|
||||
# define TXP_DITHER BIT(13)
|
||||
|
||||
/* Inverts alpha if TXP_ALPHA_ENABLE, chooses fill value for
|
||||
* !TXP_ALPHA_ENABLE.
|
||||
*/
|
||||
# define TXP_ALPHA_INVERT BIT(12)
|
||||
|
||||
/* Note: I've listed the channels here in high bit (in byte 3/2/1) to
|
||||
* low bit (in byte 0) order.
|
||||
*/
|
||||
# define TXP_FORMAT_SHIFT 8
|
||||
# define TXP_FORMAT_MASK GENMASK(11, 8)
|
||||
# define TXP_FORMAT_ABGR4444 0
|
||||
# define TXP_FORMAT_ARGB4444 1
|
||||
# define TXP_FORMAT_BGRA4444 2
|
||||
# define TXP_FORMAT_RGBA4444 3
|
||||
# define TXP_FORMAT_BGR565 6
|
||||
# define TXP_FORMAT_RGB565 7
|
||||
/* 888s are non-rotated, raster-only */
|
||||
# define TXP_FORMAT_BGR888 8
|
||||
# define TXP_FORMAT_RGB888 9
|
||||
# define TXP_FORMAT_ABGR8888 12
|
||||
# define TXP_FORMAT_ARGB8888 13
|
||||
# define TXP_FORMAT_BGRA8888 14
|
||||
# define TXP_FORMAT_RGBA8888 15
|
||||
|
||||
/* If TFORMAT is set, generates LT instead of T format. */
|
||||
# define TXP_LINEAR_UTILE BIT(7)
|
||||
|
||||
/* Rotate output by 90 degrees. */
|
||||
# define TXP_TRANSPOSE BIT(6)
|
||||
|
||||
/* Generate a tiled format for V3D. */
|
||||
# define TXP_TFORMAT BIT(5)
|
||||
|
||||
/* Generates some undefined test mode output. */
|
||||
# define TXP_TEST_MODE BIT(4)
|
||||
|
||||
/* Request odd field from HVS. */
|
||||
# define TXP_FIELD BIT(3)
|
||||
|
||||
/* Raise interrupt when idle. */
|
||||
# define TXP_EI BIT(2)
|
||||
|
||||
/* Set when generating a frame, clears when idle. */
|
||||
# define TXP_BUSY BIT(1)
|
||||
|
||||
/* Starts a frame. Self-clearing. */
|
||||
# define TXP_GO BIT(0)
|
||||
|
||||
/* Number of lines received and committed to memory. */
|
||||
#define TXP_PROGRESS 0x10
|
||||
|
||||
#define TXP_READ(offset) readl(txp->regs + (offset))
|
||||
#define TXP_WRITE(offset, val) writel(val, txp->regs + (offset))
|
||||
|
||||
struct vc4_txp {
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct drm_writeback_connector connector;
|
||||
|
||||
void __iomem *regs;
|
||||
};
|
||||
|
||||
static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder)
|
||||
{
|
||||
return container_of(encoder, struct vc4_txp, connector.encoder);
|
||||
}
|
||||
|
||||
static inline struct vc4_txp *connector_to_vc4_txp(struct drm_connector *conn)
|
||||
{
|
||||
return container_of(conn, struct vc4_txp, connector.base);
|
||||
}
|
||||
|
||||
#define TXP_REG(reg) { reg, #reg }
|
||||
static const struct {
|
||||
u32 reg;
|
||||
const char *name;
|
||||
} txp_regs[] = {
|
||||
TXP_REG(TXP_DST_PTR),
|
||||
TXP_REG(TXP_DST_PITCH),
|
||||
TXP_REG(TXP_DIM),
|
||||
TXP_REG(TXP_DST_CTRL),
|
||||
TXP_REG(TXP_PROGRESS),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
int vc4_txp_debugfs_regs(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *)m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct vc4_txp *txp = vc4->txp;
|
||||
int i;
|
||||
|
||||
if (!txp)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(txp_regs); i++) {
|
||||
seq_printf(m, "%s (0x%04x): 0x%08x\n",
|
||||
txp_regs[i].name, txp_regs[i].reg,
|
||||
TXP_READ(txp_regs[i].reg));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int vc4_txp_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
|
||||
return drm_add_modes_noedid(connector, dev->mode_config.max_width,
|
||||
dev->mode_config.max_height);
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
vc4_txp_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_mode_config *mode_config = &dev->mode_config;
|
||||
int w = mode->hdisplay, h = mode->vdisplay;
|
||||
|
||||
if (w < mode_config->min_width || w > mode_config->max_width)
|
||||
return MODE_BAD_HVALUE;
|
||||
|
||||
if (h < mode_config->min_height || h > mode_config->max_height)
|
||||
return MODE_BAD_VVALUE;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static const u32 drm_fmts[] = {
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_BGR888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_RGBX8888,
|
||||
DRM_FORMAT_BGRX8888,
|
||||
DRM_FORMAT_RGBA8888,
|
||||
DRM_FORMAT_BGRA8888,
|
||||
};
|
||||
|
||||
static const u32 txp_fmts[] = {
|
||||
TXP_FORMAT_RGB888,
|
||||
TXP_FORMAT_BGR888,
|
||||
TXP_FORMAT_ARGB8888,
|
||||
TXP_FORMAT_ABGR8888,
|
||||
TXP_FORMAT_ARGB8888,
|
||||
TXP_FORMAT_ABGR8888,
|
||||
TXP_FORMAT_RGBA8888,
|
||||
TXP_FORMAT_BGRA8888,
|
||||
TXP_FORMAT_RGBA8888,
|
||||
TXP_FORMAT_BGRA8888,
|
||||
};
|
||||
|
||||
static int vc4_txp_connector_atomic_check(struct drm_connector *conn,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_gem_cma_object *gem;
|
||||
struct drm_framebuffer *fb;
|
||||
int i;
|
||||
|
||||
if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_new_crtc_state(conn_state->state,
|
||||
conn_state->crtc);
|
||||
|
||||
fb = conn_state->writeback_job->fb;
|
||||
if (fb->width != crtc_state->mode.hdisplay ||
|
||||
fb->height != crtc_state->mode.vdisplay) {
|
||||
DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
|
||||
fb->width, fb->height);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) {
|
||||
if (fb->format->format == drm_fmts[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(drm_fmts))
|
||||
return -EINVAL;
|
||||
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
|
||||
/* Pitch must be aligned on 16 bytes. */
|
||||
if (fb->pitches[0] & GENMASK(3, 0))
|
||||
return -EINVAL;
|
||||
|
||||
vc4_crtc_txp_armed(crtc_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct vc4_txp *txp = connector_to_vc4_txp(conn);
|
||||
struct drm_gem_cma_object *gem;
|
||||
struct drm_display_mode *mode;
|
||||
struct drm_framebuffer *fb;
|
||||
u32 ctrl;
|
||||
int i;
|
||||
|
||||
if (WARN_ON(!conn_state->writeback_job ||
|
||||
!conn_state->writeback_job->fb))
|
||||
return;
|
||||
|
||||
mode = &conn_state->crtc->state->adjusted_mode;
|
||||
fb = conn_state->writeback_job->fb;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) {
|
||||
if (fb->format->format == drm_fmts[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (WARN_ON(i == ARRAY_SIZE(drm_fmts)))
|
||||
return;
|
||||
|
||||
ctrl = TXP_GO | TXP_VSTART_AT_EOF | TXP_EI |
|
||||
VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE) |
|
||||
VC4_SET_FIELD(txp_fmts[i], TXP_FORMAT);
|
||||
|
||||
if (fb->format->has_alpha)
|
||||
ctrl |= TXP_ALPHA_ENABLE;
|
||||
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
TXP_WRITE(TXP_DST_PTR, gem->paddr + fb->offsets[0]);
|
||||
TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]);
|
||||
TXP_WRITE(TXP_DIM,
|
||||
VC4_SET_FIELD(mode->hdisplay, TXP_WIDTH) |
|
||||
VC4_SET_FIELD(mode->vdisplay, TXP_HEIGHT));
|
||||
|
||||
TXP_WRITE(TXP_DST_CTRL, ctrl);
|
||||
|
||||
drm_writeback_queue_job(&txp->connector, conn_state->writeback_job);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = {
|
||||
.get_modes = vc4_txp_connector_get_modes,
|
||||
.mode_valid = vc4_txp_connector_mode_valid,
|
||||
.atomic_check = vc4_txp_connector_atomic_check,
|
||||
.atomic_commit = vc4_txp_connector_atomic_commit,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
vc4_txp_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static void vc4_txp_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs vc4_txp_connector_funcs = {
|
||||
.detect = vc4_txp_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = vc4_txp_connector_destroy,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static void vc4_txp_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct vc4_txp *txp = encoder_to_vc4_txp(encoder);
|
||||
|
||||
if (TXP_READ(TXP_DST_CTRL) & TXP_BUSY) {
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
||||
|
||||
TXP_WRITE(TXP_DST_CTRL, TXP_ABORT);
|
||||
|
||||
while (TXP_READ(TXP_DST_CTRL) & TXP_BUSY &&
|
||||
time_before(jiffies, timeout))
|
||||
;
|
||||
|
||||
WARN_ON(TXP_READ(TXP_DST_CTRL) & TXP_BUSY);
|
||||
}
|
||||
|
||||
TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = {
|
||||
.disable = vc4_txp_encoder_disable,
|
||||
};
|
||||
|
||||
static irqreturn_t vc4_txp_interrupt(int irq, void *data)
|
||||
{
|
||||
struct vc4_txp *txp = data;
|
||||
|
||||
TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI);
|
||||
vc4_crtc_handle_vblank(to_vc4_crtc(txp->connector.base.state->crtc));
|
||||
drm_writeback_signal_completion(&txp->connector, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *drm = dev_get_drvdata(master);
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
struct vc4_txp *txp;
|
||||
int ret, irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL);
|
||||
if (!txp)
|
||||
return -ENOMEM;
|
||||
|
||||
txp->pdev = pdev;
|
||||
|
||||
txp->regs = vc4_ioremap_regs(pdev, 0);
|
||||
if (IS_ERR(txp->regs))
|
||||
return PTR_ERR(txp->regs);
|
||||
|
||||
drm_connector_helper_add(&txp->connector.base,
|
||||
&vc4_txp_connector_helper_funcs);
|
||||
ret = drm_writeback_connector_init(drm, &txp->connector,
|
||||
&vc4_txp_connector_funcs,
|
||||
&vc4_txp_encoder_helper_funcs,
|
||||
drm_fmts, ARRAY_SIZE(drm_fmts));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0,
|
||||
dev_name(dev), txp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(dev, txp);
|
||||
vc4->txp = txp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vc4_txp_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(master);
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
struct vc4_txp *txp = dev_get_drvdata(dev);
|
||||
|
||||
vc4_txp_connector_destroy(&txp->connector.base);
|
||||
|
||||
vc4->txp = NULL;
|
||||
}
|
||||
|
||||
static const struct component_ops vc4_txp_ops = {
|
||||
.bind = vc4_txp_bind,
|
||||
.unbind = vc4_txp_unbind,
|
||||
};
|
||||
|
||||
static int vc4_txp_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &vc4_txp_ops);
|
||||
}
|
||||
|
||||
static int vc4_txp_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &vc4_txp_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id vc4_txp_dt_match[] = {
|
||||
{ .compatible = "brcm,bcm2835-txp" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
struct platform_driver vc4_txp_driver = {
|
||||
.probe = vc4_txp_probe,
|
||||
.remove = vc4_txp_remove,
|
||||
.driver = {
|
||||
.name = "vc4_txp",
|
||||
.of_match_table = vc4_txp_dt_match,
|
||||
},
|
||||
};
|
3
drivers/gpu/drm/vkms/Makefile
Normal file
3
drivers/gpu/drm/vkms/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o
|
||||
|
||||
obj-$(CONFIG_DRM_VKMS) += vkms.o
|
35
drivers/gpu/drm/vkms/vkms_crtc.c
Normal file
35
drivers/gpu/drm/vkms/vkms_crtc.c
Normal file
@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "vkms_drv.h"
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
static const struct drm_crtc_funcs vkms_crtc_funcs = {
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
};
|
||||
|
||||
int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
|
||||
struct drm_plane *primary, struct drm_plane *cursor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor,
|
||||
&vkms_crtc_funcs, NULL);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to init CRTC\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
139
drivers/gpu/drm/vkms/vkms_drv.c
Normal file
139
drivers/gpu/drm/vkms/vkms_drv.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include "vkms_drv.h"
|
||||
|
||||
#define DRIVER_NAME "vkms"
|
||||
#define DRIVER_DESC "Virtual Kernel Mode Setting"
|
||||
#define DRIVER_DATE "20180514"
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
|
||||
#define XRES_MIN 32
|
||||
#define YRES_MIN 32
|
||||
|
||||
#define XRES_MAX 8192
|
||||
#define YRES_MAX 8192
|
||||
|
||||
static struct vkms_device *vkms_device;
|
||||
|
||||
static const struct file_operations vkms_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.mmap = drm_gem_mmap,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.release = drm_release,
|
||||
};
|
||||
|
||||
static void vkms_release(struct drm_device *dev)
|
||||
{
|
||||
struct vkms_device *vkms = container_of(dev, struct vkms_device, drm);
|
||||
|
||||
platform_device_unregister(vkms->platform);
|
||||
drm_mode_config_cleanup(&vkms->drm);
|
||||
drm_dev_fini(&vkms->drm);
|
||||
}
|
||||
|
||||
static struct drm_driver vkms_driver = {
|
||||
.driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
|
||||
.release = vkms_release,
|
||||
.fops = &vkms_driver_fops,
|
||||
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = DRIVER_DATE,
|
||||
.major = DRIVER_MAJOR,
|
||||
.minor = DRIVER_MINOR,
|
||||
};
|
||||
|
||||
static const struct drm_mode_config_funcs vkms_mode_funcs = {
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static int vkms_modeset_init(struct vkms_device *vkmsdev)
|
||||
{
|
||||
struct drm_device *dev = &vkmsdev->drm;
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
dev->mode_config.funcs = &vkms_mode_funcs;
|
||||
dev->mode_config.min_width = XRES_MIN;
|
||||
dev->mode_config.min_height = YRES_MIN;
|
||||
dev->mode_config.max_width = XRES_MAX;
|
||||
dev->mode_config.max_height = YRES_MAX;
|
||||
|
||||
return vkms_output_init(vkmsdev);
|
||||
}
|
||||
|
||||
static int __init vkms_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
vkms_device = kzalloc(sizeof(*vkms_device), GFP_KERNEL);
|
||||
if (!vkms_device)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = drm_dev_init(&vkms_device->drm, &vkms_driver, NULL);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
vkms_device->platform =
|
||||
platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
|
||||
if (IS_ERR(vkms_device->platform)) {
|
||||
ret = PTR_ERR(vkms_device->platform);
|
||||
goto out_fini;
|
||||
}
|
||||
|
||||
ret = vkms_modeset_init(vkms_device);
|
||||
if (ret)
|
||||
goto out_unregister;
|
||||
|
||||
ret = drm_dev_register(&vkms_device->drm, 0);
|
||||
if (ret)
|
||||
goto out_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister:
|
||||
platform_device_unregister(vkms_device->platform);
|
||||
|
||||
out_fini:
|
||||
drm_dev_fini(&vkms_device->drm);
|
||||
|
||||
out_free:
|
||||
kfree(vkms_device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit vkms_exit(void)
|
||||
{
|
||||
if (!vkms_device) {
|
||||
DRM_INFO("vkms_device is NULL.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
drm_dev_unregister(&vkms_device->drm);
|
||||
drm_dev_put(&vkms_device->drm);
|
||||
|
||||
kfree(vkms_device);
|
||||
}
|
||||
|
||||
module_init(vkms_init);
|
||||
module_exit(vkms_exit);
|
||||
|
||||
MODULE_AUTHOR("Haneen Mohammed <hamohammed.sa@gmail.com>");
|
||||
MODULE_AUTHOR("Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
31
drivers/gpu/drm/vkms/vkms_drv.h
Normal file
31
drivers/gpu/drm/vkms/vkms_drv.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef _VKMS_DRV_H_
|
||||
#define _VKMS_DRV_H_
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
|
||||
static const u32 vkms_formats[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
};
|
||||
|
||||
struct vkms_output {
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
};
|
||||
|
||||
struct vkms_device {
|
||||
struct drm_device drm;
|
||||
struct platform_device *platform;
|
||||
struct vkms_output output;
|
||||
};
|
||||
|
||||
int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
|
||||
struct drm_plane *primary, struct drm_plane *cursor);
|
||||
|
||||
int vkms_output_init(struct vkms_device *vkmsdev);
|
||||
|
||||
struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev);
|
||||
|
||||
#endif /* _VKMS_DRV_H_ */
|
91
drivers/gpu/drm/vkms/vkms_output.c
Normal file
91
drivers/gpu/drm/vkms/vkms_output.c
Normal file
@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "vkms_drv.h"
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
static void vkms_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs vkms_connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = vkms_connector_destroy,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_funcs vkms_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
int vkms_output_init(struct vkms_device *vkmsdev)
|
||||
{
|
||||
struct vkms_output *output = &vkmsdev->output;
|
||||
struct drm_device *dev = &vkmsdev->drm;
|
||||
struct drm_connector *connector = &output->connector;
|
||||
struct drm_encoder *encoder = &output->encoder;
|
||||
struct drm_crtc *crtc = &output->crtc;
|
||||
struct drm_plane *primary;
|
||||
int ret;
|
||||
|
||||
primary = vkms_plane_init(vkmsdev);
|
||||
if (IS_ERR(primary))
|
||||
return PTR_ERR(primary);
|
||||
|
||||
ret = vkms_crtc_init(dev, crtc, primary, NULL);
|
||||
if (ret)
|
||||
goto err_crtc;
|
||||
|
||||
ret = drm_connector_init(dev, connector, &vkms_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VIRTUAL);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to init connector\n");
|
||||
goto err_connector;
|
||||
}
|
||||
|
||||
ret = drm_connector_register(connector);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to register connector\n");
|
||||
goto err_connector_register;
|
||||
}
|
||||
|
||||
ret = drm_encoder_init(dev, encoder, &vkms_encoder_funcs,
|
||||
DRM_MODE_ENCODER_VIRTUAL, NULL);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to init encoder\n");
|
||||
goto err_encoder;
|
||||
}
|
||||
encoder->possible_crtcs = 1;
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(connector, encoder);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to attach connector to encoder\n");
|
||||
goto err_attach;
|
||||
}
|
||||
|
||||
drm_mode_config_reset(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_attach:
|
||||
drm_encoder_cleanup(encoder);
|
||||
|
||||
err_encoder:
|
||||
drm_connector_unregister(connector);
|
||||
|
||||
err_connector_register:
|
||||
drm_connector_cleanup(connector);
|
||||
|
||||
err_connector:
|
||||
drm_crtc_cleanup(crtc);
|
||||
|
||||
err_crtc:
|
||||
drm_plane_cleanup(primary);
|
||||
return ret;
|
||||
}
|
46
drivers/gpu/drm/vkms/vkms_plane.c
Normal file
46
drivers/gpu/drm/vkms/vkms_plane.c
Normal file
@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "vkms_drv.h"
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
||||
static const struct drm_plane_funcs vkms_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
};
|
||||
|
||||
struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev)
|
||||
{
|
||||
struct drm_device *dev = &vkmsdev->drm;
|
||||
struct drm_plane *plane;
|
||||
const u32 *formats;
|
||||
int ret, nformats;
|
||||
|
||||
plane = kzalloc(sizeof(*plane), GFP_KERNEL);
|
||||
if (!plane)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
formats = vkms_formats;
|
||||
nformats = ARRAY_SIZE(vkms_formats);
|
||||
|
||||
ret = drm_universal_plane_init(dev, plane, 0,
|
||||
&vkms_plane_funcs,
|
||||
formats, nformats,
|
||||
NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
|
||||
if (ret) {
|
||||
kfree(plane);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return plane;
|
||||
}
|
@ -100,6 +100,7 @@ int __must_check drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
|
||||
bool nonblock);
|
||||
void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state);
|
||||
void drm_atomic_helper_fake_vblank(struct drm_atomic_state *state);
|
||||
void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state);
|
||||
void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state);
|
||||
|
||||
|
139
include/drm/drm_client.h
Normal file
139
include/drm/drm_client.h
Normal file
@ -0,0 +1,139 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _DRM_CLIENT_H_
|
||||
#define _DRM_CLIENT_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct drm_client_dev;
|
||||
struct drm_device;
|
||||
struct drm_file;
|
||||
struct drm_framebuffer;
|
||||
struct drm_gem_object;
|
||||
struct drm_minor;
|
||||
struct module;
|
||||
|
||||
/**
|
||||
* struct drm_client_funcs - DRM client callbacks
|
||||
*/
|
||||
struct drm_client_funcs {
|
||||
/**
|
||||
* @owner: The module owner
|
||||
*/
|
||||
struct module *owner;
|
||||
|
||||
/**
|
||||
* @unregister:
|
||||
*
|
||||
* Called when &drm_device is unregistered. The client should respond by
|
||||
* releasing it's resources using drm_client_release().
|
||||
*
|
||||
* This callback is optional.
|
||||
*/
|
||||
void (*unregister)(struct drm_client_dev *client);
|
||||
|
||||
/**
|
||||
* @restore:
|
||||
*
|
||||
* Called on drm_lastclose(). The first client instance in the list that
|
||||
* returns zero gets the privilege to restore and no more clients are
|
||||
* called. This callback is not called after @unregister has been called.
|
||||
*
|
||||
* This callback is optional.
|
||||
*/
|
||||
int (*restore)(struct drm_client_dev *client);
|
||||
|
||||
/**
|
||||
* @hotplug:
|
||||
*
|
||||
* Called on drm_kms_helper_hotplug_event().
|
||||
* This callback is not called after @unregister has been called.
|
||||
*
|
||||
* This callback is optional.
|
||||
*/
|
||||
int (*hotplug)(struct drm_client_dev *client);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_client_dev - DRM client instance
|
||||
*/
|
||||
struct drm_client_dev {
|
||||
/**
|
||||
* @dev: DRM device
|
||||
*/
|
||||
struct drm_device *dev;
|
||||
|
||||
/**
|
||||
* @name: Name of the client.
|
||||
*/
|
||||
const char *name;
|
||||
|
||||
/**
|
||||
* @list:
|
||||
*
|
||||
* List of all clients of a DRM device, linked into
|
||||
* &drm_device.clientlist. Protected by &drm_device.clientlist_mutex.
|
||||
*/
|
||||
struct list_head list;
|
||||
|
||||
/**
|
||||
* @funcs: DRM client functions (optional)
|
||||
*/
|
||||
const struct drm_client_funcs *funcs;
|
||||
|
||||
/**
|
||||
* @file: DRM file
|
||||
*/
|
||||
struct drm_file *file;
|
||||
};
|
||||
|
||||
int drm_client_new(struct drm_device *dev, struct drm_client_dev *client,
|
||||
const char *name, const struct drm_client_funcs *funcs);
|
||||
void drm_client_release(struct drm_client_dev *client);
|
||||
|
||||
void drm_client_dev_unregister(struct drm_device *dev);
|
||||
void drm_client_dev_hotplug(struct drm_device *dev);
|
||||
void drm_client_dev_restore(struct drm_device *dev);
|
||||
|
||||
/**
|
||||
* struct drm_client_buffer - DRM client buffer
|
||||
*/
|
||||
struct drm_client_buffer {
|
||||
/**
|
||||
* @client: DRM client
|
||||
*/
|
||||
struct drm_client_dev *client;
|
||||
|
||||
/**
|
||||
* @handle: Buffer handle
|
||||
*/
|
||||
u32 handle;
|
||||
|
||||
/**
|
||||
* @pitch: Buffer pitch
|
||||
*/
|
||||
u32 pitch;
|
||||
|
||||
/**
|
||||
* @gem: GEM object backing this buffer
|
||||
*/
|
||||
struct drm_gem_object *gem;
|
||||
|
||||
/**
|
||||
* @vaddr: Virtual address for the buffer
|
||||
*/
|
||||
void *vaddr;
|
||||
|
||||
/**
|
||||
* @fb: DRM framebuffer
|
||||
*/
|
||||
struct drm_framebuffer *fb;
|
||||
};
|
||||
|
||||
struct drm_client_buffer *
|
||||
drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format);
|
||||
void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
|
||||
|
||||
int drm_client_debugfs_init(struct drm_minor *minor);
|
||||
|
||||
#endif
|
@ -290,6 +290,10 @@ struct drm_display_info {
|
||||
#define DRM_BUS_FLAG_DATA_MSB_TO_LSB (1<<4)
|
||||
/* data is transmitted LSB to MSB on the bus */
|
||||
#define DRM_BUS_FLAG_DATA_LSB_TO_MSB (1<<5)
|
||||
/* drive sync on pos. edge */
|
||||
#define DRM_BUS_FLAG_SYNC_POSEDGE (1<<6)
|
||||
/* drive sync on neg. edge */
|
||||
#define DRM_BUS_FLAG_SYNC_NEGEDGE (1<<7)
|
||||
|
||||
/**
|
||||
* @bus_flags: Additional information (like pixel signal polarity) for
|
||||
@ -1187,6 +1191,9 @@ struct drm_connector *
|
||||
drm_connector_list_iter_next(struct drm_connector_list_iter *iter);
|
||||
void drm_connector_list_iter_end(struct drm_connector_list_iter *iter);
|
||||
|
||||
bool drm_connector_has_possible_encoder(struct drm_connector *connector,
|
||||
struct drm_encoder *encoder);
|
||||
|
||||
/**
|
||||
* drm_for_each_connector_iter - connector_list iterator macro
|
||||
* @connector: &struct drm_connector pointer used as cursor
|
||||
@ -1199,4 +1206,17 @@ void drm_connector_list_iter_end(struct drm_connector_list_iter *iter);
|
||||
#define drm_for_each_connector_iter(connector, iter) \
|
||||
while ((connector = drm_connector_list_iter_next(iter)))
|
||||
|
||||
/**
|
||||
* drm_connector_for_each_possible_encoder - iterate connector's possible encoders
|
||||
* @connector: &struct drm_connector pointer
|
||||
* @encoder: &struct drm_encoder pointer used as cursor
|
||||
* @__i: int iteration cursor, for macro-internal use
|
||||
*/
|
||||
#define drm_connector_for_each_possible_encoder(connector, encoder, __i) \
|
||||
for ((__i) = 0; (__i) < ARRAY_SIZE((connector)->encoder_ids) && \
|
||||
(connector)->encoder_ids[(__i)] != 0; (__i)++) \
|
||||
for_each_if((encoder) = \
|
||||
drm_encoder_find((connector)->dev, NULL, \
|
||||
(connector)->encoder_ids[(__i)])) \
|
||||
|
||||
#endif
|
||||
|
@ -119,6 +119,29 @@ struct drm_crtc_state {
|
||||
bool zpos_changed : 1;
|
||||
bool color_mgmt_changed : 1;
|
||||
|
||||
/**
|
||||
* @no_vblank:
|
||||
*
|
||||
* Reflects the ability of a CRTC to send VBLANK events. This state
|
||||
* usually depends on the pipeline configuration, and the main usuage
|
||||
* is CRTCs feeding a writeback connector operating in oneshot mode.
|
||||
* In this case the VBLANK event is only generated when a job is queued
|
||||
* to the writeback connector, and we want the core to fake VBLANK
|
||||
* events when this part of the pipeline hasn't changed but others had
|
||||
* or when the CRTC and connectors are being disabled.
|
||||
*
|
||||
* __drm_atomic_helper_crtc_duplicate_state() will not reset the value
|
||||
* from the current state, the CRTC driver is then responsible for
|
||||
* updating this field when needed.
|
||||
*
|
||||
* Note that the combination of &drm_crtc_state.event == NULL and
|
||||
* &drm_crtc_state.no_blank == true is valid and usually used when the
|
||||
* writeback connector attached to the CRTC has a new job queued. In
|
||||
* this case the driver will send the VBLANK event on its own when the
|
||||
* writeback job is complete.
|
||||
*/
|
||||
bool no_vblank : 1;
|
||||
|
||||
/* attached planes bitmask:
|
||||
* WARNING: transitional helpers do not maintain plane_mask so
|
||||
* drivers not converted over to atomic helpers should not rely
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user