drm-misc-next for v6.3:

UAPI Changes:
 
  * connector: Support analog-TV mode property
 
  * media: Add MEDIA_BUS_FMT_RGB565_1X24_CPADHI,
    MEDIA_BUS_FMT_RGB666_1X18 and MEDIA_BUS_FMT_RGB666_1X24_CPADHI
 
 Cross-subsystem Changes:
 
  * dma-buf: Documentation fixes
 
  * i2c: Introduce i2c_client_get_device_id() helper
 
 Core Changes:
 
  * Improve support for analog TV output
 
  * bridge: Remove unused drm_bridge_chain functions
 
  * debugfs: Add per-device helpers and convert various DRM drivers
 
  * dp-mst: Various fixes
 
  * fbdev emulation: Always pick 32 bpp as default
 
  * KUnit: Add tests for managed helpers; Various cleanups
 
  * panel-orientation: Add quirks for Lenovo Yoga Tab 3 X90F and DynaBook K50
 
  * TTM: Open-code ttm_bo_wait() and remove the helper
 
 Driver Changes:
 
  * Fix preferred depth and bpp values throughout DRM drivers
 
  * Remove #CONFIG_PM guards throughout DRM drivers
 
  * ast: Various fixes
 
  * bridge: Implement i2c's probe_new in various drivers; Fixes; ite-it6505:
    Locking fixes, Cache EDID data; ite-it66121: Support IT6610 chip,
    Cleanups; lontium-tl9611: Fix HDMI on DragonBoard 845c; parade-ps8640:
    Use atomic bridge functions
 
  * gud: Convert to DRM shadow-plane helpers; Perform flushing synchronously
    during atomic update
 
  * ili9486: Support 16-bit pixel data
 
  * imx: Split off IPUv3 driver; Various fixes
 
  * mipi-dbi: Convert to DRM shadow-plane helpers plus rsp driver changes;i
    Support separate I/O-voltage supply
 
  * mxsfb: Depend on ARCH_MXS or ARCH_MXC
 
  * omapdrm: Various fixes
 
  * panel: Use ktime_get_boottime() to measure power-down delay in various
    drivers; Fix auto-suspend delay in various drivers; orisetech-ota5601a:
    Add support
 
  * sprd: Cleanups
 
  * sun4i: Convert to new TV-mode property
 
  * tidss: Various fixes
 
  * v3d: Various fixes
 
  * vc4: Convert to new TV-mode property; Support Kunit tests; Cleanups;
    dpi: Support RGB565 and RGB666 formats; dsi: Convert DSI driver to
    bridge
 
  * virtio: Improve tracing
 
  * vkms: Support small cursors in IGT tests; Various fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmO0B0QACgkQaA3BHVML
 eiMKtAf9EXt5yaEonR4gsGNVD70VkebW+jPEWbEMg5hMFDHE9sjBja8T7bOxeeL1
 BVofJiAuEZW9176eqeWvShuwOuiE7lf4WQLMvXfFmtHF/Nac9HUtEvOcvc1vEDUB
 y6VFlVHe8mSp+Iy0WPLyZCtT4d7v2eM+VYm0HgFa74dSTzQTLGPiUI/XVb/YDaA7
 FHKB5NvsMX9S1XvjxCsq0jA5bo8SSzh5CVKerdAZkBJMCSmKiY09o2p5C7vw3EAz
 WUXL5CXqd9bwgvZa/9ZClQtpJaikNGoOaMSEl67rbWdVTk0LIpqx3nTY15py5LXd
 SG4Wtfor3Vf1REa9TrR2uCIOmh+1gQ==
 =0/PC
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2023-01-03' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for v6.3:

UAPI Changes:

 * connector: Support analog-TV mode property
 * media: Add MEDIA_BUS_FMT_RGB565_1X24_CPADHI,
   MEDIA_BUS_FMT_RGB666_1X18 and MEDIA_BUS_FMT_RGB666_1X24_CPADHI

Cross-subsystem Changes:

 * dma-buf: Documentation fixes
 * i2c: Introduce i2c_client_get_device_id() helper

Core Changes:

 * Improve support for analog TV output
 * bridge: Remove unused drm_bridge_chain functions
 * debugfs: Add per-device helpers and convert various DRM drivers
 * dp-mst: Various fixes
 * fbdev emulation: Always pick 32 bpp as default
 * KUnit: Add tests for managed helpers; Various cleanups
 * panel-orientation: Add quirks for Lenovo Yoga Tab 3 X90F and DynaBook K50
 * TTM: Open-code ttm_bo_wait() and remove the helper

Driver Changes:

 * Fix preferred depth and bpp values throughout DRM drivers
 * Remove #CONFIG_PM guards throughout DRM drivers
 * ast: Various fixes
 * bridge: Implement i2c's probe_new in various drivers; Fixes; ite-it6505:
   Locking fixes, Cache EDID data; ite-it66121: Support IT6610 chip,
   Cleanups; lontium-tl9611: Fix HDMI on DragonBoard 845c; parade-ps8640:
   Use atomic bridge functions
 * gud: Convert to DRM shadow-plane helpers; Perform flushing synchronously
   during atomic update
 * ili9486: Support 16-bit pixel data
 * imx: Split off IPUv3 driver; Various fixes
 * mipi-dbi: Convert to DRM shadow-plane helpers plus rsp driver changes;i
   Support separate I/O-voltage supply
 * mxsfb: Depend on ARCH_MXS or ARCH_MXC
 * omapdrm: Various fixes
 * panel: Use ktime_get_boottime() to measure power-down delay in various
   drivers; Fix auto-suspend delay in various drivers; orisetech-ota5601a:
   Add support
 * sprd: Cleanups
 * sun4i: Convert to new TV-mode property
 * tidss: Various fixes
 * v3d: Various fixes
 * vc4: Convert to new TV-mode property; Support Kunit tests; Cleanups;
   dpi: Support RGB565 and RGB666 formats; dsi: Convert DSI driver to
   bridge
 * virtio: Improve tracing
 * vkms: Support small cursors in IGT tests; Various fixes

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/Y7QIwlfElAYWxRcR@linux-uq9g
This commit is contained in:
Daniel Vetter 2023-01-04 14:59:24 +01:00
commit 03a0a10408
253 changed files with 6907 additions and 2517 deletions

View File

@ -52,9 +52,49 @@ properties:
maxItems: 1 maxItems: 1
description: extcon specifier for the Power Delivery description: extcon specifier for the Power Delivery
port: ports:
$ref: /schemas/graph.yaml#/properties/port $ref: /schemas/graph.yaml#/properties/ports
description: A port node pointing to DPI host port node
properties:
port@0:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: A port node pointing to DPI host port node
properties:
endpoint:
$ref: /schemas/graph.yaml#/$defs/endpoint-base
unevaluatedProperties: false
properties:
link-frequencies:
minItems: 1
maxItems: 1
description: Allowed max link frequencies in Hz
port@1:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: Video port for DP output
properties:
endpoint:
$ref: /schemas/graph.yaml#/$defs/endpoint-base
unevaluatedProperties: false
properties:
data-lanes:
minItems: 1
uniqueItems: true
items:
- enum: [ 0, 1 ]
- const: 1
- const: 2
- const: 3
required:
- port@0
- port@1
required: required:
- compatible - compatible
@ -63,6 +103,7 @@ required:
- interrupts - interrupts
- reset-gpios - reset-gpios
- extcon - extcon
- ports
additionalProperties: false additionalProperties: false
@ -85,9 +126,24 @@ examples:
reset-gpios = <&pio 179 1>; reset-gpios = <&pio 179 1>;
extcon = <&usbc_extcon>; extcon = <&usbc_extcon>;
port { ports {
it6505_in: endpoint { #address-cells = <1>;
remote-endpoint = <&dpi_out>; #size-cells = <0>;
port@0 {
reg = <0>;
it6505_in: endpoint {
remote-endpoint = <&dpi_out>;
link-frequencies = /bits/ 64 <150000000>;
};
};
port@1 {
reg = <1>;
it6505_out: endpoint {
remote-endpoint = <&dp_in>;
data-lanes = <0 1>;
};
}; };
}; };
}; };

View File

@ -17,7 +17,9 @@ description: |
properties: properties:
compatible: compatible:
const: ite,it66121 enum:
- ite,it66121
- ite,it6610
reg: reg:
maxItems: 1 maxItems: 1

View File

@ -0,0 +1,56 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/focaltech,gpt3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Focaltech GPT3 3.0" (640x480 pixels) IPS LCD panel
maintainers:
- Christophe Branchereau <cbranchereau@gmail.com>
allOf:
- $ref: panel-common.yaml#
- $ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
const: focaltech,gpt3
reg:
maxItems: 1
required:
- compatible
- reg
- power-supply
- reset-gpios
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "focaltech,gpt3";
reg = <0>;
spi-max-frequency = <3125000>;
reset-gpios = <&gpe 2 GPIO_ACTIVE_LOW>;
backlight = <&backlight>;
power-supply = <&vcc>;
port {
panel_input: endpoint {
remote-endpoint = <&panel_output>;
};
};
};
};

View File

@ -22,8 +22,9 @@ description: |
The standard defines the following interface signals for type C: The standard defines the following interface signals for type C:
- Power: - Power:
- Vdd: Power supply for display module - Vdd: Power supply for display module
Called power-supply in this binding.
- Vddi: Logic level supply for interface signals - Vddi: Logic level supply for interface signals
Combined into one in this binding called: power-supply Called io-supply in this binding.
- Interface: - Interface:
- CSx: Chip select - CSx: Chip select
- SCL: Serial clock - SCL: Serial clock
@ -80,6 +81,11 @@ properties:
Controller data/command selection (D/CX) in 4-line SPI mode. Controller data/command selection (D/CX) in 4-line SPI mode.
If not set, the controller is in 3-line SPI mode. If not set, the controller is in 3-line SPI mode.
io-supply:
description: |
Logic level supply for interface signals (Vddi).
No need to set if this is the same as power-supply.
required: required:
- compatible - compatible
- reg - reg

View File

@ -29,7 +29,10 @@ Things between square brackets are optional.
Valid names are:: Valid names are::
- NSTC: 480i output, with the CCIR System-M TV mode and NTSC color encoding - NSTC: 480i output, with the CCIR System-M TV mode and NTSC color encoding
- NTSC-J: 480i output, with the CCIR System-M TV mode, the NTSC color
encoding, and a black level equal to the blanking level.
- PAL: 576i output, with the CCIR System-B TV mode and PAL color encoding - PAL: 576i output, with the CCIR System-B TV mode and PAL color encoding
- PAL-M: 480i output, with the CCIR System-M TV mode and PAL color encoding
If 'M' is specified in the mode_option argument (after <yres> and before If 'M' is specified in the mode_option argument (after <yres> and before
<bpp> and <refresh>, if specified) the timings will be calculated using <bpp> and <refresh>, if specified) the timings will be calculated using
@ -70,6 +73,8 @@ Valid options are::
- reflect_y (boolean): Perform an axial symmetry on the Y axis - reflect_y (boolean): Perform an axial symmetry on the Y axis
- rotate (integer): Rotate the initial framebuffer by x - rotate (integer): Rotate the initial framebuffer by x
degrees. Valid values are 0, 90, 180 and 270. degrees. Valid values are 0, 90, 180 and 270.
- tv_mode: Analog TV mode. One of "NTSC", "NTSC-443", "NTSC-J", "PAL",
"PAL-M", "PAL-N", or "SECAM".
- panel_orientation, one of "normal", "upside_down", "left_side_up", or - panel_orientation, one of "normal", "upside_down", "left_side_up", or
"right_side_up". For KMS drivers only, this sets the "panel orientation" "right_side_up". For KMS drivers only, this sets the "panel orientation"
property on the kms connector as hint for kms users. property on the kms connector as hint for kms users.

View File

@ -188,6 +188,13 @@ Bridge Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:export: :export:
MIPI-DSI bridge operation
-------------------------
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: dsi bridge operations
Bridge Connector Helper Reference Bridge Connector Helper Reference
--------------------------------- ---------------------------------

View File

@ -520,6 +520,12 @@ HDMI Specific Connector Properties
.. kernel-doc:: drivers/gpu/drm/drm_connector.c .. kernel-doc:: drivers/gpu/drm/drm_connector.c
:doc: HDMI connector properties :doc: HDMI connector properties
Analog TV Specific Connector Properties
---------------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_connector.c
:doc: Analog TV Connector Properties
Standard CRTC Properties Standard CRTC Properties
------------------------ ------------------------

View File

@ -508,17 +508,18 @@ Clean up the debugfs support
There's a bunch of issues with it: There's a bunch of issues with it:
- The drm_info_list ->show() function doesn't even bother to cast to the drm - Convert drivers to support the drm_debugfs_add_files() function instead of
structure for you. This is lazy. the drm_debugfs_create_files() function.
- Improve late-register debugfs by rolling out the same debugfs pre-register
infrastructure for connector and crtc too. That way, the drivers won't need to
split their setup code into init and register anymore.
- We probably want to have some support for debugfs files on crtc/connectors and - We probably want to have some support for debugfs files on crtc/connectors and
maybe other kms objects directly in core. There's even drm_print support in maybe other kms objects directly in core. There's even drm_print support in
the funcs for these objects to dump kms state, so it's all there. And then the the funcs for these objects to dump kms state, so it's all there. And then the
->show() functions should obviously give you a pointer to the right object. ->show() functions should obviously give you a pointer to the right object.
- The drm_info_list stuff is centered on drm_minor instead of drm_device. For
anything we want to print drm_device (or maybe drm_file) is the right thing.
- The drm_driver->debugfs_init hooks we have is just an artifact of the old - The drm_driver->debugfs_init hooks we have is just an artifact of the old
midlayered load sequence. DRM debugfs should work more like sysfs, where you midlayered load sequence. DRM debugfs should work more like sysfs, where you
can create properties/files for an object anytime you want, and the core can create properties/files for an object anytime you want, and the core
@ -527,8 +528,6 @@ There's a bunch of issues with it:
this (together with the drm_minor->drm_device move) would allow us to remove this (together with the drm_minor->drm_device move) would allow us to remove
debugfs_init. debugfs_init.
Previous RFC that hasn't landed yet: https://lore.kernel.org/dri-devel/20200513114130.28641-2-wambui.karugax@gmail.com/
Contact: Daniel Vetter Contact: Daniel Vetter
Level: Intermediate Level: Intermediate

View File

@ -54,6 +54,25 @@ VEC (Composite TV out) encoder
.. kernel-doc:: drivers/gpu/drm/vc4/vc4_vec.c .. kernel-doc:: drivers/gpu/drm/vc4/vc4_vec.c
:doc: VC4 SDTV module :doc: VC4 SDTV module
KUnit Tests
===========
The VC4 Driver uses KUnit to perform driver-specific unit and
integration tests.
These tests are using a mock driver and can be ran using the
command below, on either arm or arm64 architectures,
.. code-block:: bash
$ ./tools/testing/kunit/kunit.py run \
--kunitconfig=drivers/gpu/drm/vc4/tests/.kunitconfig \
--cross_compile aarch64-linux-gnu- --arch arm64
Parts of the driver that are currently covered by tests are:
* The HVS to PixelValve dynamic FIFO assignment, for the BCM2835-7
and BCM2711.
Memory Management and 3D Command Submission Memory Management and 3D Command Submission
=========================================== ===========================================

View File

@ -949,6 +949,43 @@ The following tables list existing packed RGB formats.
- b\ :sub:`2` - b\ :sub:`2`
- b\ :sub:`1` - b\ :sub:`1`
- b\ :sub:`0` - b\ :sub:`0`
* .. _MEDIA-BUS-FMT-BGR666-1X18:
- MEDIA_BUS_FMT_BGR666_1X18
- 0x1023
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- b\ :sub:`5`
- b\ :sub:`4`
- b\ :sub:`3`
- b\ :sub:`2`
- b\ :sub:`1`
- b\ :sub:`0`
- g\ :sub:`5`
- g\ :sub:`4`
- g\ :sub:`3`
- g\ :sub:`2`
- g\ :sub:`1`
- g\ :sub:`0`
- r\ :sub:`5`
- r\ :sub:`4`
- r\ :sub:`3`
- r\ :sub:`2`
- r\ :sub:`1`
- r\ :sub:`0`
* .. _MEDIA-BUS-FMT-RBG888-1X24: * .. _MEDIA-BUS-FMT-RBG888-1X24:
- MEDIA_BUS_FMT_RBG888_1X24 - MEDIA_BUS_FMT_RBG888_1X24
@ -1023,6 +1060,80 @@ The following tables list existing packed RGB formats.
- b\ :sub:`2` - b\ :sub:`2`
- b\ :sub:`1` - b\ :sub:`1`
- b\ :sub:`0` - b\ :sub:`0`
* .. _MEDIA-BUS-FMT-BGR666-1X24_CPADHI:
- MEDIA_BUS_FMT_BGR666_1X24_CPADHI
- 0x1024
-
-
-
-
-
-
-
-
-
- 0
- 0
- b\ :sub:`5`
- b\ :sub:`4`
- b\ :sub:`3`
- b\ :sub:`2`
- b\ :sub:`1`
- b\ :sub:`0`
- 0
- 0
- g\ :sub:`5`
- g\ :sub:`4`
- g\ :sub:`3`
- g\ :sub:`2`
- g\ :sub:`1`
- g\ :sub:`0`
- 0
- 0
- r\ :sub:`5`
- r\ :sub:`4`
- r\ :sub:`3`
- r\ :sub:`2`
- r\ :sub:`1`
- r\ :sub:`0`
* .. _MEDIA-BUS-FMT-RGB565-1X24_CPADHI:
- MEDIA_BUS_FMT_RGB565_1X24_CPADHI
- 0x1022
-
-
-
-
-
-
-
-
-
- 0
- 0
- 0
- r\ :sub:`4`
- r\ :sub:`3`
- r\ :sub:`2`
- r\ :sub:`1`
- r\ :sub:`0`
- 0
- 0
- g\ :sub:`5`
- g\ :sub:`4`
- g\ :sub:`3`
- g\ :sub:`2`
- g\ :sub:`1`
- g\ :sub:`0`
- 0
- 0
- 0
- b\ :sub:`4`
- b\ :sub:`3`
- b\ :sub:`2`
- b\ :sub:`1`
- b\ :sub:`0`
* .. _MEDIA-BUS-FMT-BGR888-1X24: * .. _MEDIA-BUS-FMT-BGR888-1X24:
- MEDIA_BUS_FMT_BGR888_1X24 - MEDIA_BUS_FMT_BGR888_1X24

View File

@ -6984,7 +6984,7 @@ M: Philipp Zabel <p.zabel@pengutronix.de>
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org
S: Maintained S: Maintained
F: Documentation/devicetree/bindings/display/imx/ F: Documentation/devicetree/bindings/display/imx/
F: drivers/gpu/drm/imx/ F: drivers/gpu/drm/imx/ipuv3/
F: drivers/gpu/ipu-v3/ F: drivers/gpu/ipu-v3/
DRM DRIVERS FOR FREESCALE IMX BRIDGE DRM DRIVERS FOR FREESCALE IMX BRIDGE

View File

@ -1263,7 +1263,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_unmap_attachment_unlocked, DMA_BUF);
* *
* @dmabuf: [in] buffer which is moving * @dmabuf: [in] buffer which is moving
* *
* Informs all attachmenst that they need to destroy and recreated all their * Informs all attachments that they need to destroy and recreate all their
* mappings. * mappings.
*/ */
void dma_buf_move_notify(struct dma_buf *dmabuf) void dma_buf_move_notify(struct dma_buf *dmabuf)
@ -1281,11 +1281,11 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_move_notify, DMA_BUF);
/** /**
* DOC: cpu access * DOC: cpu access
* *
* There are mutliple reasons for supporting CPU access to a dma buffer object: * There are multiple reasons for supporting CPU access to a dma buffer object:
* *
* - Fallback operations in the kernel, for example when a device is connected * - Fallback operations in the kernel, for example when a device is connected
* over USB and the kernel needs to shuffle the data around first before * over USB and the kernel needs to shuffle the data around first before
* sending it away. Cache coherency is handled by braketing any transactions * sending it away. Cache coherency is handled by bracketing any transactions
* with calls to dma_buf_begin_cpu_access() and dma_buf_end_cpu_access() * with calls to dma_buf_begin_cpu_access() and dma_buf_end_cpu_access()
* access. * access.
* *
@ -1312,7 +1312,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_move_notify, DMA_BUF);
* replace ION buffers mmap support was needed. * replace ION buffers mmap support was needed.
* *
* There is no special interfaces, userspace simply calls mmap on the dma-buf * There is no special interfaces, userspace simply calls mmap on the dma-buf
* fd. But like for CPU access there's a need to braket the actual access, * fd. But like for CPU access there's a need to bracket the actual access,
* which is handled by the ioctl (DMA_BUF_IOCTL_SYNC). Note that * which is handled by the ioctl (DMA_BUF_IOCTL_SYNC). Note that
* DMA_BUF_IOCTL_SYNC can fail with -EAGAIN or -EINTR, in which case it must * DMA_BUF_IOCTL_SYNC can fail with -EAGAIN or -EINTR, in which case it must
* be restarted. * be restarted.
@ -1386,10 +1386,10 @@ static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
* preparations. Coherency is only guaranteed in the specified range for the * preparations. Coherency is only guaranteed in the specified range for the
* specified access direction. * specified access direction.
* @dmabuf: [in] buffer to prepare cpu access for. * @dmabuf: [in] buffer to prepare cpu access for.
* @direction: [in] length of range for cpu access. * @direction: [in] direction of access.
* *
* After the cpu access is complete the caller should call * After the cpu access is complete the caller should call
* dma_buf_end_cpu_access(). Only when cpu access is braketed by both calls is * dma_buf_end_cpu_access(). Only when cpu access is bracketed by both calls is
* it guaranteed to be coherent with other DMA access. * it guaranteed to be coherent with other DMA access.
* *
* This function will also wait for any DMA transactions tracked through * This function will also wait for any DMA transactions tracked through
@ -1429,7 +1429,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_begin_cpu_access, DMA_BUF);
* actions. Coherency is only guaranteed in the specified range for the * actions. Coherency is only guaranteed in the specified range for the
* specified access direction. * specified access direction.
* @dmabuf: [in] buffer to complete cpu access for. * @dmabuf: [in] buffer to complete cpu access for.
* @direction: [in] length of range for cpu access. * @direction: [in] direction of access.
* *
* This terminates CPU access started with dma_buf_begin_cpu_access(). * This terminates CPU access started with dma_buf_begin_cpu_access().
* *

View File

@ -63,6 +63,12 @@ config DRM_USE_DYNAMIC_DEBUG
bytes per callsite, the .data costs can be substantial, and bytes per callsite, the .data costs can be substantial, and
are therefore configurable. are therefore configurable.
config DRM_KUNIT_TEST_HELPERS
tristate
depends on DRM && KUNIT
help
KUnit Helpers for KMS drivers.
config DRM_KUNIT_TEST config DRM_KUNIT_TEST
tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS
depends on DRM && KUNIT depends on DRM && KUNIT
@ -73,6 +79,7 @@ config DRM_KUNIT_TEST
select DRM_KMS_HELPER select DRM_KMS_HELPER
select DRM_BUDDY select DRM_BUDDY
select DRM_EXPORT_FOR_TESTS if m select DRM_EXPORT_FOR_TESTS if m
select DRM_KUNIT_TEST_HELPERS
default KUNIT_ALL_TESTS default KUNIT_ALL_TESTS
help help
This builds unit tests for DRM. This option is not useful for This builds unit tests for DRM. This option is not useful for

View File

@ -126,7 +126,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
# Drivers and the rest # Drivers and the rest
# #
obj-$(CONFIG_DRM_KUNIT_TEST) += tests/ obj-y += tests/
obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o
obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o

View File

@ -52,8 +52,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/aer.h> #include <linux/aer.h>
#include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_bo_driver.h>
#include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_execbuf_util.h> #include <drm/ttm/ttm_execbuf_util.h>

View File

@ -25,6 +25,7 @@
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
#include <linux/sched/task.h> #include <linux/sched/task.h>
#include <drm/ttm/ttm_tt.h>
#include "amdgpu_object.h" #include "amdgpu_object.h"
#include "amdgpu_gem.h" #include "amdgpu_gem.h"

View File

@ -28,6 +28,8 @@
struct hmm_range; struct hmm_range;
struct drm_file;
struct amdgpu_device; struct amdgpu_device;
struct amdgpu_bo; struct amdgpu_bo;
struct amdgpu_bo_va; struct amdgpu_bo_va;

View File

@ -32,6 +32,8 @@
#include <drm/amdgpu_drm.h> #include <drm/amdgpu_drm.h>
#include <drm/drm_syncobj.h> #include <drm/drm_syncobj.h>
#include <drm/ttm/ttm_tt.h>
#include "amdgpu_cs.h" #include "amdgpu_cs.h"
#include "amdgpu.h" #include "amdgpu.h"
#include "amdgpu_trace.h" #include "amdgpu_trace.h"

View File

@ -23,6 +23,8 @@
#ifndef __AMDGPU_CS_H__ #ifndef __AMDGPU_CS_H__
#define __AMDGPU_CS_H__ #define __AMDGPU_CS_H__
#include <linux/ww_mutex.h>
#include "amdgpu_job.h" #include "amdgpu_job.h"
#include "amdgpu_bo_list.h" #include "amdgpu_bo_list.h"
#include "amdgpu_ring.h" #include "amdgpu_ring.h"

View File

@ -1717,7 +1717,7 @@ no_preempt:
static int amdgpu_debugfs_ib_preempt(void *data, u64 val) static int amdgpu_debugfs_ib_preempt(void *data, u64 val)
{ {
int r, resched, length; int r, length;
struct amdgpu_ring *ring; struct amdgpu_ring *ring;
struct dma_fence **fences = NULL; struct dma_fence **fences = NULL;
struct amdgpu_device *adev = (struct amdgpu_device *)data; struct amdgpu_device *adev = (struct amdgpu_device *)data;
@ -1747,8 +1747,6 @@ static int amdgpu_debugfs_ib_preempt(void *data, u64 val)
/* stop the scheduler */ /* stop the scheduler */
kthread_park(ring->sched.thread); kthread_park(ring->sched.thread);
resched = ttm_bo_lock_delayed_workqueue(&adev->mman.bdev);
/* preempt the IB */ /* preempt the IB */
r = amdgpu_ring_preempt_ib(ring); r = amdgpu_ring_preempt_ib(ring);
if (r) { if (r) {
@ -1785,8 +1783,6 @@ failure:
up_read(&adev->reset_domain->sem); up_read(&adev->reset_domain->sem);
ttm_bo_unlock_delayed_workqueue(&adev->mman.bdev, resched);
pro_end: pro_end:
kfree(fences); kfree(fences);

View File

@ -3989,10 +3989,8 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
} }
amdgpu_fence_driver_hw_fini(adev); amdgpu_fence_driver_hw_fini(adev);
if (adev->mman.initialized) { if (adev->mman.initialized)
flush_delayed_work(&adev->mman.bdev.wq); drain_workqueue(adev->mman.bdev.wq);
ttm_bo_lock_delayed_workqueue(&adev->mman.bdev);
}
if (adev->pm_sysfs_en) if (adev->pm_sysfs_en)
amdgpu_pm_sysfs_fini(adev); amdgpu_pm_sysfs_fini(adev);

View File

@ -37,6 +37,7 @@
#include "amdgpu_dma_buf.h" #include "amdgpu_dma_buf.h"
#include "amdgpu_xgmi.h" #include "amdgpu_xgmi.h"
#include <drm/amdgpu_drm.h> #include <drm/amdgpu_drm.h>
#include <drm/ttm/ttm_tt.h>
#include <linux/dma-buf.h> #include <linux/dma-buf.h>
#include <linux/dma-fence-array.h> #include <linux/dma-fence-array.h>
#include <linux/pci-p2pdma.h> #include <linux/pci-p2pdma.h>

View File

@ -30,7 +30,6 @@
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <drm/gpu_scheduler.h> #include <drm/gpu_scheduler.h>
#include <drm/drm_file.h> #include <drm/drm_file.h>
#include <drm/ttm/ttm_bo_driver.h>
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
#include "amdgpu_sync.h" #include "amdgpu_sync.h"

View File

@ -34,6 +34,7 @@
#include <drm/amdgpu_drm.h> #include <drm/amdgpu_drm.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/drm_gem_ttm_helper.h> #include <drm/drm_gem_ttm_helper.h>
#include <drm/ttm/ttm_tt.h>
#include "amdgpu.h" #include "amdgpu.h"
#include "amdgpu_display.h" #include "amdgpu_display.h"

View File

@ -35,6 +35,7 @@
#include "amdgpu_xgmi.h" #include "amdgpu_xgmi.h"
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/ttm/ttm_tt.h>
/** /**
* amdgpu_gmc_pdb0_alloc - allocate vram for pdb0 * amdgpu_gmc_pdb0_alloc - allocate vram for pdb0

View File

@ -44,10 +44,10 @@
#include <linux/module.h> #include <linux/module.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_bo_driver.h>
#include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_range_manager.h> #include <drm/ttm/ttm_range_manager.h>
#include <drm/ttm/ttm_tt.h>
#include <drm/amdgpu_drm.h> #include <drm/amdgpu_drm.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>

View File

@ -33,6 +33,7 @@
#include <drm/amdgpu_drm.h> #include <drm/amdgpu_drm.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/ttm/ttm_tt.h>
#include "amdgpu.h" #include "amdgpu.h"
#include "amdgpu_trace.h" #include "amdgpu_trace.h"
#include "amdgpu_amdkfd.h" #include "amdgpu_amdkfd.h"

View File

@ -29,7 +29,7 @@
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <drm/gpu_scheduler.h> #include <drm/gpu_scheduler.h>
#include <drm/drm_file.h> #include <drm/drm_file.h>
#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_bo.h>
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
#include "amdgpu_sync.h" #include "amdgpu_sync.h"

View File

@ -23,6 +23,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/sched/task.h> #include <linux/sched/task.h>
#include <drm/ttm/ttm_tt.h>
#include "amdgpu_sync.h" #include "amdgpu_sync.h"
#include "amdgpu_object.h" #include "amdgpu_object.h"
#include "amdgpu_vm.h" #include "amdgpu_vm.h"

View File

@ -636,7 +636,7 @@ static void ast_handle_damage(struct ast_plane *ast_plane, struct iosys_map *src
struct drm_framebuffer *fb, struct drm_framebuffer *fb,
const struct drm_rect *clip) const struct drm_rect *clip)
{ {
struct iosys_map dst = IOSYS_MAP_INIT_VADDR(ast_plane->vaddr); struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(ast_plane->vaddr);
iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip)); iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip));
drm_fb_memcpy(&dst, fb->pitches, src, fb, clip); drm_fb_memcpy(&dst, fb->pitches, src, fb, clip);

View File

@ -784,7 +784,6 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int atmel_hlcdc_dc_drm_suspend(struct device *dev) static int atmel_hlcdc_dc_drm_suspend(struct device *dev)
{ {
struct drm_device *drm_dev = dev_get_drvdata(dev); struct drm_device *drm_dev = dev_get_drvdata(dev);
@ -815,10 +814,10 @@ static int atmel_hlcdc_dc_drm_resume(struct device *dev)
return drm_atomic_helper_resume(drm_dev, dc->suspend.state); return drm_atomic_helper_resume(drm_dev, dc->suspend.state);
} }
#endif
static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, static DEFINE_SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops,
atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); atmel_hlcdc_dc_drm_suspend,
atmel_hlcdc_dc_drm_resume);
static const struct of_device_id atmel_hlcdc_dc_of_match[] = { static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
{ .compatible = "atmel,hlcdc-display-controller" }, { .compatible = "atmel,hlcdc-display-controller" },
@ -830,7 +829,7 @@ static struct platform_driver atmel_hlcdc_dc_platform_driver = {
.remove = atmel_hlcdc_dc_drm_remove, .remove = atmel_hlcdc_dc_drm_remove,
.driver = { .driver = {
.name = "atmel-hlcdc-display-controller", .name = "atmel-hlcdc-display-controller",
.pm = &atmel_hlcdc_dc_drm_pm_ops, .pm = pm_sleep_ptr(&atmel_hlcdc_dc_drm_pm_ops),
.of_match_table = atmel_hlcdc_dc_of_match, .of_match_table = atmel_hlcdc_dc_of_match,
}, },
}; };

View File

@ -1185,8 +1185,9 @@ static int adv7511_parse_dt(struct device_node *np,
return 0; return 0;
} }
static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) static int adv7511_probe(struct i2c_client *i2c)
{ {
const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
struct adv7511_link_config link_config; struct adv7511_link_config link_config;
struct adv7511 *adv7511; struct adv7511 *adv7511;
struct device *dev = &i2c->dev; struct device *dev = &i2c->dev;
@ -1392,7 +1393,7 @@ static struct i2c_driver adv7511_driver = {
.of_match_table = adv7511_of_ids, .of_match_table = adv7511_of_ids,
}, },
.id_table = adv7511_i2c_ids, .id_table = adv7511_i2c_ids,
.probe = adv7511_probe, .probe_new = adv7511_probe,
.remove = adv7511_remove, .remove = adv7511_remove,
}; };

View File

@ -692,8 +692,7 @@ static bool anx6345_get_chip_id(struct anx6345 *anx6345)
return false; return false;
} }
static int anx6345_i2c_probe(struct i2c_client *client, static int anx6345_i2c_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct anx6345 *anx6345; struct anx6345 *anx6345;
struct device *dev; struct device *dev;
@ -817,7 +816,7 @@ static struct i2c_driver anx6345_driver = {
.name = "anx6345", .name = "anx6345",
.of_match_table = of_match_ptr(anx6345_match_table), .of_match_table = of_match_ptr(anx6345_match_table),
}, },
.probe = anx6345_i2c_probe, .probe_new = anx6345_i2c_probe,
.remove = anx6345_i2c_remove, .remove = anx6345_i2c_remove,
.id_table = anx6345_id, .id_table = anx6345_id,
}; };

View File

@ -1214,8 +1214,7 @@ static const u16 anx78xx_chipid_list[] = {
0x7818, 0x7818,
}; };
static int anx78xx_i2c_probe(struct i2c_client *client, static int anx78xx_i2c_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct anx78xx *anx78xx; struct anx78xx *anx78xx;
struct anx78xx_platform_data *pdata; struct anx78xx_platform_data *pdata;
@ -1390,7 +1389,7 @@ static struct i2c_driver anx78xx_driver = {
.name = "anx7814", .name = "anx7814",
.of_match_table = of_match_ptr(anx78xx_match_table), .of_match_table = of_match_ptr(anx78xx_match_table),
}, },
.probe = anx78xx_i2c_probe, .probe_new = anx78xx_i2c_probe,
.remove = anx78xx_i2c_remove, .remove = anx78xx_i2c_remove,
.id_table = anx78xx_id, .id_table = anx78xx_id,
}; };

View File

@ -1403,7 +1403,6 @@ static void anx7625_stop_dp_work(struct anx7625_data *ctx)
{ {
ctx->hpd_status = 0; ctx->hpd_status = 0;
ctx->hpd_high_cnt = 0; ctx->hpd_high_cnt = 0;
ctx->display_timing_valid = 0;
} }
static void anx7625_start_dp_work(struct anx7625_data *ctx) static void anx7625_start_dp_work(struct anx7625_data *ctx)
@ -2562,8 +2561,7 @@ static void anx7625_runtime_disable(void *data)
pm_runtime_disable(data); pm_runtime_disable(data);
} }
static int anx7625_i2c_probe(struct i2c_client *client, static int anx7625_i2c_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct anx7625_data *platform; struct anx7625_data *platform;
struct anx7625_platform_data *pdata; struct anx7625_platform_data *pdata;
@ -2756,7 +2754,7 @@ static struct i2c_driver anx7625_driver = {
.of_match_table = anx_match_table, .of_match_table = anx_match_table,
.pm = &anx7625_pm_ops, .pm = &anx7625_pm_ops,
}, },
.probe = anx7625_i2c_probe, .probe_new = anx7625_i2c_probe,
.remove = anx7625_i2c_remove, .remove = anx7625_i2c_remove,
.id_table = anx7625_id, .id_table = anx7625_id,

View File

@ -740,8 +740,7 @@ static int chipone_dsi_probe(struct mipi_dsi_device *dsi)
return ret; return ret;
} }
static int chipone_i2c_probe(struct i2c_client *client, static int chipone_i2c_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct chipone *icn; struct chipone *icn;
@ -796,7 +795,7 @@ static struct i2c_device_id chipone_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, chipone_i2c_id); MODULE_DEVICE_TABLE(i2c, chipone_i2c_id);
static struct i2c_driver chipone_i2c_driver = { static struct i2c_driver chipone_i2c_driver = {
.probe = chipone_i2c_probe, .probe_new = chipone_i2c_probe,
.id_table = chipone_i2c_id, .id_table = chipone_i2c_id,
.driver = { .driver = {
.name = "chipone-icn6211-i2c", .name = "chipone-icn6211-i2c",

View File

@ -528,8 +528,7 @@ static const struct regmap_config ch7033_regmap_config = {
.max_register = 0x7f, .max_register = 0x7f,
}; };
static int ch7033_probe(struct i2c_client *client, static int ch7033_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct ch7033_priv *priv; struct ch7033_priv *priv;
@ -604,7 +603,7 @@ static const struct i2c_device_id ch7033_ids[] = {
MODULE_DEVICE_TABLE(i2c, ch7033_ids); MODULE_DEVICE_TABLE(i2c, ch7033_ids);
static struct i2c_driver ch7033_driver = { static struct i2c_driver ch7033_driver = {
.probe = ch7033_probe, .probe_new = ch7033_probe,
.remove = ch7033_remove, .remove = ch7033_remove,
.driver = { .driver = {
.name = "ch7033", .name = "ch7033",

View File

@ -412,7 +412,6 @@ struct it6505 {
* Mutex protects extcon and interrupt functions from interfering * Mutex protects extcon and interrupt functions from interfering
* each other. * each other.
*/ */
struct mutex irq_lock;
struct mutex extcon_lock; struct mutex extcon_lock;
struct mutex mode_lock; /* used to bridge_detect */ struct mutex mode_lock; /* used to bridge_detect */
struct mutex aux_lock; /* used to aux data transfers */ struct mutex aux_lock; /* used to aux data transfers */
@ -438,6 +437,8 @@ struct it6505 {
bool powered; bool powered;
bool hpd_state; bool hpd_state;
u32 afe_setting; u32 afe_setting;
u32 max_dpi_pixel_clock;
u32 max_lane_count;
enum hdcp_state hdcp_status; enum hdcp_state hdcp_status;
struct delayed_work hdcp_work; struct delayed_work hdcp_work;
struct work_struct hdcp_wait_ksv_list; struct work_struct hdcp_wait_ksv_list;
@ -457,6 +458,8 @@ struct it6505 {
/* it6505 driver hold option */ /* it6505 driver hold option */
bool enable_drv_hold; bool enable_drv_hold;
struct edid *cached_edid;
}; };
struct it6505_step_train_para { struct it6505_step_train_para {
@ -1463,7 +1466,8 @@ static void it6505_parse_link_capabilities(struct it6505 *it6505)
it6505->lane_count = link->num_lanes; it6505->lane_count = link->num_lanes;
DRM_DEV_DEBUG_DRIVER(dev, "Sink support %d lanes training", DRM_DEV_DEBUG_DRIVER(dev, "Sink support %d lanes training",
it6505->lane_count); it6505->lane_count);
it6505->lane_count = min_t(int, it6505->lane_count, MAX_LANE_COUNT); it6505->lane_count = min_t(int, it6505->lane_count,
it6505->max_lane_count);
it6505->branch_device = drm_dp_is_branch(it6505->dpcd); it6505->branch_device = drm_dp_is_branch(it6505->dpcd);
DRM_DEV_DEBUG_DRIVER(dev, "Sink %sbranch device", DRM_DEV_DEBUG_DRIVER(dev, "Sink %sbranch device",
@ -2244,6 +2248,12 @@ static void it6505_plugged_status_to_codec(struct it6505 *it6505)
status == connector_status_connected); status == connector_status_connected);
} }
static void it6505_remove_edid(struct it6505 *it6505)
{
kfree(it6505->cached_edid);
it6505->cached_edid = NULL;
}
static int it6505_process_hpd_irq(struct it6505 *it6505) static int it6505_process_hpd_irq(struct it6505 *it6505)
{ {
struct device *dev = &it6505->client->dev; struct device *dev = &it6505->client->dev;
@ -2270,6 +2280,7 @@ static int it6505_process_hpd_irq(struct it6505 *it6505)
it6505_reset_logic(it6505); it6505_reset_logic(it6505);
it6505_int_mask_enable(it6505); it6505_int_mask_enable(it6505);
it6505_init(it6505); it6505_init(it6505);
it6505_remove_edid(it6505);
return 0; return 0;
} }
@ -2353,6 +2364,7 @@ static void it6505_irq_hpd(struct it6505 *it6505)
it6505_video_reset(it6505); it6505_video_reset(it6505);
} else { } else {
memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
it6505_remove_edid(it6505);
if (it6505->hdcp_desired) if (it6505->hdcp_desired)
it6505_stop_hdcp(it6505); it6505_stop_hdcp(it6505);
@ -2494,10 +2506,8 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data)
}; };
int int_status[3], i; int int_status[3], i;
mutex_lock(&it6505->irq_lock); if (it6505->enable_drv_hold || pm_runtime_get_if_in_use(dev) <= 0)
return IRQ_HANDLED;
if (it6505->enable_drv_hold || !it6505->powered)
goto unlock;
int_status[0] = it6505_read(it6505, INT_STATUS_01); int_status[0] = it6505_read(it6505, INT_STATUS_01);
int_status[1] = it6505_read(it6505, INT_STATUS_02); int_status[1] = it6505_read(it6505, INT_STATUS_02);
@ -2515,16 +2525,14 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data)
if (it6505_test_bit(irq_vec[0].bit, (unsigned int *)int_status)) if (it6505_test_bit(irq_vec[0].bit, (unsigned int *)int_status))
irq_vec[0].handler(it6505); irq_vec[0].handler(it6505);
if (!it6505->hpd_state) if (it6505->hpd_state) {
goto unlock; for (i = 1; i < ARRAY_SIZE(irq_vec); i++) {
if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status))
for (i = 1; i < ARRAY_SIZE(irq_vec); i++) { irq_vec[i].handler(it6505);
if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status)) }
irq_vec[i].handler(it6505);
} }
unlock: pm_runtime_put_sync(dev);
mutex_unlock(&it6505->irq_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -2902,7 +2910,7 @@ it6505_bridge_mode_valid(struct drm_bridge *bridge,
if (mode->flags & DRM_MODE_FLAG_INTERLACE) if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE; return MODE_NO_INTERLACE;
if (mode->clock > DPI_PIXEL_CLK_MAX) if (mode->clock > it6505->max_dpi_pixel_clock)
return MODE_CLOCK_HIGH; return MODE_CLOCK_HIGH;
it6505->video_info.clock = mode->clock; it6505->video_info.clock = mode->clock;
@ -3016,16 +3024,18 @@ static struct edid *it6505_bridge_get_edid(struct drm_bridge *bridge,
{ {
struct it6505 *it6505 = bridge_to_it6505(bridge); struct it6505 *it6505 = bridge_to_it6505(bridge);
struct device *dev = &it6505->client->dev; struct device *dev = &it6505->client->dev;
struct edid *edid;
edid = drm_do_get_edid(connector, it6505_get_edid_block, it6505); if (!it6505->cached_edid) {
it6505->cached_edid = drm_do_get_edid(connector, it6505_get_edid_block,
it6505);
if (!edid) { if (!it6505->cached_edid) {
DRM_DEV_DEBUG_DRIVER(dev, "failed to get edid!"); DRM_DEV_DEBUG_DRIVER(dev, "failed to get edid!");
return NULL; return NULL;
}
} }
return edid; return drm_edid_duplicate(it6505->cached_edid);
} }
static const struct drm_bridge_funcs it6505_bridge_funcs = { static const struct drm_bridge_funcs it6505_bridge_funcs = {
@ -3089,10 +3099,32 @@ static int it6505_init_pdata(struct it6505 *it6505)
return 0; return 0;
} }
static int it6505_get_data_lanes_count(const struct device_node *endpoint,
const unsigned int min,
const unsigned int max)
{
int ret;
ret = of_property_count_u32_elems(endpoint, "data-lanes");
if (ret < 0)
return ret;
if (ret < min || ret > max)
return -EINVAL;
return ret;
}
static void it6505_parse_dt(struct it6505 *it6505) static void it6505_parse_dt(struct it6505 *it6505)
{ {
struct device *dev = &it6505->client->dev; struct device *dev = &it6505->client->dev;
struct device_node *np = dev->of_node, *ep = NULL;
int len;
u64 link_frequencies;
u32 data_lanes[4];
u32 *afe_setting = &it6505->afe_setting; u32 *afe_setting = &it6505->afe_setting;
u32 *max_lane_count = &it6505->max_lane_count;
u32 *max_dpi_pixel_clock = &it6505->max_dpi_pixel_clock;
it6505->lane_swap_disabled = it6505->lane_swap_disabled =
device_property_read_bool(dev, "no-laneswap"); device_property_read_bool(dev, "no-laneswap");
@ -3108,7 +3140,56 @@ static void it6505_parse_dt(struct it6505 *it6505)
} else { } else {
*afe_setting = 0; *afe_setting = 0;
} }
DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %d", *afe_setting);
ep = of_graph_get_endpoint_by_regs(np, 1, 0);
of_node_put(ep);
if (ep) {
len = it6505_get_data_lanes_count(ep, 1, 4);
if (len > 0 && len != 3) {
of_property_read_u32_array(ep, "data-lanes",
data_lanes, len);
*max_lane_count = len;
} else {
*max_lane_count = MAX_LANE_COUNT;
dev_err(dev, "error data-lanes, use default");
}
} else {
*max_lane_count = MAX_LANE_COUNT;
dev_err(dev, "error endpoint, use default");
}
ep = of_graph_get_endpoint_by_regs(np, 0, 0);
of_node_put(ep);
if (ep) {
len = of_property_read_variable_u64_array(ep,
"link-frequencies",
&link_frequencies, 0,
1);
if (len >= 0) {
do_div(link_frequencies, 1000);
if (link_frequencies > 297000) {
dev_err(dev,
"max pixel clock error, use default");
*max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX;
} else {
*max_dpi_pixel_clock = link_frequencies;
}
} else {
dev_err(dev, "error link frequencies, use default");
*max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX;
}
} else {
dev_err(dev, "error endpoint, use default");
*max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX;
}
DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %u, max_lane_count: %u",
it6505->afe_setting, it6505->max_lane_count);
DRM_DEV_DEBUG_DRIVER(dev, "using max_dpi_pixel_clock: %u kHz",
it6505->max_dpi_pixel_clock);
} }
static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf, static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf,
@ -3265,8 +3346,7 @@ static void it6505_shutdown(struct i2c_client *client)
it6505_lane_off(it6505); it6505_lane_off(it6505);
} }
static int it6505_i2c_probe(struct i2c_client *client, static int it6505_i2c_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct it6505 *it6505; struct it6505 *it6505;
struct device *dev = &client->dev; struct device *dev = &client->dev;
@ -3277,7 +3357,6 @@ static int it6505_i2c_probe(struct i2c_client *client,
if (!it6505) if (!it6505)
return -ENOMEM; return -ENOMEM;
mutex_init(&it6505->irq_lock);
mutex_init(&it6505->extcon_lock); mutex_init(&it6505->extcon_lock);
mutex_init(&it6505->mode_lock); mutex_init(&it6505->mode_lock);
mutex_init(&it6505->aux_lock); mutex_init(&it6505->aux_lock);
@ -3367,6 +3446,7 @@ static void it6505_i2c_remove(struct i2c_client *client)
drm_dp_aux_unregister(&it6505->aux); drm_dp_aux_unregister(&it6505->aux);
it6505_debugfs_remove(it6505); it6505_debugfs_remove(it6505);
it6505_poweroff(it6505); it6505_poweroff(it6505);
it6505_remove_edid(it6505);
} }
static const struct i2c_device_id it6505_id[] = { static const struct i2c_device_id it6505_id[] = {
@ -3387,7 +3467,7 @@ static struct i2c_driver it6505_i2c_driver = {
.of_match_table = it6505_of_match, .of_match_table = it6505_of_match,
.pm = &it6505_bridge_pm_ops, .pm = &it6505_bridge_pm_ops,
}, },
.probe = it6505_i2c_probe, .probe_new = it6505_i2c_probe,
.remove = it6505_i2c_remove, .remove = it6505_i2c_remove,
.shutdown = it6505_shutdown, .shutdown = it6505_shutdown,
.id_table = it6505_id, .id_table = it6505_id,

View File

@ -35,10 +35,6 @@
#define IT66121_DEVICE_ID0_REG 0x02 #define IT66121_DEVICE_ID0_REG 0x02
#define IT66121_DEVICE_ID1_REG 0x03 #define IT66121_DEVICE_ID1_REG 0x03
#define IT66121_VENDOR_ID0 0x54
#define IT66121_VENDOR_ID1 0x49
#define IT66121_DEVICE_ID0 0x12
#define IT66121_DEVICE_ID1 0x06
#define IT66121_REVISION_MASK GENMASK(7, 4) #define IT66121_REVISION_MASK GENMASK(7, 4)
#define IT66121_DEVICE_ID1_MASK GENMASK(3, 0) #define IT66121_DEVICE_ID1_MASK GENMASK(3, 0)
@ -72,6 +68,7 @@
#define IT66121_AFE_XP_ENO BIT(4) #define IT66121_AFE_XP_ENO BIT(4)
#define IT66121_AFE_XP_RESETB BIT(3) #define IT66121_AFE_XP_RESETB BIT(3)
#define IT66121_AFE_XP_PWDI BIT(2) #define IT66121_AFE_XP_PWDI BIT(2)
#define IT6610_AFE_XP_BYPASS BIT(0)
#define IT66121_AFE_IP_REG 0x64 #define IT66121_AFE_IP_REG 0x64
#define IT66121_AFE_IP_GAINBIT BIT(7) #define IT66121_AFE_IP_GAINBIT BIT(7)
@ -286,13 +283,18 @@
#define IT66121_AUD_SWL_16BIT 0x2 #define IT66121_AUD_SWL_16BIT 0x2
#define IT66121_AUD_SWL_NOT_INDICATED 0x0 #define IT66121_AUD_SWL_NOT_INDICATED 0x0
#define IT66121_VENDOR_ID0 0x54
#define IT66121_VENDOR_ID1 0x49
#define IT66121_DEVICE_ID0 0x12
#define IT66121_DEVICE_ID1 0x06
#define IT66121_DEVICE_MASK 0x0F
#define IT66121_AFE_CLK_HIGH 80000 /* Khz */ #define IT66121_AFE_CLK_HIGH 80000 /* Khz */
enum chip_id {
ID_IT6610,
ID_IT66121,
};
struct it66121_chip_info {
enum chip_id id;
u16 vid, pid;
};
struct it66121_ctx { struct it66121_ctx {
struct regmap *regmap; struct regmap *regmap;
struct drm_bridge bridge; struct drm_bridge bridge;
@ -301,7 +303,6 @@ struct it66121_ctx {
struct device *dev; struct device *dev;
struct gpio_desc *gpio_reset; struct gpio_desc *gpio_reset;
struct i2c_client *client; struct i2c_client *client;
struct regulator_bulk_data supplies[3];
u32 bus_width; u32 bus_width;
struct mutex lock; /* Protects fields below and device registers */ struct mutex lock; /* Protects fields below and device registers */
struct hdmi_avi_infoframe hdmi_avi_infoframe; struct hdmi_avi_infoframe hdmi_avi_infoframe;
@ -312,6 +313,7 @@ struct it66121_ctx {
u8 swl; u8 swl;
bool auto_cts; bool auto_cts;
} audio; } audio;
const struct it66121_chip_info *info;
}; };
static const struct regmap_range_cfg it66121_regmap_banks[] = { static const struct regmap_range_cfg it66121_regmap_banks[] = {
@ -342,16 +344,6 @@ static void it66121_hw_reset(struct it66121_ctx *ctx)
gpiod_set_value(ctx->gpio_reset, 0); gpiod_set_value(ctx->gpio_reset, 0);
} }
static inline int ite66121_power_on(struct it66121_ctx *ctx)
{
return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
}
static inline int ite66121_power_off(struct it66121_ctx *ctx)
{
return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
}
static inline int it66121_preamble_ddc(struct it66121_ctx *ctx) static inline int it66121_preamble_ddc(struct it66121_ctx *ctx)
{ {
return regmap_write(ctx->regmap, IT66121_MASTER_SEL_REG, IT66121_MASTER_SEL_HOST); return regmap_write(ctx->regmap, IT66121_MASTER_SEL_REG, IT66121_MASTER_SEL_HOST);
@ -406,16 +398,22 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
IT66121_AFE_IP_GAINBIT | IT66121_AFE_IP_GAINBIT |
IT66121_AFE_IP_ER0 | IT66121_AFE_IP_ER0,
IT66121_AFE_IP_EC1,
IT66121_AFE_IP_GAINBIT); IT66121_AFE_IP_GAINBIT);
if (ret) if (ret)
return ret; return ret;
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, if (ctx->info->id == ID_IT66121) {
IT66121_AFE_XP_EC1_LOWCLK, 0x80); ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
if (ret) IT66121_AFE_IP_EC1, 0);
return ret; if (ret)
return ret;
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG,
IT66121_AFE_XP_EC1_LOWCLK, 0x80);
if (ret)
return ret;
}
} else { } else {
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG,
IT66121_AFE_XP_GAINBIT | IT66121_AFE_XP_GAINBIT |
@ -426,17 +424,24 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
IT66121_AFE_IP_GAINBIT | IT66121_AFE_IP_GAINBIT |
IT66121_AFE_IP_ER0 | IT66121_AFE_IP_ER0,
IT66121_AFE_IP_EC1, IT66121_AFE_IP_ER0 | IT66121_AFE_IP_ER0);
IT66121_AFE_IP_EC1);
if (ret) if (ret)
return ret; return ret;
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, if (ctx->info->id == ID_IT66121) {
IT66121_AFE_XP_EC1_LOWCLK, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
IT66121_AFE_XP_EC1_LOWCLK); IT66121_AFE_IP_EC1,
if (ret) IT66121_AFE_IP_EC1);
return ret; if (ret)
return ret;
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG,
IT66121_AFE_XP_EC1_LOWCLK,
IT66121_AFE_XP_EC1_LOWCLK);
if (ret)
return ret;
}
} }
/* Clear reset flags */ /* Clear reset flags */
@ -445,38 +450,36 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,
if (ret) if (ret)
return ret; return ret;
if (ctx->info->id == ID_IT6610) {
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG,
IT6610_AFE_XP_BYPASS,
IT6610_AFE_XP_BYPASS);
if (ret)
return ret;
}
return it66121_fire_afe(ctx); return it66121_fire_afe(ctx);
} }
static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx) static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx)
{ {
int ret, val; int ret, val;
u32 busy = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | u32 error = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS |
IT66121_DDC_STATUS_ARBI_LOSE; IT66121_DDC_STATUS_ARBI_LOSE;
u32 done = IT66121_DDC_STATUS_TX_DONE;
ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, true, ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val,
IT66121_EDID_SLEEP_US, IT66121_EDID_TIMEOUT_US); val & (error | done), IT66121_EDID_SLEEP_US,
IT66121_EDID_TIMEOUT_US);
if (ret) if (ret)
return ret; return ret;
if (val & busy) if (val & error)
return -EAGAIN; return -EAGAIN;
return 0; return 0;
} }
static int it66121_clear_ddc_fifo(struct it66121_ctx *ctx)
{
int ret;
ret = it66121_preamble_ddc(ctx);
if (ret)
return ret;
return regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG,
IT66121_DDC_COMMAND_FIFO_CLR);
}
static int it66121_abort_ddc_ops(struct it66121_ctx *ctx) static int it66121_abort_ddc_ops(struct it66121_ctx *ctx)
{ {
int ret; int ret;
@ -516,7 +519,6 @@ static int it66121_get_edid_block(void *context, u8 *buf,
unsigned int block, size_t len) unsigned int block, size_t len)
{ {
struct it66121_ctx *ctx = context; struct it66121_ctx *ctx = context;
unsigned int val;
int remain = len; int remain = len;
int offset = 0; int offset = 0;
int ret, cnt; int ret, cnt;
@ -524,26 +526,9 @@ static int it66121_get_edid_block(void *context, u8 *buf,
offset = (block % 2) * len; offset = (block % 2) * len;
block = block / 2; block = block / 2;
ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val);
if (ret)
return ret;
if (val & IT66121_INT_STATUS1_DDC_BUSHANG) {
ret = it66121_abort_ddc_ops(ctx);
if (ret)
return ret;
}
ret = it66121_clear_ddc_fifo(ctx);
if (ret)
return ret;
while (remain > 0) { while (remain > 0) {
cnt = (remain > IT66121_EDID_FIFO_SIZE) ? cnt = (remain > IT66121_EDID_FIFO_SIZE) ?
IT66121_EDID_FIFO_SIZE : remain; IT66121_EDID_FIFO_SIZE : remain;
ret = it66121_preamble_ddc(ctx);
if (ret)
return ret;
ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG,
IT66121_DDC_COMMAND_FIFO_CLR); IT66121_DDC_COMMAND_FIFO_CLR);
@ -554,25 +539,6 @@ static int it66121_get_edid_block(void *context, u8 *buf,
if (ret) if (ret)
return ret; return ret;
ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val);
if (ret)
return ret;
if (val & IT66121_INT_STATUS1_DDC_BUSHANG) {
ret = it66121_abort_ddc_ops(ctx);
if (ret)
return ret;
}
ret = it66121_preamble_ddc(ctx);
if (ret)
return ret;
ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG,
IT66121_DDC_HEADER_EDID);
if (ret)
return ret;
ret = regmap_write(ctx->regmap, IT66121_DDC_OFFSET_REG, offset); ret = regmap_write(ctx->regmap, IT66121_DDC_OFFSET_REG, offset);
if (ret) if (ret)
return ret; return ret;
@ -593,20 +559,18 @@ static int it66121_get_edid_block(void *context, u8 *buf,
offset += cnt; offset += cnt;
remain -= cnt; remain -= cnt;
/* Per programming manual, sleep here before emptying the FIFO */
msleep(20);
ret = it66121_wait_ddc_ready(ctx); ret = it66121_wait_ddc_ready(ctx);
if (ret) {
it66121_abort_ddc_ops(ctx);
return ret;
}
ret = regmap_noinc_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG,
buf, cnt);
if (ret) if (ret)
return ret; return ret;
do { buf += cnt;
ret = regmap_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, &val);
if (ret)
return ret;
*(buf++) = val;
cnt--;
} while (cnt > 0);
} }
return 0; return 0;
@ -635,10 +599,12 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,
if (ret) if (ret)
return ret; return ret;
ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, if (ctx->info->id == ID_IT66121) {
IT66121_CLK_BANK_PWROFF_RCLK, 0); ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
if (ret) IT66121_CLK_BANK_PWROFF_RCLK, 0);
return ret; if (ret)
return ret;
}
ret = regmap_write_bits(ctx->regmap, IT66121_INT_REG, ret = regmap_write_bits(ctx->regmap, IT66121_INT_REG,
IT66121_INT_TX_CLK_OFF, 0); IT66121_INT_TX_CLK_OFF, 0);
@ -684,11 +650,7 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,
/* Per programming manual, sleep here for bridge to settle */ /* Per programming manual, sleep here for bridge to settle */
msleep(50); msleep(50);
/* Start interrupts */ return 0;
return regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG,
IT66121_INT_MASK1_DDC_NOACK |
IT66121_INT_MASK1_DDC_FIFOERR |
IT66121_INT_MASK1_DDC_BUSHANG, 0);
} }
static int it66121_set_mute(struct it66121_ctx *ctx, bool mute) static int it66121_set_mute(struct it66121_ctx *ctx, bool mute)
@ -780,29 +742,32 @@ static void it66121_bridge_disable(struct drm_bridge *bridge,
ctx->connector = NULL; ctx->connector = NULL;
} }
static int it66121_bridge_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
if (ctx->info->id == ID_IT6610) {
/* The IT6610 only supports these settings */
bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH |
DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
bridge_state->input_bus_cfg.flags &=
~DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
}
return 0;
}
static static
void it66121_bridge_mode_set(struct drm_bridge *bridge, void it66121_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode) const struct drm_display_mode *adjusted_mode)
{ {
int ret, i;
u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
const u16 aviinfo_reg[HDMI_AVI_INFOFRAME_SIZE] = { int ret;
IT66121_AVIINFO_DB1_REG,
IT66121_AVIINFO_DB2_REG,
IT66121_AVIINFO_DB3_REG,
IT66121_AVIINFO_DB4_REG,
IT66121_AVIINFO_DB5_REG,
IT66121_AVIINFO_DB6_REG,
IT66121_AVIINFO_DB7_REG,
IT66121_AVIINFO_DB8_REG,
IT66121_AVIINFO_DB9_REG,
IT66121_AVIINFO_DB10_REG,
IT66121_AVIINFO_DB11_REG,
IT66121_AVIINFO_DB12_REG,
IT66121_AVIINFO_DB13_REG
};
mutex_lock(&ctx->lock); mutex_lock(&ctx->lock);
@ -822,10 +787,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,
} }
/* Write new AVI infoframe packet */ /* Write new AVI infoframe packet */
for (i = 0; i < HDMI_AVI_INFOFRAME_SIZE; i++) { ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG,
if (regmap_write(ctx->regmap, aviinfo_reg[i], buf[i + HDMI_INFOFRAME_HEADER_SIZE])) &buf[HDMI_INFOFRAME_HEADER_SIZE],
goto unlock; HDMI_AVI_INFOFRAME_SIZE);
} if (ret)
goto unlock;
if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3])) if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3]))
goto unlock; goto unlock;
@ -838,9 +805,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,
if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI))
goto unlock; goto unlock;
if (regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, if (ctx->info->id == ID_IT66121 &&
IT66121_CLK_BANK_PWROFF_TXCLK, IT66121_CLK_BANK_PWROFF_TXCLK)) regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
IT66121_CLK_BANK_PWROFF_TXCLK,
IT66121_CLK_BANK_PWROFF_TXCLK)) {
goto unlock; goto unlock;
}
if (it66121_configure_input(ctx)) if (it66121_configure_input(ctx))
goto unlock; goto unlock;
@ -848,7 +818,11 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,
if (it66121_configure_afe(ctx, adjusted_mode)) if (it66121_configure_afe(ctx, adjusted_mode))
goto unlock; goto unlock;
regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, IT66121_CLK_BANK_PWROFF_TXCLK, 0); if (ctx->info->id == ID_IT66121 &&
regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
IT66121_CLK_BANK_PWROFF_TXCLK, 0)) {
goto unlock;
}
unlock: unlock:
mutex_unlock(&ctx->lock); mutex_unlock(&ctx->lock);
@ -906,9 +880,25 @@ static struct edid *it66121_bridge_get_edid(struct drm_bridge *bridge,
{ {
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
struct edid *edid; struct edid *edid;
int ret;
mutex_lock(&ctx->lock); mutex_lock(&ctx->lock);
ret = it66121_preamble_ddc(ctx);
if (ret) {
edid = ERR_PTR(ret);
goto out_unlock;
}
ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG,
IT66121_DDC_HEADER_EDID);
if (ret) {
edid = ERR_PTR(ret);
goto out_unlock;
}
edid = drm_do_get_edid(connector, it66121_get_edid_block, ctx); edid = drm_do_get_edid(connector, it66121_get_edid_block, ctx);
out_unlock:
mutex_unlock(&ctx->lock); mutex_unlock(&ctx->lock);
return edid; return edid;
@ -923,6 +913,7 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = {
.atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts, .atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts,
.atomic_enable = it66121_bridge_enable, .atomic_enable = it66121_bridge_enable,
.atomic_disable = it66121_bridge_disable, .atomic_disable = it66121_bridge_disable,
.atomic_check = it66121_bridge_check,
.mode_set = it66121_bridge_mode_set, .mode_set = it66121_bridge_mode_set,
.mode_valid = it66121_bridge_mode_valid, .mode_valid = it66121_bridge_mode_valid,
.detect = it66121_bridge_detect, .detect = it66121_bridge_detect,
@ -952,21 +943,14 @@ static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id)
ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val);
if (ret) { if (ret) {
dev_err(dev, "Cannot read STATUS1_REG %d\n", ret); dev_err(dev, "Cannot read STATUS1_REG %d\n", ret);
} else { } else if (val & IT66121_INT_STATUS1_HPD_STATUS) {
if (val & IT66121_INT_STATUS1_DDC_FIFOERR) regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG,
it66121_clear_ddc_fifo(ctx); IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD);
if (val & (IT66121_INT_STATUS1_DDC_BUSHANG |
IT66121_INT_STATUS1_DDC_NOACK))
it66121_abort_ddc_ops(ctx);
if (val & IT66121_INT_STATUS1_HPD_STATUS) {
regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG,
IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD);
status = it66121_is_hpd_detect(ctx) ? connector_status_connected status = it66121_is_hpd_detect(ctx) ? connector_status_connected
: connector_status_disconnected; : connector_status_disconnected;
event = true; event = true;
}
} }
regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG, regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG,
@ -1512,9 +1496,13 @@ static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev)
return PTR_ERR_OR_ZERO(ctx->audio.pdev); return PTR_ERR_OR_ZERO(ctx->audio.pdev);
} }
static int it66121_probe(struct i2c_client *client, static const char * const it66121_supplies[] = {
const struct i2c_device_id *id) "vcn33", "vcn18", "vrf12"
};
static int it66121_probe(struct i2c_client *client)
{ {
const struct i2c_device_id *id = i2c_client_get_device_id(client);
u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 }; u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 };
struct device_node *ep; struct device_node *ep;
int ret; int ret;
@ -1536,6 +1524,7 @@ static int it66121_probe(struct i2c_client *client,
ctx->dev = dev; ctx->dev = dev;
ctx->client = client; ctx->client = client;
ctx->info = (const struct it66121_chip_info *) id->driver_data;
of_property_read_u32(ep, "bus-width", &ctx->bus_width); of_property_read_u32(ep, "bus-width", &ctx->bus_width);
of_node_put(ep); of_node_put(ep);
@ -1565,26 +1554,18 @@ static int it66121_probe(struct i2c_client *client,
i2c_set_clientdata(client, ctx); i2c_set_clientdata(client, ctx);
mutex_init(&ctx->lock); mutex_init(&ctx->lock);
ctx->supplies[0].supply = "vcn33"; ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(it66121_supplies),
ctx->supplies[1].supply = "vcn18"; it66121_supplies);
ctx->supplies[2].supply = "vrf12";
ret = devm_regulator_bulk_get(ctx->dev, 3, ctx->supplies);
if (ret) { if (ret) {
dev_err(ctx->dev, "regulator_bulk failed\n"); dev_err(dev, "Failed to enable power supplies\n");
return ret; return ret;
} }
ret = ite66121_power_on(ctx);
if (ret)
return ret;
it66121_hw_reset(ctx); it66121_hw_reset(ctx);
ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config); ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config);
if (IS_ERR(ctx->regmap)) { if (IS_ERR(ctx->regmap))
ite66121_power_off(ctx);
return PTR_ERR(ctx->regmap); return PTR_ERR(ctx->regmap);
}
regmap_read(ctx->regmap, IT66121_VENDOR_ID0_REG, &vendor_ids[0]); regmap_read(ctx->regmap, IT66121_VENDOR_ID0_REG, &vendor_ids[0]);
regmap_read(ctx->regmap, IT66121_VENDOR_ID1_REG, &vendor_ids[1]); regmap_read(ctx->regmap, IT66121_VENDOR_ID1_REG, &vendor_ids[1]);
@ -1595,9 +1576,8 @@ static int it66121_probe(struct i2c_client *client,
revision_id = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]); revision_id = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]);
device_ids[1] &= IT66121_DEVICE_ID1_MASK; device_ids[1] &= IT66121_DEVICE_ID1_MASK;
if (vendor_ids[0] != IT66121_VENDOR_ID0 || vendor_ids[1] != IT66121_VENDOR_ID1 || if ((vendor_ids[1] << 8 | vendor_ids[0]) != ctx->info->vid ||
device_ids[0] != IT66121_DEVICE_ID0 || device_ids[1] != IT66121_DEVICE_ID1) { (device_ids[1] << 8 | device_ids[0]) != ctx->info->pid) {
ite66121_power_off(ctx);
return -ENODEV; return -ENODEV;
} }
@ -1610,7 +1590,6 @@ static int it66121_probe(struct i2c_client *client,
IRQF_ONESHOT, dev_name(dev), ctx); IRQF_ONESHOT, dev_name(dev), ctx);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Failed to request irq %d:%d\n", client->irq, ret); dev_err(dev, "Failed to request irq %d:%d\n", client->irq, ret);
ite66121_power_off(ctx);
return ret; return ret;
} }
@ -1627,19 +1606,32 @@ static void it66121_remove(struct i2c_client *client)
{ {
struct it66121_ctx *ctx = i2c_get_clientdata(client); struct it66121_ctx *ctx = i2c_get_clientdata(client);
ite66121_power_off(ctx);
drm_bridge_remove(&ctx->bridge); drm_bridge_remove(&ctx->bridge);
mutex_destroy(&ctx->lock); mutex_destroy(&ctx->lock);
} }
static const struct of_device_id it66121_dt_match[] = { static const struct of_device_id it66121_dt_match[] = {
{ .compatible = "ite,it66121" }, { .compatible = "ite,it66121" },
{ .compatible = "ite,it6610" },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, it66121_dt_match); MODULE_DEVICE_TABLE(of, it66121_dt_match);
static const struct it66121_chip_info it66121_chip_info = {
.id = ID_IT66121,
.vid = 0x4954,
.pid = 0x0612,
};
static const struct it66121_chip_info it6610_chip_info = {
.id = ID_IT6610,
.vid = 0xca00,
.pid = 0x0611,
};
static const struct i2c_device_id it66121_id[] = { static const struct i2c_device_id it66121_id[] = {
{ "it66121", 0 }, { "it66121", (kernel_ulong_t) &it66121_chip_info },
{ "it6610", (kernel_ulong_t) &it6610_chip_info },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, it66121_id); MODULE_DEVICE_TABLE(i2c, it66121_id);
@ -1649,7 +1641,7 @@ static struct i2c_driver it66121_driver = {
.name = "it66121", .name = "it66121",
.of_match_table = it66121_dt_match, .of_match_table = it66121_dt_match,
}, },
.probe = it66121_probe, .probe_new = it66121_probe,
.remove = it66121_remove, .remove = it66121_remove,
.id_table = it66121_id, .id_table = it66121_id,
}; };

View File

@ -517,14 +517,27 @@ static int lt8912_attach_dsi(struct lt8912 *lt)
return 0; return 0;
} }
static void lt8912_bridge_hpd_cb(void *data, enum drm_connector_status status)
{
struct lt8912 *lt = data;
if (lt->bridge.dev)
drm_helper_hpd_irq_event(lt->bridge.dev);
}
static int lt8912_bridge_connector_init(struct drm_bridge *bridge) static int lt8912_bridge_connector_init(struct drm_bridge *bridge)
{ {
int ret; int ret;
struct lt8912 *lt = bridge_to_lt8912(bridge); struct lt8912 *lt = bridge_to_lt8912(bridge);
struct drm_connector *connector = &lt->connector; struct drm_connector *connector = &lt->connector;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) {
DRM_CONNECTOR_POLL_DISCONNECT; drm_bridge_hpd_enable(lt->hdmi_port, lt8912_bridge_hpd_cb, lt);
connector->polled = DRM_CONNECTOR_POLL_HPD;
} else {
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
}
ret = drm_connector_init(bridge->dev, connector, ret = drm_connector_init(bridge->dev, connector,
&lt8912_connector_funcs, &lt8912_connector_funcs,
@ -578,6 +591,10 @@ static void lt8912_bridge_detach(struct drm_bridge *bridge)
if (lt->is_attached) { if (lt->is_attached) {
lt8912_hard_power_off(lt); lt8912_hard_power_off(lt);
if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD)
drm_bridge_hpd_disable(lt->hdmi_port);
drm_connector_unregister(&lt->connector); drm_connector_unregister(&lt->connector);
drm_connector_cleanup(&lt->connector); drm_connector_cleanup(&lt->connector);
} }
@ -685,8 +702,7 @@ static int lt8912_put_dt(struct lt8912 *lt)
return 0; return 0;
} }
static int lt8912_probe(struct i2c_client *client, static int lt8912_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
static struct lt8912 *lt; static struct lt8912 *lt;
int ret = 0; int ret = 0;
@ -758,7 +774,7 @@ static struct i2c_driver lt8912_i2c_driver = {
.name = "lt8912", .name = "lt8912",
.of_match_table = lt8912_dt_match, .of_match_table = lt8912_dt_match,
}, },
.probe = lt8912_probe, .probe_new = lt8912_probe,
.remove = lt8912_remove, .remove = lt8912_remove,
.id_table = lt8912_id, .id_table = lt8912_id,
}; };

View File

@ -720,8 +720,7 @@ static int lt9211_host_attach(struct lt9211 *ctx)
return 0; return 0;
} }
static int lt9211_probe(struct i2c_client *client, static int lt9211_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct lt9211 *ctx; struct lt9211 *ctx;
@ -786,7 +785,7 @@ static const struct of_device_id lt9211_match_table[] = {
MODULE_DEVICE_TABLE(of, lt9211_match_table); MODULE_DEVICE_TABLE(of, lt9211_match_table);
static struct i2c_driver lt9211_driver = { static struct i2c_driver lt9211_driver = {
.probe = lt9211_probe, .probe_new = lt9211_probe,
.remove = lt9211_remove, .remove = lt9211_remove,
.id_table = lt9211_id, .id_table = lt9211_id,
.driver = { .driver = {

View File

@ -259,6 +259,7 @@ static int lt9611_pll_setup(struct lt9611 *lt9611, const struct drm_display_mode
{ 0x8126, 0x55 }, { 0x8126, 0x55 },
{ 0x8127, 0x66 }, { 0x8127, 0x66 },
{ 0x8128, 0x88 }, { 0x8128, 0x88 },
{ 0x812a, 0x20 },
}; };
regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg));
@ -1108,8 +1109,7 @@ static void lt9611_audio_exit(struct lt9611 *lt9611)
} }
} }
static int lt9611_probe(struct i2c_client *client, static int lt9611_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct lt9611 *lt9611; struct lt9611 *lt9611;
struct device *dev = &client->dev; struct device *dev = &client->dev;
@ -1248,7 +1248,7 @@ static struct i2c_driver lt9611_driver = {
.name = "lt9611", .name = "lt9611",
.of_match_table = lt9611_match_table, .of_match_table = lt9611_match_table,
}, },
.probe = lt9611_probe, .probe_new = lt9611_probe,
.remove = lt9611_remove, .remove = lt9611_remove,
.id_table = lt9611_id, .id_table = lt9611_id,
}; };

View File

@ -844,8 +844,7 @@ static const struct attribute_group *lt9611uxc_attr_groups[] = {
NULL, NULL,
}; };
static int lt9611uxc_probe(struct i2c_client *client, static int lt9611uxc_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct lt9611uxc *lt9611uxc; struct lt9611uxc *lt9611uxc;
struct device *dev = &client->dev; struct device *dev = &client->dev;
@ -1012,7 +1011,7 @@ static struct i2c_driver lt9611uxc_driver = {
.of_match_table = lt9611uxc_match_table, .of_match_table = lt9611uxc_match_table,
.dev_groups = lt9611uxc_attr_groups, .dev_groups = lt9611uxc_attr_groups,
}, },
.probe = lt9611uxc_probe, .probe_new = lt9611uxc_probe,
.remove = lt9611uxc_remove, .remove = lt9611uxc_remove,
.id_table = lt9611uxc_id, .id_table = lt9611uxc_id,
}; };

View File

@ -336,8 +336,7 @@ static int ge_b850v3_register(void)
"ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr); "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr);
} }
static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c)
const struct i2c_device_id *id)
{ {
struct device *dev = &stdp4028_i2c->dev; struct device *dev = &stdp4028_i2c->dev;
int ret; int ret;
@ -376,7 +375,7 @@ MODULE_DEVICE_TABLE(of, stdp4028_ge_b850v3_fw_match);
static struct i2c_driver stdp4028_ge_b850v3_fw_driver = { static struct i2c_driver stdp4028_ge_b850v3_fw_driver = {
.id_table = stdp4028_ge_b850v3_fw_i2c_table, .id_table = stdp4028_ge_b850v3_fw_i2c_table,
.probe = stdp4028_ge_b850v3_fw_probe, .probe_new = stdp4028_ge_b850v3_fw_probe,
.remove = stdp4028_ge_b850v3_fw_remove, .remove = stdp4028_ge_b850v3_fw_remove,
.driver = { .driver = {
.name = "stdp4028-ge-b850v3-fw", .name = "stdp4028-ge-b850v3-fw",
@ -384,8 +383,7 @@ static struct i2c_driver stdp4028_ge_b850v3_fw_driver = {
}, },
}; };
static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c, static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c)
const struct i2c_device_id *id)
{ {
struct device *dev = &stdp2690_i2c->dev; struct device *dev = &stdp2690_i2c->dev;
int ret; int ret;
@ -424,7 +422,7 @@ MODULE_DEVICE_TABLE(of, stdp2690_ge_b850v3_fw_match);
static struct i2c_driver stdp2690_ge_b850v3_fw_driver = { static struct i2c_driver stdp2690_ge_b850v3_fw_driver = {
.id_table = stdp2690_ge_b850v3_fw_i2c_table, .id_table = stdp2690_ge_b850v3_fw_i2c_table,
.probe = stdp2690_ge_b850v3_fw_probe, .probe_new = stdp2690_ge_b850v3_fw_probe,
.remove = stdp2690_ge_b850v3_fw_remove, .remove = stdp2690_ge_b850v3_fw_remove,
.driver = { .driver = {
.name = "stdp2690-ge-b850v3-fw", .name = "stdp2690-ge-b850v3-fw",
@ -440,7 +438,11 @@ static int __init stdpxxxx_ge_b850v3_init(void)
if (ret) if (ret)
return ret; return ret;
return i2c_add_driver(&stdp2690_ge_b850v3_fw_driver); ret = i2c_add_driver(&stdp2690_ge_b850v3_fw_driver);
if (ret)
i2c_del_driver(&stdp4028_ge_b850v3_fw_driver);
return ret;
} }
module_init(stdpxxxx_ge_b850v3_init); module_init(stdpxxxx_ge_b850v3_init);

View File

@ -257,8 +257,7 @@ static const struct drm_bridge_funcs ptn3460_bridge_funcs = {
.get_edid = ptn3460_get_edid, .get_edid = ptn3460_get_edid,
}; };
static int ptn3460_probe(struct i2c_client *client, static int ptn3460_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct ptn3460_bridge *ptn_bridge; struct ptn3460_bridge *ptn_bridge;
@ -336,7 +335,7 @@ MODULE_DEVICE_TABLE(of, ptn3460_match);
static struct i2c_driver ptn3460_driver = { static struct i2c_driver ptn3460_driver = {
.id_table = ptn3460_i2c_table, .id_table = ptn3460_i2c_table,
.probe = ptn3460_probe, .probe_new = ptn3460_probe,
.remove = ptn3460_remove, .remove = ptn3460_remove,
.driver = { .driver = {
.name = "nxp,ptn3460", .name = "nxp,ptn3460",

View File

@ -364,6 +364,8 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
devres_free(ptr); devres_free(ptr);
} }
bridge->pre_enable_prev_first = panel->prepare_prev_first;
return bridge; return bridge;
} }
EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed); EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed);
@ -402,6 +404,8 @@ struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm,
if (ret) if (ret)
return ERR_PTR(ret); return ERR_PTR(ret);
bridge->pre_enable_prev_first = panel->prepare_prev_first;
return bridge; return bridge;
} }
EXPORT_SYMBOL(drmm_panel_bridge_add); EXPORT_SYMBOL(drmm_panel_bridge_add);

View File

@ -442,9 +442,9 @@ static const struct of_device_id ps8622_devices[] = {
}; };
MODULE_DEVICE_TABLE(of, ps8622_devices); MODULE_DEVICE_TABLE(of, ps8622_devices);
static int ps8622_probe(struct i2c_client *client, static int ps8622_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct ps8622_bridge *ps8622; struct ps8622_bridge *ps8622;
struct drm_bridge *panel_bridge; struct drm_bridge *panel_bridge;
@ -538,7 +538,7 @@ MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table);
static struct i2c_driver ps8622_driver = { static struct i2c_driver ps8622_driver = {
.id_table = ps8622_i2c_table, .id_table = ps8622_i2c_table,
.probe = ps8622_probe, .probe_new = ps8622_probe,
.remove = ps8622_remove, .remove = ps8622_remove,
.driver = { .driver = {
.name = "ps8622", .name = "ps8622",

View File

@ -15,6 +15,7 @@
#include <drm/display/drm_dp_aux_bus.h> #include <drm/display/drm_dp_aux_bus.h>
#include <drm/display/drm_dp_helper.h> #include <drm/display/drm_dp_helper.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h> #include <drm/drm_bridge.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_mipi_dsi.h> #include <drm/drm_mipi_dsi.h>
@ -442,7 +443,8 @@ static const struct dev_pm_ops ps8640_pm_ops = {
pm_runtime_force_resume) pm_runtime_force_resume)
}; };
static void ps8640_pre_enable(struct drm_bridge *bridge) static void ps8640_atomic_pre_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ {
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL]; struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL];
@ -476,7 +478,8 @@ static void ps8640_pre_enable(struct drm_bridge *bridge)
ps_bridge->pre_enabled = true; ps_bridge->pre_enabled = true;
} }
static void ps8640_post_disable(struct drm_bridge *bridge) static void ps8640_atomic_post_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{ {
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
@ -554,7 +557,7 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,
* EDID, for this chip, we need to do a full poweron, otherwise it will * EDID, for this chip, we need to do a full poweron, otherwise it will
* fail. * fail.
*/ */
drm_bridge_chain_pre_enable(bridge); drm_atomic_bridge_chain_pre_enable(bridge, connector->state->state);
edid = drm_get_edid(connector, edid = drm_get_edid(connector,
ps_bridge->page[PAGE0_DP_CNTL]->adapter); ps_bridge->page[PAGE0_DP_CNTL]->adapter);
@ -564,7 +567,7 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,
* before, return the chip to its original power state. * before, return the chip to its original power state.
*/ */
if (poweroff) if (poweroff)
drm_bridge_chain_post_disable(bridge); drm_atomic_bridge_chain_post_disable(bridge, connector->state->state);
return edid; return edid;
} }
@ -579,8 +582,11 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = {
.attach = ps8640_bridge_attach, .attach = ps8640_bridge_attach,
.detach = ps8640_bridge_detach, .detach = ps8640_bridge_detach,
.get_edid = ps8640_bridge_get_edid, .get_edid = ps8640_bridge_get_edid,
.post_disable = ps8640_post_disable, .atomic_post_disable = ps8640_atomic_post_disable,
.pre_enable = ps8640_pre_enable, .atomic_pre_enable = ps8640_atomic_pre_enable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
}; };
static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge) static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge)
@ -734,13 +740,13 @@ static int ps8640_probe(struct i2c_client *client)
pm_runtime_enable(dev); pm_runtime_enable(dev);
/* /*
* Powering on ps8640 takes ~300ms. To avoid wasting time on power * Powering on ps8640 takes ~300ms. To avoid wasting time on power
* cycling ps8640 too often, set autosuspend_delay to 1000ms to ensure * cycling ps8640 too often, set autosuspend_delay to 2000ms to ensure
* the bridge wouldn't suspend in between each _aux_transfer_msg() call * the bridge wouldn't suspend in between each _aux_transfer_msg() call
* during EDID read (~20ms in my experiment) and in between the last * during EDID read (~20ms in my experiment) and in between the last
* _aux_transfer_msg() call during EDID read and the _pre_enable() call * _aux_transfer_msg() call during EDID read and the _pre_enable() call
* (~100ms in my experiment). * (~100ms in my experiment).
*/ */
pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_set_autosuspend_delay(dev, 2000);
pm_runtime_use_autosuspend(dev); pm_runtime_use_autosuspend(dev);
pm_suspend_ignore_children(dev, true); pm_suspend_ignore_children(dev, true);
ret = devm_add_action_or_reset(dev, ps8640_runtime_disable, dev); ret = devm_add_action_or_reset(dev, ps8640_runtime_disable, dev);

View File

@ -171,7 +171,6 @@ struct sii902x {
struct drm_connector connector; struct drm_connector connector;
struct gpio_desc *reset_gpio; struct gpio_desc *reset_gpio;
struct i2c_mux_core *i2cmux; struct i2c_mux_core *i2cmux;
struct regulator_bulk_data supplies[2];
bool sink_is_hdmi; bool sink_is_hdmi;
/* /*
* Mutex protects audio and video functions from interfering * Mutex protects audio and video functions from interfering
@ -1066,12 +1065,12 @@ static int sii902x_init(struct sii902x *sii902x)
return i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0); return i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0);
} }
static int sii902x_probe(struct i2c_client *client, static int sii902x_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct device_node *endpoint; struct device_node *endpoint;
struct sii902x *sii902x; struct sii902x *sii902x;
static const char * const supplies[] = {"iovcc", "cvcc12"};
int ret; int ret;
ret = i2c_check_functionality(client->adapter, ret = i2c_check_functionality(client->adapter,
@ -1122,27 +1121,11 @@ static int sii902x_probe(struct i2c_client *client,
mutex_init(&sii902x->mutex); mutex_init(&sii902x->mutex);
sii902x->supplies[0].supply = "iovcc"; ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supplies), supplies);
sii902x->supplies[1].supply = "cvcc12";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sii902x->supplies),
sii902x->supplies);
if (ret < 0) if (ret < 0)
return ret; return dev_err_probe(dev, ret, "Failed to enable supplies");
ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies), return sii902x_init(sii902x);
sii902x->supplies);
if (ret < 0) {
dev_err_probe(dev, ret, "Failed to enable supplies");
return ret;
}
ret = sii902x_init(sii902x);
if (ret < 0) {
regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies),
sii902x->supplies);
}
return ret;
} }
static void sii902x_remove(struct i2c_client *client) static void sii902x_remove(struct i2c_client *client)
@ -1152,8 +1135,6 @@ static void sii902x_remove(struct i2c_client *client)
i2c_mux_del_adapters(sii902x->i2cmux); i2c_mux_del_adapters(sii902x->i2cmux);
drm_bridge_remove(&sii902x->bridge); drm_bridge_remove(&sii902x->bridge);
regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies),
sii902x->supplies);
} }
static const struct of_device_id sii902x_dt_ids[] = { static const struct of_device_id sii902x_dt_ids[] = {
@ -1169,7 +1150,7 @@ static const struct i2c_device_id sii902x_i2c_ids[] = {
MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids); MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids);
static struct i2c_driver sii902x_driver = { static struct i2c_driver sii902x_driver = {
.probe = sii902x_probe, .probe_new = sii902x_probe,
.remove = sii902x_remove, .remove = sii902x_remove,
.driver = { .driver = {
.name = "sii902x", .name = "sii902x",

View File

@ -886,8 +886,7 @@ static const struct drm_bridge_funcs sii9234_bridge_funcs = {
.mode_valid = sii9234_mode_valid, .mode_valid = sii9234_mode_valid,
}; };
static int sii9234_probe(struct i2c_client *client, static int sii9234_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct i2c_adapter *adapter = client->adapter; struct i2c_adapter *adapter = client->adapter;
struct sii9234 *ctx; struct sii9234 *ctx;
@ -961,7 +960,7 @@ static struct i2c_driver sii9234_driver = {
.name = "sii9234", .name = "sii9234",
.of_match_table = sii9234_dt_match, .of_match_table = sii9234_dt_match,
}, },
.probe = sii9234_probe, .probe_new = sii9234_probe,
.remove = sii9234_remove, .remove = sii9234_remove,
.id_table = sii9234_id, .id_table = sii9234_id,
}; };

View File

@ -2284,8 +2284,7 @@ static const struct drm_bridge_funcs sii8620_bridge_funcs = {
.mode_valid = sii8620_mode_valid, .mode_valid = sii8620_mode_valid,
}; };
static int sii8620_probe(struct i2c_client *client, static int sii8620_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct sii8620 *ctx; struct sii8620 *ctx;
@ -2379,7 +2378,7 @@ static struct i2c_driver sii8620_driver = {
.name = "sii8620", .name = "sii8620",
.of_match_table = of_match_ptr(sii8620_dt_match), .of_match_table = of_match_ptr(sii8620_dt_match),
}, },
.probe = sii8620_probe, .probe_new = sii8620_probe,
.remove = sii8620_remove, .remove = sii8620_remove,
.id_table = sii8620_id, .id_table = sii8620_id,
}; };

View File

@ -2029,7 +2029,7 @@ static void tc_clk_disable(void *data)
clk_disable_unprepare(refclk); clk_disable_unprepare(refclk);
} }
static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) static int tc_probe(struct i2c_client *client)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct tc_data *tc; struct tc_data *tc;
@ -2209,7 +2209,7 @@ static struct i2c_driver tc358767_driver = {
.of_match_table = tc358767_of_ids, .of_match_table = tc358767_of_ids,
}, },
.id_table = tc358767_i2c_ids, .id_table = tc358767_i2c_ids,
.probe = tc_probe, .probe_new = tc_probe,
.remove = tc_remove, .remove = tc_remove,
}; };
module_i2c_driver(tc358767_driver); module_i2c_driver(tc358767_driver);

View File

@ -1018,8 +1018,7 @@ static int tc358768_get_regulators(struct tc358768_priv *priv)
return ret; return ret;
} }
static int tc358768_i2c_probe(struct i2c_client *client, static int tc358768_i2c_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct tc358768_priv *priv; struct tc358768_priv *priv;
struct device *dev = &client->dev; struct device *dev = &client->dev;
@ -1085,7 +1084,7 @@ static struct i2c_driver tc358768_driver = {
.of_match_table = tc358768_of_ids, .of_match_table = tc358768_of_ids,
}, },
.id_table = tc358768_i2c_ids, .id_table = tc358768_i2c_ids,
.probe = tc358768_i2c_probe, .probe_new = tc358768_i2c_probe,
.remove = tc358768_i2c_remove, .remove = tc358768_i2c_remove,
}; };
module_i2c_driver(tc358768_driver); module_i2c_driver(tc358768_driver);

View File

@ -637,7 +637,7 @@ static int tc_attach_host(struct tc_data *tc)
return 0; return 0;
} }
static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) static int tc_probe(struct i2c_client *client)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct tc_data *tc; struct tc_data *tc;
@ -729,7 +729,7 @@ static struct i2c_driver tc358775_driver = {
.of_match_table = tc358775_of_ids, .of_match_table = tc358775_of_ids,
}, },
.id_table = tc358775_i2c_ids, .id_table = tc358775_i2c_ids,
.probe = tc_probe, .probe_new = tc_probe,
.remove = tc_remove, .remove = tc_remove,
}; };
module_i2c_driver(tc358775_driver); module_i2c_driver(tc358775_driver);

View File

@ -346,7 +346,7 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
/* Deassert reset */ /* Deassert reset */
gpiod_set_value_cansleep(ctx->enable_gpio, 1); gpiod_set_value_cansleep(ctx->enable_gpio, 1);
usleep_range(1000, 1100); usleep_range(10000, 11000);
/* Get the LVDS format from the bridge state. */ /* Get the LVDS format from the bridge state. */
bridge_state = drm_atomic_get_new_bridge_state(state, bridge); bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
@ -653,9 +653,9 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx)
return 0; return 0;
} }
static int sn65dsi83_probe(struct i2c_client *client, static int sn65dsi83_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct device *dev = &client->dev; struct device *dev = &client->dev;
enum sn65dsi83_model model; enum sn65dsi83_model model;
struct sn65dsi83 *ctx; struct sn65dsi83 *ctx;
@ -730,7 +730,7 @@ static const struct of_device_id sn65dsi83_match_table[] = {
MODULE_DEVICE_TABLE(of, sn65dsi83_match_table); MODULE_DEVICE_TABLE(of, sn65dsi83_match_table);
static struct i2c_driver sn65dsi83_driver = { static struct i2c_driver sn65dsi83_driver = {
.probe = sn65dsi83_probe, .probe_new = sn65dsi83_probe,
.remove = sn65dsi83_remove, .remove = sn65dsi83_remove,
.id_table = sn65dsi83_id, .id_table = sn65dsi83_id,
.driver = { .driver = {

View File

@ -1852,8 +1852,7 @@ static int ti_sn65dsi86_parse_regulators(struct ti_sn65dsi86 *pdata)
pdata->supplies); pdata->supplies);
} }
static int ti_sn65dsi86_probe(struct i2c_client *client, static int ti_sn65dsi86_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct ti_sn65dsi86 *pdata; struct ti_sn65dsi86 *pdata;
@ -1952,7 +1951,7 @@ static struct i2c_driver ti_sn65dsi86_driver = {
.of_match_table = ti_sn65dsi86_match_table, .of_match_table = ti_sn65dsi86_match_table,
.pm = &ti_sn65dsi86_pm_ops, .pm = &ti_sn65dsi86_pm_ops,
}, },
.probe = ti_sn65dsi86_probe, .probe_new = ti_sn65dsi86_probe,
.id_table = ti_sn65dsi86_id, .id_table = ti_sn65dsi86_id,
}; };

View File

@ -379,8 +379,7 @@ static struct platform_driver tfp410_platform_driver = {
#if IS_ENABLED(CONFIG_I2C) #if IS_ENABLED(CONFIG_I2C)
/* There is currently no i2c functionality. */ /* There is currently no i2c functionality. */
static int tfp410_i2c_probe(struct i2c_client *client, static int tfp410_i2c_probe(struct i2c_client *client)
const struct i2c_device_id *id)
{ {
int reg; int reg;
@ -411,7 +410,7 @@ static struct i2c_driver tfp410_i2c_driver = {
.of_match_table = of_match_ptr(tfp410_match), .of_match_table = of_match_ptr(tfp410_match),
}, },
.id_table = tfp410_i2c_ids, .id_table = tfp410_i2c_ids,
.probe = tfp410_i2c_probe, .probe_new = tfp410_i2c_probe,
.remove = tfp410_i2c_remove, .remove = tfp410_i2c_remove,
}; };
#endif /* IS_ENABLED(CONFIG_I2C) */ #endif /* IS_ENABLED(CONFIG_I2C) */

View File

@ -3309,8 +3309,13 @@ int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr,
int ret; int ret;
port = drm_dp_mst_topology_get_port_validated(mgr, payload->port); port = drm_dp_mst_topology_get_port_validated(mgr, payload->port);
if (!port) if (!port) {
drm_dbg_kms(mgr->dev,
"VCPI %d for port %p not in topology, not creating a payload\n",
payload->vcpi, payload->port);
payload->vc_start_slot = -1;
return 0; return 0;
}
if (mgr->payload_count == 0) if (mgr->payload_count == 0)
mgr->next_start_slot = mst_state->start_slot; mgr->next_start_slot = mst_state->start_slot;
@ -3641,6 +3646,9 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
ret = 0; ret = 0;
mgr->payload_id_table_cleared = false; mgr->payload_id_table_cleared = false;
memset(&mgr->down_rep_recv, 0, sizeof(mgr->down_rep_recv));
memset(&mgr->up_req_recv, 0, sizeof(mgr->up_req_recv));
} }
out_unlock: out_unlock:
@ -3853,7 +3861,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
struct drm_dp_sideband_msg_rx *msg = &mgr->down_rep_recv; struct drm_dp_sideband_msg_rx *msg = &mgr->down_rep_recv;
if (!drm_dp_get_one_sb_msg(mgr, false, &mstb)) if (!drm_dp_get_one_sb_msg(mgr, false, &mstb))
goto out; goto out_clear_reply;
/* Multi-packet message transmission, don't clear the reply */ /* Multi-packet message transmission, don't clear the reply */
if (!msg->have_eomt) if (!msg->have_eomt)

View File

@ -880,7 +880,7 @@ EXPORT_SYMBOL(drm_atomic_get_private_obj_state);
* or NULL if the private_obj is not part of the global atomic state. * or NULL if the private_obj is not part of the global atomic state.
*/ */
struct drm_private_state * struct drm_private_state *
drm_atomic_get_old_private_obj_state(struct drm_atomic_state *state, drm_atomic_get_old_private_obj_state(const struct drm_atomic_state *state,
struct drm_private_obj *obj) struct drm_private_obj *obj)
{ {
int i; int i;
@ -902,7 +902,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_private_obj_state);
* or NULL if the private_obj is not part of the global atomic state. * or NULL if the private_obj is not part of the global atomic state.
*/ */
struct drm_private_state * struct drm_private_state *
drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state, drm_atomic_get_new_private_obj_state(const struct drm_atomic_state *state,
struct drm_private_obj *obj) struct drm_private_obj *obj)
{ {
int i; int i;
@ -934,7 +934,7 @@ EXPORT_SYMBOL(drm_atomic_get_new_private_obj_state);
* not connected. * not connected.
*/ */
struct drm_connector * struct drm_connector *
drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state, drm_atomic_get_old_connector_for_encoder(const struct drm_atomic_state *state,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
struct drm_connector_state *conn_state; struct drm_connector_state *conn_state;
@ -968,7 +968,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_connector_for_encoder);
* not connected. * not connected.
*/ */
struct drm_connector * struct drm_connector *
drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state, drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_state *state,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
struct drm_connector_state *conn_state; struct drm_connector_state *conn_state;
@ -1117,7 +1117,7 @@ EXPORT_SYMBOL(drm_atomic_get_bridge_state);
* the bridge is not part of the global atomic state. * the bridge is not part of the global atomic state.
*/ */
struct drm_bridge_state * struct drm_bridge_state *
drm_atomic_get_old_bridge_state(struct drm_atomic_state *state, drm_atomic_get_old_bridge_state(const struct drm_atomic_state *state,
struct drm_bridge *bridge) struct drm_bridge *bridge)
{ {
struct drm_private_state *obj_state; struct drm_private_state *obj_state;
@ -1139,7 +1139,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_bridge_state);
* the bridge is not part of the global atomic state. * the bridge is not part of the global atomic state.
*/ */
struct drm_bridge_state * struct drm_bridge_state *
drm_atomic_get_new_bridge_state(struct drm_atomic_state *state, drm_atomic_get_new_bridge_state(const struct drm_atomic_state *state,
struct drm_bridge *bridge) struct drm_bridge *bridge)
{ {
struct drm_private_state *obj_state; struct drm_private_state *obj_state;
@ -1756,8 +1756,8 @@ EXPORT_SYMBOL(drm_state_dump);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static int drm_state_info(struct seq_file *m, void *data) static int drm_state_info(struct seq_file *m, void *data)
{ {
struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_debugfs_entry *entry = m->private;
struct drm_device *dev = node->minor->dev; struct drm_device *dev = entry->dev;
struct drm_printer p = drm_seq_file_printer(m); struct drm_printer p = drm_seq_file_printer(m);
__drm_state_dump(dev, &p, true); __drm_state_dump(dev, &p, true);
@ -1766,14 +1766,13 @@ static int drm_state_info(struct seq_file *m, void *data)
} }
/* any use in debugfs files to dump individual planes/crtc/etc? */ /* any use in debugfs files to dump individual planes/crtc/etc? */
static const struct drm_info_list drm_atomic_debugfs_list[] = { static const struct drm_debugfs_info drm_atomic_debugfs_list[] = {
{"state", drm_state_info, 0}, {"state", drm_state_info, 0},
}; };
void drm_atomic_debugfs_init(struct drm_minor *minor) void drm_atomic_debugfs_init(struct drm_minor *minor)
{ {
drm_debugfs_create_files(drm_atomic_debugfs_list, drm_debugfs_add_files(minor->dev, drm_atomic_debugfs_list,
ARRAY_SIZE(drm_atomic_debugfs_list), ARRAY_SIZE(drm_atomic_debugfs_list));
minor->debugfs_root, minor);
} }
#endif #endif

View File

@ -481,6 +481,130 @@ void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector *connecto
} }
EXPORT_SYMBOL(drm_atomic_helper_connector_tv_margins_reset); EXPORT_SYMBOL(drm_atomic_helper_connector_tv_margins_reset);
/**
* drm_atomic_helper_connector_tv_reset - Resets Analog TV connector properties
* @connector: DRM connector
*
* Resets the analog TV properties attached to a connector
*/
void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_cmdline_mode *cmdline = &connector->cmdline_mode;
struct drm_connector_state *state = connector->state;
struct drm_property *prop;
uint64_t val;
prop = dev->mode_config.tv_mode_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.mode = val;
if (cmdline->tv_mode_specified)
state->tv.mode = cmdline->tv_mode;
prop = dev->mode_config.tv_select_subconnector_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.select_subconnector = val;
prop = dev->mode_config.tv_subconnector_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.subconnector = val;
prop = dev->mode_config.tv_brightness_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.brightness = val;
prop = dev->mode_config.tv_contrast_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.contrast = val;
prop = dev->mode_config.tv_flicker_reduction_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.flicker_reduction = val;
prop = dev->mode_config.tv_overscan_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.overscan = val;
prop = dev->mode_config.tv_saturation_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.saturation = val;
prop = dev->mode_config.tv_hue_property;
if (prop)
if (!drm_object_property_get_default_value(&connector->base,
prop, &val))
state->tv.hue = val;
drm_atomic_helper_connector_tv_margins_reset(connector);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset);
/**
* @drm_atomic_helper_connector_tv_check: Validate an analog TV connector state
* @connector: DRM Connector
* @state: the DRM State object
*
* Checks the state object to see if the requested state is valid for an
* analog TV connector.
*
* Returns:
* Zero for success, a negative error code on error.
*/
int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
struct drm_atomic_state *state)
{
struct drm_connector_state *old_conn_state =
drm_atomic_get_old_connector_state(state, connector);
struct drm_connector_state *new_conn_state =
drm_atomic_get_new_connector_state(state, connector);
struct drm_crtc_state *crtc_state;
struct drm_crtc *crtc;
crtc = new_conn_state->crtc;
if (!crtc)
return 0;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (!crtc_state)
return -EINVAL;
if (old_conn_state->tv.mode != new_conn_state->tv.mode)
crtc_state->mode_changed = true;
if (old_conn_state->tv.margins.left != new_conn_state->tv.margins.left ||
old_conn_state->tv.margins.right != new_conn_state->tv.margins.right ||
old_conn_state->tv.margins.top != new_conn_state->tv.margins.top ||
old_conn_state->tv.margins.bottom != new_conn_state->tv.margins.bottom ||
old_conn_state->tv.mode != new_conn_state->tv.mode ||
old_conn_state->tv.brightness != new_conn_state->tv.brightness ||
old_conn_state->tv.contrast != new_conn_state->tv.contrast ||
old_conn_state->tv.flicker_reduction != new_conn_state->tv.flicker_reduction ||
old_conn_state->tv.overscan != new_conn_state->tv.overscan ||
old_conn_state->tv.saturation != new_conn_state->tv.saturation ||
old_conn_state->tv.hue != new_conn_state->tv.hue)
crtc_state->connectors_changed = true;
return 0;
}
EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check);
/** /**
* __drm_atomic_helper_connector_duplicate_state - copy atomic connector state * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
* @connector: connector object * @connector: connector object

View File

@ -698,6 +698,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
state->tv.margins.top = val; state->tv.margins.top = val;
} else if (property == config->tv_bottom_margin_property) { } else if (property == config->tv_bottom_margin_property) {
state->tv.margins.bottom = val; state->tv.margins.bottom = val;
} else if (property == config->legacy_tv_mode_property) {
state->tv.legacy_mode = val;
} else if (property == config->tv_mode_property) { } else if (property == config->tv_mode_property) {
state->tv.mode = val; state->tv.mode = val;
} else if (property == config->tv_brightness_property) { } else if (property == config->tv_brightness_property) {
@ -808,6 +810,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
*val = state->tv.margins.top; *val = state->tv.margins.top;
} else if (property == config->tv_bottom_margin_property) { } else if (property == config->tv_bottom_margin_property) {
*val = state->tv.margins.bottom; *val = state->tv.margins.bottom;
} else if (property == config->legacy_tv_mode_property) {
*val = state->tv.legacy_mode;
} else if (property == config->tv_mode_property) { } else if (property == config->tv_mode_property) {
*val = state->tv.mode; *val = state->tv.mode;
} else if (property == config->tv_brightness_property) { } else if (property == config->tv_brightness_property) {

View File

@ -153,6 +153,45 @@
* situation when probing. * situation when probing.
*/ */
/**
* DOC: dsi bridge operations
*
* DSI host interfaces are expected to be implemented as bridges rather than
* encoders, however there are a few aspects of their operation that need to
* be defined in order to provide a consistent interface.
*
* A DSI host should keep the PHY powered down until the pre_enable operation is
* called. All lanes are in an undefined idle state up to this point, and it
* must not be assumed that it is LP-11.
* pre_enable should initialise the PHY, set the data lanes to LP-11, and the
* clock lane to either LP-11 or HS depending on the mode_flag
* %MIPI_DSI_CLOCK_NON_CONTINUOUS.
*
* Ordinarily the downstream bridge DSI peripheral pre_enable will have been
* called before the DSI host. If the DSI peripheral requires LP-11 and/or
* the clock lane to be in HS mode prior to pre_enable, then it can set the
* &pre_enable_prev_first flag to request the pre_enable (and
* post_disable) order to be altered to enable the DSI host first.
*
* Either the CRTC being enabled, or the DSI host enable operation should switch
* the host to actively transmitting video on the data lanes.
*
* The reverse also applies. The DSI host disable operation or stopping the CRTC
* should stop transmitting video, and the data lanes should return to the LP-11
* state. The DSI host &post_disable operation should disable the PHY.
* If the &pre_enable_prev_first flag is set, then the DSI peripheral's
* bridge &post_disable will be called before the DSI host's post_disable.
*
* Whilst it is valid to call &host_transfer prior to pre_enable or after
* post_disable, the exact state of the lanes is undefined at this point. The
* DSI host should initialise the interface, transmit the data, and then disable
* the interface again.
*
* Ultra Low Power State (ULPS) is not explicitly supported by DRM. If
* implemented, it therefore needs to be handled entirely within the DSI Host
* driver.
*/
static DEFINE_MUTEX(bridge_lock); static DEFINE_MUTEX(bridge_lock);
static LIST_HEAD(bridge_list); static LIST_HEAD(bridge_list);
@ -509,61 +548,6 @@ drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
} }
EXPORT_SYMBOL(drm_bridge_chain_mode_valid); EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
/**
* drm_bridge_chain_disable - disables all bridges in the encoder chain
* @bridge: bridge control structure
*
* Calls &drm_bridge_funcs.disable op for all the bridges in the encoder
* chain, starting from the last bridge to the first. These are called before
* calling the encoder's prepare op.
*
* Note: the bridge passed should be the one closest to the encoder
*/
void drm_bridge_chain_disable(struct drm_bridge *bridge)
{
struct drm_encoder *encoder;
struct drm_bridge *iter;
if (!bridge)
return;
encoder = bridge->encoder;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->disable)
iter->funcs->disable(iter);
if (iter == bridge)
break;
}
}
EXPORT_SYMBOL(drm_bridge_chain_disable);
/**
* drm_bridge_chain_post_disable - cleans up after disabling all bridges in the
* encoder chain
* @bridge: bridge control structure
*
* Calls &drm_bridge_funcs.post_disable op for all the bridges in the
* encoder chain, starting from the first bridge to the last. These are called
* after completing the encoder's prepare op.
*
* Note: the bridge passed should be the one closest to the encoder
*/
void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
{
struct drm_encoder *encoder;
if (!bridge)
return;
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);
}
}
EXPORT_SYMBOL(drm_bridge_chain_post_disable);
/** /**
* drm_bridge_chain_mode_set - set proposed mode for all bridges in the * drm_bridge_chain_mode_set - set proposed mode for all bridges in the
* encoder chain * encoder chain
@ -593,61 +577,6 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
} }
EXPORT_SYMBOL(drm_bridge_chain_mode_set); EXPORT_SYMBOL(drm_bridge_chain_mode_set);
/**
* drm_bridge_chain_pre_enable - prepares for enabling all bridges in the
* encoder chain
* @bridge: bridge control structure
*
* Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder
* chain, starting from the last bridge to the first. These are called
* before calling the encoder's commit op.
*
* Note: the bridge passed should be the one closest to the encoder
*/
void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
{
struct drm_encoder *encoder;
struct drm_bridge *iter;
if (!bridge)
return;
encoder = bridge->encoder;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);
if (iter == bridge)
break;
}
}
EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
/**
* drm_bridge_chain_enable - enables all bridges in the encoder chain
* @bridge: bridge control structure
*
* Calls &drm_bridge_funcs.enable op for all the bridges in the encoder
* chain, starting from the first bridge to the last. These are called
* after completing the encoder's commit op.
*
* Note that the bridge passed should be the one closest to the encoder
*/
void drm_bridge_chain_enable(struct drm_bridge *bridge)
{
struct drm_encoder *encoder;
if (!bridge)
return;
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->enable)
bridge->funcs->enable(bridge);
}
}
EXPORT_SYMBOL(drm_bridge_chain_enable);
/** /**
* drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
* @bridge: bridge control structure * @bridge: bridge control structure
@ -691,6 +620,25 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
} }
EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *old_state)
{
if (old_state && bridge->funcs->atomic_post_disable) {
struct drm_bridge_state *old_bridge_state;
old_bridge_state =
drm_atomic_get_old_bridge_state(old_state,
bridge);
if (WARN_ON(!old_bridge_state))
return;
bridge->funcs->atomic_post_disable(bridge,
old_bridge_state);
} else if (bridge->funcs->post_disable) {
bridge->funcs->post_disable(bridge);
}
}
/** /**
* drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
* in the encoder chain * in the encoder chain
@ -702,36 +650,86 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
* starting from the first bridge to the last. These are called after completing * starting from the first bridge to the last. These are called after completing
* &drm_encoder_helper_funcs.atomic_disable * &drm_encoder_helper_funcs.atomic_disable
* *
* If a bridge sets @pre_enable_prev_first, then the @post_disable for that
* bridge will be called before the previous one to reverse the @pre_enable
* calling direction.
*
* Note: the bridge passed should be the one closest to the encoder * Note: the bridge passed should be the one closest to the encoder
*/ */
void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *old_state) struct drm_atomic_state *old_state)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_bridge *next, *limit;
if (!bridge) if (!bridge)
return; return;
encoder = bridge->encoder; encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->atomic_post_disable) { limit = NULL;
struct drm_bridge_state *old_bridge_state;
old_bridge_state = if (!list_is_last(&bridge->chain_node, &encoder->bridge_chain)) {
drm_atomic_get_old_bridge_state(old_state, next = list_next_entry(bridge, chain_node);
bridge);
if (WARN_ON(!old_bridge_state))
return;
bridge->funcs->atomic_post_disable(bridge, if (next->pre_enable_prev_first) {
old_bridge_state); /* next bridge had requested that prev
} else if (bridge->funcs->post_disable) { * was enabled first, so disabled last
bridge->funcs->post_disable(bridge); */
limit = next;
/* Find the next bridge that has NOT requested
* prev to be enabled first / disabled last
*/
list_for_each_entry_from(next, &encoder->bridge_chain,
chain_node) {
if (next->pre_enable_prev_first) {
next = list_prev_entry(next, chain_node);
limit = next;
break;
}
}
/* Call these bridges in reverse order */
list_for_each_entry_from_reverse(next, &encoder->bridge_chain,
chain_node) {
if (next == bridge)
break;
drm_atomic_bridge_call_post_disable(next,
old_state);
}
}
} }
drm_atomic_bridge_call_post_disable(bridge, old_state);
if (limit)
/* Jump all bridges that we have already post_disabled */
bridge = limit;
} }
} }
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *old_state)
{
if (old_state && bridge->funcs->atomic_pre_enable) {
struct drm_bridge_state *old_bridge_state;
old_bridge_state =
drm_atomic_get_old_bridge_state(old_state,
bridge);
if (WARN_ON(!old_bridge_state))
return;
bridge->funcs->atomic_pre_enable(bridge, old_bridge_state);
} else if (bridge->funcs->pre_enable) {
bridge->funcs->pre_enable(bridge);
}
}
/** /**
* drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
* the encoder chain * the encoder chain
@ -743,33 +741,61 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
* starting from the last bridge to the first. These are called before calling * starting from the last bridge to the first. These are called before calling
* &drm_encoder_helper_funcs.atomic_enable * &drm_encoder_helper_funcs.atomic_enable
* *
* If a bridge sets @pre_enable_prev_first, then the pre_enable for the
* prev bridge will be called before pre_enable of this bridge.
*
* Note: the bridge passed should be the one closest to the encoder * Note: the bridge passed should be the one closest to the encoder
*/ */
void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *old_state) struct drm_atomic_state *old_state)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_bridge *iter; struct drm_bridge *iter, *next, *limit;
if (!bridge) if (!bridge)
return; return;
encoder = bridge->encoder; encoder = bridge->encoder;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->atomic_pre_enable) { if (iter->pre_enable_prev_first) {
struct drm_bridge_state *old_bridge_state; next = iter;
limit = bridge;
list_for_each_entry_from_reverse(next,
&encoder->bridge_chain,
chain_node) {
if (next == bridge)
break;
old_bridge_state = if (!next->pre_enable_prev_first) {
drm_atomic_get_old_bridge_state(old_state, /* Found first bridge that does NOT
iter); * request prev to be enabled first
if (WARN_ON(!old_bridge_state)) */
return; limit = list_prev_entry(next, chain_node);
break;
}
}
iter->funcs->atomic_pre_enable(iter, old_bridge_state); list_for_each_entry_from(next, &encoder->bridge_chain, chain_node) {
} else if (iter->funcs->pre_enable) { /* Call requested prev bridge pre_enable
iter->funcs->pre_enable(iter); * in order.
*/
if (next == iter)
/* At the first bridge to request prev
* bridges called first.
*/
break;
drm_atomic_bridge_call_pre_enable(next, old_state);
}
} }
drm_atomic_bridge_call_pre_enable(iter, old_state);
if (iter->pre_enable_prev_first)
/* Jump all bridges that we have already pre_enabled */
iter = limit;
if (iter == bridge) if (iter == bridge)
break; break;
} }

View File

@ -480,8 +480,8 @@ EXPORT_SYMBOL(drm_client_framebuffer_flush);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
{ {
struct drm_info_node *node = m->private; struct drm_debugfs_entry *entry = m->private;
struct drm_device *dev = node->minor->dev; struct drm_device *dev = entry->dev;
struct drm_printer p = drm_seq_file_printer(m); struct drm_printer p = drm_seq_file_printer(m);
struct drm_client_dev *client; struct drm_client_dev *client;
@ -493,14 +493,13 @@ static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
return 0; return 0;
} }
static const struct drm_info_list drm_client_debugfs_list[] = { static const struct drm_debugfs_info drm_client_debugfs_list[] = {
{ "internal_clients", drm_client_debugfs_internal_clients, 0 }, { "internal_clients", drm_client_debugfs_internal_clients, 0 },
}; };
void drm_client_debugfs_init(struct drm_minor *minor) void drm_client_debugfs_init(struct drm_minor *minor)
{ {
drm_debugfs_create_files(drm_client_debugfs_list, drm_debugfs_add_files(minor->dev, drm_client_debugfs_list,
ARRAY_SIZE(drm_client_debugfs_list), ARRAY_SIZE(drm_client_debugfs_list));
minor->debugfs_root, minor);
} }
#endif #endif

View File

@ -188,10 +188,6 @@ static struct drm_display_mode *drm_connector_pick_cmdline_mode(struct drm_conne
prefer_non_interlace = !cmdline_mode->interlace; prefer_non_interlace = !cmdline_mode->interlace;
again: again:
list_for_each_entry(mode, &connector->modes, head) { list_for_each_entry(mode, &connector->modes, head) {
/* Check (optional) mode name first */
if (!strcmp(mode->name, cmdline_mode->name))
return mode;
/* check width/height */ /* check width/height */
if (mode->hdisplay != cmdline_mode->xres || if (mode->hdisplay != cmdline_mode->xres ||
mode->vdisplay != cmdline_mode->yres) mode->vdisplay != cmdline_mode->yres)

View File

@ -984,6 +984,41 @@ static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
drm_dvi_i_subconnector_enum_list) drm_dvi_i_subconnector_enum_list)
static const struct drm_prop_enum_list drm_tv_mode_enum_list[] = {
{ DRM_MODE_TV_MODE_NTSC, "NTSC" },
{ DRM_MODE_TV_MODE_NTSC_443, "NTSC-443" },
{ DRM_MODE_TV_MODE_NTSC_J, "NTSC-J" },
{ DRM_MODE_TV_MODE_PAL, "PAL" },
{ DRM_MODE_TV_MODE_PAL_M, "PAL-M" },
{ DRM_MODE_TV_MODE_PAL_N, "PAL-N" },
{ DRM_MODE_TV_MODE_SECAM, "SECAM" },
};
DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list)
/**
* drm_get_tv_mode_from_name - Translates a TV mode name into its enum value
* @name: TV Mode name we want to convert
* @len: Length of @name
*
* Translates @name into an enum drm_connector_tv_mode.
*
* Returns: the enum value on success, a negative errno otherwise.
*/
int drm_get_tv_mode_from_name(const char *name, size_t len)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(drm_tv_mode_enum_list); i++) {
const struct drm_prop_enum_list *item = &drm_tv_mode_enum_list[i];
if (strlen(item->name) == len && !strncmp(item->name, name, len))
return item->type;
}
return -EINVAL;
}
EXPORT_SYMBOL(drm_get_tv_mode_from_name);
static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
@ -1552,6 +1587,71 @@ EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property);
* infoframe values is done through drm_hdmi_avi_infoframe_content_type(). * infoframe values is done through drm_hdmi_avi_infoframe_content_type().
*/ */
/*
* TODO: Document the properties:
* - left margin
* - right margin
* - top margin
* - bottom margin
* - brightness
* - contrast
* - flicker reduction
* - hue
* - mode
* - overscan
* - saturation
* - select subconnector
* - subconnector
*/
/**
* DOC: Analog TV Connector Properties
*
* TV Mode:
* Indicates the TV Mode used on an analog TV connector. The value
* of this property can be one of the following:
*
* NTSC:
* TV Mode is CCIR System M (aka 525-lines) together with
* the NTSC Color Encoding.
*
* NTSC-443:
*
* TV Mode is CCIR System M (aka 525-lines) together with
* the NTSC Color Encoding, but with a color subcarrier
* frequency of 4.43MHz
*
* NTSC-J:
*
* TV Mode is CCIR System M (aka 525-lines) together with
* the NTSC Color Encoding, but with a black level equal to
* the blanking level.
*
* PAL:
*
* TV Mode is CCIR System B (aka 625-lines) together with
* the PAL Color Encoding.
*
* PAL-M:
*
* TV Mode is CCIR System M (aka 525-lines) together with
* the PAL Color Encoding.
*
* PAL-N:
*
* TV Mode is CCIR System N together with the PAL Color
* Encoding, a color subcarrier frequency of 3.58MHz, the
* SECAM color space, and narrower channels than other PAL
* variants.
*
* SECAM:
*
* TV Mode is CCIR System B (aka 625-lines) together with
* the SECAM Color Encoding.
*
* Drivers can set up this property by calling
* drm_mode_create_tv_properties().
*/
/** /**
* drm_connector_attach_content_type_property - attach content-type property * drm_connector_attach_content_type_property - attach content-type property
* @connector: connector to attach content type property on. * @connector: connector to attach content type property on.
@ -1604,7 +1704,7 @@ EXPORT_SYMBOL(drm_connector_attach_tv_margin_properties);
* Called by a driver's HDMI connector initialization routine, this function * Called by a driver's HDMI connector initialization routine, this function
* creates the TV margin properties for a given device. No need to call this * creates the TV margin properties for a given device. No need to call this
* function for an SDTV connector, it's already called from * function for an SDTV connector, it's already called from
* drm_mode_create_tv_properties(). * drm_mode_create_tv_properties_legacy().
* *
* Returns: * Returns:
* 0 on success or a negative error code on failure. * 0 on success or a negative error code on failure.
@ -1639,7 +1739,7 @@ int drm_mode_create_tv_margin_properties(struct drm_device *dev)
EXPORT_SYMBOL(drm_mode_create_tv_margin_properties); EXPORT_SYMBOL(drm_mode_create_tv_margin_properties);
/** /**
* drm_mode_create_tv_properties - create TV specific connector properties * drm_mode_create_tv_properties_legacy - create TV specific connector properties
* @dev: DRM device * @dev: DRM device
* @num_modes: number of different TV formats (modes) supported * @num_modes: number of different TV formats (modes) supported
* @modes: array of pointers to strings containing name of each format * @modes: array of pointers to strings containing name of each format
@ -1649,12 +1749,16 @@ EXPORT_SYMBOL(drm_mode_create_tv_margin_properties);
* responsible for allocating a list of format names and passing them to * responsible for allocating a list of format names and passing them to
* this routine. * this routine.
* *
* NOTE: This functions registers the deprecated "mode" connector
* property to select the analog TV mode (ie, NTSC, PAL, etc.). New
* drivers must use drm_mode_create_tv_properties() instead.
*
* Returns: * Returns:
* 0 on success or a negative error code on failure. * 0 on success or a negative error code on failure.
*/ */
int drm_mode_create_tv_properties(struct drm_device *dev, int drm_mode_create_tv_properties_legacy(struct drm_device *dev,
unsigned int num_modes, unsigned int num_modes,
const char * const modes[]) const char * const modes[])
{ {
struct drm_property *tv_selector; struct drm_property *tv_selector;
struct drm_property *tv_subconnector; struct drm_property *tv_subconnector;
@ -1690,15 +1794,17 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
if (drm_mode_create_tv_margin_properties(dev)) if (drm_mode_create_tv_margin_properties(dev))
goto nomem; goto nomem;
dev->mode_config.tv_mode_property = if (num_modes) {
drm_property_create(dev, DRM_MODE_PROP_ENUM, dev->mode_config.legacy_tv_mode_property =
"mode", num_modes); drm_property_create(dev, DRM_MODE_PROP_ENUM,
if (!dev->mode_config.tv_mode_property) "mode", num_modes);
goto nomem; if (!dev->mode_config.legacy_tv_mode_property)
goto nomem;
for (i = 0; i < num_modes; i++) for (i = 0; i < num_modes; i++)
drm_property_add_enum(dev->mode_config.tv_mode_property, drm_property_add_enum(dev->mode_config.legacy_tv_mode_property,
i, modes[i]); i, modes[i]);
}
dev->mode_config.tv_brightness_property = dev->mode_config.tv_brightness_property =
drm_property_create_range(dev, 0, "brightness", 0, 100); drm_property_create_range(dev, 0, "brightness", 0, 100);
@ -1734,6 +1840,47 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
nomem: nomem:
return -ENOMEM; return -ENOMEM;
} }
EXPORT_SYMBOL(drm_mode_create_tv_properties_legacy);
/**
* drm_mode_create_tv_properties - create TV specific connector properties
* @dev: DRM device
* @supported_tv_modes: Bitmask of TV modes supported (See DRM_MODE_TV_MODE_*)
* Called by a driver's TV initialization routine, this function creates
* the TV specific connector properties for a given device.
*
* Returns:
* 0 on success or a negative error code on failure.
*/
int drm_mode_create_tv_properties(struct drm_device *dev,
unsigned int supported_tv_modes)
{
struct drm_prop_enum_list tv_mode_list[DRM_MODE_TV_MODE_MAX];
struct drm_property *tv_mode;
unsigned int i, len = 0;
if (dev->mode_config.tv_mode_property)
return 0;
for (i = 0; i < DRM_MODE_TV_MODE_MAX; i++) {
if (!(supported_tv_modes & BIT(i)))
continue;
tv_mode_list[len].type = i;
tv_mode_list[len].name = drm_get_tv_mode_name(i);
len++;
}
tv_mode = drm_property_create_enum(dev, 0, "TV mode",
tv_mode_list, len);
if (!tv_mode)
return -ENOMEM;
dev->mode_config.tv_mode_property = tv_mode;
return drm_mode_create_tv_properties_legacy(dev, 0, NULL);
}
EXPORT_SYMBOL(drm_mode_create_tv_properties); EXPORT_SYMBOL(drm_mode_create_tv_properties);
/** /**

View File

@ -38,6 +38,7 @@
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_file.h> #include <drm/drm_file.h>
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_managed.h>
#include "drm_crtc_internal.h" #include "drm_crtc_internal.h"
#include "drm_internal.h" #include "drm_internal.h"
@ -50,9 +51,8 @@
static int drm_name_info(struct seq_file *m, void *data) static int drm_name_info(struct seq_file *m, void *data)
{ {
struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_debugfs_entry *entry = m->private;
struct drm_minor *minor = node->minor; struct drm_device *dev = entry->dev;
struct drm_device *dev = minor->dev;
struct drm_master *master; struct drm_master *master;
mutex_lock(&dev->master_mutex); mutex_lock(&dev->master_mutex);
@ -72,8 +72,8 @@ static int drm_name_info(struct seq_file *m, void *data)
static int drm_clients_info(struct seq_file *m, void *data) static int drm_clients_info(struct seq_file *m, void *data)
{ {
struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_debugfs_entry *entry = m->private;
struct drm_device *dev = node->minor->dev; struct drm_device *dev = entry->dev;
struct drm_file *priv; struct drm_file *priv;
kuid_t uid; kuid_t uid;
@ -124,8 +124,8 @@ static int drm_gem_one_name_info(int id, void *ptr, void *data)
static int drm_gem_name_info(struct seq_file *m, void *data) static int drm_gem_name_info(struct seq_file *m, void *data)
{ {
struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_debugfs_entry *entry = m->private;
struct drm_device *dev = node->minor->dev; struct drm_device *dev = entry->dev;
seq_printf(m, " name size handles refcount\n"); seq_printf(m, " name size handles refcount\n");
@ -136,7 +136,7 @@ static int drm_gem_name_info(struct seq_file *m, void *data)
return 0; return 0;
} }
static const struct drm_info_list drm_debugfs_list[] = { static const struct drm_debugfs_info drm_debugfs_list[] = {
{"name", drm_name_info, 0}, {"name", drm_name_info, 0},
{"clients", drm_clients_info, 0}, {"clients", drm_clients_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM}, {"gem_names", drm_gem_name_info, DRIVER_GEM},
@ -151,6 +151,21 @@ static int drm_debugfs_open(struct inode *inode, struct file *file)
return single_open(file, node->info_ent->show, node); return single_open(file, node->info_ent->show, node);
} }
static int drm_debugfs_entry_open(struct inode *inode, struct file *file)
{
struct drm_debugfs_entry *entry = inode->i_private;
struct drm_debugfs_info *node = &entry->file;
return single_open(file, node->show, entry);
}
static const struct file_operations drm_debugfs_entry_fops = {
.owner = THIS_MODULE,
.open = drm_debugfs_entry_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations drm_debugfs_fops = { static const struct file_operations drm_debugfs_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
@ -207,6 +222,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
struct dentry *root) struct dentry *root)
{ {
struct drm_device *dev = minor->dev; struct drm_device *dev = minor->dev;
struct drm_debugfs_entry *entry, *tmp;
char name[64]; char name[64];
INIT_LIST_HEAD(&minor->debugfs_list); INIT_LIST_HEAD(&minor->debugfs_list);
@ -214,8 +230,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
sprintf(name, "%d", minor_id); sprintf(name, "%d", minor_id);
minor->debugfs_root = debugfs_create_dir(name, root); minor->debugfs_root = debugfs_create_dir(name, root);
drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, drm_debugfs_add_files(minor->dev, drm_debugfs_list, DRM_DEBUGFS_ENTRIES);
minor->debugfs_root, minor);
if (drm_drv_uses_atomic_modeset(dev)) { if (drm_drv_uses_atomic_modeset(dev)) {
drm_atomic_debugfs_init(minor); drm_atomic_debugfs_init(minor);
@ -230,9 +245,29 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
if (dev->driver->debugfs_init) if (dev->driver->debugfs_init)
dev->driver->debugfs_init(minor); dev->driver->debugfs_init(minor);
list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) {
debugfs_create_file(entry->file.name, S_IFREG | S_IRUGO,
minor->debugfs_root, entry, &drm_debugfs_entry_fops);
list_del(&entry->list);
}
return 0; return 0;
} }
void drm_debugfs_late_register(struct drm_device *dev)
{
struct drm_minor *minor = dev->primary;
struct drm_debugfs_entry *entry, *tmp;
if (!minor)
return;
list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) {
debugfs_create_file(entry->file.name, S_IFREG | S_IRUGO,
minor->debugfs_root, entry, &drm_debugfs_entry_fops);
list_del(&entry->list);
}
}
int drm_debugfs_remove_files(const struct drm_info_list *files, int count, int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
struct drm_minor *minor) struct drm_minor *minor)
@ -281,6 +316,53 @@ void drm_debugfs_cleanup(struct drm_minor *minor)
minor->debugfs_root = NULL; minor->debugfs_root = NULL;
} }
/**
* drm_debugfs_add_file - Add a given file to the DRM device debugfs file list
* @dev: drm device for the ioctl
* @name: debugfs file name
* @show: show callback
* @data: driver-private data, should not be device-specific
*
* Add a given file entry to the DRM device debugfs file list to be created on
* drm_debugfs_init.
*/
void drm_debugfs_add_file(struct drm_device *dev, const char *name,
int (*show)(struct seq_file*, void*), void *data)
{
struct drm_debugfs_entry *entry = drmm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
if (!entry)
return;
entry->file.name = name;
entry->file.show = show;
entry->file.data = data;
entry->dev = dev;
mutex_lock(&dev->debugfs_mutex);
list_add(&entry->list, &dev->debugfs_list);
mutex_unlock(&dev->debugfs_mutex);
}
EXPORT_SYMBOL(drm_debugfs_add_file);
/**
* drm_debugfs_add_files - Add an array of files to the DRM device debugfs file list
* @dev: drm device for the ioctl
* @files: The array of files to create
* @count: The number of files given
*
* Add a given set of debugfs files represented by an array of
* &struct drm_debugfs_info in the DRM device debugfs file list.
*/
void drm_debugfs_add_files(struct drm_device *dev, const struct drm_debugfs_info *files, int count)
{
int i;
for (i = 0; i < count; i++)
drm_debugfs_add_file(dev, files[i].name, files[i].show, files[i].data);
}
EXPORT_SYMBOL(drm_debugfs_add_files);
static int connector_show(struct seq_file *m, void *data) static int connector_show(struct seq_file *m, void *data)
{ {
struct drm_connector *connector = m->private; struct drm_connector *connector = m->private;

View File

@ -598,6 +598,7 @@ static void drm_dev_init_release(struct drm_device *dev, void *res)
mutex_destroy(&dev->clientlist_mutex); mutex_destroy(&dev->clientlist_mutex);
mutex_destroy(&dev->filelist_mutex); mutex_destroy(&dev->filelist_mutex);
mutex_destroy(&dev->struct_mutex); mutex_destroy(&dev->struct_mutex);
mutex_destroy(&dev->debugfs_mutex);
drm_legacy_destroy_members(dev); drm_legacy_destroy_members(dev);
} }
@ -638,12 +639,14 @@ static int drm_dev_init(struct drm_device *dev,
INIT_LIST_HEAD(&dev->filelist_internal); INIT_LIST_HEAD(&dev->filelist_internal);
INIT_LIST_HEAD(&dev->clientlist); INIT_LIST_HEAD(&dev->clientlist);
INIT_LIST_HEAD(&dev->vblank_event_list); INIT_LIST_HEAD(&dev->vblank_event_list);
INIT_LIST_HEAD(&dev->debugfs_list);
spin_lock_init(&dev->event_lock); spin_lock_init(&dev->event_lock);
mutex_init(&dev->struct_mutex); mutex_init(&dev->struct_mutex);
mutex_init(&dev->filelist_mutex); mutex_init(&dev->filelist_mutex);
mutex_init(&dev->clientlist_mutex); mutex_init(&dev->clientlist_mutex);
mutex_init(&dev->master_mutex); mutex_init(&dev->master_mutex);
mutex_init(&dev->debugfs_mutex);
ret = drmm_add_action_or_reset(dev, drm_dev_init_release, NULL); ret = drmm_add_action_or_reset(dev, drm_dev_init_release, NULL);
if (ret) if (ret)
@ -929,8 +932,8 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
dev->registered = true; dev->registered = true;
if (dev->driver->load) { if (driver->load) {
ret = dev->driver->load(dev, flags); ret = driver->load(dev, flags);
if (ret) if (ret)
goto err_minors; goto err_minors;
} }

View File

@ -431,7 +431,6 @@ static const struct drm_client_funcs drm_fbdev_client_funcs = {
* drm_fbdev_generic_setup() - Setup generic fbdev emulation * drm_fbdev_generic_setup() - Setup generic fbdev emulation
* @dev: DRM device * @dev: DRM device
* @preferred_bpp: Preferred bits per pixel for the 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 * This function sets up generic fbdev emulation for drivers that supports
* dumb buffers with a virtual address and that can be mmap'ed. * dumb buffers with a virtual address and that can be mmap'ed.
@ -475,12 +474,16 @@ void drm_fbdev_generic_setup(struct drm_device *dev,
} }
/* /*
* FIXME: This mixes up depth with bpp, which results in a glorious * Pick a preferred bpp of 32 if no value has been given. This
* mess, resulting in some drivers picking wrong fbdev defaults and * will select XRGB8888 for the framebuffer formats. All drivers
* others wrong preferred_depth defaults. * have to support XRGB8888 for backwards compatibility with legacy
* userspace, so it's the safe choice here.
*
* TODO: Replace struct drm_mode_config.preferred_depth and this
* bpp value with a preferred format that is given as struct
* drm_format_info. Then derive all other values from the
* format.
*/ */
if (!preferred_bpp)
preferred_bpp = dev->mode_config.preferred_depth;
if (!preferred_bpp) if (!preferred_bpp)
preferred_bpp = 32; preferred_bpp = 32;
fb_helper->preferred_bpp = preferred_bpp; fb_helper->preferred_bpp = preferred_bpp;

View File

@ -1203,8 +1203,8 @@ void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static int drm_framebuffer_info(struct seq_file *m, void *data) static int drm_framebuffer_info(struct seq_file *m, void *data)
{ {
struct drm_info_node *node = m->private; struct drm_debugfs_entry *entry = m->private;
struct drm_device *dev = node->minor->dev; struct drm_device *dev = entry->dev;
struct drm_printer p = drm_seq_file_printer(m); struct drm_printer p = drm_seq_file_printer(m);
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
@ -1218,14 +1218,13 @@ static int drm_framebuffer_info(struct seq_file *m, void *data)
return 0; return 0;
} }
static const struct drm_info_list drm_framebuffer_debugfs_list[] = { static const struct drm_debugfs_info drm_framebuffer_debugfs_list[] = {
{ "framebuffer", drm_framebuffer_info, 0 }, { "framebuffer", drm_framebuffer_info, 0 },
}; };
void drm_framebuffer_debugfs_init(struct drm_minor *minor) void drm_framebuffer_debugfs_init(struct drm_minor *minor)
{ {
drm_debugfs_create_files(drm_framebuffer_debugfs_list, drm_debugfs_add_files(minor->dev, drm_framebuffer_debugfs_list,
ARRAY_SIZE(drm_framebuffer_debugfs_list), ARRAY_SIZE(drm_framebuffer_debugfs_list));
minor->debugfs_root, minor);
} }
#endif #endif

View File

@ -26,11 +26,8 @@
* call drm_gem_plane_helper_prepare_fb() from their implementation of * call drm_gem_plane_helper_prepare_fb() from their implementation of
* struct &drm_plane_helper.prepare_fb . It sets the plane's fence from * struct &drm_plane_helper.prepare_fb . It sets the plane's fence from
* the framebuffer so that the DRM core can synchronize access automatically. * the framebuffer so that the DRM core can synchronize access automatically.
*
* drm_gem_plane_helper_prepare_fb() can also be used directly as * drm_gem_plane_helper_prepare_fb() can also be used directly as
* implementation of prepare_fb. For drivers based on * implementation of prepare_fb.
* struct drm_simple_display_pipe, drm_gem_simple_display_pipe_prepare_fb()
* provides equivalent functionality.
* *
* .. code-block:: c * .. code-block:: c
* *
@ -41,11 +38,6 @@
* . prepare_fb = drm_gem_plane_helper_prepare_fb, * . prepare_fb = drm_gem_plane_helper_prepare_fb,
* }; * };
* *
* struct drm_simple_display_pipe_funcs driver_pipe_funcs = {
* ...,
* . prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
* };
*
* A driver using a shadow buffer copies the content of the shadow buffers * A driver using a shadow buffer copies the content of the shadow buffers
* into the HW's framebuffer memory during an atomic update. This requires * into the HW's framebuffer memory during an atomic update. This requires
* a mapping of the shadow buffer into kernel address space. The mappings * a mapping of the shadow buffer into kernel address space. The mappings
@ -205,27 +197,6 @@ error:
} }
EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb); EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb);
/**
* drm_gem_simple_display_pipe_prepare_fb - prepare_fb helper for &drm_simple_display_pipe
* @pipe: Simple display pipe
* @plane_state: Plane state
*
* This function uses drm_gem_plane_helper_prepare_fb() to extract the fences
* from &drm_gem_object.resv and attaches them to the plane state for the atomic
* helper to wait on. This is necessary to correctly implement implicit
* synchronization for any buffers shared as a struct &dma_buf. Drivers can use
* this as their &drm_simple_display_pipe_funcs.prepare_fb callback.
*
* See drm_gem_plane_helper_prepare_fb() for a discussion of implicit and
* explicit fencing in atomic modeset updates.
*/
int drm_gem_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
{
return drm_gem_plane_helper_prepare_fb(&pipe->plane, plane_state);
}
EXPORT_SYMBOL(drm_gem_simple_display_pipe_prepare_fb);
/* /*
* Shadow-buffered Planes * Shadow-buffered Planes
*/ */

View File

@ -3,6 +3,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <drm/drm_gem_ttm_helper.h> #include <drm/drm_gem_ttm_helper.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_tt.h>
/** /**
* DOC: overview * DOC: overview

View File

@ -19,6 +19,7 @@
#include <drm/drm_simple_kms_helper.h> #include <drm/drm_simple_kms_helper.h>
#include <drm/ttm/ttm_range_manager.h> #include <drm/ttm/ttm_range_manager.h>
#include <drm/ttm/ttm_tt.h>
static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; static const struct drm_gem_object_funcs drm_gem_vram_object_funcs;
@ -956,8 +957,8 @@ static struct ttm_device_funcs bo_driver = {
static int drm_vram_mm_debugfs(struct seq_file *m, void *data) static int drm_vram_mm_debugfs(struct seq_file *m, void *data)
{ {
struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_debugfs_entry *entry = m->private;
struct drm_vram_mm *vmm = node->minor->dev->vram_mm; struct drm_vram_mm *vmm = entry->dev->vram_mm;
struct ttm_resource_manager *man = ttm_manager_type(&vmm->bdev, TTM_PL_VRAM); struct ttm_resource_manager *man = ttm_manager_type(&vmm->bdev, TTM_PL_VRAM);
struct drm_printer p = drm_seq_file_printer(m); struct drm_printer p = drm_seq_file_printer(m);
@ -965,7 +966,7 @@ static int drm_vram_mm_debugfs(struct seq_file *m, void *data)
return 0; return 0;
} }
static const struct drm_info_list drm_vram_mm_debugfs_list[] = { static const struct drm_debugfs_info drm_vram_mm_debugfs_list[] = {
{ "vram-mm", drm_vram_mm_debugfs, 0, NULL }, { "vram-mm", drm_vram_mm_debugfs, 0, NULL },
}; };
@ -977,9 +978,8 @@ static const struct drm_info_list drm_vram_mm_debugfs_list[] = {
*/ */
void drm_vram_mm_debugfs_init(struct drm_minor *minor) void drm_vram_mm_debugfs_init(struct drm_minor *minor)
{ {
drm_debugfs_create_files(drm_vram_mm_debugfs_list, drm_debugfs_add_files(minor->dev, drm_vram_mm_debugfs_list,
ARRAY_SIZE(drm_vram_mm_debugfs_list), ARRAY_SIZE(drm_vram_mm_debugfs_list));
minor->debugfs_root, minor);
} }
EXPORT_SYMBOL(drm_vram_mm_debugfs_init); EXPORT_SYMBOL(drm_vram_mm_debugfs_init);

View File

@ -186,6 +186,7 @@ int drm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
int drm_debugfs_init(struct drm_minor *minor, int minor_id, int drm_debugfs_init(struct drm_minor *minor, int minor_id,
struct dentry *root); struct dentry *root);
void drm_debugfs_cleanup(struct drm_minor *minor); void drm_debugfs_cleanup(struct drm_minor *minor);
void drm_debugfs_late_register(struct drm_device *dev);
void drm_debugfs_connector_add(struct drm_connector *connector); void drm_debugfs_connector_add(struct drm_connector *connector);
void drm_debugfs_connector_remove(struct drm_connector *connector); void drm_debugfs_connector_remove(struct drm_connector *connector);
void drm_debugfs_crtc_add(struct drm_crtc *crtc); void drm_debugfs_crtc_add(struct drm_crtc *crtc);
@ -202,6 +203,10 @@ static inline void drm_debugfs_cleanup(struct drm_minor *minor)
{ {
} }
static inline void drm_debugfs_late_register(struct drm_device *dev)
{
}
static inline void drm_debugfs_connector_add(struct drm_connector *connector) static inline void drm_debugfs_connector_add(struct drm_connector *connector)
{ {
} }

View File

@ -21,6 +21,7 @@
#include <drm/drm_fourcc.h> #include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h> #include <drm/drm_framebuffer.h>
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mipi_dbi.h> #include <drm/drm_mipi_dbi.h>
#include <drm/drm_modes.h> #include <drm/drm_modes.h>
@ -192,6 +193,7 @@ EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
/** /**
* mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary
* @dst: The destination buffer * @dst: The destination buffer
* @src: The source buffer
* @fb: The source framebuffer * @fb: The source framebuffer
* @clip: Clipping rectangle of the area to be copied * @clip: Clipping rectangle of the area to be copied
* @swap: When true, swap MSB/LSB of 16-bit values * @swap: When true, swap MSB/LSB of 16-bit values
@ -199,12 +201,10 @@ EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
* Returns: * Returns:
* Zero on success, negative error code on failure. * Zero on success, negative error code on failure.
*/ */
int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb,
struct drm_rect *clip, bool swap) struct drm_rect *clip, bool swap)
{ {
struct drm_gem_object *gem = drm_gem_fb_get_obj(fb, 0); struct drm_gem_object *gem = drm_gem_fb_get_obj(fb, 0);
struct iosys_map map[DRM_FORMAT_MAX_PLANES];
struct iosys_map data[DRM_FORMAT_MAX_PLANES];
struct iosys_map dst_map = IOSYS_MAP_INIT_VADDR(dst); struct iosys_map dst_map = IOSYS_MAP_INIT_VADDR(dst);
int ret; int ret;
@ -212,19 +212,15 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
if (ret) if (ret)
return ret; return ret;
ret = drm_gem_fb_vmap(fb, map, data);
if (ret)
goto out_drm_gem_fb_end_cpu_access;
switch (fb->format->format) { switch (fb->format->format) {
case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB565:
if (swap) if (swap)
drm_fb_swab(&dst_map, NULL, data, fb, clip, !gem->import_attach); drm_fb_swab(&dst_map, NULL, src, fb, clip, !gem->import_attach);
else else
drm_fb_memcpy(&dst_map, NULL, data, fb, clip); drm_fb_memcpy(&dst_map, NULL, src, fb, clip);
break; break;
case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XRGB8888:
drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, data, fb, clip, swap); drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, src, fb, clip, swap);
break; break;
default: default:
drm_err_once(fb->dev, "Format is not supported: %p4cc\n", drm_err_once(fb->dev, "Format is not supported: %p4cc\n",
@ -232,8 +228,6 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
ret = -EINVAL; ret = -EINVAL;
} }
drm_gem_fb_vunmap(fb, map);
out_drm_gem_fb_end_cpu_access:
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
return ret; return ret;
@ -257,29 +251,18 @@ static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev,
ys & 0xff, (ye >> 8) & 0xff, ye & 0xff); ys & 0xff, (ye >> 8) & 0xff, ye & 0xff);
} }
static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) static void mipi_dbi_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb,
struct drm_rect *rect)
{ {
struct iosys_map map[DRM_FORMAT_MAX_PLANES];
struct iosys_map data[DRM_FORMAT_MAX_PLANES];
struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev);
unsigned int height = rect->y2 - rect->y1; unsigned int height = rect->y2 - rect->y1;
unsigned int width = rect->x2 - rect->x1; unsigned int width = rect->x2 - rect->x1;
struct mipi_dbi *dbi = &dbidev->dbi; struct mipi_dbi *dbi = &dbidev->dbi;
bool swap = dbi->swap_bytes; bool swap = dbi->swap_bytes;
int idx, ret = 0; int ret = 0;
bool full; bool full;
void *tr; void *tr;
if (WARN_ON(!fb))
return;
if (!drm_dev_enter(fb->dev, &idx))
return;
ret = drm_gem_fb_vmap(fb, map, data);
if (ret)
goto err_drm_dev_exit;
full = width == fb->width && height == fb->height; full = width == fb->width && height == fb->height;
DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
@ -287,11 +270,11 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
if (!dbi->dc || !full || swap || if (!dbi->dc || !full || swap ||
fb->format->format == DRM_FORMAT_XRGB8888) { fb->format->format == DRM_FORMAT_XRGB8888) {
tr = dbidev->tx_buf; tr = dbidev->tx_buf;
ret = mipi_dbi_buf_copy(dbidev->tx_buf, fb, rect, swap); ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap);
if (ret) if (ret)
goto err_msg; goto err_msg;
} else { } else {
tr = data[0].vaddr; /* TODO: Use mapping abstraction properly */ tr = src->vaddr; /* TODO: Use mapping abstraction properly */
} }
mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1, mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1,
@ -302,11 +285,6 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
err_msg: err_msg:
if (ret) if (ret)
drm_err_once(fb->dev, "Failed to update display %d\n", ret); drm_err_once(fb->dev, "Failed to update display %d\n", ret);
drm_gem_fb_vunmap(fb, map);
err_drm_dev_exit:
drm_dev_exit(idx);
} }
/** /**
@ -339,13 +317,24 @@ void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state) struct drm_plane_state *old_state)
{ {
struct drm_plane_state *state = pipe->plane.state; struct drm_plane_state *state = pipe->plane.state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
struct drm_framebuffer *fb = state->fb;
struct drm_rect rect; struct drm_rect rect;
int idx;
if (!pipe->crtc.state->active) if (!pipe->crtc.state->active)
return; return;
if (WARN_ON(!fb))
return;
if (!drm_dev_enter(fb->dev, &idx))
return;
if (drm_atomic_helper_damage_merged(old_state, state, &rect)) if (drm_atomic_helper_damage_merged(old_state, state, &rect))
mipi_dbi_fb_dirty(state->fb, &rect); mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect);
drm_dev_exit(idx);
} }
EXPORT_SYMBOL(mipi_dbi_pipe_update); EXPORT_SYMBOL(mipi_dbi_pipe_update);
@ -366,6 +355,7 @@ void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev,
struct drm_crtc_state *crtc_state, struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state) struct drm_plane_state *plane_state)
{ {
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb; struct drm_framebuffer *fb = plane_state->fb;
struct drm_rect rect = { struct drm_rect rect = {
.x1 = 0, .x1 = 0,
@ -378,7 +368,7 @@ void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev,
if (!drm_dev_enter(&dbidev->drm, &idx)) if (!drm_dev_enter(&dbidev->drm, &idx))
return; return;
mipi_dbi_fb_dirty(fb, &rect); mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect);
backlight_enable(dbidev->backlight); backlight_enable(dbidev->backlight);
drm_dev_exit(idx); drm_dev_exit(idx);
@ -427,9 +417,95 @@ void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
if (dbidev->regulator) if (dbidev->regulator)
regulator_disable(dbidev->regulator); regulator_disable(dbidev->regulator);
if (dbidev->io_regulator)
regulator_disable(dbidev->io_regulator);
} }
EXPORT_SYMBOL(mipi_dbi_pipe_disable); EXPORT_SYMBOL(mipi_dbi_pipe_disable);
/**
* mipi_dbi_pipe_begin_fb_access - MIPI DBI pipe begin-access helper
* @pipe: Display pipe
* @plane_state: Plane state
*
* This function implements struct &drm_simple_display_funcs.begin_fb_access.
*
* See drm_gem_begin_shadow_fb_access() for details and mipi_dbi_pipe_cleanup_fb()
* for cleanup.
*
* Returns:
* 0 on success, or a negative errno code otherwise.
*/
int mipi_dbi_pipe_begin_fb_access(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
{
return drm_gem_begin_shadow_fb_access(&pipe->plane, plane_state);
}
EXPORT_SYMBOL(mipi_dbi_pipe_begin_fb_access);
/**
* mipi_dbi_pipe_end_fb_access - MIPI DBI pipe end-access helper
* @pipe: Display pipe
* @plane_state: Plane state
*
* This function implements struct &drm_simple_display_funcs.end_fb_access.
*
* See mipi_dbi_pipe_begin_fb_access().
*/
void mipi_dbi_pipe_end_fb_access(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
{
drm_gem_end_shadow_fb_access(&pipe->plane, plane_state);
}
EXPORT_SYMBOL(mipi_dbi_pipe_end_fb_access);
/**
* mipi_dbi_pipe_reset_plane - MIPI DBI plane-reset helper
* @pipe: Display pipe
*
* This function implements struct &drm_simple_display_funcs.reset_plane
* for MIPI DBI planes.
*/
void mipi_dbi_pipe_reset_plane(struct drm_simple_display_pipe *pipe)
{
drm_gem_reset_shadow_plane(&pipe->plane);
}
EXPORT_SYMBOL(mipi_dbi_pipe_reset_plane);
/**
* mipi_dbi_pipe_duplicate_plane_state - duplicates MIPI DBI plane state
* @pipe: Display pipe
*
* This function implements struct &drm_simple_display_funcs.duplicate_plane_state
* for MIPI DBI planes.
*
* See drm_gem_duplicate_shadow_plane_state() for additional details.
*
* Returns:
* A pointer to a new plane state on success, or NULL otherwise.
*/
struct drm_plane_state *mipi_dbi_pipe_duplicate_plane_state(struct drm_simple_display_pipe *pipe)
{
return drm_gem_duplicate_shadow_plane_state(&pipe->plane);
}
EXPORT_SYMBOL(mipi_dbi_pipe_duplicate_plane_state);
/**
* mipi_dbi_pipe_destroy_plane_state - cleans up MIPI DBI plane state
* @pipe: Display pipe
* @plane_state: Plane state
*
* This function implements struct drm_simple_display_funcs.destroy_plane_state
* for MIPI DBI planes.
*
* See drm_gem_destroy_shadow_plane_state() for additional details.
*/
void mipi_dbi_pipe_destroy_plane_state(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
{
drm_gem_destroy_shadow_plane_state(&pipe->plane, plane_state);
}
EXPORT_SYMBOL(mipi_dbi_pipe_destroy_plane_state);
static int mipi_dbi_connector_get_modes(struct drm_connector *connector) static int mipi_dbi_connector_get_modes(struct drm_connector *connector)
{ {
struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(connector->dev); struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(connector->dev);
@ -652,6 +728,16 @@ static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool
} }
} }
if (dbidev->io_regulator) {
ret = regulator_enable(dbidev->io_regulator);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to enable I/O regulator (%d)\n", ret);
if (dbidev->regulator)
regulator_disable(dbidev->regulator);
return ret;
}
}
if (cond && mipi_dbi_display_is_on(dbi)) if (cond && mipi_dbi_display_is_on(dbi))
return 1; return 1;
@ -661,6 +747,8 @@ static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool
DRM_DEV_ERROR(dev, "Failed to send reset command (%d)\n", ret); DRM_DEV_ERROR(dev, "Failed to send reset command (%d)\n", ret);
if (dbidev->regulator) if (dbidev->regulator)
regulator_disable(dbidev->regulator); regulator_disable(dbidev->regulator);
if (dbidev->io_regulator)
regulator_disable(dbidev->io_regulator);
return ret; return ret;
} }

View File

@ -54,6 +54,8 @@ int drm_modeset_register_all(struct drm_device *dev)
if (ret) if (ret)
goto err_connector; goto err_connector;
drm_debugfs_late_register(dev);
return 0; return 0;
err_connector: err_connector:

View File

@ -116,6 +116,482 @@ void drm_mode_probed_add(struct drm_connector *connector,
} }
EXPORT_SYMBOL(drm_mode_probed_add); EXPORT_SYMBOL(drm_mode_probed_add);
enum drm_mode_analog {
DRM_MODE_ANALOG_NTSC, /* 525 lines, 60Hz */
DRM_MODE_ANALOG_PAL, /* 625 lines, 50Hz */
};
/*
* The timings come from:
* - https://web.archive.org/web/20220406232708/http://www.kolumbus.fi/pami1/video/pal_ntsc.html
* - https://web.archive.org/web/20220406124914/http://martin.hinner.info/vga/pal.html
* - https://web.archive.org/web/20220609202433/http://www.batsocks.co.uk/readme/video_timing.htm
*/
#define NTSC_LINE_DURATION_NS 63556U
#define NTSC_LINES_NUMBER 525
#define NTSC_HBLK_DURATION_TYP_NS 10900U
#define NTSC_HBLK_DURATION_MIN_NS (NTSC_HBLK_DURATION_TYP_NS - 200)
#define NTSC_HBLK_DURATION_MAX_NS (NTSC_HBLK_DURATION_TYP_NS + 200)
#define NTSC_HACT_DURATION_TYP_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_TYP_NS)
#define NTSC_HACT_DURATION_MIN_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MAX_NS)
#define NTSC_HACT_DURATION_MAX_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MIN_NS)
#define NTSC_HFP_DURATION_TYP_NS 1500
#define NTSC_HFP_DURATION_MIN_NS 1270
#define NTSC_HFP_DURATION_MAX_NS 2220
#define NTSC_HSLEN_DURATION_TYP_NS 4700
#define NTSC_HSLEN_DURATION_MIN_NS (NTSC_HSLEN_DURATION_TYP_NS - 100)
#define NTSC_HSLEN_DURATION_MAX_NS (NTSC_HSLEN_DURATION_TYP_NS + 100)
#define NTSC_HBP_DURATION_TYP_NS 4700
/*
* I couldn't find the actual tolerance for the back porch, so let's
* just reuse the sync length ones.
*/
#define NTSC_HBP_DURATION_MIN_NS (NTSC_HBP_DURATION_TYP_NS - 100)
#define NTSC_HBP_DURATION_MAX_NS (NTSC_HBP_DURATION_TYP_NS + 100)
#define PAL_LINE_DURATION_NS 64000U
#define PAL_LINES_NUMBER 625
#define PAL_HACT_DURATION_TYP_NS 51950U
#define PAL_HACT_DURATION_MIN_NS (PAL_HACT_DURATION_TYP_NS - 100)
#define PAL_HACT_DURATION_MAX_NS (PAL_HACT_DURATION_TYP_NS + 400)
#define PAL_HBLK_DURATION_TYP_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_TYP_NS)
#define PAL_HBLK_DURATION_MIN_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MAX_NS)
#define PAL_HBLK_DURATION_MAX_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MIN_NS)
#define PAL_HFP_DURATION_TYP_NS 1650
#define PAL_HFP_DURATION_MIN_NS (PAL_HFP_DURATION_TYP_NS - 100)
#define PAL_HFP_DURATION_MAX_NS (PAL_HFP_DURATION_TYP_NS + 400)
#define PAL_HSLEN_DURATION_TYP_NS 4700
#define PAL_HSLEN_DURATION_MIN_NS (PAL_HSLEN_DURATION_TYP_NS - 200)
#define PAL_HSLEN_DURATION_MAX_NS (PAL_HSLEN_DURATION_TYP_NS + 200)
#define PAL_HBP_DURATION_TYP_NS 5700
#define PAL_HBP_DURATION_MIN_NS (PAL_HBP_DURATION_TYP_NS - 200)
#define PAL_HBP_DURATION_MAX_NS (PAL_HBP_DURATION_TYP_NS + 200)
struct analog_param_field {
unsigned int even, odd;
};
#define PARAM_FIELD(_odd, _even) \
{ .even = _even, .odd = _odd }
struct analog_param_range {
unsigned int min, typ, max;
};
#define PARAM_RANGE(_min, _typ, _max) \
{ .min = _min, .typ = _typ, .max = _max }
struct analog_parameters {
unsigned int num_lines;
unsigned int line_duration_ns;
struct analog_param_range hact_ns;
struct analog_param_range hfp_ns;
struct analog_param_range hslen_ns;
struct analog_param_range hbp_ns;
struct analog_param_range hblk_ns;
unsigned int bt601_hfp;
struct analog_param_field vfp_lines;
struct analog_param_field vslen_lines;
struct analog_param_field vbp_lines;
};
#define TV_MODE_PARAMETER(_mode, _lines, _line_dur, _hact, _hfp, \
_hslen, _hbp, _hblk, _bt601_hfp, _vfp, \
_vslen, _vbp) \
[_mode] = { \
.num_lines = _lines, \
.line_duration_ns = _line_dur, \
.hact_ns = _hact, \
.hfp_ns = _hfp, \
.hslen_ns = _hslen, \
.hbp_ns = _hbp, \
.hblk_ns = _hblk, \
.bt601_hfp = _bt601_hfp, \
.vfp_lines = _vfp, \
.vslen_lines = _vslen, \
.vbp_lines = _vbp, \
}
static const struct analog_parameters tv_modes_parameters[] = {
TV_MODE_PARAMETER(DRM_MODE_ANALOG_NTSC,
NTSC_LINES_NUMBER,
NTSC_LINE_DURATION_NS,
PARAM_RANGE(NTSC_HACT_DURATION_MIN_NS,
NTSC_HACT_DURATION_TYP_NS,
NTSC_HACT_DURATION_MAX_NS),
PARAM_RANGE(NTSC_HFP_DURATION_MIN_NS,
NTSC_HFP_DURATION_TYP_NS,
NTSC_HFP_DURATION_MAX_NS),
PARAM_RANGE(NTSC_HSLEN_DURATION_MIN_NS,
NTSC_HSLEN_DURATION_TYP_NS,
NTSC_HSLEN_DURATION_MAX_NS),
PARAM_RANGE(NTSC_HBP_DURATION_MIN_NS,
NTSC_HBP_DURATION_TYP_NS,
NTSC_HBP_DURATION_MAX_NS),
PARAM_RANGE(NTSC_HBLK_DURATION_MIN_NS,
NTSC_HBLK_DURATION_TYP_NS,
NTSC_HBLK_DURATION_MAX_NS),
16,
PARAM_FIELD(3, 3),
PARAM_FIELD(3, 3),
PARAM_FIELD(16, 17)),
TV_MODE_PARAMETER(DRM_MODE_ANALOG_PAL,
PAL_LINES_NUMBER,
PAL_LINE_DURATION_NS,
PARAM_RANGE(PAL_HACT_DURATION_MIN_NS,
PAL_HACT_DURATION_TYP_NS,
PAL_HACT_DURATION_MAX_NS),
PARAM_RANGE(PAL_HFP_DURATION_MIN_NS,
PAL_HFP_DURATION_TYP_NS,
PAL_HFP_DURATION_MAX_NS),
PARAM_RANGE(PAL_HSLEN_DURATION_MIN_NS,
PAL_HSLEN_DURATION_TYP_NS,
PAL_HSLEN_DURATION_MAX_NS),
PARAM_RANGE(PAL_HBP_DURATION_MIN_NS,
PAL_HBP_DURATION_TYP_NS,
PAL_HBP_DURATION_MAX_NS),
PARAM_RANGE(PAL_HBLK_DURATION_MIN_NS,
PAL_HBLK_DURATION_TYP_NS,
PAL_HBLK_DURATION_MAX_NS),
12,
/*
* The front porch is actually 6 short sync
* pulses for the even field, and 5 for the
* odd field. Each sync takes half a life so
* the odd field front porch is shorter by
* half a line.
*
* In progressive, we're supposed to use 6
* pulses, so we're fine there
*/
PARAM_FIELD(3, 2),
/*
* The vsync length is 5 long sync pulses,
* each field taking half a line. We're
* shorter for both fields by half a line.
*
* In progressive, we're supposed to use 5
* pulses, so we're off by half
* a line.
*
* In interlace, we're now off by half a line
* for the even field and one line for the odd
* field.
*/
PARAM_FIELD(3, 3),
/*
* The back porch starts with post-equalizing
* pulses, consisting in 5 short sync pulses
* for the even field, 4 for the odd field. In
* progressive, it's 5 short syncs.
*
* In progressive, we thus have 2.5 lines,
* plus the 0.5 line we were missing
* previously, so we should use 3 lines.
*
* In interlace, the even field is in the
* exact same case than progressive. For the
* odd field, we should be using 2 lines but
* we're one line short, so we'll make up for
* it here by using 3.
*
* The entire blanking area is supposed to
* take 25 lines, so we also need to account
* for the rest of the blanking area that
* can't be in either the front porch or sync
* period.
*/
PARAM_FIELD(19, 20)),
};
static int fill_analog_mode(struct drm_device *dev,
struct drm_display_mode *mode,
const struct analog_parameters *params,
unsigned long pixel_clock_hz,
unsigned int hactive,
unsigned int vactive,
bool interlace)
{
unsigned long pixel_duration_ns = NSEC_PER_SEC / pixel_clock_hz;
unsigned int htotal, vtotal;
unsigned int max_hact, hact_duration_ns;
unsigned int hblk, hblk_duration_ns;
unsigned int hfp, hfp_duration_ns;
unsigned int hslen, hslen_duration_ns;
unsigned int hbp, hbp_duration_ns;
unsigned int porches, porches_duration_ns;
unsigned int vfp, vfp_min;
unsigned int vbp, vbp_min;
unsigned int vslen;
bool bt601 = false;
int porches_rem;
u64 result;
drm_dbg_kms(dev,
"Generating a %ux%u%c, %u-line mode with a %lu kHz clock\n",
hactive, vactive,
interlace ? 'i' : 'p',
params->num_lines,
pixel_clock_hz / 1000);
max_hact = params->hact_ns.max / pixel_duration_ns;
if (pixel_clock_hz == 13500000 && hactive > max_hact && hactive <= 720) {
drm_dbg_kms(dev, "Trying to generate a BT.601 mode. Disabling checks.\n");
bt601 = true;
}
/*
* Our pixel duration is going to be round down by the division,
* so rounding up is probably going to introduce even more
* deviation.
*/
result = (u64)params->line_duration_ns * pixel_clock_hz;
do_div(result, NSEC_PER_SEC);
htotal = result;
drm_dbg_kms(dev, "Total Horizontal Number of Pixels: %u\n", htotal);
hact_duration_ns = hactive * pixel_duration_ns;
if (!bt601 &&
(hact_duration_ns < params->hact_ns.min ||
hact_duration_ns > params->hact_ns.max)) {
DRM_ERROR("Invalid horizontal active area duration: %uns (min: %u, max %u)\n",
hact_duration_ns, params->hact_ns.min, params->hact_ns.max);
return -EINVAL;
}
hblk = htotal - hactive;
drm_dbg_kms(dev, "Horizontal Blanking Period: %u\n", hblk);
hblk_duration_ns = hblk * pixel_duration_ns;
if (!bt601 &&
(hblk_duration_ns < params->hblk_ns.min ||
hblk_duration_ns > params->hblk_ns.max)) {
DRM_ERROR("Invalid horizontal blanking duration: %uns (min: %u, max %u)\n",
hblk_duration_ns, params->hblk_ns.min, params->hblk_ns.max);
return -EINVAL;
}
hslen = DIV_ROUND_UP(params->hslen_ns.typ, pixel_duration_ns);
drm_dbg_kms(dev, "Horizontal Sync Period: %u\n", hslen);
hslen_duration_ns = hslen * pixel_duration_ns;
if (!bt601 &&
(hslen_duration_ns < params->hslen_ns.min ||
hslen_duration_ns > params->hslen_ns.max)) {
DRM_ERROR("Invalid horizontal sync duration: %uns (min: %u, max %u)\n",
hslen_duration_ns, params->hslen_ns.min, params->hslen_ns.max);
return -EINVAL;
}
porches = hblk - hslen;
drm_dbg_kms(dev, "Remaining horizontal pixels for both porches: %u\n", porches);
porches_duration_ns = porches * pixel_duration_ns;
if (!bt601 &&
(porches_duration_ns > (params->hfp_ns.max + params->hbp_ns.max) ||
porches_duration_ns < (params->hfp_ns.min + params->hbp_ns.min))) {
DRM_ERROR("Invalid horizontal porches duration: %uns\n", porches_duration_ns);
return -EINVAL;
}
if (bt601) {
hfp = params->bt601_hfp;
} else {
unsigned int hfp_min = DIV_ROUND_UP(params->hfp_ns.min,
pixel_duration_ns);
unsigned int hbp_min = DIV_ROUND_UP(params->hbp_ns.min,
pixel_duration_ns);
int porches_rem = porches - hfp_min - hbp_min;
hfp = hfp_min + DIV_ROUND_UP(porches_rem, 2);
}
drm_dbg_kms(dev, "Horizontal Front Porch: %u\n", hfp);
hfp_duration_ns = hfp * pixel_duration_ns;
if (!bt601 &&
(hfp_duration_ns < params->hfp_ns.min ||
hfp_duration_ns > params->hfp_ns.max)) {
DRM_ERROR("Invalid horizontal front porch duration: %uns (min: %u, max %u)\n",
hfp_duration_ns, params->hfp_ns.min, params->hfp_ns.max);
return -EINVAL;
}
hbp = porches - hfp;
drm_dbg_kms(dev, "Horizontal Back Porch: %u\n", hbp);
hbp_duration_ns = hbp * pixel_duration_ns;
if (!bt601 &&
(hbp_duration_ns < params->hbp_ns.min ||
hbp_duration_ns > params->hbp_ns.max)) {
DRM_ERROR("Invalid horizontal back porch duration: %uns (min: %u, max %u)\n",
hbp_duration_ns, params->hbp_ns.min, params->hbp_ns.max);
return -EINVAL;
}
if (htotal != (hactive + hfp + hslen + hbp))
return -EINVAL;
mode->clock = pixel_clock_hz / 1000;
mode->hdisplay = hactive;
mode->hsync_start = mode->hdisplay + hfp;
mode->hsync_end = mode->hsync_start + hslen;
mode->htotal = mode->hsync_end + hbp;
if (interlace) {
vfp_min = params->vfp_lines.even + params->vfp_lines.odd;
vbp_min = params->vbp_lines.even + params->vbp_lines.odd;
vslen = params->vslen_lines.even + params->vslen_lines.odd;
} else {
/*
* By convention, NTSC (aka 525/60) systems start with
* the even field, but PAL (aka 625/50) systems start
* with the odd one.
*
* PAL systems also have asymmetric timings between the
* even and odd field, while NTSC is symmetric.
*
* Moreover, if we want to create a progressive mode for
* PAL, we need to use the odd field timings.
*
* Since odd == even for NTSC, we can just use the odd
* one all the time to simplify the code a bit.
*/
vfp_min = params->vfp_lines.odd;
vbp_min = params->vbp_lines.odd;
vslen = params->vslen_lines.odd;
}
drm_dbg_kms(dev, "Vertical Sync Period: %u\n", vslen);
porches = params->num_lines - vactive - vslen;
drm_dbg_kms(dev, "Remaining vertical pixels for both porches: %u\n", porches);
porches_rem = porches - vfp_min - vbp_min;
vfp = vfp_min + (porches_rem / 2);
drm_dbg_kms(dev, "Vertical Front Porch: %u\n", vfp);
vbp = porches - vfp;
drm_dbg_kms(dev, "Vertical Back Porch: %u\n", vbp);
vtotal = vactive + vfp + vslen + vbp;
if (params->num_lines != vtotal) {
DRM_ERROR("Invalid vertical total: %upx (expected %upx)\n",
vtotal, params->num_lines);
return -EINVAL;
}
mode->vdisplay = vactive;
mode->vsync_start = mode->vdisplay + vfp;
mode->vsync_end = mode->vsync_start + vslen;
mode->vtotal = mode->vsync_end + vbp;
if (mode->vtotal != params->num_lines)
return -EINVAL;
mode->type = DRM_MODE_TYPE_DRIVER;
mode->flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC;
if (interlace)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
drm_mode_set_name(mode);
drm_dbg_kms(dev, "Generated mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
return 0;
}
/**
* drm_analog_tv_mode - create a display mode for an analog TV
* @dev: drm device
* @tv_mode: TV Mode standard to create a mode for. See DRM_MODE_TV_MODE_*.
* @pixel_clock_hz: Pixel Clock Frequency, in Hertz
* @hdisplay: hdisplay size
* @vdisplay: vdisplay size
* @interlace: whether to compute an interlaced mode
*
* This function creates a struct drm_display_mode instance suited for
* an analog TV output, for one of the usual analog TV mode.
*
* Note that @hdisplay is larger than the usual constraints for the PAL
* and NTSC timings, and we'll choose to ignore most timings constraints
* to reach those resolutions.
*
* Returns:
*
* A pointer to the mode, allocated with drm_mode_create(). Returns NULL
* on error.
*/
struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev,
enum drm_connector_tv_mode tv_mode,
unsigned long pixel_clock_hz,
unsigned int hdisplay,
unsigned int vdisplay,
bool interlace)
{
struct drm_display_mode *mode;
enum drm_mode_analog analog;
int ret;
switch (tv_mode) {
case DRM_MODE_TV_MODE_NTSC:
fallthrough;
case DRM_MODE_TV_MODE_NTSC_443:
fallthrough;
case DRM_MODE_TV_MODE_NTSC_J:
fallthrough;
case DRM_MODE_TV_MODE_PAL_M:
analog = DRM_MODE_ANALOG_NTSC;
break;
case DRM_MODE_TV_MODE_PAL:
fallthrough;
case DRM_MODE_TV_MODE_PAL_N:
fallthrough;
case DRM_MODE_TV_MODE_SECAM:
analog = DRM_MODE_ANALOG_PAL;
break;
default:
return NULL;
}
mode = drm_mode_create(dev);
if (!mode)
return NULL;
ret = fill_analog_mode(dev, mode,
&tv_modes_parameters[analog],
pixel_clock_hz, hdisplay, vdisplay, interlace);
if (ret)
goto err_free_mode;
return mode;
err_free_mode:
drm_mode_destroy(dev, mode);
return NULL;
}
EXPORT_SYMBOL(drm_analog_tv_mode);
/** /**
* drm_cvt_mode -create a modeline based on the CVT algorithm * drm_cvt_mode -create a modeline based on the CVT algorithm
* @dev: drm device * @dev: drm device
@ -1659,6 +2135,30 @@ static int drm_mode_parse_panel_orientation(const char *delim,
return 0; return 0;
} }
static int drm_mode_parse_tv_mode(const char *delim,
struct drm_cmdline_mode *mode)
{
const char *value;
int ret;
if (*delim != '=')
return -EINVAL;
value = delim + 1;
delim = strchr(value, ',');
if (!delim)
delim = value + strlen(value);
ret = drm_get_tv_mode_from_name(value, delim - value);
if (ret < 0)
return ret;
mode->tv_mode_specified = true;
mode->tv_mode = ret;
return 0;
}
static int drm_mode_parse_cmdline_options(const char *str, static int drm_mode_parse_cmdline_options(const char *str,
bool freestanding, bool freestanding,
const struct drm_connector *connector, const struct drm_connector *connector,
@ -1728,6 +2228,9 @@ static int drm_mode_parse_cmdline_options(const char *str,
} else if (!strncmp(option, "panel_orientation", delim - option)) { } else if (!strncmp(option, "panel_orientation", delim - option)) {
if (drm_mode_parse_panel_orientation(delim, mode)) if (drm_mode_parse_panel_orientation(delim, mode))
return -EINVAL; return -EINVAL;
} else if (!strncmp(option, "tv_mode", delim - option)) {
if (drm_mode_parse_tv_mode(delim, mode))
return -EINVAL;
} else { } else {
return -EINVAL; return -EINVAL;
} }
@ -1756,20 +2259,24 @@ struct drm_named_mode {
unsigned int xres; unsigned int xres;
unsigned int yres; unsigned int yres;
unsigned int flags; unsigned int flags;
unsigned int tv_mode;
}; };
#define NAMED_MODE(_name, _pclk, _x, _y, _flags) \ #define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \
{ \ { \
.name = _name, \ .name = _name, \
.pixel_clock_khz = _pclk, \ .pixel_clock_khz = _pclk, \
.xres = _x, \ .xres = _x, \
.yres = _y, \ .yres = _y, \
.flags = _flags, \ .flags = _flags, \
.tv_mode = _mode, \
} }
static const struct drm_named_mode drm_named_modes[] = { static const struct drm_named_mode drm_named_modes[] = {
NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE), NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC),
NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE), NAMED_MODE("NTSC-J", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_J),
NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL),
NAMED_MODE("PAL-M", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_M),
}; };
static int drm_mode_parse_cmdline_named_mode(const char *name, static int drm_mode_parse_cmdline_named_mode(const char *name,
@ -1809,11 +2316,13 @@ static int drm_mode_parse_cmdline_named_mode(const char *name,
if (ret != name_end) if (ret != name_end)
continue; continue;
strcpy(cmdline_mode->name, mode->name); strscpy(cmdline_mode->name, mode->name, sizeof(cmdline_mode->name));
cmdline_mode->pixel_clock = mode->pixel_clock_khz; cmdline_mode->pixel_clock = mode->pixel_clock_khz;
cmdline_mode->xres = mode->xres; cmdline_mode->xres = mode->xres;
cmdline_mode->yres = mode->yres; cmdline_mode->yres = mode->yres;
cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
cmdline_mode->tv_mode = mode->tv_mode;
cmdline_mode->tv_mode_specified = true;
cmdline_mode->specified = true; cmdline_mode->specified = true;
return 1; return 1;
@ -1992,6 +2501,31 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
} }
EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector); EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector);
static struct drm_display_mode *drm_named_mode(struct drm_device *dev,
struct drm_cmdline_mode *cmd)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) {
const struct drm_named_mode *named_mode = &drm_named_modes[i];
if (strcmp(cmd->name, named_mode->name))
continue;
if (!cmd->tv_mode_specified)
continue;
return drm_analog_tv_mode(dev,
named_mode->tv_mode,
named_mode->pixel_clock_khz * 1000,
named_mode->xres,
named_mode->yres,
named_mode->flags & DRM_MODE_FLAG_INTERLACE);
}
return NULL;
}
/** /**
* drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode * drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode
* @dev: DRM device to create the new mode for * @dev: DRM device to create the new mode for
@ -2009,7 +2543,9 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev,
if (cmd->xres == 0 || cmd->yres == 0) if (cmd->xres == 0 || cmd->yres == 0)
return NULL; return NULL;
if (cmd->cvt) if (strlen(cmd->name))
mode = drm_named_mode(dev, cmd);
else if (cmd->cvt)
mode = drm_cvt_mode(dev, mode = drm_cvt_mode(dev,
cmd->xres, cmd->yres, cmd->xres, cmd->yres,
cmd->refresh_specified ? cmd->refresh : 60, cmd->refresh_specified ? cmd->refresh : 60,

View File

@ -30,12 +30,6 @@ struct drm_dmi_panel_orientation_data {
int orientation; int orientation;
}; };
static const struct drm_dmi_panel_orientation_data asus_t100ha = {
.width = 800,
.height = 1280,
.orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
};
static const struct drm_dmi_panel_orientation_data gpd_micropc = { static const struct drm_dmi_panel_orientation_data gpd_micropc = {
.width = 720, .width = 720,
.height = 1280, .height = 1280,
@ -97,6 +91,12 @@ static const struct drm_dmi_panel_orientation_data lcd720x1280_rightside_up = {
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
}; };
static const struct drm_dmi_panel_orientation_data lcd800x1280_leftside_up = {
.width = 800,
.height = 1280,
.orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
};
static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = { static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = {
.width = 800, .width = 800,
.height = 1280, .height = 1280,
@ -127,6 +127,12 @@ static const struct drm_dmi_panel_orientation_data lcd1600x2560_leftside_up = {
.orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
}; };
static const struct drm_dmi_panel_orientation_data lcd1600x2560_rightside_up = {
.width = 1600,
.height = 2560,
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
};
static const struct dmi_system_id orientation_data[] = { static const struct dmi_system_id orientation_data[] = {
{ /* Acer One 10 (S1003) */ { /* Acer One 10 (S1003) */
.matches = { .matches = {
@ -151,7 +157,7 @@ static const struct dmi_system_id orientation_data[] = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"),
}, },
.driver_data = (void *)&asus_t100ha, .driver_data = (void *)&lcd800x1280_leftside_up,
}, { /* Asus T101HA */ }, { /* Asus T101HA */
.matches = { .matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
@ -196,6 +202,12 @@ static const struct dmi_system_id orientation_data[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Hi10 pro tablet"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Hi10 pro tablet"),
}, },
.driver_data = (void *)&lcd1200x1920_rightside_up, .driver_data = (void *)&lcd1200x1920_rightside_up,
}, { /* Dynabook K50 */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dynabook Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "dynabook K50/FR"),
},
.driver_data = (void *)&lcd800x1280_leftside_up,
}, { /* GPD MicroPC (generic strings, also match on bios date) */ }, { /* GPD MicroPC (generic strings, also match on bios date) */
.matches = { .matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"),
@ -325,6 +337,13 @@ static const struct dmi_system_id orientation_data[] = {
DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"),
}, },
.driver_data = (void *)&lcd1200x1920_rightside_up, .driver_data = (void *)&lcd1200x1920_rightside_up,
}, { /* Lenovo Yoga Tab 3 X90F */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
},
.driver_data = (void *)&lcd1600x2560_rightside_up,
}, { /* Nanote UMPC-01 */ }, { /* Nanote UMPC-01 */
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "RWC CO.,LTD"), DMI_MATCH(DMI_SYS_VENDOR, "RWC CO.,LTD"),

View File

@ -1146,3 +1146,85 @@ int drm_connector_helper_get_modes(struct drm_connector *connector)
return count; return count;
} }
EXPORT_SYMBOL(drm_connector_helper_get_modes); EXPORT_SYMBOL(drm_connector_helper_get_modes);
/**
* drm_connector_helper_tv_get_modes - Fills the modes availables to a TV connector
* @connector: The connector
*
* Fills the available modes for a TV connector based on the supported
* TV modes, and the default mode expressed by the kernel command line.
*
* This can be used as the default TV connector helper .get_modes() hook
* if the driver does not need any special processing.
*
* Returns:
* The number of modes added to the connector.
*/
int drm_connector_helper_tv_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_property *tv_mode_property =
dev->mode_config.tv_mode_property;
struct drm_cmdline_mode *cmdline = &connector->cmdline_mode;
unsigned int ntsc_modes = BIT(DRM_MODE_TV_MODE_NTSC) |
BIT(DRM_MODE_TV_MODE_NTSC_443) |
BIT(DRM_MODE_TV_MODE_NTSC_J) |
BIT(DRM_MODE_TV_MODE_PAL_M);
unsigned int pal_modes = BIT(DRM_MODE_TV_MODE_PAL) |
BIT(DRM_MODE_TV_MODE_PAL_N) |
BIT(DRM_MODE_TV_MODE_SECAM);
unsigned int tv_modes[2] = { UINT_MAX, UINT_MAX };
unsigned int i, supported_tv_modes = 0;
if (!tv_mode_property)
return 0;
for (i = 0; i < tv_mode_property->num_values; i++)
supported_tv_modes |= BIT(tv_mode_property->values[i]);
if ((supported_tv_modes & ntsc_modes) &&
(supported_tv_modes & pal_modes)) {
uint64_t default_mode;
if (drm_object_property_get_default_value(&connector->base,
tv_mode_property,
&default_mode))
return 0;
if (cmdline->tv_mode_specified)
default_mode = cmdline->tv_mode;
if (BIT(default_mode) & ntsc_modes) {
tv_modes[0] = DRM_MODE_TV_MODE_NTSC;
tv_modes[1] = DRM_MODE_TV_MODE_PAL;
} else {
tv_modes[0] = DRM_MODE_TV_MODE_PAL;
tv_modes[1] = DRM_MODE_TV_MODE_NTSC;
}
} else if (supported_tv_modes & ntsc_modes) {
tv_modes[0] = DRM_MODE_TV_MODE_NTSC;
} else if (supported_tv_modes & pal_modes) {
tv_modes[0] = DRM_MODE_TV_MODE_PAL;
} else {
return 0;
}
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
struct drm_display_mode *mode;
if (tv_modes[i] == DRM_MODE_TV_MODE_NTSC)
mode = drm_mode_analog_ntsc_480i(dev);
else if (tv_modes[i] == DRM_MODE_TV_MODE_PAL)
mode = drm_mode_analog_pal_576i(dev);
else
break;
if (!mode)
return i;
if (!i)
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
}
return i;
}
EXPORT_SYMBOL(drm_connector_helper_tv_get_modes);

View File

@ -267,7 +267,7 @@ static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
WARN_ON_ONCE(pipe->funcs && pipe->funcs->cleanup_fb); WARN_ON_ONCE(pipe->funcs && pipe->funcs->cleanup_fb);
return drm_gem_simple_display_pipe_prepare_fb(pipe, state); return drm_gem_plane_helper_prepare_fb(plane, state);
} }
return pipe->funcs->prepare_fb(pipe, state); return pipe->funcs->prepare_fb(pipe, state);

View File

@ -710,7 +710,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
#ifdef CONFIG_PM
static int exynos5433_decon_suspend(struct device *dev) static int exynos5433_decon_suspend(struct device *dev)
{ {
struct decon_context *ctx = dev_get_drvdata(dev); struct decon_context *ctx = dev_get_drvdata(dev);
@ -741,14 +740,10 @@ err:
return ret; return ret;
} }
#endif
static const struct dev_pm_ops exynos5433_decon_pm_ops = { static DEFINE_RUNTIME_DEV_PM_OPS(exynos5433_decon_pm_ops,
SET_RUNTIME_PM_OPS(exynos5433_decon_suspend, exynos5433_decon_resume, exynos5433_decon_suspend,
NULL) exynos5433_decon_resume, NULL);
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static const struct of_device_id exynos5433_decon_driver_dt_match[] = { static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
{ {
@ -881,7 +876,7 @@ struct platform_driver exynos5433_decon_driver = {
.remove = exynos5433_decon_remove, .remove = exynos5433_decon_remove,
.driver = { .driver = {
.name = "exynos5433-decon", .name = "exynos5433-decon",
.pm = &exynos5433_decon_pm_ops, .pm = pm_ptr(&exynos5433_decon_pm_ops),
.of_match_table = exynos5433_decon_driver_dt_match, .of_match_table = exynos5433_decon_driver_dt_match,
}, },
}; };

View File

@ -779,7 +779,6 @@ static int decon_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int exynos7_decon_suspend(struct device *dev) static int exynos7_decon_suspend(struct device *dev)
{ {
struct decon_context *ctx = dev_get_drvdata(dev); struct decon_context *ctx = dev_get_drvdata(dev);
@ -836,21 +835,16 @@ err_aclk_enable:
err_pclk_enable: err_pclk_enable:
return ret; return ret;
} }
#endif
static const struct dev_pm_ops exynos7_decon_pm_ops = { static DEFINE_RUNTIME_DEV_PM_OPS(exynos7_decon_pm_ops, exynos7_decon_suspend,
SET_RUNTIME_PM_OPS(exynos7_decon_suspend, exynos7_decon_resume, exynos7_decon_resume, NULL);
NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
struct platform_driver decon_driver = { struct platform_driver decon_driver = {
.probe = decon_probe, .probe = decon_probe,
.remove = decon_remove, .remove = decon_remove,
.driver = { .driver = {
.name = "exynos-decon", .name = "exynos-decon",
.pm = &exynos7_decon_pm_ops, .pm = pm_ptr(&exynos7_decon_pm_ops),
.of_match_table = decon_driver_dt_match, .of_match_table = decon_driver_dt_match,
}, },
}; };

View File

@ -260,7 +260,6 @@ static int exynos_dp_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int exynos_dp_suspend(struct device *dev) static int exynos_dp_suspend(struct device *dev)
{ {
struct exynos_dp_device *dp = dev_get_drvdata(dev); struct exynos_dp_device *dp = dev_get_drvdata(dev);
@ -274,13 +273,9 @@ static int exynos_dp_resume(struct device *dev)
return analogix_dp_resume(dp->adp); return analogix_dp_resume(dp->adp);
} }
#endif
static const struct dev_pm_ops exynos_dp_pm_ops = { static DEFINE_RUNTIME_DEV_PM_OPS(exynos_dp_pm_ops, exynos_dp_suspend,
SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL) exynos_dp_resume, NULL);
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static const struct of_device_id exynos_dp_match[] = { static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" }, { .compatible = "samsung,exynos5-dp" },
@ -294,7 +289,7 @@ struct platform_driver dp_driver = {
.driver = { .driver = {
.name = "exynos-dp", .name = "exynos-dp",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &exynos_dp_pm_ops, .pm = pm_ptr(&exynos_dp_pm_ops),
.of_match_table = exynos_dp_match, .of_match_table = exynos_dp_match,
}, },
}; };

View File

@ -1381,7 +1381,6 @@ static int fimc_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int fimc_runtime_suspend(struct device *dev) static int fimc_runtime_suspend(struct device *dev)
{ {
struct fimc_context *ctx = get_fimc_context(dev); struct fimc_context *ctx = get_fimc_context(dev);
@ -1398,13 +1397,9 @@ static int fimc_runtime_resume(struct device *dev)
DRM_DEV_DEBUG_KMS(dev, "id[%d]\n", ctx->id); DRM_DEV_DEBUG_KMS(dev, "id[%d]\n", ctx->id);
return clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]); return clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]);
} }
#endif
static const struct dev_pm_ops fimc_pm_ops = { static DEFINE_RUNTIME_DEV_PM_OPS(fimc_pm_ops, fimc_runtime_suspend,
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, fimc_runtime_resume, NULL);
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL)
};
static const struct of_device_id fimc_of_match[] = { static const struct of_device_id fimc_of_match[] = {
{ .compatible = "samsung,exynos4210-fimc" }, { .compatible = "samsung,exynos4210-fimc" },
@ -1420,6 +1415,6 @@ struct platform_driver fimc_driver = {
.of_match_table = fimc_of_match, .of_match_table = fimc_of_match,
.name = "exynos-drm-fimc", .name = "exynos-drm-fimc",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &fimc_pm_ops, .pm = pm_ptr(&fimc_pm_ops),
}, },
}; };

View File

@ -1287,7 +1287,6 @@ static int fimd_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int exynos_fimd_suspend(struct device *dev) static int exynos_fimd_suspend(struct device *dev)
{ {
struct fimd_context *ctx = dev_get_drvdata(dev); struct fimd_context *ctx = dev_get_drvdata(dev);
@ -1321,13 +1320,9 @@ static int exynos_fimd_resume(struct device *dev)
return 0; return 0;
} }
#endif
static const struct dev_pm_ops exynos_fimd_pm_ops = { static DEFINE_RUNTIME_DEV_PM_OPS(exynos_fimd_pm_ops, exynos_fimd_suspend,
SET_RUNTIME_PM_OPS(exynos_fimd_suspend, exynos_fimd_resume, NULL) exynos_fimd_resume, NULL);
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
struct platform_driver fimd_driver = { struct platform_driver fimd_driver = {
.probe = fimd_probe, .probe = fimd_probe,
@ -1335,7 +1330,7 @@ struct platform_driver fimd_driver = {
.driver = { .driver = {
.name = "exynos4-fb", .name = "exynos4-fb",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &exynos_fimd_pm_ops, .pm = pm_ptr(&exynos_fimd_pm_ops),
.of_match_table = fimd_driver_dt_match, .of_match_table = fimd_driver_dt_match,
}, },
}; };

View File

@ -1549,7 +1549,6 @@ static int g2d_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int g2d_suspend(struct device *dev) static int g2d_suspend(struct device *dev)
{ {
struct g2d_data *g2d = dev_get_drvdata(dev); struct g2d_data *g2d = dev_get_drvdata(dev);
@ -1574,9 +1573,7 @@ static int g2d_resume(struct device *dev)
return 0; return 0;
} }
#endif
#ifdef CONFIG_PM
static int g2d_runtime_suspend(struct device *dev) static int g2d_runtime_suspend(struct device *dev)
{ {
struct g2d_data *g2d = dev_get_drvdata(dev); struct g2d_data *g2d = dev_get_drvdata(dev);
@ -1597,11 +1594,10 @@ static int g2d_runtime_resume(struct device *dev)
return ret; return ret;
} }
#endif
static const struct dev_pm_ops g2d_pm_ops = { static const struct dev_pm_ops g2d_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(g2d_suspend, g2d_resume) SYSTEM_SLEEP_PM_OPS(g2d_suspend, g2d_resume)
SET_RUNTIME_PM_OPS(g2d_runtime_suspend, g2d_runtime_resume, NULL) RUNTIME_PM_OPS(g2d_runtime_suspend, g2d_runtime_resume, NULL)
}; };
static const struct of_device_id exynos_g2d_match[] = { static const struct of_device_id exynos_g2d_match[] = {
@ -1617,7 +1613,7 @@ struct platform_driver g2d_driver = {
.driver = { .driver = {
.name = "exynos-drm-g2d", .name = "exynos-drm-g2d",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &g2d_pm_ops, .pm = pm_ptr(&g2d_pm_ops),
.of_match_table = exynos_g2d_match, .of_match_table = exynos_g2d_match,
}, },
}; };

View File

@ -340,7 +340,6 @@ static const struct component_ops exynos_mic_component_ops = {
.unbind = exynos_mic_unbind, .unbind = exynos_mic_unbind,
}; };
#ifdef CONFIG_PM
static int exynos_mic_suspend(struct device *dev) static int exynos_mic_suspend(struct device *dev)
{ {
struct exynos_mic *mic = dev_get_drvdata(dev); struct exynos_mic *mic = dev_get_drvdata(dev);
@ -369,13 +368,9 @@ static int exynos_mic_resume(struct device *dev)
} }
return 0; return 0;
} }
#endif
static const struct dev_pm_ops exynos_mic_pm_ops = { static DEFINE_RUNTIME_DEV_PM_OPS(exynos_mic_pm_ops, exynos_mic_suspend,
SET_RUNTIME_PM_OPS(exynos_mic_suspend, exynos_mic_resume, NULL) exynos_mic_resume, NULL);
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static int exynos_mic_probe(struct platform_device *pdev) static int exynos_mic_probe(struct platform_device *pdev)
{ {
@ -470,7 +465,7 @@ struct platform_driver mic_driver = {
.remove = exynos_mic_remove, .remove = exynos_mic_remove,
.driver = { .driver = {
.name = "exynos-mic", .name = "exynos-mic",
.pm = &exynos_mic_pm_ops, .pm = pm_ptr(&exynos_mic_pm_ops),
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = exynos_mic_of_match, .of_match_table = exynos_mic_of_match,
}, },

View File

@ -340,7 +340,6 @@ static int rotator_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int rotator_runtime_suspend(struct device *dev) static int rotator_runtime_suspend(struct device *dev)
{ {
struct rot_context *rot = dev_get_drvdata(dev); struct rot_context *rot = dev_get_drvdata(dev);
@ -355,7 +354,6 @@ static int rotator_runtime_resume(struct device *dev)
return clk_prepare_enable(rot->clock); return clk_prepare_enable(rot->clock);
} }
#endif
static const struct drm_exynos_ipp_limit rotator_s5pv210_rbg888_limits[] = { static const struct drm_exynos_ipp_limit rotator_s5pv210_rbg888_limits[] = {
{ IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) }, { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) },
@ -450,12 +448,8 @@ static const struct of_device_id exynos_rotator_match[] = {
}; };
MODULE_DEVICE_TABLE(of, exynos_rotator_match); MODULE_DEVICE_TABLE(of, exynos_rotator_match);
static const struct dev_pm_ops rotator_pm_ops = { static DEFINE_RUNTIME_DEV_PM_OPS(rotator_pm_ops, rotator_runtime_suspend,
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, rotator_runtime_resume, NULL);
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
NULL)
};
struct platform_driver rotator_driver = { struct platform_driver rotator_driver = {
.probe = rotator_probe, .probe = rotator_probe,
@ -463,7 +457,7 @@ struct platform_driver rotator_driver = {
.driver = { .driver = {
.name = "exynos-rotator", .name = "exynos-rotator",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &rotator_pm_ops, .pm = pm_ptr(&rotator_pm_ops),
.of_match_table = exynos_rotator_match, .of_match_table = exynos_rotator_match,
}, },
}; };

View File

@ -550,8 +550,6 @@ static int scaler_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int clk_disable_unprepare_wrapper(struct clk *clk) static int clk_disable_unprepare_wrapper(struct clk *clk)
{ {
clk_disable_unprepare(clk); clk_disable_unprepare(clk);
@ -584,13 +582,9 @@ static int scaler_runtime_resume(struct device *dev)
return scaler_clk_ctrl(scaler, true); return scaler_clk_ctrl(scaler, true);
} }
#endif
static const struct dev_pm_ops scaler_pm_ops = { static DEFINE_RUNTIME_DEV_PM_OPS(scaler_pm_ops, scaler_runtime_suspend,
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, scaler_runtime_resume, NULL);
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(scaler_runtime_suspend, scaler_runtime_resume, NULL)
};
static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_hv_limits[] = { static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_hv_limits[] = {
{ IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) }, { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) },
@ -731,7 +725,7 @@ struct platform_driver scaler_driver = {
.driver = { .driver = {
.name = "exynos-scaler", .name = "exynos-scaler",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &scaler_pm_ops, .pm = pm_ptr(&scaler_pm_ops),
.of_match_table = exynos_scaler_match, .of_match_table = exynos_scaler_match,
}, },
}; };

View File

@ -303,7 +303,7 @@ static int gud_connector_atomic_check(struct drm_connector *connector,
old_state->tv.margins.right != new_state->tv.margins.right || old_state->tv.margins.right != new_state->tv.margins.right ||
old_state->tv.margins.top != new_state->tv.margins.top || old_state->tv.margins.top != new_state->tv.margins.top ||
old_state->tv.margins.bottom != new_state->tv.margins.bottom || old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
old_state->tv.mode != new_state->tv.mode || old_state->tv.legacy_mode != new_state->tv.legacy_mode ||
old_state->tv.brightness != new_state->tv.brightness || old_state->tv.brightness != new_state->tv.brightness ||
old_state->tv.contrast != new_state->tv.contrast || old_state->tv.contrast != new_state->tv.contrast ||
old_state->tv.flicker_reduction != new_state->tv.flicker_reduction || old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
@ -400,7 +400,7 @@ static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connect
for (i = 0; i < num_modes; i++) for (i = 0; i < num_modes; i++)
modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN]; modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes); ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes);
free: free:
kfree(buf); kfree(buf);
if (ret < 0) if (ret < 0)
@ -424,7 +424,7 @@ gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
case GUD_PROPERTY_TV_BOTTOM_MARGIN: case GUD_PROPERTY_TV_BOTTOM_MARGIN:
return config->tv_bottom_margin_property; return config->tv_bottom_margin_property;
case GUD_PROPERTY_TV_MODE: case GUD_PROPERTY_TV_MODE:
return config->tv_mode_property; return config->legacy_tv_mode_property;
case GUD_PROPERTY_TV_BRIGHTNESS: case GUD_PROPERTY_TV_BRIGHTNESS:
return config->tv_brightness_property; return config->tv_brightness_property;
case GUD_PROPERTY_TV_CONTRAST: case GUD_PROPERTY_TV_CONTRAST:
@ -454,7 +454,7 @@ static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connecto
case GUD_PROPERTY_TV_BOTTOM_MARGIN: case GUD_PROPERTY_TV_BOTTOM_MARGIN:
return &state->margins.bottom; return &state->margins.bottom;
case GUD_PROPERTY_TV_MODE: case GUD_PROPERTY_TV_MODE:
return &state->mode; return &state->legacy_mode;
case GUD_PROPERTY_TV_BRIGHTNESS: case GUD_PROPERTY_TV_BRIGHTNESS:
return &state->brightness; return &state->brightness;
case GUD_PROPERTY_TV_CONTRAST: case GUD_PROPERTY_TV_CONTRAST:
@ -539,7 +539,7 @@ static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_conn
fallthrough; fallthrough;
case GUD_PROPERTY_TV_HUE: case GUD_PROPERTY_TV_HUE:
/* This is a no-op if already added. */ /* This is a no-op if already added. */
ret = drm_mode_create_tv_properties(drm, 0, NULL); ret = drm_mode_create_tv_properties_legacy(drm, 0, NULL);
if (ret) if (ret)
goto out; goto out;
break; break;

View File

@ -365,6 +365,7 @@ static void gud_debugfs_init(struct drm_minor *minor)
static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = { static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = {
.check = gud_pipe_check, .check = gud_pipe_check,
.update = gud_pipe_update, .update = gud_pipe_update,
DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS
}; };
static const struct drm_mode_config_funcs gud_mode_config_funcs = { static const struct drm_mode_config_funcs gud_mode_config_funcs = {

View File

@ -43,6 +43,7 @@ struct gud_device {
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
struct drm_rect damage; struct drm_rect damage;
bool prev_flush_failed; bool prev_flush_failed;
void *shadow_buf;
}; };
static inline struct gud_device *to_gud_device(struct drm_device *drm) static inline struct gud_device *to_gud_device(struct drm_device *drm)

View File

@ -5,6 +5,7 @@
#include <linux/lz4.h> #include <linux/lz4.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
@ -15,6 +16,7 @@
#include <drm/drm_fourcc.h> #include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h> #include <drm/drm_framebuffer.h>
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_rect.h> #include <drm/drm_rect.h>
@ -24,17 +26,13 @@
#include "gud_internal.h" #include "gud_internal.h"
/* /*
* Some userspace rendering loops runs all displays in the same loop. * Some userspace rendering loops run all displays in the same loop.
* This means that a fast display will have to wait for a slow one. * This means that a fast display will have to wait for a slow one.
* For this reason gud does flushing asynchronous by default. * Such users might want to enable this module parameter.
* The down side is that in e.g. a single display setup userspace thinks
* the display is insanely fast since the driver reports back immediately
* that the flush/pageflip is done. This wastes CPU and power.
* Such users might want to set this module parameter to false.
*/ */
static bool gud_async_flush = true; static bool gud_async_flush;
module_param_named(async_flush, gud_async_flush, bool, 0644); module_param_named(async_flush, gud_async_flush, bool, 0644);
MODULE_PARM_DESC(async_flush, "Enable asynchronous flushing [default=true]"); MODULE_PARM_DESC(async_flush, "Enable asynchronous flushing [default=0]");
/* /*
* FIXME: The driver is probably broken on Big Endian machines. * FIXME: The driver is probably broken on Big Endian machines.
@ -152,32 +150,21 @@ static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *forma
} }
static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb, static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
const struct iosys_map *src, bool cached_reads,
const struct drm_format_info *format, struct drm_rect *rect, const struct drm_format_info *format, struct drm_rect *rect,
struct gud_set_buffer_req *req) struct gud_set_buffer_req *req)
{ {
struct dma_buf_attachment *import_attach = fb->obj[0]->import_attach;
u8 compression = gdrm->compression; u8 compression = gdrm->compression;
struct iosys_map map[DRM_FORMAT_MAX_PLANES];
struct iosys_map map_data[DRM_FORMAT_MAX_PLANES];
struct iosys_map dst; struct iosys_map dst;
void *vaddr, *buf; void *vaddr, *buf;
size_t pitch, len; size_t pitch, len;
int ret = 0;
pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(rect)); pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(rect));
len = pitch * drm_rect_height(rect); len = pitch * drm_rect_height(rect);
if (len > gdrm->bulk_len) if (len > gdrm->bulk_len)
return -E2BIG; return -E2BIG;
ret = drm_gem_fb_vmap(fb, map, map_data); vaddr = src[0].vaddr;
if (ret)
return ret;
vaddr = map_data[0].vaddr;
ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
if (ret)
goto vunmap;
retry: retry:
if (compression) if (compression)
buf = gdrm->compress_buf; buf = gdrm->compress_buf;
@ -192,29 +179,27 @@ retry:
if (format != fb->format) { if (format != fb->format) {
if (format->format == GUD_DRM_FORMAT_R1) { if (format->format == GUD_DRM_FORMAT_R1) {
len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect); len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect);
if (!len) { if (!len)
ret = -ENOMEM; return -ENOMEM;
goto end_cpu_access;
}
} else if (format->format == DRM_FORMAT_R8) { } else if (format->format == DRM_FORMAT_R8) {
drm_fb_xrgb8888_to_gray8(&dst, NULL, map_data, fb, rect); drm_fb_xrgb8888_to_gray8(&dst, NULL, src, fb, rect);
} else if (format->format == DRM_FORMAT_RGB332) { } else if (format->format == DRM_FORMAT_RGB332) {
drm_fb_xrgb8888_to_rgb332(&dst, NULL, map_data, fb, rect); drm_fb_xrgb8888_to_rgb332(&dst, NULL, src, fb, rect);
} else if (format->format == DRM_FORMAT_RGB565) { } else if (format->format == DRM_FORMAT_RGB565) {
drm_fb_xrgb8888_to_rgb565(&dst, NULL, map_data, fb, rect, drm_fb_xrgb8888_to_rgb565(&dst, NULL, src, fb, rect,
gud_is_big_endian()); gud_is_big_endian());
} else if (format->format == DRM_FORMAT_RGB888) { } else if (format->format == DRM_FORMAT_RGB888) {
drm_fb_xrgb8888_to_rgb888(&dst, NULL, map_data, fb, rect); drm_fb_xrgb8888_to_rgb888(&dst, NULL, src, fb, rect);
} else { } else {
len = gud_xrgb8888_to_color(buf, format, vaddr, fb, rect); len = gud_xrgb8888_to_color(buf, format, vaddr, fb, rect);
} }
} else if (gud_is_big_endian() && format->cpp[0] > 1) { } else if (gud_is_big_endian() && format->cpp[0] > 1) {
drm_fb_swab(&dst, NULL, map_data, fb, rect, !import_attach); drm_fb_swab(&dst, NULL, src, fb, rect, cached_reads);
} else if (compression && !import_attach && pitch == fb->pitches[0]) { } else if (compression && cached_reads && pitch == fb->pitches[0]) {
/* can compress directly from the framebuffer */ /* can compress directly from the framebuffer */
buf = vaddr + rect->y1 * pitch; buf = vaddr + rect->y1 * pitch;
} else { } else {
drm_fb_memcpy(&dst, NULL, map_data, fb, rect); drm_fb_memcpy(&dst, NULL, src, fb, rect);
} }
memset(req, 0, sizeof(*req)); memset(req, 0, sizeof(*req));
@ -237,12 +222,7 @@ retry:
req->compressed_length = cpu_to_le32(complen); req->compressed_length = cpu_to_le32(complen);
} }
end_cpu_access: return 0;
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
vunmap:
drm_gem_fb_vunmap(fb, map);
return ret;
} }
struct gud_usb_bulk_context { struct gud_usb_bulk_context {
@ -285,6 +265,7 @@ static int gud_usb_bulk(struct gud_device *gdrm, size_t len)
} }
static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb, static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb,
const struct iosys_map *src, bool cached_reads,
const struct drm_format_info *format, struct drm_rect *rect) const struct drm_format_info *format, struct drm_rect *rect)
{ {
struct gud_set_buffer_req req; struct gud_set_buffer_req req;
@ -293,7 +274,7 @@ static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb,
drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
ret = gud_prep_flush(gdrm, fb, format, rect, &req); ret = gud_prep_flush(gdrm, fb, src, cached_reads, format, rect, &req);
if (ret) if (ret)
return ret; return ret;
@ -333,46 +314,51 @@ void gud_clear_damage(struct gud_device *gdrm)
gdrm->damage.y2 = 0; gdrm->damage.y2 = 0;
} }
static void gud_add_damage(struct gud_device *gdrm, struct drm_rect *damage) static void gud_flush_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
const struct iosys_map *src, bool cached_reads,
struct drm_rect *damage)
{ {
gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1); const struct drm_format_info *format;
gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1); unsigned int i, lines;
gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2); size_t pitch;
gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2); int ret;
}
static void gud_retry_failed_flush(struct gud_device *gdrm, struct drm_framebuffer *fb, format = fb->format;
struct drm_rect *damage) if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
{ format = gdrm->xrgb8888_emulation_format;
/*
* pipe_update waits for the worker when the display mode is going to change.
* This ensures that the width and height is still the same making it safe to
* add back the damage.
*/
mutex_lock(&gdrm->damage_lock); /* Split update if it's too big */
if (!gdrm->fb) { pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(damage));
drm_framebuffer_get(fb); lines = drm_rect_height(damage);
gdrm->fb = fb;
if (gdrm->bulk_len < lines * pitch)
lines = gdrm->bulk_len / pitch;
for (i = 0; i < DIV_ROUND_UP(drm_rect_height(damage), lines); i++) {
struct drm_rect rect = *damage;
rect.y1 += i * lines;
rect.y2 = min_t(u32, rect.y1 + lines, damage->y2);
ret = gud_flush_rect(gdrm, fb, src, cached_reads, format, &rect);
if (ret) {
if (ret != -ENODEV && ret != -ECONNRESET &&
ret != -ESHUTDOWN && ret != -EPROTO)
dev_err_ratelimited(fb->dev->dev,
"Failed to flush framebuffer: error=%d\n", ret);
gdrm->prev_flush_failed = true;
break;
}
} }
gud_add_damage(gdrm, damage);
mutex_unlock(&gdrm->damage_lock);
/* Retry only once to avoid a possible storm in case of continues errors. */
if (!gdrm->prev_flush_failed)
queue_work(system_long_wq, &gdrm->work);
gdrm->prev_flush_failed = true;
} }
void gud_flush_work(struct work_struct *work) void gud_flush_work(struct work_struct *work)
{ {
struct gud_device *gdrm = container_of(work, struct gud_device, work); struct gud_device *gdrm = container_of(work, struct gud_device, work);
const struct drm_format_info *format; struct iosys_map shadow_map;
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
struct drm_rect damage; struct drm_rect damage;
unsigned int i, lines; int idx;
int idx, ret = 0;
size_t pitch;
if (!drm_dev_enter(&gdrm->drm, &idx)) if (!drm_dev_enter(&gdrm->drm, &idx))
return; return;
@ -380,6 +366,7 @@ void gud_flush_work(struct work_struct *work)
mutex_lock(&gdrm->damage_lock); mutex_lock(&gdrm->damage_lock);
fb = gdrm->fb; fb = gdrm->fb;
gdrm->fb = NULL; gdrm->fb = NULL;
iosys_map_set_vaddr(&shadow_map, gdrm->shadow_buf);
damage = gdrm->damage; damage = gdrm->damage;
gud_clear_damage(gdrm); gud_clear_damage(gdrm);
mutex_unlock(&gdrm->damage_lock); mutex_unlock(&gdrm->damage_lock);
@ -387,59 +374,43 @@ void gud_flush_work(struct work_struct *work)
if (!fb) if (!fb)
goto out; goto out;
format = fb->format; gud_flush_damage(gdrm, fb, &shadow_map, true, &damage);
if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
format = gdrm->xrgb8888_emulation_format;
/* Split update if it's too big */
pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(&damage));
lines = drm_rect_height(&damage);
if (gdrm->bulk_len < lines * pitch)
lines = gdrm->bulk_len / pitch;
for (i = 0; i < DIV_ROUND_UP(drm_rect_height(&damage), lines); i++) {
struct drm_rect rect = damage;
rect.y1 += i * lines;
rect.y2 = min_t(u32, rect.y1 + lines, damage.y2);
ret = gud_flush_rect(gdrm, fb, format, &rect);
if (ret) {
if (ret != -ENODEV && ret != -ECONNRESET &&
ret != -ESHUTDOWN && ret != -EPROTO) {
bool prev_flush_failed = gdrm->prev_flush_failed;
gud_retry_failed_flush(gdrm, fb, &damage);
if (!prev_flush_failed)
dev_err_ratelimited(fb->dev->dev,
"Failed to flush framebuffer: error=%d\n", ret);
}
break;
}
gdrm->prev_flush_failed = false;
}
drm_framebuffer_put(fb); drm_framebuffer_put(fb);
out: out:
drm_dev_exit(idx); drm_dev_exit(idx);
} }
static void gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb, static int gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
struct drm_rect *damage) const struct iosys_map *src, struct drm_rect *damage)
{ {
struct drm_framebuffer *old_fb = NULL; struct drm_framebuffer *old_fb = NULL;
struct iosys_map shadow_map;
mutex_lock(&gdrm->damage_lock); mutex_lock(&gdrm->damage_lock);
if (!gdrm->shadow_buf) {
gdrm->shadow_buf = vzalloc(fb->pitches[0] * fb->height);
if (!gdrm->shadow_buf) {
mutex_unlock(&gdrm->damage_lock);
return -ENOMEM;
}
}
iosys_map_set_vaddr(&shadow_map, gdrm->shadow_buf);
iosys_map_incr(&shadow_map, drm_fb_clip_offset(fb->pitches[0], fb->format, damage));
drm_fb_memcpy(&shadow_map, fb->pitches, src, fb, damage);
if (fb != gdrm->fb) { if (fb != gdrm->fb) {
old_fb = gdrm->fb; old_fb = gdrm->fb;
drm_framebuffer_get(fb); drm_framebuffer_get(fb);
gdrm->fb = fb; gdrm->fb = fb;
} }
gud_add_damage(gdrm, damage); gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1);
gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1);
gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2);
gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2);
mutex_unlock(&gdrm->damage_lock); mutex_unlock(&gdrm->damage_lock);
@ -447,6 +418,26 @@ static void gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer
if (old_fb) if (old_fb)
drm_framebuffer_put(old_fb); drm_framebuffer_put(old_fb);
return 0;
}
static void gud_fb_handle_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
const struct iosys_map *src, struct drm_rect *damage)
{
int ret;
if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE)
drm_rect_init(damage, 0, 0, fb->width, fb->height);
if (gud_async_flush) {
ret = gud_fb_queue_damage(gdrm, fb, src, damage);
if (ret != -ENOMEM)
return;
}
/* Imported buffers are assumed to be WriteCombined with uncached reads */
gud_flush_damage(gdrm, fb, src, !fb->obj[0]->import_attach, damage);
} }
int gud_pipe_check(struct drm_simple_display_pipe *pipe, int gud_pipe_check(struct drm_simple_display_pipe *pipe,
@ -571,10 +562,11 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_device *drm = pipe->crtc.dev; struct drm_device *drm = pipe->crtc.dev;
struct gud_device *gdrm = to_gud_device(drm); struct gud_device *gdrm = to_gud_device(drm);
struct drm_plane_state *state = pipe->plane.state; struct drm_plane_state *state = pipe->plane.state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
struct drm_framebuffer *fb = state->fb; struct drm_framebuffer *fb = state->fb;
struct drm_crtc *crtc = &pipe->crtc; struct drm_crtc *crtc = &pipe->crtc;
struct drm_rect damage; struct drm_rect damage;
int idx; int ret, idx;
if (crtc->state->mode_changed || !crtc->state->enable) { if (crtc->state->mode_changed || !crtc->state->enable) {
cancel_work_sync(&gdrm->work); cancel_work_sync(&gdrm->work);
@ -584,6 +576,8 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe,
gdrm->fb = NULL; gdrm->fb = NULL;
} }
gud_clear_damage(gdrm); gud_clear_damage(gdrm);
vfree(gdrm->shadow_buf);
gdrm->shadow_buf = NULL;
mutex_unlock(&gdrm->damage_lock); mutex_unlock(&gdrm->damage_lock);
} }
@ -599,14 +593,19 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe,
if (crtc->state->active_changed) if (crtc->state->active_changed)
gud_usb_set_u8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active); gud_usb_set_u8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active);
if (drm_atomic_helper_damage_merged(old_state, state, &damage)) { if (!fb)
if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE) goto ctrl_disable;
drm_rect_init(&damage, 0, 0, fb->width, fb->height);
gud_fb_queue_damage(gdrm, fb, &damage);
if (!gud_async_flush)
flush_work(&gdrm->work);
}
ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
if (ret)
goto ctrl_disable;
if (drm_atomic_helper_damage_merged(old_state, state, &damage))
gud_fb_handle_damage(gdrm, fb, &shadow_plane_state->data[0], &damage);
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
ctrl_disable:
if (!crtc->state->enable) if (!crtc->state->enable)
gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0); gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);

View File

@ -106,7 +106,7 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv)
dev->mode_config.max_width = 1920; dev->mode_config.max_width = 1920;
dev->mode_config.max_height = 1200; dev->mode_config.max_height = 1200;
dev->mode_config.preferred_depth = 32; dev->mode_config.preferred_depth = 24;
dev->mode_config.prefer_shadow = 1; dev->mode_config.prefer_shadow = 1;
dev->mode_config.funcs = (void *)&hibmc_mode_funcs; dev->mode_config.funcs = (void *)&hibmc_mode_funcs;
@ -340,7 +340,7 @@ static int hibmc_pci_probe(struct pci_dev *pdev,
goto err_unload; goto err_unload;
} }
drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth); drm_fbdev_generic_setup(dev, 32);
return 0; return 0;

View File

@ -250,7 +250,7 @@ static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct drm_mode_config *conf = &dev->mode_config; struct drm_mode_config *conf = &dev->mode_config;
drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names); drm_mode_create_tv_properties_legacy(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
priv->scale_property = drm_property_create_range(dev, 0, "scale", 0, 2); priv->scale_property = drm_property_create_range(dev, 0, "scale", 0, 2);
if (!priv->scale_property) if (!priv->scale_property)
@ -264,8 +264,8 @@ static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
priv->hmargin); priv->hmargin);
drm_object_attach_property(&connector->base, conf->tv_bottom_margin_property, drm_object_attach_property(&connector->base, conf->tv_bottom_margin_property,
priv->vmargin); priv->vmargin);
drm_object_attach_property(&connector->base, conf->tv_mode_property, drm_object_attach_property(&connector->base, conf->legacy_tv_mode_property,
priv->norm); priv->norm);
drm_object_attach_property(&connector->base, conf->tv_brightness_property, drm_object_attach_property(&connector->base, conf->tv_brightness_property,
priv->brightness); priv->brightness);
drm_object_attach_property(&connector->base, conf->tv_contrast_property, drm_object_attach_property(&connector->base, conf->tv_contrast_property,
@ -315,7 +315,7 @@ static int ch7006_encoder_set_property(struct drm_encoder *encoder,
ch7006_load_reg(client, state, CH7006_POV); ch7006_load_reg(client, state, CH7006_POV);
ch7006_load_reg(client, state, CH7006_VPOS); ch7006_load_reg(client, state, CH7006_VPOS);
} else if (property == conf->tv_mode_property) { } else if (property == conf->legacy_tv_mode_property) {
if (connector->dpms != DRM_MODE_DPMS_OFF) if (connector->dpms != DRM_MODE_DPMS_OFF)
return -EINVAL; return -EINVAL;

View File

@ -1905,10 +1905,10 @@ static void intel_tv_add_properties(struct drm_connector *connector)
tv_format_names[i] = tv_modes[i].name; tv_format_names[i] = tv_modes[i].name;
} }
drm_mode_create_tv_properties(&i915->drm, i, tv_format_names); drm_mode_create_tv_properties_legacy(&i915->drm, i, tv_format_names);
drm_object_attach_property(&connector->base, drm_object_attach_property(&connector->base,
i915->drm.mode_config.tv_mode_property, i915->drm.mode_config.legacy_tv_mode_property,
conn_state->tv.mode); conn_state->tv.mode);
drm_object_attach_property(&connector->base, drm_object_attach_property(&connector->base,
i915->drm.mode_config.tv_left_margin_property, i915->drm.mode_config.tv_left_margin_property,

View File

@ -10,7 +10,7 @@
#include <linux/mmu_notifier.h> #include <linux/mmu_notifier.h>
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo.h>
#include <uapi/drm/i915_drm.h> #include <uapi/drm/i915_drm.h>
#include "i915_active.h" #include "i915_active.h"

View File

@ -5,8 +5,8 @@
#include <linux/shmem_fs.h> #include <linux/shmem_fs.h>
#include <drm/ttm/ttm_bo_driver.h>
#include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_tt.h>
#include <drm/drm_buddy.h> #include <drm/drm_buddy.h>
#include "i915_drv.h" #include "i915_drv.h"
@ -599,13 +599,16 @@ i915_ttm_resource_get_st(struct drm_i915_gem_object *obj,
static int i915_ttm_truncate(struct drm_i915_gem_object *obj) static int i915_ttm_truncate(struct drm_i915_gem_object *obj)
{ {
struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
int err; long err;
WARN_ON_ONCE(obj->mm.madv == I915_MADV_WILLNEED); WARN_ON_ONCE(obj->mm.madv == I915_MADV_WILLNEED);
err = ttm_bo_wait(bo, true, false); err = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP,
if (err) true, 15 * HZ);
if (err < 0)
return err; return err;
if (err == 0)
return -EBUSY;
err = i915_ttm_move_notify(bo); err = i915_ttm_move_notify(bo);
if (err) if (err)

View File

@ -3,7 +3,7 @@
* Copyright © 2021 Intel Corporation * Copyright © 2021 Intel Corporation
*/ */
#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_tt.h>
#include "i915_deps.h" #include "i915_deps.h"
#include "i915_drv.h" #include "i915_drv.h"

View File

@ -164,7 +164,6 @@ sysfs_gt_attribute_r_func(struct kobject *kobj, struct attribute *attr,
NULL); \ NULL); \
INTEL_GT_ATTR_RO(_name) INTEL_GT_ATTR_RO(_name)
#ifdef CONFIG_PM
static u32 get_residency(struct intel_gt *gt, enum intel_rc6_res_type id) static u32 get_residency(struct intel_gt *gt, enum intel_rc6_res_type id)
{ {
intel_wakeref_t wakeref; intel_wakeref_t wakeref;
@ -300,7 +299,7 @@ static void intel_sysfs_rc6_init(struct intel_gt *gt, struct kobject *kobj)
{ {
int ret; int ret;
if (!HAS_RC6(gt->i915)) if (!IS_ENABLED(CONFIG_PM) || !HAS_RC6(gt->i915))
return; return;
ret = __intel_gt_sysfs_create_group(kobj, rc6_attr_group); ret = __intel_gt_sysfs_create_group(kobj, rc6_attr_group);
@ -329,11 +328,6 @@ static void intel_sysfs_rc6_init(struct intel_gt *gt, struct kobject *kobj)
gt->info.id, ERR_PTR(ret)); gt->info.id, ERR_PTR(ret));
} }
} }
#else
static void intel_sysfs_rc6_init(struct intel_gt *gt, struct kobject *kobj)
{
}
#endif /* CONFIG_PM */
static u32 __act_freq_mhz_show(struct intel_gt *gt) static u32 __act_freq_mhz_show(struct intel_gt *gt)
{ {

View File

@ -6,7 +6,7 @@
#include <linux/dma-fence.h> #include <linux/dma-fence.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo.h>
#include "i915_deps.h" #include "i915_deps.h"

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