drm-misc-next for 5.5:

UAPI Changes:
 -Colorspace: Expose different prop values for DP vs. HDMI (Gwan-gyeong Mun)
 -fourcc: Add DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED (Raymond)
 -not_actually: s/ENOTSUPP/EOPNOTSUPP/ in drm_edid and drm_mipi_dbi. This should
     not reach userspace, but adding here to specifically call that out (Daniel)
 -i810: Prevent underflow in dispatch ioctls (Dan)
 -komeda: Add ACLK sysfs attribute (Mihail)
 -v3d: Allow userspace to clean up after render jobs (Iago)
 
 Cross-subsystem Changes:
 -MAINTAINERS:
  -Add Alyssa & Steven as panfrost reviewers (Rob)
  -Add Jernej as DE2 reviewer (Maxime)
  -Add Chen-Yu as Allwinner maintainer (Maxime)
 -staging: Make some stack arrays static const (Colin)
 
 Core Changes:
 -ttm: Allow drivers to specify their vma manager (to use gem mgr) (Gerd)
 -docs: Various fixes in connector/encoder/bridge docs (Daniel, Lyude, Laurent)
 -connector: Allow more than 3 possible encoders for a connector (José)
 -dp_cec: Allow a connector to be associated with a cec device (Dariusz)
 -various: Fix some compile/sparse warnings (Ville)
 -mm: Ensure mm node removals are properly serialised (Chris)
 -panel: Specify the type of panel for drm_panels for later use (Laurent)
 -panel: Use drm_panel_init to init device and funcs (Laurent)
 -mst: Refactors and cleanups in anticipation of suspend/resume support (Lyude)
 -vram:
  -Add lazy unmapping for gem bo's (Thomas)
  -Unify and rationalize vram mm and gem vram (Thomas)
  -Expose vmap and vunmap for gem vram objects (Thomas)
  -Allow objects to be pinned at the top of vram to avoid fragmentation (Thomas)
 
 Driver Changes:
 -various: Include drm_bridge.h instead of relying on drm_crtc.h (Boris)
 -ast/mgag200: Refactor show_cursor(), move cursor to top of video mem (Thomas)
 -komeda:
  -Add error event printing (behind CONFIG) and reg dump support (Lowry)
  -Add suspend/resume support (Lowry)
  -Workaround D71 shadow registers not flushing on disable (Lowry)
 -meson: Add suspend/resume support (Neil)
 -omap: Miscellaneous refactors and improvements (Tomi/Jyri)
 -panfrost/shmem: Silence lockdep by using mutex_trylock (Rob)
 -panfrost: Miscellaneous small fixes (Rob/Steven)
 -sti: Fix warnings (Benjamin/Linus)
 -sun4i:
  -Add vcc-dsi regulator to sun6i_mipi_dsi (Jagan)
  -A few patches to figure out the DRQ/start delay calc on dsi (Jagan/Icenowy)
 -virtio:
  -Add module param to switch resource reuse workaround on/off (Gerd)
  -Avoid calling vmexit while holding spinlock (Gerd)
  -Use gem shmem helpers instead of ttm (Gerd)
  -Accommodate command buffer allocations too big for cma (David)
 
 Cc: Rob Herring <robh@kernel.org>
 Cc: Maxime Ripard <mripard@kernel.org>
 Cc: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
 Cc: Gerd Hoffmann <kraxel@redhat.com>
 Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
 Cc: Lyude Paul <lyude@redhat.com>
 Cc: José Roberto de Souza <jose.souza@intel.com>
 Cc: Dariusz Marcinkiewicz <darekm@google.com>
 Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
 Cc: Raymond Smith <raymond.smith@arm.com>
 Cc: Chris Wilson <chris@chris-wilson.co.uk>
 Cc: Colin Ian King <colin.king@canonical.com>
 Cc: Thomas Zimmermann <tzimmermann@suse.de>
 Cc: Dan Carpenter <dan.carpenter@oracle.com>
 Cc: Mihail Atanassov <Mihail.Atanassov@arm.com>
 Cc: Lowry Li <Lowry.Li@arm.com>
 Cc: Neil Armstrong <narmstrong@baylibre.com>
 Cc: Jyri Sarha <jsarha@ti.com>
 Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
 Cc: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
 Cc: Steven Price <steven.price@arm.com>
 Cc: Benjamin Gaignard <benjamin.gaignard@st.com>
 Cc: Linus Walleij <linus.walleij@linaro.org>
 Cc: Jagan Teki <jagan@amarulasolutions.com>
 Cc: Icenowy Zheng <icenowy@aosc.io>
 Cc: Iago Toral Quiroga <itoral@igalia.com>
 Cc: David Riley <davidriley@chromium.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEHF6rntfJ3enn8gh8cywAJXLcr3kFAl2d9h8ACgkQcywAJXLc
 r3ms5gf9HIFpqwJ16CqaRukSnpcBcDoYUM8DGrOic+vw2bw14BQwFqvEOqrCkKL4
 V6h/OCJlNFPtOcc1LvU/jeXxYf4AQWh/2qZeg+oee7HAGX5x8Y3f08GsEjO8+55t
 QvSVxCKVti04M1ErPRfKrM7KPVE+IC+KdY26nO8Bf5zDGeCAkiPIDrdh2aZGMRdC
 Eer0DJ96cgWW9LrhseCdj5nKwcR78DlbWa79zuPAss4LaBBbXqThNXYYzg/mZMKB
 +VYgzs48tGYKK1NXXJ6biVI3brHrM52bqv5JpIncD5HepF1oIartWOMnbAO7MAqh
 h/tgJWxL+4bnl9aqY87by1BtyVgl3w==
 =kaOE
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2019-10-09-2' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for 5.5:

UAPI Changes:
-Colorspace: Expose different prop values for DP vs. HDMI (Gwan-gyeong Mun)
-fourcc: Add DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED (Raymond)
-not_actually: s/ENOTSUPP/EOPNOTSUPP/ in drm_edid and drm_mipi_dbi. This should
    not reach userspace, but adding here to specifically call that out (Daniel)
-i810: Prevent underflow in dispatch ioctls (Dan)
-komeda: Add ACLK sysfs attribute (Mihail)
-v3d: Allow userspace to clean up after render jobs (Iago)

Cross-subsystem Changes:
-MAINTAINERS:
 -Add Alyssa & Steven as panfrost reviewers (Rob)
 -Add Jernej as DE2 reviewer (Maxime)
 -Add Chen-Yu as Allwinner maintainer (Maxime)
-staging: Make some stack arrays static const (Colin)

Core Changes:
-ttm: Allow drivers to specify their vma manager (to use gem mgr) (Gerd)
-docs: Various fixes in connector/encoder/bridge docs (Daniel, Lyude, Laurent)
-connector: Allow more than 3 possible encoders for a connector (José)
-dp_cec: Allow a connector to be associated with a cec device (Dariusz)
-various: Fix some compile/sparse warnings (Ville)
-mm: Ensure mm node removals are properly serialised (Chris)
-panel: Specify the type of panel for drm_panels for later use (Laurent)
-panel: Use drm_panel_init to init device and funcs (Laurent)
-mst: Refactors and cleanups in anticipation of suspend/resume support (Lyude)
-vram:
 -Add lazy unmapping for gem bo's (Thomas)
 -Unify and rationalize vram mm and gem vram (Thomas)
 -Expose vmap and vunmap for gem vram objects (Thomas)
 -Allow objects to be pinned at the top of vram to avoid fragmentation (Thomas)

Driver Changes:
-various: Include drm_bridge.h instead of relying on drm_crtc.h (Boris)
-ast/mgag200: Refactor show_cursor(), move cursor to top of video mem (Thomas)
-komeda:
 -Add error event printing (behind CONFIG) and reg dump support (Lowry)
 -Add suspend/resume support (Lowry)
 -Workaround D71 shadow registers not flushing on disable (Lowry)
-meson: Add suspend/resume support (Neil)
-omap: Miscellaneous refactors and improvements (Tomi/Jyri)
-panfrost/shmem: Silence lockdep by using mutex_trylock (Rob)
-panfrost: Miscellaneous small fixes (Rob/Steven)
-sti: Fix warnings (Benjamin/Linus)
-sun4i:
 -Add vcc-dsi regulator to sun6i_mipi_dsi (Jagan)
 -A few patches to figure out the DRQ/start delay calc on dsi (Jagan/Icenowy)
-virtio:
 -Add module param to switch resource reuse workaround on/off (Gerd)
 -Avoid calling vmexit while holding spinlock (Gerd)
 -Use gem shmem helpers instead of ttm (Gerd)
 -Accommodate command buffer allocations too big for cma (David)

Cc: Rob Herring <robh@kernel.org>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Lyude Paul <lyude@redhat.com>
Cc: José Roberto de Souza <jose.souza@intel.com>
Cc: Dariusz Marcinkiewicz <darekm@google.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Raymond Smith <raymond.smith@arm.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Colin Ian King <colin.king@canonical.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Mihail Atanassov <Mihail.Atanassov@arm.com>
Cc: Lowry Li <Lowry.Li@arm.com>
Cc: Neil Armstrong <narmstrong@baylibre.com>
Cc: Jyri Sarha <jsarha@ti.com>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Cc: Steven Price <steven.price@arm.com>
Cc: Benjamin Gaignard <benjamin.gaignard@st.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Jagan Teki <jagan@amarulasolutions.com>
Cc: Icenowy Zheng <icenowy@aosc.io>
Cc: Iago Toral Quiroga <itoral@igalia.com>
Cc: David Riley <davidriley@chromium.org>
Signed-off-by: Dave Airlie <airlied@redhat.com>

# gpg: Signature made Thu 10 Oct 2019 01:00:47 AM AEST
# gpg:                using RSA key 732C002572DCAF79
# gpg: Can't check signature: public key not found

# Conflicts:
#	drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
#	drivers/gpu/drm/i915/i915_drv.c
#	drivers/gpu/drm/i915/i915_gem.c
#	drivers/gpu/drm/i915/i915_gem_gtt.c
#	drivers/gpu/drm/i915/i915_vma.c
From: Sean Paul <sean@poorly.run>
Link: https://patchwork.freedesktop.org/patch/msgid/20191009150825.GA227673@art_vandelay
This commit is contained in:
Dave Airlie 2019-10-11 09:30:52 +10:00
commit 7ed093602e
299 changed files with 4661 additions and 2998 deletions

View File

@ -36,6 +36,9 @@ properties:
resets: resets:
maxItems: 1 maxItems: 1
vcc-dsi-supply:
description: VCC-DSI power supply of the DSI encoder
phys: phys:
maxItems: 1 maxItems: 1
@ -64,6 +67,7 @@ required:
- phys - phys
- phy-names - phy-names
- resets - resets
- vcc-dsi-supply
- port - port
additionalProperties: false additionalProperties: false
@ -79,6 +83,7 @@ examples:
resets = <&ccu 4>; resets = <&ccu 4>;
phys = <&dphy0>; phys = <&dphy0>;
phy-names = "dphy"; phy-names = "dphy";
vcc-dsi-supply = <&reg_dcdc1>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;

View File

@ -6,7 +6,11 @@ designed for portable devices.
Required properties: Required properties:
- compatible : "analogix,anx7814" - compatible : Must be one of:
"analogix,anx7808"
"analogix,anx7812"
"analogix,anx7814"
"analogix,anx7818"
- reg : I2C address of the device - reg : I2C address of the device
- interrupts : Should contain the INTP interrupt - interrupts : Should contain the INTP interrupt
- hpd-gpios : Which GPIO to use for hpd - hpd-gpios : Which GPIO to use for hpd

View File

@ -400,16 +400,13 @@ GEM VRAM Helper Functions Reference
.. kernel-doc:: drivers/gpu/drm/drm_gem_vram_helper.c .. kernel-doc:: drivers/gpu/drm/drm_gem_vram_helper.c
:export: :export:
VRAM MM Helper Functions Reference GEM TTM Helper Functions Reference
---------------------------------- -----------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_vram_mm_helper.c .. kernel-doc:: drivers/gpu/drm/drm_gem_ttm_helper.c
:doc: overview :doc: overview
.. kernel-doc:: include/drm/drm_vram_mm_helper.h .. kernel-doc:: drivers/gpu/drm/drm_gem_ttm_helper.c
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_vram_mm_helper.c
:export: :export:
VMA Offset Manager VMA Offset Manager

View File

@ -5,4 +5,4 @@
======================================================= =======================================================
.. kernel-doc:: drivers/gpu/drm/mcde/mcde_drv.c .. kernel-doc:: drivers/gpu/drm/mcde/mcde_drv.c
:doc: ST-Ericsson MCDE DRM Driver :doc: ST-Ericsson MCDE Driver

View File

@ -284,6 +284,18 @@ drm_fb_helper tasks
removed: drm_fb_helper_single_add_all_connectors(), removed: drm_fb_helper_single_add_all_connectors(),
drm_fb_helper_add_one_connector() and drm_fb_helper_remove_one_connector(). drm_fb_helper_add_one_connector() and drm_fb_helper_remove_one_connector().
connector register/unregister fixes
-----------------------------------
- For most connectors it's a no-op to call drm_connector_register/unregister
directly from driver code, drm_dev_register/unregister take care of this
already. We can remove all of them.
- For dp drivers it's a bit more a mess, since we need the connector to be
registered when calling drm_dp_aux_register. Fix this by instead calling
drm_dp_aux_init, and moving the actual registering into a late_register
callback as recommended in the kerneldoc.
Core refactorings Core refactorings
================= =================

View File

@ -1272,6 +1272,8 @@ F: Documentation/gpu/afbc.rst
ARM MALI PANFROST DRM DRIVER ARM MALI PANFROST DRM DRIVER
M: Rob Herring <robh@kernel.org> M: Rob Herring <robh@kernel.org>
M: Tomeu Vizoso <tomeu.vizoso@collabora.com> M: Tomeu Vizoso <tomeu.vizoso@collabora.com>
R: Steven Price <steven.price@arm.com>
R: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org
S: Supported S: Supported
T: git git://anongit.freedesktop.org/drm/drm-misc T: git git://anongit.freedesktop.org/drm/drm-misc
@ -5376,12 +5378,22 @@ F: include/linux/vga*
DRM DRIVERS FOR ALLWINNER A10 DRM DRIVERS FOR ALLWINNER A10
M: Maxime Ripard <mripard@kernel.org> M: Maxime Ripard <mripard@kernel.org>
M: Chen-Yu Tsai <wens@csie.org>
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org
S: Supported S: Supported
F: drivers/gpu/drm/sun4i/ F: drivers/gpu/drm/sun4i/
F: Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt F: Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
T: git git://anongit.freedesktop.org/drm/drm-misc T: git git://anongit.freedesktop.org/drm/drm-misc
DRM DRIVER FOR ALLWINNER DE2 AND DE3 ENGINE
M: Maxime Ripard <mripard@kernel.org>
M: Chen-Yu Tsai <wens@csie.org>
R: Jernej Skrabec <jernej.skrabec@siol.net>
L: dri-devel@lists.freedesktop.org
S: Supported
F: drivers/gpu/drm/sun4i/sun8i*
T: git git://anongit.freedesktop.org/drm/drm-misc
DRM DRIVERS FOR AMLOGIC SOCS DRM DRIVERS FOR AMLOGIC SOCS
M: Neil Armstrong <narmstrong@baylibre.com> M: Neil Armstrong <narmstrong@baylibre.com>
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org

View File

@ -273,6 +273,30 @@ void dma_fence_free(struct dma_fence *fence)
} }
EXPORT_SYMBOL(dma_fence_free); EXPORT_SYMBOL(dma_fence_free);
static bool __dma_fence_enable_signaling(struct dma_fence *fence)
{
bool was_set;
lockdep_assert_held(fence->lock);
was_set = test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
&fence->flags);
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
return false;
if (!was_set && fence->ops->enable_signaling) {
trace_dma_fence_enable_signal(fence);
if (!fence->ops->enable_signaling(fence)) {
dma_fence_signal_locked(fence);
return false;
}
}
return true;
}
/** /**
* dma_fence_enable_sw_signaling - enable signaling on fence * dma_fence_enable_sw_signaling - enable signaling on fence
* @fence: the fence to enable * @fence: the fence to enable
@ -285,19 +309,12 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence)
{ {
unsigned long flags; unsigned long flags;
if (!test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
&fence->flags) && return;
!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) &&
fence->ops->enable_signaling) {
trace_dma_fence_enable_signal(fence);
spin_lock_irqsave(fence->lock, flags); spin_lock_irqsave(fence->lock, flags);
__dma_fence_enable_signaling(fence);
if (!fence->ops->enable_signaling(fence))
dma_fence_signal_locked(fence);
spin_unlock_irqrestore(fence->lock, flags); spin_unlock_irqrestore(fence->lock, flags);
}
} }
EXPORT_SYMBOL(dma_fence_enable_sw_signaling); EXPORT_SYMBOL(dma_fence_enable_sw_signaling);
@ -331,7 +348,6 @@ int dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb,
{ {
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
bool was_set;
if (WARN_ON(!fence || !func)) if (WARN_ON(!fence || !func))
return -EINVAL; return -EINVAL;
@ -343,25 +359,14 @@ int dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb,
spin_lock_irqsave(fence->lock, flags); spin_lock_irqsave(fence->lock, flags);
was_set = test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, if (__dma_fence_enable_signaling(fence)) {
&fence->flags);
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
ret = -ENOENT;
else if (!was_set && fence->ops->enable_signaling) {
trace_dma_fence_enable_signal(fence);
if (!fence->ops->enable_signaling(fence)) {
dma_fence_signal_locked(fence);
ret = -ENOENT;
}
}
if (!ret) {
cb->func = func; cb->func = func;
list_add_tail(&cb->node, &fence->cb_list); list_add_tail(&cb->node, &fence->cb_list);
} else } else {
INIT_LIST_HEAD(&cb->node); INIT_LIST_HEAD(&cb->node);
ret = -ENOENT;
}
spin_unlock_irqrestore(fence->lock, flags); spin_unlock_irqrestore(fence->lock, flags);
return ret; return ret;
@ -461,7 +466,6 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
struct default_wait_cb cb; struct default_wait_cb cb;
unsigned long flags; unsigned long flags;
signed long ret = timeout ? timeout : 1; signed long ret = timeout ? timeout : 1;
bool was_set;
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
return ret; return ret;
@ -473,21 +477,9 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
goto out; goto out;
} }
was_set = test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, if (!__dma_fence_enable_signaling(fence))
&fence->flags);
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
goto out; goto out;
if (!was_set && fence->ops->enable_signaling) {
trace_dma_fence_enable_signal(fence);
if (!fence->ops->enable_signaling(fence)) {
dma_fence_signal_locked(fence);
goto out;
}
}
if (!timeout) { if (!timeout) {
ret = 0; ret = 0;
goto out; goto out;

View File

@ -471,7 +471,7 @@ unlock:
if (pfence_excl) if (pfence_excl)
*pfence_excl = fence_excl; *pfence_excl = fence_excl;
else if (fence_excl) else if (fence_excl)
shared[++shared_count] = fence_excl; shared[shared_count++] = fence_excl;
if (!shared_count) { if (!shared_count) {
kfree(shared); kfree(shared);

View File

@ -168,10 +168,16 @@ config DRM_TTM
config DRM_VRAM_HELPER config DRM_VRAM_HELPER
tristate tristate
depends on DRM depends on DRM
select DRM_TTM
help help
Helpers for VRAM memory management Helpers for VRAM memory management
config DRM_TTM_HELPER
tristate
depends on DRM
select DRM_TTM
help
Helpers for ttm-based gem objects
config DRM_GEM_CMA_HELPER config DRM_GEM_CMA_HELPER
bool bool
depends on DRM depends on DRM

View File

@ -33,10 +33,12 @@ drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
drm_vram_helper-y := drm_gem_vram_helper.o \ drm_vram_helper-y := drm_gem_vram_helper.o \
drm_vram_helper_common.o \ drm_vram_helper_common.o
drm_vram_mm_helper.o
obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o
drm_ttm_helper-y := drm_gem_ttm_helper.o
obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper.o \ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper.o \
drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \

View File

@ -217,11 +217,10 @@ amdgpu_connector_update_scratch_regs(struct drm_connector *connector,
struct drm_encoder *encoder; struct drm_encoder *encoder;
const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private;
bool connected; bool connected;
int i;
best_encoder = connector_funcs->best_encoder(connector); best_encoder = connector_funcs->best_encoder(connector);
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
if ((encoder == best_encoder) && (status == connector_status_connected)) if ((encoder == best_encoder) && (status == connector_status_connected))
connected = true; connected = true;
else else
@ -236,9 +235,8 @@ amdgpu_connector_find_encoder(struct drm_connector *connector,
int encoder_type) int encoder_type)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
int i;
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
if (encoder->encoder_type == encoder_type) if (encoder->encoder_type == encoder_type)
return encoder; return encoder;
} }
@ -347,10 +345,9 @@ static struct drm_encoder *
amdgpu_connector_best_single_encoder(struct drm_connector *connector) amdgpu_connector_best_single_encoder(struct drm_connector *connector)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
int i;
/* pick the first one */ /* pick the first one */
drm_connector_for_each_possible_encoder(connector, encoder, i) drm_connector_for_each_possible_encoder(connector, encoder)
return encoder; return encoder;
return NULL; return NULL;
@ -1065,9 +1062,8 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
/* find analog encoder */ /* find analog encoder */
if (amdgpu_connector->dac_load_detect) { if (amdgpu_connector->dac_load_detect) {
struct drm_encoder *encoder; struct drm_encoder *encoder;
int i;
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && if (encoder->encoder_type != DRM_MODE_ENCODER_DAC &&
encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) encoder->encoder_type != DRM_MODE_ENCODER_TVDAC)
continue; continue;
@ -1117,9 +1113,8 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector)
{ {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
struct drm_encoder *encoder; struct drm_encoder *encoder;
int i;
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
if (amdgpu_connector->use_digital == true) { if (amdgpu_connector->use_digital == true) {
if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
return encoder; return encoder;
@ -1134,7 +1129,7 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector)
/* then check use digitial */ /* then check use digitial */
/* pick the first one */ /* pick the first one */
drm_connector_for_each_possible_encoder(connector, encoder, i) drm_connector_for_each_possible_encoder(connector, encoder)
return encoder; return encoder;
return NULL; return NULL;
@ -1271,9 +1266,8 @@ u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct amdgpu_encoder *amdgpu_encoder; struct amdgpu_encoder *amdgpu_encoder;
int i;
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
amdgpu_encoder = to_amdgpu_encoder(encoder); amdgpu_encoder = to_amdgpu_encoder(encoder);
switch (amdgpu_encoder->encoder_id) { switch (amdgpu_encoder->encoder_id) {
@ -1292,10 +1286,9 @@ static bool amdgpu_connector_encoder_is_hbr2(struct drm_connector *connector)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct amdgpu_encoder *amdgpu_encoder; struct amdgpu_encoder *amdgpu_encoder;
int i;
bool found = false; bool found = false;
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
amdgpu_encoder = to_amdgpu_encoder(encoder); amdgpu_encoder = to_amdgpu_encoder(encoder);
if (amdgpu_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2) if (amdgpu_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2)
found = true; found = true;

View File

@ -1049,7 +1049,7 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
} }
/* Get rid of things like offb */ /* Get rid of things like offb */
ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "amdgpudrmfb"); ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "amdgpudrmfb");
if (ret) if (ret)
return ret; return ret;

View File

@ -1731,6 +1731,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
r = ttm_bo_device_init(&adev->mman.bdev, r = ttm_bo_device_init(&adev->mman.bdev,
&amdgpu_bo_driver, &amdgpu_bo_driver,
adev->ddev->anon_inode->i_mapping, adev->ddev->anon_inode->i_mapping,
adev->ddev->vma_offset_manager,
dma_addressing_limited(adev->dev)); dma_addressing_limited(adev->dev));
if (r) { if (r) {
DRM_ERROR("failed initializing buffer object driver(%d).\n", r); DRM_ERROR("failed initializing buffer object driver(%d).\n", r);

View File

@ -260,15 +260,14 @@ static struct drm_encoder *
dce_virtual_encoder(struct drm_connector *connector) dce_virtual_encoder(struct drm_connector *connector)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
int i;
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
return encoder; return encoder;
} }
/* pick the first one */ /* pick the first one */
drm_connector_for_each_possible_encoder(connector, encoder, i) drm_connector_for_each_possible_encoder(connector, encoder)
return encoder; return encoder;
return NULL; return NULL;

View File

@ -4837,7 +4837,13 @@ static int to_drm_connector_type(enum signal_type st)
static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector) static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector)
{ {
return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); struct drm_encoder *encoder;
/* There is only one encoder per connector */
drm_connector_for_each_possible_encoder(connector, encoder)
return encoder;
return NULL;
} }
static void amdgpu_dm_get_native_mode(struct drm_connector *connector) static void amdgpu_dm_get_native_mode(struct drm_connector *connector)

View File

@ -416,7 +416,7 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
drm_dp_aux_register(&aconnector->dm_dp_aux.aux); drm_dp_aux_register(&aconnector->dm_dp_aux.aux);
drm_dp_cec_register_connector(&aconnector->dm_dp_aux.aux, drm_dp_cec_register_connector(&aconnector->dm_dp_aux.aux,
aconnector->base.name, dm->adev->dev); &aconnector->base);
aconnector->mst_mgr.cbs = &dm_mst_cbs; aconnector->mst_mgr.cbs = &dm_mst_cbs;
drm_dp_mst_topology_mgr_init( drm_dp_mst_topology_mgr_init(
&aconnector->mst_mgr, &aconnector->mst_mgr,

View File

@ -5,6 +5,7 @@
* Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com) * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
*/ */
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>
#include <drm/drm_device.h> #include <drm/drm_device.h>

View File

@ -12,3 +12,9 @@ config DRM_KOMEDA
Processor driver. It supports the D71 variants of the hardware. Processor driver. It supports the D71 variants of the hardware.
If compiled as a module it will be called komeda. If compiled as a module it will be called komeda.
config DRM_KOMEDA_ERROR_PRINT
bool "Enable komeda error print"
depends on DRM_KOMEDA
help
Choose this option to enable error printing.

View File

@ -22,4 +22,6 @@ komeda-y += \
d71/d71_dev.o \ d71/d71_dev.o \
d71/d71_component.o d71/d71_component.o
komeda-$(CONFIG_DRM_KOMEDA_ERROR_PRINT) += komeda_event.o
obj-$(CONFIG_DRM_KOMEDA) += komeda.o obj-$(CONFIG_DRM_KOMEDA) += komeda.o

View File

@ -1218,6 +1218,90 @@ int d71_probe_block(struct d71_dev *d71,
return err; return err;
} }
static void d71_gcu_dump(struct d71_dev *d71, struct seq_file *sf)
{
u32 v[5];
seq_puts(sf, "\n------ GCU ------\n");
get_values_from_reg(d71->gcu_addr, 0, 3, v);
seq_printf(sf, "GLB_ARCH_ID:\t\t0x%X\n", v[0]);
seq_printf(sf, "GLB_CORE_ID:\t\t0x%X\n", v[1]);
seq_printf(sf, "GLB_CORE_INFO:\t\t0x%X\n", v[2]);
get_values_from_reg(d71->gcu_addr, 0x10, 1, v);
seq_printf(sf, "GLB_IRQ_STATUS:\t\t0x%X\n", v[0]);
get_values_from_reg(d71->gcu_addr, 0xA0, 5, v);
seq_printf(sf, "GCU_IRQ_RAW_STATUS:\t0x%X\n", v[0]);
seq_printf(sf, "GCU_IRQ_CLEAR:\t\t0x%X\n", v[1]);
seq_printf(sf, "GCU_IRQ_MASK:\t\t0x%X\n", v[2]);
seq_printf(sf, "GCU_IRQ_STATUS:\t\t0x%X\n", v[3]);
seq_printf(sf, "GCU_STATUS:\t\t0x%X\n", v[4]);
get_values_from_reg(d71->gcu_addr, 0xD0, 3, v);
seq_printf(sf, "GCU_CONTROL:\t\t0x%X\n", v[0]);
seq_printf(sf, "GCU_CONFIG_VALID0:\t0x%X\n", v[1]);
seq_printf(sf, "GCU_CONFIG_VALID1:\t0x%X\n", v[2]);
}
static void d71_lpu_dump(struct d71_pipeline *pipe, struct seq_file *sf)
{
u32 v[6];
seq_printf(sf, "\n------ LPU%d ------\n", pipe->base.id);
dump_block_header(sf, pipe->lpu_addr);
get_values_from_reg(pipe->lpu_addr, 0xA0, 6, v);
seq_printf(sf, "LPU_IRQ_RAW_STATUS:\t0x%X\n", v[0]);
seq_printf(sf, "LPU_IRQ_CLEAR:\t\t0x%X\n", v[1]);
seq_printf(sf, "LPU_IRQ_MASK:\t\t0x%X\n", v[2]);
seq_printf(sf, "LPU_IRQ_STATUS:\t\t0x%X\n", v[3]);
seq_printf(sf, "LPU_STATUS:\t\t0x%X\n", v[4]);
seq_printf(sf, "LPU_TBU_STATUS:\t\t0x%X\n", v[5]);
get_values_from_reg(pipe->lpu_addr, 0xC0, 1, v);
seq_printf(sf, "LPU_INFO:\t\t0x%X\n", v[0]);
get_values_from_reg(pipe->lpu_addr, 0xD0, 3, v);
seq_printf(sf, "LPU_RAXI_CONTROL:\t0x%X\n", v[0]);
seq_printf(sf, "LPU_WAXI_CONTROL:\t0x%X\n", v[1]);
seq_printf(sf, "LPU_TBU_CONTROL:\t0x%X\n", v[2]);
}
static void d71_dou_dump(struct d71_pipeline *pipe, struct seq_file *sf)
{
u32 v[5];
seq_printf(sf, "\n------ DOU%d ------\n", pipe->base.id);
dump_block_header(sf, pipe->dou_addr);
get_values_from_reg(pipe->dou_addr, 0xA0, 5, v);
seq_printf(sf, "DOU_IRQ_RAW_STATUS:\t0x%X\n", v[0]);
seq_printf(sf, "DOU_IRQ_CLEAR:\t\t0x%X\n", v[1]);
seq_printf(sf, "DOU_IRQ_MASK:\t\t0x%X\n", v[2]);
seq_printf(sf, "DOU_IRQ_STATUS:\t\t0x%X\n", v[3]);
seq_printf(sf, "DOU_STATUS:\t\t0x%X\n", v[4]);
}
static void d71_pipeline_dump(struct komeda_pipeline *pipe, struct seq_file *sf)
{
struct d71_pipeline *d71_pipe = to_d71_pipeline(pipe);
d71_lpu_dump(d71_pipe, sf);
d71_dou_dump(d71_pipe, sf);
}
const struct komeda_pipeline_funcs d71_pipeline_funcs = { const struct komeda_pipeline_funcs d71_pipeline_funcs = {
.downscaling_clk_check = d71_downscaling_clk_check, .downscaling_clk_check = d71_downscaling_clk_check,
.dump_register = d71_pipeline_dump,
}; };
void d71_dump(struct komeda_dev *mdev, struct seq_file *sf)
{
struct d71_dev *d71 = mdev->chip_data;
d71_gcu_dump(d71, sf);
}

View File

@ -195,7 +195,7 @@ d71_irq_handler(struct komeda_dev *mdev, struct komeda_events *evts)
if (gcu_status & GLB_IRQ_STATUS_PIPE1) if (gcu_status & GLB_IRQ_STATUS_PIPE1)
evts->pipes[1] |= get_pipeline_event(d71->pipes[1], gcu_status); evts->pipes[1] |= get_pipeline_event(d71->pipes[1], gcu_status);
return gcu_status ? IRQ_HANDLED : IRQ_NONE; return IRQ_RETVAL(gcu_status);
} }
#define ENABLED_GCU_IRQS (GCU_IRQ_CVAL0 | GCU_IRQ_CVAL1 | \ #define ENABLED_GCU_IRQS (GCU_IRQ_CVAL0 | GCU_IRQ_CVAL1 | \
@ -395,6 +395,22 @@ static int d71_enum_resources(struct komeda_dev *mdev)
err = PTR_ERR(pipe); err = PTR_ERR(pipe);
goto err_cleanup; goto err_cleanup;
} }
/* D71 HW doesn't update shadow registers when display output
* is turning off, so when we disable all pipeline components
* together with display output disable by one flush or one
* operation, the disable operation updated registers will not
* be flush to or valid in HW, which may leads problem.
* To workaround this problem, introduce a two phase disable.
* Phase1: Disabling components with display is on to make sure
* the disable can be flushed to HW.
* Phase2: Only turn-off display output.
*/
value = KOMEDA_PIPELINE_IMPROCS |
BIT(KOMEDA_COMPONENT_TIMING_CTRLR);
pipe->standalone_disabled_comps = value;
d71->pipes[i] = to_d71_pipeline(pipe); d71->pipes[i] = to_d71_pipeline(pipe);
} }
@ -572,6 +588,7 @@ static const struct komeda_dev_funcs d71_chip_funcs = {
.flush = d71_flush, .flush = d71_flush,
.connect_iommu = d71_connect_iommu, .connect_iommu = d71_connect_iommu,
.disconnect_iommu = d71_disconnect_iommu, .disconnect_iommu = d71_disconnect_iommu,
.dump_register = d71_dump,
}; };
const struct komeda_dev_funcs * const struct komeda_dev_funcs *

View File

@ -49,4 +49,6 @@ int d71_probe_block(struct d71_dev *d71,
struct block_header *blk, u32 __iomem *reg); struct block_header *blk, u32 __iomem *reg);
void d71_read_block_header(u32 __iomem *reg, struct block_header *blk); void d71_read_block_header(u32 __iomem *reg, struct block_header *blk);
void d71_dump(struct komeda_dev *mdev, struct seq_file *sf);
#endif /* !_D71_DEV_H_ */ #endif /* !_D71_DEV_H_ */

View File

@ -5,7 +5,6 @@
* *
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
@ -250,23 +249,57 @@ komeda_crtc_atomic_enable(struct drm_crtc *crtc,
{ {
komeda_crtc_prepare(to_kcrtc(crtc)); komeda_crtc_prepare(to_kcrtc(crtc));
drm_crtc_vblank_on(crtc); drm_crtc_vblank_on(crtc);
WARN_ON(drm_crtc_vblank_get(crtc));
komeda_crtc_do_flush(crtc, old); komeda_crtc_do_flush(crtc, old);
} }
static void
komeda_crtc_flush_and_wait_for_flip_done(struct komeda_crtc *kcrtc,
struct completion *input_flip_done)
{
struct drm_device *drm = kcrtc->base.dev;
struct komeda_dev *mdev = kcrtc->master->mdev;
struct completion *flip_done;
struct completion temp;
int timeout;
/* if caller doesn't send a flip_done, use a private flip_done */
if (input_flip_done) {
flip_done = input_flip_done;
} else {
init_completion(&temp);
kcrtc->disable_done = &temp;
flip_done = &temp;
}
mdev->funcs->flush(mdev, kcrtc->master->id, 0);
/* wait the flip take affect.*/
timeout = wait_for_completion_timeout(flip_done, HZ);
if (timeout == 0) {
DRM_ERROR("wait pipe%d flip done timeout\n", kcrtc->master->id);
if (!input_flip_done) {
unsigned long flags;
spin_lock_irqsave(&drm->event_lock, flags);
kcrtc->disable_done = NULL;
spin_unlock_irqrestore(&drm->event_lock, flags);
}
}
}
static void static void
komeda_crtc_atomic_disable(struct drm_crtc *crtc, komeda_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old) struct drm_crtc_state *old)
{ {
struct komeda_crtc *kcrtc = to_kcrtc(crtc); struct komeda_crtc *kcrtc = to_kcrtc(crtc);
struct komeda_crtc_state *old_st = to_kcrtc_st(old); struct komeda_crtc_state *old_st = to_kcrtc_st(old);
struct komeda_dev *mdev = crtc->dev->dev_private;
struct komeda_pipeline *master = kcrtc->master; struct komeda_pipeline *master = kcrtc->master;
struct komeda_pipeline *slave = kcrtc->slave; struct komeda_pipeline *slave = kcrtc->slave;
struct completion *disable_done = &crtc->state->commit->flip_done; struct completion *disable_done = &crtc->state->commit->flip_done;
struct completion temp; bool needs_phase2 = false;
int timeout;
DRM_DEBUG_ATOMIC("CRTC%d_DISABLE: active_pipes: 0x%x, affected: 0x%x.\n", DRM_DEBUG_ATOMIC("CRTC%d_DISABLE: active_pipes: 0x%x, affected: 0x%x\n",
drm_crtc_index(crtc), drm_crtc_index(crtc),
old_st->active_pipes, old_st->affected_pipes); old_st->active_pipes, old_st->affected_pipes);
@ -274,7 +307,7 @@ komeda_crtc_atomic_disable(struct drm_crtc *crtc,
komeda_pipeline_disable(slave, old->state); komeda_pipeline_disable(slave, old->state);
if (has_bit(master->id, old_st->active_pipes)) if (has_bit(master->id, old_st->active_pipes))
komeda_pipeline_disable(master, old->state); needs_phase2 = komeda_pipeline_disable(master, old->state);
/* crtc_disable has two scenarios according to the state->active switch. /* crtc_disable has two scenarios according to the state->active switch.
* 1. active -> inactive * 1. active -> inactive
@ -293,32 +326,23 @@ komeda_crtc_atomic_disable(struct drm_crtc *crtc,
* That's also the reason why skip modeset commit in * That's also the reason why skip modeset commit in
* komeda_crtc_atomic_flush() * komeda_crtc_atomic_flush()
*/ */
if (crtc->state->active) { disable_done = (needs_phase2 || crtc->state->active) ?
struct komeda_pipeline_state *pipe_st; NULL : &crtc->state->commit->flip_done;
/* clear the old active_comps to zero */
pipe_st = komeda_pipeline_get_old_state(master, old->state);
pipe_st->active_comps = 0;
init_completion(&temp); /* wait phase 1 disable done */
kcrtc->disable_done = &temp; komeda_crtc_flush_and_wait_for_flip_done(kcrtc, disable_done);
disable_done = &temp;
} /* phase 2 */
if (needs_phase2) {
mdev->funcs->flush(mdev, master->id, 0); komeda_pipeline_disable(kcrtc->master, old->state);
/* wait the disable take affect.*/ disable_done = crtc->state->active ?
timeout = wait_for_completion_timeout(disable_done, HZ); NULL : &crtc->state->commit->flip_done;
if (timeout == 0) {
DRM_ERROR("disable pipeline%d timeout.\n", kcrtc->master->id); komeda_crtc_flush_and_wait_for_flip_done(kcrtc, disable_done);
if (crtc->state->active) {
unsigned long flags;
spin_lock_irqsave(&crtc->dev->event_lock, flags);
kcrtc->disable_done = NULL;
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
} }
drm_crtc_vblank_put(crtc);
drm_crtc_vblank_off(crtc); drm_crtc_vblank_off(crtc);
komeda_crtc_unprepare(kcrtc); komeda_crtc_unprepare(kcrtc);
} }

View File

@ -25,6 +25,8 @@ static int komeda_register_show(struct seq_file *sf, void *x)
struct komeda_dev *mdev = sf->private; struct komeda_dev *mdev = sf->private;
int i; int i;
seq_puts(sf, "\n====== Komeda register dump =========\n");
if (mdev->funcs->dump_register) if (mdev->funcs->dump_register)
mdev->funcs->dump_register(mdev, sf); mdev->funcs->dump_register(mdev, sf);
@ -91,9 +93,19 @@ config_id_show(struct device *dev, struct device_attribute *attr, char *buf)
} }
static DEVICE_ATTR_RO(config_id); static DEVICE_ATTR_RO(config_id);
static ssize_t
aclk_hz_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct komeda_dev *mdev = dev_to_mdev(dev);
return snprintf(buf, PAGE_SIZE, "%lu\n", clk_get_rate(mdev->aclk));
}
static DEVICE_ATTR_RO(aclk_hz);
static struct attribute *komeda_sysfs_entries[] = { static struct attribute *komeda_sysfs_entries[] = {
&dev_attr_core_id.attr, &dev_attr_core_id.attr,
&dev_attr_config_id.attr, &dev_attr_config_id.attr,
&dev_attr_aclk_hz.attr,
NULL, NULL,
}; };
@ -216,7 +228,7 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
product->product_id, product->product_id,
MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id)); MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id));
err = -ENODEV; err = -ENODEV;
goto err_cleanup; goto disable_clk;
} }
DRM_INFO("Found ARM Mali-D%x version r%dp%d\n", DRM_INFO("Found ARM Mali-D%x version r%dp%d\n",
@ -229,19 +241,19 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
err = mdev->funcs->enum_resources(mdev); err = mdev->funcs->enum_resources(mdev);
if (err) { if (err) {
DRM_ERROR("enumerate display resource failed.\n"); DRM_ERROR("enumerate display resource failed.\n");
goto err_cleanup; goto disable_clk;
} }
err = komeda_parse_dt(dev, mdev); err = komeda_parse_dt(dev, mdev);
if (err) { if (err) {
DRM_ERROR("parse device tree failed.\n"); DRM_ERROR("parse device tree failed.\n");
goto err_cleanup; goto disable_clk;
} }
err = komeda_assemble_pipelines(mdev); err = komeda_assemble_pipelines(mdev);
if (err) { if (err) {
DRM_ERROR("assemble display pipelines failed.\n"); DRM_ERROR("assemble display pipelines failed.\n");
goto err_cleanup; goto disable_clk;
} }
dev->dma_parms = &mdev->dma_parms; dev->dma_parms = &mdev->dma_parms;
@ -254,11 +266,14 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
if (mdev->iommu && mdev->funcs->connect_iommu) { if (mdev->iommu && mdev->funcs->connect_iommu) {
err = mdev->funcs->connect_iommu(mdev); err = mdev->funcs->connect_iommu(mdev);
if (err) { if (err) {
DRM_ERROR("connect iommu failed.\n");
mdev->iommu = NULL; mdev->iommu = NULL;
goto err_cleanup; goto disable_clk;
} }
} }
clk_disable_unprepare(mdev->aclk);
err = sysfs_create_group(&dev->kobj, &komeda_sysfs_attr_group); err = sysfs_create_group(&dev->kobj, &komeda_sysfs_attr_group);
if (err) { if (err) {
DRM_ERROR("create sysfs group failed.\n"); DRM_ERROR("create sysfs group failed.\n");
@ -271,6 +286,8 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
return mdev; return mdev;
disable_clk:
clk_disable_unprepare(mdev->aclk);
err_cleanup: err_cleanup:
komeda_dev_destroy(mdev); komeda_dev_destroy(mdev);
return ERR_PTR(err); return ERR_PTR(err);
@ -288,8 +305,12 @@ void komeda_dev_destroy(struct komeda_dev *mdev)
debugfs_remove_recursive(mdev->debugfs_root); debugfs_remove_recursive(mdev->debugfs_root);
#endif #endif
if (mdev->aclk)
clk_prepare_enable(mdev->aclk);
if (mdev->iommu && mdev->funcs->disconnect_iommu) if (mdev->iommu && mdev->funcs->disconnect_iommu)
mdev->funcs->disconnect_iommu(mdev); if (mdev->funcs->disconnect_iommu(mdev))
DRM_ERROR("disconnect iommu failed.\n");
mdev->iommu = NULL; mdev->iommu = NULL;
for (i = 0; i < mdev->n_pipelines; i++) { for (i = 0; i < mdev->n_pipelines; i++) {
@ -317,3 +338,47 @@ void komeda_dev_destroy(struct komeda_dev *mdev)
devm_kfree(dev, mdev); devm_kfree(dev, mdev);
} }
int komeda_dev_resume(struct komeda_dev *mdev)
{
int ret = 0;
clk_prepare_enable(mdev->aclk);
if (mdev->iommu && mdev->funcs->connect_iommu) {
ret = mdev->funcs->connect_iommu(mdev);
if (ret < 0) {
DRM_ERROR("connect iommu failed.\n");
goto disable_clk;
}
}
ret = mdev->funcs->enable_irq(mdev);
disable_clk:
clk_disable_unprepare(mdev->aclk);
return ret;
}
int komeda_dev_suspend(struct komeda_dev *mdev)
{
int ret = 0;
clk_prepare_enable(mdev->aclk);
if (mdev->iommu && mdev->funcs->disconnect_iommu) {
ret = mdev->funcs->disconnect_iommu(mdev);
if (ret < 0) {
DRM_ERROR("disconnect iommu failed.\n");
goto disable_clk;
}
}
ret = mdev->funcs->disable_irq(mdev);
disable_clk:
clk_disable_unprepare(mdev->aclk);
return ret;
}

View File

@ -40,6 +40,17 @@
#define KOMEDA_ERR_TTNG BIT_ULL(30) #define KOMEDA_ERR_TTNG BIT_ULL(30)
#define KOMEDA_ERR_TTF BIT_ULL(31) #define KOMEDA_ERR_TTF BIT_ULL(31)
#define KOMEDA_ERR_EVENTS \
(KOMEDA_EVENT_URUN | KOMEDA_EVENT_IBSY | KOMEDA_EVENT_OVR |\
KOMEDA_ERR_TETO | KOMEDA_ERR_TEMR | KOMEDA_ERR_TITR |\
KOMEDA_ERR_CPE | KOMEDA_ERR_CFGE | KOMEDA_ERR_AXIE |\
KOMEDA_ERR_ACE0 | KOMEDA_ERR_ACE1 | KOMEDA_ERR_ACE2 |\
KOMEDA_ERR_ACE3 | KOMEDA_ERR_DRIFTTO | KOMEDA_ERR_FRAMETO |\
KOMEDA_ERR_ZME | KOMEDA_ERR_MERR | KOMEDA_ERR_TCF |\
KOMEDA_ERR_TTNG | KOMEDA_ERR_TTF)
#define KOMEDA_WARN_EVENTS KOMEDA_ERR_CSCE
/* malidp device id */ /* malidp device id */
enum { enum {
MALI_D71 = 0, MALI_D71 = 0,
@ -207,4 +218,13 @@ void komeda_dev_destroy(struct komeda_dev *mdev);
struct komeda_dev *dev_to_mdev(struct device *dev); struct komeda_dev *dev_to_mdev(struct device *dev);
#ifdef CONFIG_DRM_KOMEDA_ERROR_PRINT
void komeda_print_events(struct komeda_events *evts);
#else
static inline void komeda_print_events(struct komeda_events *evts) {}
#endif
int komeda_dev_resume(struct komeda_dev *mdev);
int komeda_dev_suspend(struct komeda_dev *mdev);
#endif /*_KOMEDA_DEV_H_*/ #endif /*_KOMEDA_DEV_H_*/

View File

@ -8,6 +8,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/component.h> #include <linux/component.h>
#include <linux/pm_runtime.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include "komeda_dev.h" #include "komeda_dev.h"
#include "komeda_kms.h" #include "komeda_kms.h"
@ -136,13 +137,40 @@ static const struct of_device_id komeda_of_match[] = {
MODULE_DEVICE_TABLE(of, komeda_of_match); MODULE_DEVICE_TABLE(of, komeda_of_match);
static int __maybe_unused komeda_pm_suspend(struct device *dev)
{
struct komeda_drv *mdrv = dev_get_drvdata(dev);
struct drm_device *drm = &mdrv->kms->base;
int res;
res = drm_mode_config_helper_suspend(drm);
komeda_dev_suspend(mdrv->mdev);
return res;
}
static int __maybe_unused komeda_pm_resume(struct device *dev)
{
struct komeda_drv *mdrv = dev_get_drvdata(dev);
struct drm_device *drm = &mdrv->kms->base;
komeda_dev_resume(mdrv->mdev);
return drm_mode_config_helper_resume(drm);
}
static const struct dev_pm_ops komeda_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(komeda_pm_suspend, komeda_pm_resume)
};
static struct platform_driver komeda_platform_driver = { static struct platform_driver komeda_platform_driver = {
.probe = komeda_platform_probe, .probe = komeda_platform_probe,
.remove = komeda_platform_remove, .remove = komeda_platform_remove,
.driver = { .driver = {
.name = "komeda", .name = "komeda",
.of_match_table = komeda_of_match, .of_match_table = komeda_of_match,
.pm = NULL, .pm = &komeda_pm_ops,
}, },
}; };

View File

@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0
/*
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
* Author: James.Qian.Wang <james.qian.wang@arm.com>
*
*/
#include <drm/drm_print.h>
#include "komeda_dev.h"
struct komeda_str {
char *str;
u32 sz;
u32 len;
};
/* return 0 on success, < 0 on no space.
*/
static int komeda_sprintf(struct komeda_str *str, const char *fmt, ...)
{
va_list args;
int num, free_sz;
int err;
free_sz = str->sz - str->len - 1;
if (free_sz <= 0)
return -ENOSPC;
va_start(args, fmt);
num = vsnprintf(str->str + str->len, free_sz, fmt, args);
va_end(args);
if (num < free_sz) {
str->len += num;
err = 0;
} else {
str->len = str->sz - 1;
err = -ENOSPC;
}
return err;
}
static void evt_sprintf(struct komeda_str *str, u64 evt, const char *msg)
{
if (evt)
komeda_sprintf(str, msg);
}
static void evt_str(struct komeda_str *str, u64 events)
{
if (events == 0ULL) {
komeda_sprintf(str, "None");
return;
}
evt_sprintf(str, events & KOMEDA_EVENT_VSYNC, "VSYNC|");
evt_sprintf(str, events & KOMEDA_EVENT_FLIP, "FLIP|");
evt_sprintf(str, events & KOMEDA_EVENT_EOW, "EOW|");
evt_sprintf(str, events & KOMEDA_EVENT_MODE, "OP-MODE|");
evt_sprintf(str, events & KOMEDA_EVENT_URUN, "UNDERRUN|");
evt_sprintf(str, events & KOMEDA_EVENT_OVR, "OVERRUN|");
/* GLB error */
evt_sprintf(str, events & KOMEDA_ERR_MERR, "MERR|");
evt_sprintf(str, events & KOMEDA_ERR_FRAMETO, "FRAMETO|");
/* DOU error */
evt_sprintf(str, events & KOMEDA_ERR_DRIFTTO, "DRIFTTO|");
evt_sprintf(str, events & KOMEDA_ERR_FRAMETO, "FRAMETO|");
evt_sprintf(str, events & KOMEDA_ERR_TETO, "TETO|");
evt_sprintf(str, events & KOMEDA_ERR_CSCE, "CSCE|");
/* LPU errors or events */
evt_sprintf(str, events & KOMEDA_EVENT_IBSY, "IBSY|");
evt_sprintf(str, events & KOMEDA_ERR_AXIE, "AXIE|");
evt_sprintf(str, events & KOMEDA_ERR_ACE0, "ACE0|");
evt_sprintf(str, events & KOMEDA_ERR_ACE1, "ACE1|");
evt_sprintf(str, events & KOMEDA_ERR_ACE2, "ACE2|");
evt_sprintf(str, events & KOMEDA_ERR_ACE3, "ACE3|");
/* LPU TBU errors*/
evt_sprintf(str, events & KOMEDA_ERR_TCF, "TCF|");
evt_sprintf(str, events & KOMEDA_ERR_TTNG, "TTNG|");
evt_sprintf(str, events & KOMEDA_ERR_TITR, "TITR|");
evt_sprintf(str, events & KOMEDA_ERR_TEMR, "TEMR|");
evt_sprintf(str, events & KOMEDA_ERR_TTF, "TTF|");
/* CU errors*/
evt_sprintf(str, events & KOMEDA_ERR_CPE, "COPROC|");
evt_sprintf(str, events & KOMEDA_ERR_ZME, "ZME|");
evt_sprintf(str, events & KOMEDA_ERR_CFGE, "CFGE|");
evt_sprintf(str, events & KOMEDA_ERR_TEMR, "TEMR|");
if (str->len > 0 && (str->str[str->len - 1] == '|')) {
str->str[str->len - 1] = 0;
str->len--;
}
}
static bool is_new_frame(struct komeda_events *a)
{
return (a->pipes[0] | a->pipes[1]) &
(KOMEDA_EVENT_FLIP | KOMEDA_EVENT_EOW);
}
void komeda_print_events(struct komeda_events *evts)
{
u64 print_evts = KOMEDA_ERR_EVENTS;
static bool en_print = true;
/* reduce the same msg print, only print the first evt for one frame */
if (evts->global || is_new_frame(evts))
en_print = true;
if (!en_print)
return;
if ((evts->global | evts->pipes[0] | evts->pipes[1]) & print_evts) {
char msg[256];
struct komeda_str str;
str.str = msg;
str.sz = sizeof(msg);
str.len = 0;
komeda_sprintf(&str, "gcu: ");
evt_str(&str, evts->global);
komeda_sprintf(&str, ", pipes[0]: ");
evt_str(&str, evts->pipes[0]);
komeda_sprintf(&str, ", pipes[1]: ");
evt_str(&str, evts->pipes[1]);
DRM_ERROR("err detect: %s\n", msg);
en_print = false;
}
}

View File

@ -48,6 +48,8 @@ static irqreturn_t komeda_kms_irq_handler(int irq, void *data)
memset(&evts, 0, sizeof(evts)); memset(&evts, 0, sizeof(evts));
status = mdev->funcs->irq_handler(mdev, &evts); status = mdev->funcs->irq_handler(mdev, &evts);
komeda_print_events(&evts);
/* Notify the crtc to handle the events */ /* Notify the crtc to handle the events */
for (i = 0; i < kms->n_crtcs; i++) for (i = 0; i < kms->n_crtcs; i++)
komeda_crtc_handle_event(&kms->crtcs[i], &evts); komeda_crtc_handle_event(&kms->crtcs[i], &evts);

View File

@ -389,6 +389,18 @@ struct komeda_pipeline {
int id; int id;
/** @avail_comps: available components mask of pipeline */ /** @avail_comps: available components mask of pipeline */
u32 avail_comps; u32 avail_comps;
/**
* @standalone_disabled_comps:
*
* When disable the pipeline, some components can not be disabled
* together with others, but need a sparated and standalone disable.
* The standalone_disabled_comps are the components which need to be
* disabled standalone, and this concept also introduce concept of
* two phase.
* phase 1: for disabling the common components.
* phase 2: for disabling the standalong_disabled_comps.
*/
u32 standalone_disabled_comps;
/** @n_layers: the number of layer on @layers */ /** @n_layers: the number of layer on @layers */
int n_layers; int n_layers;
/** @layers: the pipeline layers */ /** @layers: the pipeline layers */
@ -535,7 +547,7 @@ int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe,
struct komeda_pipeline_state * struct komeda_pipeline_state *
komeda_pipeline_get_old_state(struct komeda_pipeline *pipe, komeda_pipeline_get_old_state(struct komeda_pipeline *pipe,
struct drm_atomic_state *state); struct drm_atomic_state *state);
void komeda_pipeline_disable(struct komeda_pipeline *pipe, bool komeda_pipeline_disable(struct komeda_pipeline *pipe,
struct drm_atomic_state *old_state); struct drm_atomic_state *old_state);
void komeda_pipeline_update(struct komeda_pipeline *pipe, void komeda_pipeline_update(struct komeda_pipeline *pipe,
struct drm_atomic_state *old_state); struct drm_atomic_state *old_state);

View File

@ -1218,7 +1218,17 @@ int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe,
return 0; return 0;
} }
void komeda_pipeline_disable(struct komeda_pipeline *pipe, /* Since standalong disabled components must be disabled separately and in the
* last, So a complete disable operation may needs to call pipeline_disable
* twice (two phase disabling).
* Phase 1: disable the common components, flush it.
* Phase 2: disable the standalone disabled components, flush it.
*
* RETURNS:
* true: disable is not complete, needs a phase 2 disable.
* false: disable is complete.
*/
bool komeda_pipeline_disable(struct komeda_pipeline *pipe,
struct drm_atomic_state *old_state) struct drm_atomic_state *old_state)
{ {
struct komeda_pipeline_state *old; struct komeda_pipeline_state *old;
@ -1228,9 +1238,14 @@ void komeda_pipeline_disable(struct komeda_pipeline *pipe,
old = komeda_pipeline_get_old_state(pipe, old_state); old = komeda_pipeline_get_old_state(pipe, old_state);
disabling_comps = old->active_comps; disabling_comps = old->active_comps &
DRM_DEBUG_ATOMIC("PIPE%d: disabling_comps: 0x%x.\n", (~pipe->standalone_disabled_comps);
pipe->id, disabling_comps); if (!disabling_comps)
disabling_comps = old->active_comps &
pipe->standalone_disabled_comps;
DRM_DEBUG_ATOMIC("PIPE%d: active_comps: 0x%x, disabling_comps: 0x%x.\n",
pipe->id, old->active_comps, disabling_comps);
dp_for_each_set_bit(id, disabling_comps) { dp_for_each_set_bit(id, disabling_comps) {
c = komeda_pipeline_get_component(pipe, id); c = komeda_pipeline_get_component(pipe, id);
@ -1248,6 +1263,13 @@ void komeda_pipeline_disable(struct komeda_pipeline *pipe,
c->funcs->disable(c); c->funcs->disable(c);
} }
/* Update the pipeline state, if there are components that are still
* active, return true for calling the phase 2 disable.
*/
old->active_comps &= ~disabling_comps;
return old->active_comps ? true : false;
} }
void komeda_pipeline_update(struct komeda_pipeline *pipe, void komeda_pipeline_update(struct komeda_pipeline *pipe,

View File

@ -4,6 +4,8 @@ config DRM_AST
depends on DRM && PCI && MMU depends on DRM && PCI && MMU
select DRM_KMS_HELPER select DRM_KMS_HELPER
select DRM_VRAM_HELPER select DRM_VRAM_HELPER
select DRM_TTM
select DRM_TTM_HELPER
help help
Say yes for experimental AST GPU driver. Do not enable Say yes for experimental AST GPU driver. Do not enable
this driver without having a working -modesetting, this driver without having a working -modesetting,

View File

@ -35,7 +35,6 @@
#include <drm/drm_gem_vram_helper.h> #include <drm/drm_gem_vram_helper.h>
#include <drm/drm_pci.h> #include <drm/drm_pci.h>
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include <drm/drm_vram_mm_helper.h>
#include "ast_drv.h" #include "ast_drv.h"

View File

@ -82,6 +82,25 @@ enum ast_tx_chip {
#define AST_DRAM_4Gx16 7 #define AST_DRAM_4Gx16 7
#define AST_DRAM_8Gx16 8 #define AST_DRAM_8Gx16 8
#define AST_MAX_HWC_WIDTH 64
#define AST_MAX_HWC_HEIGHT 64
#define AST_HWC_SIZE (AST_MAX_HWC_WIDTH * AST_MAX_HWC_HEIGHT * 2)
#define AST_HWC_SIGNATURE_SIZE 32
#define AST_DEFAULT_HWC_NUM 2
/* define for signature structure */
#define AST_HWC_SIGNATURE_CHECKSUM 0x00
#define AST_HWC_SIGNATURE_SizeX 0x04
#define AST_HWC_SIGNATURE_SizeY 0x08
#define AST_HWC_SIGNATURE_X 0x0C
#define AST_HWC_SIGNATURE_Y 0x10
#define AST_HWC_SIGNATURE_HOTSPOTX 0x14
#define AST_HWC_SIGNATURE_HOTSPOTY 0x18
struct ast_private { struct ast_private {
struct drm_device *dev; struct drm_device *dev;
@ -97,8 +116,11 @@ struct ast_private {
int fb_mtrr; int fb_mtrr;
struct drm_gem_object *cursor_cache; struct {
int next_cursor; struct drm_gem_vram_object *gbo[AST_DEFAULT_HWC_NUM];
unsigned int next_index;
} cursor;
bool support_wide_screen; bool support_wide_screen;
enum { enum {
ast_use_p2a, ast_use_p2a,
@ -199,23 +221,6 @@ static inline void ast_open_key(struct ast_private *ast)
#define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M #define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M
#define AST_MAX_HWC_WIDTH 64
#define AST_MAX_HWC_HEIGHT 64
#define AST_HWC_SIZE (AST_MAX_HWC_WIDTH*AST_MAX_HWC_HEIGHT*2)
#define AST_HWC_SIGNATURE_SIZE 32
#define AST_DEFAULT_HWC_NUM 2
/* define for signature structure */
#define AST_HWC_SIGNATURE_CHECKSUM 0x00
#define AST_HWC_SIGNATURE_SizeX 0x04
#define AST_HWC_SIGNATURE_SizeY 0x08
#define AST_HWC_SIGNATURE_X 0x0C
#define AST_HWC_SIGNATURE_Y 0x10
#define AST_HWC_SIGNATURE_HOTSPOTX 0x14
#define AST_HWC_SIGNATURE_HOTSPOTY 0x18
struct ast_i2c_chan { struct ast_i2c_chan {
struct i2c_adapter adapter; struct i2c_adapter adapter;
struct drm_device *dev; struct drm_device *dev;

View File

@ -33,7 +33,6 @@
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_vram_helper.h> #include <drm/drm_gem_vram_helper.h>
#include <drm/drm_vram_mm_helper.h>
#include "ast_drv.h" #include "ast_drv.h"

View File

@ -687,17 +687,6 @@ static void ast_encoder_destroy(struct drm_encoder *encoder)
kfree(encoder); kfree(encoder);
} }
static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector)
{
int enc_id = connector->encoder_ids[0];
/* pick the encoder ids */
if (enc_id)
return drm_encoder_find(connector->dev, NULL, enc_id);
return NULL;
}
static const struct drm_encoder_funcs ast_enc_funcs = { static const struct drm_encoder_funcs ast_enc_funcs = {
.destroy = ast_encoder_destroy, .destroy = ast_encoder_destroy,
}; };
@ -847,7 +836,6 @@ static void ast_connector_destroy(struct drm_connector *connector)
static const struct drm_connector_helper_funcs ast_connector_helper_funcs = { static const struct drm_connector_helper_funcs ast_connector_helper_funcs = {
.mode_valid = ast_mode_valid, .mode_valid = ast_mode_valid,
.get_modes = ast_get_modes, .get_modes = ast_get_modes,
.best_encoder = ast_best_single_encoder,
}; };
static const struct drm_connector_funcs ast_connector_funcs = { static const struct drm_connector_funcs ast_connector_funcs = {
@ -895,50 +883,53 @@ static int ast_connector_init(struct drm_device *dev)
static int ast_cursor_init(struct drm_device *dev) static int ast_cursor_init(struct drm_device *dev)
{ {
struct ast_private *ast = dev->dev_private; struct ast_private *ast = dev->dev_private;
int size; size_t size, i;
int ret;
struct drm_gem_object *obj;
struct drm_gem_vram_object *gbo; struct drm_gem_vram_object *gbo;
s64 gpu_addr; int ret;
void *base;
size = (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE) * AST_DEFAULT_HWC_NUM; size = roundup(AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE, PAGE_SIZE);
ret = ast_gem_create(dev, size, true, &obj); for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) {
if (ret) gbo = drm_gem_vram_create(dev, &dev->vram_mm->bdev,
return ret; size, 0, false);
gbo = drm_gem_vram_of_gem(obj); if (IS_ERR(gbo)) {
ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM); ret = PTR_ERR(gbo);
if (ret) goto err_drm_gem_vram_put;
goto fail; }
gpu_addr = drm_gem_vram_offset(gbo); ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM |
if (gpu_addr < 0) { DRM_GEM_VRAM_PL_FLAG_TOPDOWN);
drm_gem_vram_unpin(gbo); if (ret) {
ret = (int)gpu_addr; drm_gem_vram_put(gbo);
goto fail; goto err_drm_gem_vram_put;
} }
/* kmap the object */ ast->cursor.gbo[i] = gbo;
base = drm_gem_vram_kmap(gbo, true, NULL);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
goto fail;
} }
ast->cursor_cache = obj;
return 0; return 0;
fail:
err_drm_gem_vram_put:
while (i) {
--i;
gbo = ast->cursor.gbo[i];
drm_gem_vram_unpin(gbo);
drm_gem_vram_put(gbo);
ast->cursor.gbo[i] = NULL;
}
return ret; return ret;
} }
static void ast_cursor_fini(struct drm_device *dev) static void ast_cursor_fini(struct drm_device *dev)
{ {
struct ast_private *ast = dev->dev_private; struct ast_private *ast = dev->dev_private;
struct drm_gem_vram_object *gbo = size_t i;
drm_gem_vram_of_gem(ast->cursor_cache); struct drm_gem_vram_object *gbo;
drm_gem_vram_kunmap(gbo);
for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) {
gbo = ast->cursor.gbo[i];
drm_gem_vram_unpin(gbo); drm_gem_vram_unpin(gbo);
drm_gem_object_put_unlocked(ast->cursor_cache); drm_gem_vram_put(gbo);
}
} }
int ast_mode_init(struct drm_device *dev) int ast_mode_init(struct drm_device *dev)
@ -1076,23 +1067,6 @@ static void ast_i2c_destroy(struct ast_i2c_chan *i2c)
kfree(i2c); kfree(i2c);
} }
static void ast_show_cursor(struct drm_crtc *crtc)
{
struct ast_private *ast = crtc->dev->dev_private;
u8 jreg;
jreg = 0x2;
/* enable ARGB cursor */
jreg |= 1;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg);
}
static void ast_hide_cursor(struct drm_crtc *crtc)
{
struct ast_private *ast = crtc->dev->dev_private;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, 0x00);
}
static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height) static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height)
{ {
union { union {
@ -1149,21 +1123,99 @@ static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height)
return csum; return csum;
} }
static int ast_cursor_update(void *dst, void *src, unsigned int width,
unsigned int height)
{
u32 csum;
/* do data transfer to cursor cache */
csum = copy_cursor_image(src, dst, width, height);
/* write checksum + signature */
dst += AST_HWC_SIZE;
writel(csum, dst);
writel(width, dst + AST_HWC_SIGNATURE_SizeX);
writel(height, dst + AST_HWC_SIGNATURE_SizeY);
writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX);
writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY);
return 0;
}
static void ast_cursor_set_base(struct ast_private *ast, u64 address)
{
u8 addr0 = (address >> 3) & 0xff;
u8 addr1 = (address >> 11) & 0xff;
u8 addr2 = (address >> 19) & 0xff;
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc8, addr0);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc9, addr1);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xca, addr2);
}
static int ast_show_cursor(struct drm_crtc *crtc, void *src,
unsigned int width, unsigned int height)
{
struct ast_private *ast = crtc->dev->dev_private;
struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
struct drm_gem_vram_object *gbo;
void *dst;
s64 off;
int ret;
u8 jreg;
gbo = ast->cursor.gbo[ast->cursor.next_index];
dst = drm_gem_vram_vmap(gbo);
if (IS_ERR(dst))
return PTR_ERR(dst);
off = drm_gem_vram_offset(gbo);
if (off < 0) {
ret = (int)off;
goto err_drm_gem_vram_vunmap;
}
ret = ast_cursor_update(dst, src, width, height);
if (ret)
goto err_drm_gem_vram_vunmap;
ast_cursor_set_base(ast, off);
ast_crtc->offset_x = AST_MAX_HWC_WIDTH - width;
ast_crtc->offset_y = AST_MAX_HWC_WIDTH - height;
jreg = 0x2;
/* enable ARGB cursor */
jreg |= 1;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg);
++ast->cursor.next_index;
ast->cursor.next_index %= ARRAY_SIZE(ast->cursor.gbo);
drm_gem_vram_vunmap(gbo, dst);
return 0;
err_drm_gem_vram_vunmap:
drm_gem_vram_vunmap(gbo, dst);
return ret;
}
static void ast_hide_cursor(struct drm_crtc *crtc)
{
struct ast_private *ast = crtc->dev->dev_private;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, 0x00);
}
static int ast_cursor_set(struct drm_crtc *crtc, static int ast_cursor_set(struct drm_crtc *crtc,
struct drm_file *file_priv, struct drm_file *file_priv,
uint32_t handle, uint32_t handle,
uint32_t width, uint32_t width,
uint32_t height) uint32_t height)
{ {
struct ast_private *ast = crtc->dev->dev_private;
struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
struct drm_gem_object *obj; struct drm_gem_object *obj;
struct drm_gem_vram_object *gbo; struct drm_gem_vram_object *gbo;
s64 dst_gpu; u8 *src;
u64 gpu_addr;
u32 csum;
int ret; int ret;
u8 *src, *dst;
if (!handle) { if (!handle) {
ast_hide_cursor(crtc); ast_hide_cursor(crtc);
@ -1179,70 +1231,23 @@ static int ast_cursor_set(struct drm_crtc *crtc,
return -ENOENT; return -ENOENT;
} }
gbo = drm_gem_vram_of_gem(obj); gbo = drm_gem_vram_of_gem(obj);
src = drm_gem_vram_vmap(gbo);
ret = drm_gem_vram_pin(gbo, 0);
if (ret)
goto err_drm_gem_object_put_unlocked;
src = drm_gem_vram_kmap(gbo, true, NULL);
if (IS_ERR(src)) { if (IS_ERR(src)) {
ret = PTR_ERR(src); ret = PTR_ERR(src);
goto err_drm_gem_vram_unpin; goto err_drm_gem_object_put_unlocked;
} }
dst = drm_gem_vram_kmap(drm_gem_vram_of_gem(ast->cursor_cache), ret = ast_show_cursor(crtc, src, width, height);
false, NULL); if (ret)
if (IS_ERR(dst)) { goto err_drm_gem_vram_vunmap;
ret = PTR_ERR(dst);
goto err_drm_gem_vram_kunmap;
}
dst_gpu = drm_gem_vram_offset(drm_gem_vram_of_gem(ast->cursor_cache));
if (dst_gpu < 0) {
ret = (int)dst_gpu;
goto err_drm_gem_vram_kunmap;
}
dst += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor; drm_gem_vram_vunmap(gbo, src);
/* do data transfer to cursor cache */
csum = copy_cursor_image(src, dst, width, height);
/* write checksum + signature */
{
struct drm_gem_vram_object *dst_gbo =
drm_gem_vram_of_gem(ast->cursor_cache);
u8 *dst = drm_gem_vram_kmap(dst_gbo, false, NULL);
dst += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor + AST_HWC_SIZE;
writel(csum, dst);
writel(width, dst + AST_HWC_SIGNATURE_SizeX);
writel(height, dst + AST_HWC_SIGNATURE_SizeY);
writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX);
writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY);
/* set pattern offset */
gpu_addr = (u64)dst_gpu;
gpu_addr += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor;
gpu_addr >>= 3;
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc8, gpu_addr & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc9, (gpu_addr >> 8) & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xca, (gpu_addr >> 16) & 0xff);
}
ast_crtc->offset_x = AST_MAX_HWC_WIDTH - width;
ast_crtc->offset_y = AST_MAX_HWC_WIDTH - height;
ast->next_cursor = (ast->next_cursor + 1) % AST_DEFAULT_HWC_NUM;
ast_show_cursor(crtc);
drm_gem_vram_kunmap(gbo);
drm_gem_vram_unpin(gbo);
drm_gem_object_put_unlocked(obj); drm_gem_object_put_unlocked(obj);
return 0; return 0;
err_drm_gem_vram_kunmap: err_drm_gem_vram_vunmap:
drm_gem_vram_kunmap(gbo); drm_gem_vram_vunmap(gbo, src);
err_drm_gem_vram_unpin:
drm_gem_vram_unpin(gbo);
err_drm_gem_object_put_unlocked: err_drm_gem_object_put_unlocked:
drm_gem_object_put_unlocked(obj); drm_gem_object_put_unlocked(obj);
return ret; return ret;
@ -1253,12 +1258,17 @@ static int ast_cursor_move(struct drm_crtc *crtc,
{ {
struct ast_crtc *ast_crtc = to_ast_crtc(crtc); struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
struct ast_private *ast = crtc->dev->dev_private; struct ast_private *ast = crtc->dev->dev_private;
struct drm_gem_vram_object *gbo;
int x_offset, y_offset; int x_offset, y_offset;
u8 *sig; u8 *dst, *sig;
u8 jreg;
sig = drm_gem_vram_kmap(drm_gem_vram_of_gem(ast->cursor_cache), gbo = ast->cursor.gbo[ast->cursor.next_index];
false, NULL); dst = drm_gem_vram_vmap(gbo);
sig += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor + AST_HWC_SIZE; if (IS_ERR(dst))
return PTR_ERR(dst);
sig = dst + AST_HWC_SIZE;
writel(x, sig + AST_HWC_SIGNATURE_X); writel(x, sig + AST_HWC_SIGNATURE_X);
writel(y, sig + AST_HWC_SIGNATURE_Y); writel(y, sig + AST_HWC_SIGNATURE_Y);
@ -1281,7 +1291,11 @@ static int ast_cursor_move(struct drm_crtc *crtc,
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, ((y >> 8) & 0x07)); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, ((y >> 8) & 0x07));
/* dummy write to fire HWC */ /* dummy write to fire HWC */
ast_show_cursor(crtc); jreg = 0x02 |
0x01; /* enable ARGB4444 cursor */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg);
drm_gem_vram_vunmap(gbo, dst);
return 0; return 0;
} }

View File

@ -30,7 +30,6 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_gem_vram_helper.h> #include <drm/drm_gem_vram_helper.h>
#include <drm/drm_vram_mm_helper.h>
#include "ast_drv.h" #include "ast_drv.h"
@ -42,7 +41,7 @@ int ast_mm_init(struct ast_private *ast)
vmm = drm_vram_helper_alloc_mm( vmm = drm_vram_helper_alloc_mm(
dev, pci_resource_start(dev->pdev, 0), dev, pci_resource_start(dev->pdev, 0),
ast->vram_size, &drm_gem_vram_mm_funcs); ast->vram_size);
if (IS_ERR(vmm)) { if (IS_ERR(vmm)) {
ret = PTR_ERR(vmm); ret = PTR_ERR(vmm);
DRM_ERROR("Error initializing VRAM MM; %d\n", ret); DRM_ERROR("Error initializing VRAM MM; %d\n", ret);

View File

@ -107,7 +107,8 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
output->encoder.possible_crtcs = 0x1; output->encoder.possible_crtcs = 0x1;
if (panel) { if (panel) {
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_Unknown);
if (IS_ERR(bridge)) if (IS_ERR(bridge))
return PTR_ERR(bridge); return PTR_ERR(bridge);
} }

View File

@ -4,6 +4,8 @@ config DRM_BOCHS
depends on DRM && PCI && MMU depends on DRM && PCI && MMU
select DRM_KMS_HELPER select DRM_KMS_HELPER
select DRM_VRAM_HELPER select DRM_VRAM_HELPER
select DRM_TTM
select DRM_TTM_HELPER
help help
Choose this option for qemu. Choose this option for qemu.
If M is selected the module will be called bochs-drm. If M is selected the module will be called bochs-drm.

View File

@ -10,7 +10,6 @@
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_gem_vram_helper.h> #include <drm/drm_gem_vram_helper.h>
#include <drm/drm_simple_kms_helper.h> #include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vram_mm_helper.h>
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */

View File

@ -114,7 +114,7 @@ static int bochs_pci_probe(struct pci_dev *pdev,
return -ENOMEM; return -ENOMEM;
} }
ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "bochsdrmfb"); ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "bochsdrmfb");
if (ret) if (ret)
return ret; return ret;

View File

@ -11,8 +11,7 @@ int bochs_mm_init(struct bochs_device *bochs)
struct drm_vram_mm *vmm; struct drm_vram_mm *vmm;
vmm = drm_vram_helper_alloc_mm(bochs->dev, bochs->fb_base, vmm = drm_vram_helper_alloc_mm(bochs->dev, bochs->fb_base,
bochs->fb_size, bochs->fb_size);
&drm_gem_vram_mm_funcs);
return PTR_ERR_OR_ZERO(vmm); return PTR_ERR_OR_ZERO(vmm);
} }

View File

@ -19,6 +19,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
@ -715,7 +716,9 @@ static int anx78xx_init_pdata(struct anx78xx *anx78xx)
/* 1.0V digital core power regulator */ /* 1.0V digital core power regulator */
pdata->dvdd10 = devm_regulator_get(dev, "dvdd10"); pdata->dvdd10 = devm_regulator_get(dev, "dvdd10");
if (IS_ERR(pdata->dvdd10)) { if (IS_ERR(pdata->dvdd10)) {
if (PTR_ERR(pdata->dvdd10) != -EPROBE_DEFER)
DRM_ERROR("DVDD10 regulator not found\n"); DRM_ERROR("DVDD10 regulator not found\n");
return PTR_ERR(pdata->dvdd10); return PTR_ERR(pdata->dvdd10);
} }
@ -1301,6 +1304,7 @@ static const struct regmap_config anx78xx_regmap_config = {
}; };
static const u16 anx78xx_chipid_list[] = { static const u16 anx78xx_chipid_list[] = {
0x7808,
0x7812, 0x7812,
0x7814, 0x7814,
0x7818, 0x7818,
@ -1332,7 +1336,9 @@ static int anx78xx_i2c_probe(struct i2c_client *client,
err = anx78xx_init_pdata(anx78xx); err = anx78xx_init_pdata(anx78xx);
if (err) { if (err) {
if (err != -EPROBE_DEFER)
DRM_ERROR("Failed to initialize pdata: %d\n", err); DRM_ERROR("Failed to initialize pdata: %d\n", err);
return err; return err;
} }
@ -1350,15 +1356,18 @@ static int anx78xx_i2c_probe(struct i2c_client *client,
/* Map slave addresses of ANX7814 */ /* Map slave addresses of ANX7814 */
for (i = 0; i < I2C_NUM_ADDRESSES; i++) { for (i = 0; i < I2C_NUM_ADDRESSES; i++) {
anx78xx->i2c_dummy[i] = i2c_new_dummy(client->adapter, struct i2c_client *i2c_dummy;
i2c_dummy = i2c_new_dummy_device(client->adapter,
anx78xx_i2c_addresses[i] >> 1); anx78xx_i2c_addresses[i] >> 1);
if (!anx78xx->i2c_dummy[i]) { if (IS_ERR(i2c_dummy)) {
err = -ENOMEM; err = PTR_ERR(i2c_dummy);
DRM_ERROR("Failed to reserve I2C bus %02x\n", DRM_ERROR("Failed to reserve I2C bus %02x: %d\n",
anx78xx_i2c_addresses[i]); anx78xx_i2c_addresses[i], err);
goto err_unregister_i2c; goto err_unregister_i2c;
} }
anx78xx->i2c_dummy[i] = i2c_dummy;
anx78xx->map[i] = devm_regmap_init_i2c(anx78xx->i2c_dummy[i], anx78xx->map[i] = devm_regmap_init_i2c(anx78xx->i2c_dummy[i],
&anx78xx_regmap_config); &anx78xx_regmap_config);
if (IS_ERR(anx78xx->map[i])) { if (IS_ERR(anx78xx->map[i])) {
@ -1463,7 +1472,10 @@ MODULE_DEVICE_TABLE(i2c, anx78xx_id);
#if IS_ENABLED(CONFIG_OF) #if IS_ENABLED(CONFIG_OF)
static const struct of_device_id anx78xx_match_table[] = { static const struct of_device_id anx78xx_match_table[] = {
{ .compatible = "analogix,anx7808", },
{ .compatible = "analogix,anx7812", },
{ .compatible = "analogix,anx7814", }, { .compatible = "analogix,anx7814", },
{ .compatible = "analogix,anx7818", },
{ /* sentinel */ }, { /* sentinel */ },
}; };
MODULE_DEVICE_TABLE(of, anx78xx_match_table); MODULE_DEVICE_TABLE(of, anx78xx_match_table);

View File

@ -21,6 +21,7 @@
#include <drm/bridge/analogix_dp.h> #include <drm/bridge/analogix_dp.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_device.h> #include <drm/drm_device.h>
#include <drm/drm_panel.h> #include <drm/drm_panel.h>

View File

@ -956,7 +956,8 @@ static int cdns_dsi_attach(struct mipi_dsi_host *host,
panel = of_drm_find_panel(np); panel = of_drm_find_panel(np);
if (!IS_ERR(panel)) { if (!IS_ERR(panel)) {
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI); bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_DSI);
} else { } else {
bridge = of_drm_find_bridge(dev->dev.of_node); bridge = of_drm_find_bridge(dev->dev.of_node);
if (!bridge) if (!bridge)

View File

@ -12,6 +12,7 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>

View File

@ -106,7 +106,8 @@ static int lvds_encoder_probe(struct platform_device *pdev)
} }
lvds_encoder->panel_bridge = lvds_encoder->panel_bridge =
devm_drm_panel_bridge_add(dev, panel, DRM_MODE_CONNECTOR_LVDS); devm_drm_panel_bridge_add_typed(dev, panel,
DRM_MODE_CONNECTOR_LVDS);
if (IS_ERR(lvds_encoder->panel_bridge)) if (IS_ERR(lvds_encoder->panel_bridge))
return PTR_ERR(lvds_encoder->panel_bridge); return PTR_ERR(lvds_encoder->panel_bridge);

View File

@ -25,6 +25,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>

View File

@ -11,6 +11,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>

View File

@ -5,6 +5,7 @@
*/ */
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h> #include <drm/drm_connector.h>
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_modeset_helper_vtables.h>
@ -133,8 +134,6 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
* just calls the appropriate functions from &drm_panel. * just calls the appropriate functions from &drm_panel.
* *
* @panel: The drm_panel being wrapped. Must be non-NULL. * @panel: The drm_panel being wrapped. Must be non-NULL.
* @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be
* created.
* *
* For drivers converting from directly using drm_panel: The expected * For drivers converting from directly using drm_panel: The expected
* usage pattern is that during either encoder module probe or DSI * usage pattern is that during either encoder module probe or DSI
@ -148,10 +147,36 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
* drm_mode_config_cleanup() if the bridge has already been attached), then * drm_mode_config_cleanup() if the bridge has already been attached), then
* drm_panel_bridge_remove() to free it. * drm_panel_bridge_remove() to free it.
* *
* The connector type is set to @panel->connector_type, which must be set to a
* known type. Calling this function with a panel whose connector type is
* DRM_MODE_CONNECTOR_Unknown will return NULL.
*
* See devm_drm_panel_bridge_add() for an automatically manged version of this * See devm_drm_panel_bridge_add() for an automatically manged version of this
* function. * function.
*/ */
struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel)
{
if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
return NULL;
return drm_panel_bridge_add_typed(panel, panel->connector_type);
}
EXPORT_SYMBOL(drm_panel_bridge_add);
/**
* drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with
* an explicit connector type.
* @panel: The drm_panel being wrapped. Must be non-NULL.
* @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
*
* This is just like drm_panel_bridge_add(), but forces the connector type to
* @connector_type instead of infering it from the panel.
*
* This function is deprecated and should not be used in new drivers. Use
* drm_panel_bridge_add() instead, and fix panel drivers as necessary if they
* don't report a connector type.
*/
struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
u32 connector_type) u32 connector_type)
{ {
struct panel_bridge *panel_bridge; struct panel_bridge *panel_bridge;
@ -176,7 +201,7 @@ struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
return &panel_bridge->bridge; return &panel_bridge->bridge;
} }
EXPORT_SYMBOL(drm_panel_bridge_add); EXPORT_SYMBOL(drm_panel_bridge_add_typed);
/** /**
* drm_panel_bridge_remove - Unregisters and frees a drm_bridge * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
@ -213,13 +238,36 @@ static void devm_drm_panel_bridge_release(struct device *dev, void *res)
* that just calls the appropriate functions from &drm_panel. * that just calls the appropriate functions from &drm_panel.
* @dev: device to tie the bridge lifetime to * @dev: device to tie the bridge lifetime to
* @panel: The drm_panel being wrapped. Must be non-NULL. * @panel: The drm_panel being wrapped. Must be non-NULL.
* @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be
* created.
* *
* This is the managed version of drm_panel_bridge_add() which automatically * This is the managed version of drm_panel_bridge_add() which automatically
* calls drm_panel_bridge_remove() when @dev is unbound. * calls drm_panel_bridge_remove() when @dev is unbound.
*/ */
struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
struct drm_panel *panel)
{
if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
return NULL;
return devm_drm_panel_bridge_add_typed(dev, panel,
panel->connector_type);
}
EXPORT_SYMBOL(devm_drm_panel_bridge_add);
/**
* devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and
* &drm_connector with an explicit connector type.
* @dev: device to tie the bridge lifetime to
* @panel: The drm_panel being wrapped. Must be non-NULL.
* @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
*
* This is just like devm_drm_panel_bridge_add(), but forces the connector type
* to @connector_type instead of infering it from the panel.
*
* This function is deprecated and should not be used in new drivers. Use
* devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if
* they don't report a connector type.
*/
struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
struct drm_panel *panel, struct drm_panel *panel,
u32 connector_type) u32 connector_type)
{ {
@ -230,7 +278,7 @@ struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
if (!ptr) if (!ptr)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
bridge = drm_panel_bridge_add(panel, connector_type); bridge = drm_panel_bridge_add_typed(panel, connector_type);
if (!IS_ERR(bridge)) { if (!IS_ERR(bridge)) {
*ptr = bridge; *ptr = bridge;
devres_add(dev, ptr); devres_add(dev, ptr);
@ -240,4 +288,4 @@ struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
return bridge; return bridge;
} }
EXPORT_SYMBOL(devm_drm_panel_bridge_add); EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed);

View File

@ -17,6 +17,7 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include <drm/drm_panel.h> #include <drm/drm_panel.h>

View File

@ -20,6 +20,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>

View File

@ -13,6 +13,7 @@
* Dharam Kumar <dharam.kr@samsung.com> * Dharam Kumar <dharam.kr@samsung.com>
*/ */
#include <drm/bridge/mhl.h> #include <drm/bridge/mhl.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>

View File

@ -9,6 +9,7 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <drm/bridge/mhl.h> #include <drm/bridge/mhl.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>

View File

@ -285,7 +285,7 @@ static int dw_hdmi_cec_probe(struct platform_device *pdev)
ret = cec_register_adapter(cec->adap, pdev->dev.parent); ret = cec_register_adapter(cec->adap, pdev->dev.parent);
if (ret < 0) { if (ret < 0) {
cec_notifier_cec_adap_unregister(cec->notify); cec_notifier_cec_adap_unregister(cec->notify, cec->adap);
return ret; return ret;
} }
@ -302,7 +302,7 @@ static int dw_hdmi_cec_remove(struct platform_device *pdev)
{ {
struct dw_hdmi_cec *cec = platform_get_drvdata(pdev); struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
cec_notifier_cec_adap_unregister(cec->notify); cec_notifier_cec_adap_unregister(cec->notify, cec->adap);
cec_unregister_adapter(cec->adap); cec_unregister_adapter(cec->adap);
return 0; return 0;

View File

@ -102,6 +102,7 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
} }
dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate); dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
dw_hdmi_set_channel_status(hdmi, hparms->iec.status);
dw_hdmi_set_channel_count(hdmi, hparms->channels); dw_hdmi_set_channel_count(hdmi, hparms->channels);
dw_hdmi_set_channel_allocation(hdmi, hparms->cea.channel_allocation); dw_hdmi_set_channel_allocation(hdmi, hparms->cea.channel_allocation);
@ -109,6 +110,14 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
hdmi_write(audio, conf0, HDMI_AUD_CONF0); hdmi_write(audio, conf0, HDMI_AUD_CONF0);
hdmi_write(audio, conf1, HDMI_AUD_CONF1); hdmi_write(audio, conf1, HDMI_AUD_CONF1);
return 0;
}
static int dw_hdmi_i2s_audio_startup(struct device *dev, void *data)
{
struct dw_hdmi_i2s_audio_data *audio = data;
struct dw_hdmi *hdmi = audio->hdmi;
dw_hdmi_audio_enable(hdmi); dw_hdmi_audio_enable(hdmi);
return 0; return 0;
@ -153,6 +162,7 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
static struct hdmi_codec_ops dw_hdmi_i2s_ops = { static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
.hw_params = dw_hdmi_i2s_hw_params, .hw_params = dw_hdmi_i2s_hw_params,
.audio_startup = dw_hdmi_i2s_audio_startup,
.audio_shutdown = dw_hdmi_i2s_audio_shutdown, .audio_shutdown = dw_hdmi_i2s_audio_shutdown,
.get_eld = dw_hdmi_i2s_get_eld, .get_eld = dw_hdmi_i2s_get_eld,
.get_dai_id = dw_hdmi_i2s_get_dai_id, .get_dai_id = dw_hdmi_i2s_get_dai_id,

View File

@ -26,6 +26,7 @@
#include <drm/bridge/dw_hdmi.h> #include <drm/bridge/dw_hdmi.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
@ -36,6 +37,7 @@
#include "dw-hdmi-cec.h" #include "dw-hdmi-cec.h"
#include "dw-hdmi.h" #include "dw-hdmi.h"
#define DDC_CI_ADDR 0x37
#define DDC_SEGMENT_ADDR 0x30 #define DDC_SEGMENT_ADDR 0x30
#define HDMI_EDID_LEN 512 #define HDMI_EDID_LEN 512
@ -398,6 +400,15 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
u8 addr = msgs[0].addr; u8 addr = msgs[0].addr;
int i, ret = 0; int i, ret = 0;
if (addr == DDC_CI_ADDR)
/*
* The internal I2C controller does not support the multi-byte
* read and write operations needed for DDC/CI.
* TOFIX: Blacklist the DDC/CI address until we filter out
* unsupported I2C operations.
*/
return -EOPNOTSUPP;
dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr); dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
@ -580,6 +591,26 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
return n; return n;
} }
/*
* When transmitting IEC60958 linear PCM audio, these registers allow to
* configure the channel status information of all the channel status
* bits in the IEC60958 frame. For the moment this configuration is only
* used when the I2S audio interface, General Purpose Audio (GPA),
* or AHB audio DMA (AHBAUDDMA) interface is active
* (for S/PDIF interface this information comes from the stream).
*/
void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi,
u8 *channel_status)
{
/*
* Set channel status register for frequency and word length.
* Use default values for other registers.
*/
hdmi_writeb(hdmi, channel_status[3], HDMI_FC_AUDSCHNLS7);
hdmi_writeb(hdmi, channel_status[4], HDMI_FC_AUDSCHNLS8);
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_status);
static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
unsigned long pixel_clk, unsigned int sample_rate) unsigned long pixel_clk, unsigned int sample_rate)
{ {

View File

@ -158,6 +158,8 @@
#define HDMI_FC_SPDDEVICEINF 0x1062 #define HDMI_FC_SPDDEVICEINF 0x1062
#define HDMI_FC_AUDSCONF 0x1063 #define HDMI_FC_AUDSCONF 0x1063
#define HDMI_FC_AUDSSTAT 0x1064 #define HDMI_FC_AUDSSTAT 0x1064
#define HDMI_FC_AUDSCHNLS7 0x106e
#define HDMI_FC_AUDSCHNLS8 0x106f
#define HDMI_FC_DATACH0FILL 0x1070 #define HDMI_FC_DATACH0FILL 0x1070
#define HDMI_FC_DATACH1FILL 0x1071 #define HDMI_FC_DATACH1FILL 0x1071
#define HDMI_FC_DATACH2FILL 0x1072 #define HDMI_FC_DATACH2FILL 0x1072

View File

@ -316,7 +316,8 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
return ret; return ret;
if (panel) { if (panel) {
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI); bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(bridge)) if (IS_ERR(bridge))
return PTR_ERR(bridge); return PTR_ERR(bridge);
} }
@ -981,7 +982,6 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct reset_control *apb_rst; struct reset_control *apb_rst;
struct dw_mipi_dsi *dsi; struct dw_mipi_dsi *dsi;
struct resource *res;
int ret; int ret;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
@ -997,11 +997,7 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
} }
if (!plat_data->base) { if (!plat_data->base) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dsi->base = devm_platform_ioremap_resource(pdev, 0);
if (!res)
return ERR_PTR(-ENODEV);
dsi->base = devm_ioremap_resource(dev, res);
if (IS_ERR(dsi->base)) if (IS_ERR(dsi->base))
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);

View File

@ -16,6 +16,7 @@
#include <video/mipi_display.h> #include <video/mipi_display.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h> #include <drm/drm_fb_helper.h>
#include <drm/drm_mipi_dsi.h> #include <drm/drm_mipi_dsi.h>

View File

@ -26,6 +26,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>

View File

@ -17,6 +17,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
#include <drm/drm_mipi_dsi.h> #include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>

View File

@ -14,6 +14,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>

View File

@ -532,7 +532,7 @@ static int cirrus_pci_probe(struct pci_dev *pdev,
struct cirrus_device *cirrus; struct cirrus_device *cirrus;
int ret; int ret;
ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "cirrusdrmfb"); ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "cirrusdrmfb");
if (ret) if (ret)
return ret; return ret;

View File

@ -31,6 +31,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h> #include <drm/drm_atomic_uapi.h>
#include <drm/drm_bridge.h>
#include <drm/drm_damage_helper.h> #include <drm/drm_damage_helper.h>
#include <drm/drm_device.h> #include <drm/drm_device.h>
#include <drm/drm_plane_helper.h> #include <drm/drm_plane_helper.h>
@ -97,17 +98,6 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
} }
} }
/*
* For connectors that support multiple encoders, either the
* .atomic_best_encoder() or .best_encoder() operation must be implemented.
*/
static struct drm_encoder *
pick_single_encoder_for_connector(struct drm_connector *connector)
{
WARN_ON(connector->encoder_ids[1]);
return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
}
static int handle_conflicting_encoders(struct drm_atomic_state *state, static int handle_conflicting_encoders(struct drm_atomic_state *state,
bool disable_conflicting_encoders) bool disable_conflicting_encoders)
{ {
@ -135,7 +125,7 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state,
else if (funcs->best_encoder) else if (funcs->best_encoder)
new_encoder = funcs->best_encoder(connector); new_encoder = funcs->best_encoder(connector);
else else
new_encoder = pick_single_encoder_for_connector(connector); new_encoder = drm_connector_get_single_encoder(connector);
if (new_encoder) { if (new_encoder) {
if (encoder_mask & drm_encoder_mask(new_encoder)) { if (encoder_mask & drm_encoder_mask(new_encoder)) {
@ -359,7 +349,7 @@ update_connector_routing(struct drm_atomic_state *state,
else if (funcs->best_encoder) else if (funcs->best_encoder)
new_encoder = funcs->best_encoder(connector); new_encoder = funcs->best_encoder(connector);
else else
new_encoder = pick_single_encoder_for_connector(connector); new_encoder = drm_connector_get_single_encoder(connector);
if (!new_encoder) { if (!new_encoder) {
DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n", DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
@ -482,7 +472,7 @@ mode_fixup(struct drm_atomic_state *state)
continue; continue;
funcs = crtc->helper_private; funcs = crtc->helper_private;
if (!funcs->mode_fixup) if (!funcs || !funcs->mode_fixup)
continue; continue;
ret = funcs->mode_fixup(crtc, &new_crtc_state->mode, ret = funcs->mode_fixup(crtc, &new_crtc_state->mode,

View File

@ -1405,7 +1405,7 @@ retry:
} else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
ret = drm_atomic_nonblocking_commit(state); ret = drm_atomic_nonblocking_commit(state);
} else { } else {
if (unlikely(drm_debug & DRM_UT_STATE)) if (drm_debug_enabled(DRM_UT_STATE))
drm_atomic_print_state(state); drm_atomic_print_state(state);
ret = drm_atomic_commit(state); ret = drm_atomic_commit(state);

View File

@ -130,7 +130,12 @@
* Z position is set up with drm_plane_create_zpos_immutable_property() and * Z position is set up with drm_plane_create_zpos_immutable_property() and
* drm_plane_create_zpos_property(). It controls the visibility of overlapping * drm_plane_create_zpos_property(). It controls the visibility of overlapping
* planes. Without this property the primary plane is always below the cursor * planes. Without this property the primary plane is always below the cursor
* plane, and ordering between all other planes is undefined. * plane, and ordering between all other planes is undefined. The positive
* Z axis points towards the user, i.e. planes with lower Z position values
* are underneath planes with higher Z position values. Note that the Z
* position value can also be immutable, to inform userspace about the
* hard-coded stacking of overlay planes, see
* drm_plane_create_zpos_immutable_property().
* *
* pixel blend mode: * pixel blend mode:
* Pixel blend mode is set up with drm_plane_create_blend_mode_property(). * Pixel blend mode is set up with drm_plane_create_blend_mode_property().

View File

@ -415,9 +415,8 @@ static bool connector_has_possible_crtc(struct drm_connector *connector,
struct drm_crtc *crtc) struct drm_crtc *crtc)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
int i;
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
if (encoder->possible_crtcs & drm_crtc_mask(crtc)) if (encoder->possible_crtcs & drm_crtc_mask(crtc))
return true; return true;
} }

View File

@ -365,8 +365,6 @@ EXPORT_SYMBOL(drm_connector_attach_edid_property);
int drm_connector_attach_encoder(struct drm_connector *connector, int drm_connector_attach_encoder(struct drm_connector *connector,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
int i;
/* /*
* In the past, drivers have attempted to model the static association * In the past, drivers have attempted to model the static association
* of connector to encoder in simple connector/encoder devices using a * of connector to encoder in simple connector/encoder devices using a
@ -381,18 +379,15 @@ int drm_connector_attach_encoder(struct drm_connector *connector,
if (WARN_ON(connector->encoder)) if (WARN_ON(connector->encoder))
return -EINVAL; return -EINVAL;
for (i = 0; i < ARRAY_SIZE(connector->encoder_ids); i++) { connector->possible_encoders |= drm_encoder_mask(encoder);
if (connector->encoder_ids[i] == 0) {
connector->encoder_ids[i] = encoder->base.id;
return 0; return 0;
}
}
return -ENOMEM;
} }
EXPORT_SYMBOL(drm_connector_attach_encoder); EXPORT_SYMBOL(drm_connector_attach_encoder);
/** /**
* drm_connector_has_possible_encoder - check if the connector and encoder are assosicated with each other * drm_connector_has_possible_encoder - check if the connector and encoder are
* associated with each other
* @connector: the connector * @connector: the connector
* @encoder: the encoder * @encoder: the encoder
* *
@ -402,15 +397,7 @@ EXPORT_SYMBOL(drm_connector_attach_encoder);
bool drm_connector_has_possible_encoder(struct drm_connector *connector, bool drm_connector_has_possible_encoder(struct drm_connector *connector,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
struct drm_encoder *enc; return connector->possible_encoders & drm_encoder_mask(encoder);
int i;
drm_connector_for_each_possible_encoder(connector, enc, i) {
if (enc == encoder)
return true;
}
return false;
} }
EXPORT_SYMBOL(drm_connector_has_possible_encoder); EXPORT_SYMBOL(drm_connector_has_possible_encoder);
@ -480,7 +467,10 @@ EXPORT_SYMBOL(drm_connector_cleanup);
* drm_connector_register - register a connector * drm_connector_register - register a connector
* @connector: the connector to register * @connector: the connector to register
* *
* Register userspace interfaces for a connector * Register userspace interfaces for a connector. Only call this for connectors
* which can be hotplugged after drm_dev_register() has been called already,
* e.g. DP MST connectors. All other connectors will be registered automatically
* when calling drm_dev_register().
* *
* Returns: * Returns:
* Zero on success, error code on failure. * Zero on success, error code on failure.
@ -526,7 +516,10 @@ EXPORT_SYMBOL(drm_connector_register);
* drm_connector_unregister - unregister a connector * drm_connector_unregister - unregister a connector
* @connector: the connector to unregister * @connector: the connector to unregister
* *
* Unregister userspace interfaces for a connector * Unregister userspace interfaces for a connector. Only call this for
* connectors which have registered explicitly by calling drm_dev_register(),
* since connectors are unregistered automatically when drm_dev_unregister() is
* called.
*/ */
void drm_connector_unregister(struct drm_connector *connector) void drm_connector_unregister(struct drm_connector *connector)
{ {
@ -882,6 +875,38 @@ static const struct drm_prop_enum_list hdmi_colorspaces[] = {
{ DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER, "DCI-P3_RGB_Theater" }, { DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER, "DCI-P3_RGB_Theater" },
}; };
/*
* As per DP 1.4a spec, 2.2.5.7.5 VSC SDP Payload for Pixel Encoding/Colorimetry
* Format Table 2-120
*/
static const struct drm_prop_enum_list dp_colorspaces[] = {
/* For Default case, driver will set the colorspace */
{ DRM_MODE_COLORIMETRY_DEFAULT, "Default" },
{ DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED, "RGB_Wide_Gamut_Fixed_Point" },
/* Colorimetry based on scRGB (IEC 61966-2-2) */
{ DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT, "RGB_Wide_Gamut_Floating_Point" },
/* Colorimetry based on IEC 61966-2-5 */
{ DRM_MODE_COLORIMETRY_OPRGB, "opRGB" },
/* Colorimetry based on SMPTE RP 431-2 */
{ DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65, "DCI-P3_RGB_D65" },
/* Colorimetry based on ITU-R BT.2020 */
{ DRM_MODE_COLORIMETRY_BT2020_RGB, "BT2020_RGB" },
{ DRM_MODE_COLORIMETRY_BT601_YCC, "BT601_YCC" },
{ DRM_MODE_COLORIMETRY_BT709_YCC, "BT709_YCC" },
/* Standard Definition Colorimetry based on IEC 61966-2-4 */
{ DRM_MODE_COLORIMETRY_XVYCC_601, "XVYCC_601" },
/* High Definition Colorimetry based on IEC 61966-2-4 */
{ DRM_MODE_COLORIMETRY_XVYCC_709, "XVYCC_709" },
/* Colorimetry based on IEC 61966-2-1/Amendment 1 */
{ DRM_MODE_COLORIMETRY_SYCC_601, "SYCC_601" },
/* Colorimetry based on IEC 61966-2-5 [33] */
{ DRM_MODE_COLORIMETRY_OPYCC_601, "opYCC_601" },
/* Colorimetry based on ITU-R BT.2020 */
{ DRM_MODE_COLORIMETRY_BT2020_CYCC, "BT2020_CYCC" },
/* Colorimetry based on ITU-R BT.2020 */
{ DRM_MODE_COLORIMETRY_BT2020_YCC, "BT2020_YCC" },
};
/** /**
* DOC: standard connector properties * DOC: standard connector properties
* *
@ -1674,7 +1699,6 @@ EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
* DOC: standard connector properties * DOC: standard connector properties
* *
* Colorspace: * Colorspace:
* drm_mode_create_colorspace_property - create colorspace property
* This property helps select a suitable colorspace based on the sink * This property helps select a suitable colorspace based on the sink
* capability. Modern sink devices support wider gamut like BT2020. * capability. Modern sink devices support wider gamut like BT2020.
* This helps switch to BT2020 mode if the BT2020 encoded video stream * This helps switch to BT2020 mode if the BT2020 encoded video stream
@ -1694,32 +1718,68 @@ EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
* - This property is just to inform sink what colorspace * - This property is just to inform sink what colorspace
* source is trying to drive. * source is trying to drive.
* *
* Called by a driver the first time it's needed, must be attached to desired * Because between HDMI and DP have different colorspaces,
* connectors. * drm_mode_create_hdmi_colorspace_property() is used for HDMI connector and
* drm_mode_create_dp_colorspace_property() is used for DP connector.
*/ */
int drm_mode_create_colorspace_property(struct drm_connector *connector)
/**
* drm_mode_create_hdmi_colorspace_property - create hdmi colorspace property
* @connector: connector to create the Colorspace property on.
*
* Called by a driver the first time it's needed, must be attached to desired
* HDMI connectors.
*
* Returns:
* Zero on success, negative errono on failure.
*/
int drm_mode_create_hdmi_colorspace_property(struct drm_connector *connector)
{ {
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
struct drm_property *prop;
if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || if (connector->colorspace_property)
connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { return 0;
prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
"Colorspace", connector->colorspace_property =
drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "Colorspace",
hdmi_colorspaces, hdmi_colorspaces,
ARRAY_SIZE(hdmi_colorspaces)); ARRAY_SIZE(hdmi_colorspaces));
if (!prop)
return -ENOMEM;
} else {
DRM_DEBUG_KMS("Colorspace property not supported\n");
return 0;
}
connector->colorspace_property = prop; if (!connector->colorspace_property)
return -ENOMEM;
return 0; return 0;
} }
EXPORT_SYMBOL(drm_mode_create_colorspace_property); EXPORT_SYMBOL(drm_mode_create_hdmi_colorspace_property);
/**
* drm_mode_create_dp_colorspace_property - create dp colorspace property
* @connector: connector to create the Colorspace property on.
*
* Called by a driver the first time it's needed, must be attached to desired
* DP connectors.
*
* Returns:
* Zero on success, negative errono on failure.
*/
int drm_mode_create_dp_colorspace_property(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
if (connector->colorspace_property)
return 0;
connector->colorspace_property =
drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "Colorspace",
dp_colorspaces,
ARRAY_SIZE(dp_colorspaces));
if (!connector->colorspace_property)
return -ENOMEM;
return 0;
}
EXPORT_SYMBOL(drm_mode_create_dp_colorspace_property);
/** /**
* drm_mode_create_content_type_property - create content type property * drm_mode_create_content_type_property - create content type property
@ -2121,7 +2181,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
int encoders_count = 0; int encoders_count = 0;
int ret = 0; int ret = 0;
int copied = 0; int copied = 0;
int i;
struct drm_mode_modeinfo u_mode; struct drm_mode_modeinfo u_mode;
struct drm_mode_modeinfo __user *mode_ptr; struct drm_mode_modeinfo __user *mode_ptr;
uint32_t __user *encoder_ptr; uint32_t __user *encoder_ptr;
@ -2136,14 +2195,13 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
if (!connector) if (!connector)
return -ENOENT; return -ENOENT;
drm_connector_for_each_possible_encoder(connector, encoder, i) encoders_count = hweight32(connector->possible_encoders);
encoders_count++;
if ((out_resp->count_encoders >= encoders_count) && encoders_count) { if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
copied = 0; copied = 0;
encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
if (put_user(encoder->base.id, encoder_ptr + copied)) { if (put_user(encoder->base.id, encoder_ptr + copied)) {
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;

View File

@ -36,6 +36,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h> #include <drm/drm_atomic_uapi.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
@ -459,6 +460,22 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
__drm_helper_disable_unused_functions(dev); __drm_helper_disable_unused_functions(dev);
} }
/*
* For connectors that support multiple encoders, either the
* .atomic_best_encoder() or .best_encoder() operation must be implemented.
*/
struct drm_encoder *
drm_connector_get_single_encoder(struct drm_connector *connector)
{
struct drm_encoder *encoder;
WARN_ON(hweight32(connector->possible_encoders) > 1);
drm_connector_for_each_possible_encoder(connector, encoder)
return encoder;
return NULL;
}
/** /**
* drm_crtc_helper_set_config - set a new config from userspace * drm_crtc_helper_set_config - set a new config from userspace
* @set: mode set configuration * @set: mode set configuration
@ -624,7 +641,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
new_encoder = connector->encoder; new_encoder = connector->encoder;
for (ro = 0; ro < set->num_connectors; ro++) { for (ro = 0; ro < set->num_connectors; ro++) {
if (set->connectors[ro] == connector) { if (set->connectors[ro] == connector) {
if (connector_funcs->best_encoder)
new_encoder = connector_funcs->best_encoder(connector); new_encoder = connector_funcs->best_encoder(connector);
else
new_encoder = drm_connector_get_single_encoder(connector);
/* if we can't get an encoder for a connector /* if we can't get an encoder for a connector
we are setting now - then fail */ we are setting now - then fail */
if (new_encoder == NULL) if (new_encoder == NULL)

View File

@ -75,3 +75,6 @@ enum drm_mode_status drm_encoder_mode_valid(struct drm_encoder *encoder,
const struct drm_display_mode *mode); const struct drm_display_mode *mode);
enum drm_mode_status drm_connector_mode_valid(struct drm_connector *connector, enum drm_mode_status drm_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode); struct drm_display_mode *mode);
struct drm_encoder *
drm_connector_get_single_encoder(struct drm_connector *connector);

View File

@ -212,8 +212,14 @@ retry:
drm_for_each_plane(plane, fb->dev) { drm_for_each_plane(plane, fb->dev) {
struct drm_plane_state *plane_state; struct drm_plane_state *plane_state;
if (plane->state->fb != fb) ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx);
if (ret)
goto out;
if (plane->state->fb != fb) {
drm_modeset_unlock(&plane->mutex);
continue; continue;
}
plane_state = drm_atomic_get_plane_state(state, plane); plane_state = drm_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state)) { if (IS_ERR(plane_state)) {

View File

@ -334,19 +334,17 @@ static ssize_t crtc_crc_read(struct file *filep, char __user *user_buf,
return LINE_LEN(crc->values_cnt); return LINE_LEN(crc->values_cnt);
} }
static unsigned int crtc_crc_poll(struct file *file, poll_table *wait) static __poll_t crtc_crc_poll(struct file *file, poll_table *wait)
{ {
struct drm_crtc *crtc = file->f_inode->i_private; struct drm_crtc *crtc = file->f_inode->i_private;
struct drm_crtc_crc *crc = &crtc->crc; struct drm_crtc_crc *crc = &crtc->crc;
unsigned ret; __poll_t ret = 0;
poll_wait(file, &crc->wq, wait); poll_wait(file, &crc->wq, wait);
spin_lock_irq(&crc->lock); spin_lock_irq(&crc->lock);
if (crc->source && crtc_crc_data_count(crc)) if (crc->source && crtc_crc_data_count(crc))
ret = POLLIN | POLLRDNORM; ret |= EPOLLIN | EPOLLRDNORM;
else
ret = 0;
spin_unlock_irq(&crc->lock); spin_unlock_irq(&crc->lock);
return ret; return ret;

View File

@ -8,7 +8,9 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <drm/drm_connector.h>
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
#include <drm/drmP.h>
#include <media/cec.h> #include <media/cec.h>
/* /*
@ -295,7 +297,10 @@ static void drm_dp_cec_unregister_work(struct work_struct *work)
*/ */
void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid) void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid)
{ {
u32 cec_caps = CEC_CAP_DEFAULTS | CEC_CAP_NEEDS_HPD; struct drm_connector *connector = aux->cec.connector;
u32 cec_caps = CEC_CAP_DEFAULTS | CEC_CAP_NEEDS_HPD |
CEC_CAP_CONNECTOR_INFO;
struct cec_connector_info conn_info;
unsigned int num_las = 1; unsigned int num_las = 1;
u8 cap; u8 cap;
@ -344,13 +349,17 @@ void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid)
/* Create a new adapter */ /* Create a new adapter */
aux->cec.adap = cec_allocate_adapter(&drm_dp_cec_adap_ops, aux->cec.adap = cec_allocate_adapter(&drm_dp_cec_adap_ops,
aux, aux->cec.name, cec_caps, aux, connector->name, cec_caps,
num_las); num_las);
if (IS_ERR(aux->cec.adap)) { if (IS_ERR(aux->cec.adap)) {
aux->cec.adap = NULL; aux->cec.adap = NULL;
goto unlock; goto unlock;
} }
if (cec_register_adapter(aux->cec.adap, aux->cec.parent)) {
cec_fill_conn_info_from_drm(&conn_info, connector);
cec_s_conn_info(aux->cec.adap, &conn_info);
if (cec_register_adapter(aux->cec.adap, connector->dev->dev)) {
cec_delete_adapter(aux->cec.adap); cec_delete_adapter(aux->cec.adap);
aux->cec.adap = NULL; aux->cec.adap = NULL;
} else { } else {
@ -406,22 +415,20 @@ EXPORT_SYMBOL(drm_dp_cec_unset_edid);
/** /**
* drm_dp_cec_register_connector() - register a new connector * drm_dp_cec_register_connector() - register a new connector
* @aux: DisplayPort AUX channel * @aux: DisplayPort AUX channel
* @name: name of the CEC device * @connector: drm connector
* @parent: parent device
* *
* A new connector was registered with associated CEC adapter name and * A new connector was registered with associated CEC adapter name and
* CEC adapter parent device. After registering the name and parent * CEC adapter parent device. After registering the name and parent
* drm_dp_cec_set_edid() is called to check if the connector supports * drm_dp_cec_set_edid() is called to check if the connector supports
* CEC and to register a CEC adapter if that is the case. * CEC and to register a CEC adapter if that is the case.
*/ */
void drm_dp_cec_register_connector(struct drm_dp_aux *aux, const char *name, void drm_dp_cec_register_connector(struct drm_dp_aux *aux,
struct device *parent) struct drm_connector *connector)
{ {
WARN_ON(aux->cec.adap); WARN_ON(aux->cec.adap);
if (WARN_ON(!aux->transfer)) if (WARN_ON(!aux->transfer))
return; return;
aux->cec.name = name; aux->cec.connector = connector;
aux->cec.parent = parent;
INIT_DELAYED_WORK(&aux->cec.unregister_work, INIT_DELAYED_WORK(&aux->cec.unregister_work,
drm_dp_cec_unregister_work); drm_dp_cec_unregister_work);
} }

View File

@ -1109,6 +1109,14 @@ EXPORT_SYMBOL(drm_dp_aux_init);
* @aux: DisplayPort AUX channel * @aux: DisplayPort AUX channel
* *
* Automatically calls drm_dp_aux_init() if this hasn't been done yet. * Automatically calls drm_dp_aux_init() if this hasn't been done yet.
* This should only be called when the underlying &struct drm_connector is
* initialiazed already. Therefore the best place to call this is from
* &drm_connector_funcs.late_register. Not that drivers which don't follow this
* will Oops when CONFIG_DRM_DP_AUX_CHARDEV is enabled.
*
* Drivers which need to use the aux channel before that point (e.g. at driver
* load time, before drm_dev_register() has been called) need to call
* drm_dp_aux_init().
* *
* Returns 0 on success or a negative error code on failure. * Returns 0 on success or a negative error code on failure.
*/ */

View File

@ -32,11 +32,11 @@
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_dp_mst_helper.h> #include <drm/drm_dp_mst_helper.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/drm_fixed.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include "drm_crtc_helper_internal.h" #include "drm_crtc_helper_internal.h"
#include "drm_dp_mst_topology_internal.h"
/** /**
* DOC: dp mst helper * DOC: dp mst helper
@ -47,7 +47,6 @@
*/ */
static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
char *buf); char *buf);
static int test_calc_pbn_mode(void);
static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port); static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port);
@ -74,6 +73,8 @@ static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux);
static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux); static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux);
static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr); static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
#define DBG_PREFIX "[dp_mst]"
#define DP_STR(x) [DP_ ## x] = #x #define DP_STR(x) [DP_ ## x] = #x
static const char *drm_dp_mst_req_type_str(u8 req_type) static const char *drm_dp_mst_req_type_str(u8 req_type)
@ -130,6 +131,43 @@ static const char *drm_dp_mst_nak_reason_str(u8 nak_reason)
} }
#undef DP_STR #undef DP_STR
#define DP_STR(x) [DRM_DP_SIDEBAND_TX_ ## x] = #x
static const char *drm_dp_mst_sideband_tx_state_str(int state)
{
static const char * const sideband_reason_str[] = {
DP_STR(QUEUED),
DP_STR(START_SEND),
DP_STR(SENT),
DP_STR(RX),
DP_STR(TIMEOUT),
};
if (state >= ARRAY_SIZE(sideband_reason_str) ||
!sideband_reason_str[state])
return "unknown";
return sideband_reason_str[state];
}
static int
drm_dp_mst_rad_to_str(const u8 rad[8], u8 lct, char *out, size_t len)
{
int i;
u8 unpacked_rad[16];
for (i = 0; i < lct; i++) {
if (i % 2)
unpacked_rad[i] = rad[i / 2] >> 4;
else
unpacked_rad[i] = rad[i / 2] & BIT_MASK(4);
}
/* TODO: Eventually add something to printk so we can format the rad
* like this: 1.2.3
*/
return snprintf(out, len, "%*phC", lct, unpacked_rad);
}
/* sideband msg handling */ /* sideband msg handling */
static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles) static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
@ -262,7 +300,8 @@ static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
return true; return true;
} }
static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req, void
drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
struct drm_dp_sideband_msg_tx *raw) struct drm_dp_sideband_msg_tx *raw)
{ {
int idx = 0; int idx = 0;
@ -272,6 +311,8 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
switch (req->req_type) { switch (req->req_type) {
case DP_ENUM_PATH_RESOURCES: case DP_ENUM_PATH_RESOURCES:
case DP_POWER_DOWN_PHY:
case DP_POWER_UP_PHY:
buf[idx] = (req->u.port_num.port_number & 0xf) << 4; buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
idx++; idx++;
break; break;
@ -359,15 +400,254 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes); memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
idx += req->u.i2c_write.num_bytes; idx += req->u.i2c_write.num_bytes;
break; break;
case DP_POWER_DOWN_PHY:
case DP_POWER_UP_PHY:
buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
idx++;
break;
} }
raw->cur_len = idx; raw->cur_len = idx;
} }
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_encode_sideband_req);
/* Decode a sideband request we've encoded, mainly used for debugging */
int
drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
struct drm_dp_sideband_msg_req_body *req)
{
const u8 *buf = raw->msg;
int i, idx = 0;
req->req_type = buf[idx++] & 0x7f;
switch (req->req_type) {
case DP_ENUM_PATH_RESOURCES:
case DP_POWER_DOWN_PHY:
case DP_POWER_UP_PHY:
req->u.port_num.port_number = (buf[idx] >> 4) & 0xf;
break;
case DP_ALLOCATE_PAYLOAD:
{
struct drm_dp_allocate_payload *a =
&req->u.allocate_payload;
a->number_sdp_streams = buf[idx] & 0xf;
a->port_number = (buf[idx] >> 4) & 0xf;
WARN_ON(buf[++idx] & 0x80);
a->vcpi = buf[idx] & 0x7f;
a->pbn = buf[++idx] << 8;
a->pbn |= buf[++idx];
idx++;
for (i = 0; i < a->number_sdp_streams; i++) {
a->sdp_stream_sink[i] =
(buf[idx + (i / 2)] >> ((i % 2) ? 0 : 4)) & 0xf;
}
}
break;
case DP_QUERY_PAYLOAD:
req->u.query_payload.port_number = (buf[idx] >> 4) & 0xf;
WARN_ON(buf[++idx] & 0x80);
req->u.query_payload.vcpi = buf[idx] & 0x7f;
break;
case DP_REMOTE_DPCD_READ:
{
struct drm_dp_remote_dpcd_read *r = &req->u.dpcd_read;
r->port_number = (buf[idx] >> 4) & 0xf;
r->dpcd_address = (buf[idx] << 16) & 0xf0000;
r->dpcd_address |= (buf[++idx] << 8) & 0xff00;
r->dpcd_address |= buf[++idx] & 0xff;
r->num_bytes = buf[++idx];
}
break;
case DP_REMOTE_DPCD_WRITE:
{
struct drm_dp_remote_dpcd_write *w =
&req->u.dpcd_write;
w->port_number = (buf[idx] >> 4) & 0xf;
w->dpcd_address = (buf[idx] << 16) & 0xf0000;
w->dpcd_address |= (buf[++idx] << 8) & 0xff00;
w->dpcd_address |= buf[++idx] & 0xff;
w->num_bytes = buf[++idx];
w->bytes = kmemdup(&buf[++idx], w->num_bytes,
GFP_KERNEL);
if (!w->bytes)
return -ENOMEM;
}
break;
case DP_REMOTE_I2C_READ:
{
struct drm_dp_remote_i2c_read *r = &req->u.i2c_read;
struct drm_dp_remote_i2c_read_tx *tx;
bool failed = false;
r->num_transactions = buf[idx] & 0x3;
r->port_number = (buf[idx] >> 4) & 0xf;
for (i = 0; i < r->num_transactions; i++) {
tx = &r->transactions[i];
tx->i2c_dev_id = buf[++idx] & 0x7f;
tx->num_bytes = buf[++idx];
tx->bytes = kmemdup(&buf[++idx],
tx->num_bytes,
GFP_KERNEL);
if (!tx->bytes) {
failed = true;
break;
}
idx += tx->num_bytes;
tx->no_stop_bit = (buf[idx] >> 5) & 0x1;
tx->i2c_transaction_delay = buf[idx] & 0xf;
}
if (failed) {
for (i = 0; i < r->num_transactions; i++)
kfree(tx->bytes);
return -ENOMEM;
}
r->read_i2c_device_id = buf[++idx] & 0x7f;
r->num_bytes_read = buf[++idx];
}
break;
case DP_REMOTE_I2C_WRITE:
{
struct drm_dp_remote_i2c_write *w = &req->u.i2c_write;
w->port_number = (buf[idx] >> 4) & 0xf;
w->write_i2c_device_id = buf[++idx] & 0x7f;
w->num_bytes = buf[++idx];
w->bytes = kmemdup(&buf[++idx], w->num_bytes,
GFP_KERNEL);
if (!w->bytes)
return -ENOMEM;
}
break;
}
return 0;
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_decode_sideband_req);
void
drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
int indent, struct drm_printer *printer)
{
int i;
#define P(f, ...) drm_printf_indent(printer, indent, f, ##__VA_ARGS__)
if (req->req_type == DP_LINK_ADDRESS) {
/* No contents to print */
P("type=%s\n", drm_dp_mst_req_type_str(req->req_type));
return;
}
P("type=%s contents:\n", drm_dp_mst_req_type_str(req->req_type));
indent++;
switch (req->req_type) {
case DP_ENUM_PATH_RESOURCES:
case DP_POWER_DOWN_PHY:
case DP_POWER_UP_PHY:
P("port=%d\n", req->u.port_num.port_number);
break;
case DP_ALLOCATE_PAYLOAD:
P("port=%d vcpi=%d pbn=%d sdp_streams=%d %*ph\n",
req->u.allocate_payload.port_number,
req->u.allocate_payload.vcpi, req->u.allocate_payload.pbn,
req->u.allocate_payload.number_sdp_streams,
req->u.allocate_payload.number_sdp_streams,
req->u.allocate_payload.sdp_stream_sink);
break;
case DP_QUERY_PAYLOAD:
P("port=%d vcpi=%d\n",
req->u.query_payload.port_number,
req->u.query_payload.vcpi);
break;
case DP_REMOTE_DPCD_READ:
P("port=%d dpcd_addr=%05x len=%d\n",
req->u.dpcd_read.port_number, req->u.dpcd_read.dpcd_address,
req->u.dpcd_read.num_bytes);
break;
case DP_REMOTE_DPCD_WRITE:
P("port=%d addr=%05x len=%d: %*ph\n",
req->u.dpcd_write.port_number,
req->u.dpcd_write.dpcd_address,
req->u.dpcd_write.num_bytes, req->u.dpcd_write.num_bytes,
req->u.dpcd_write.bytes);
break;
case DP_REMOTE_I2C_READ:
P("port=%d num_tx=%d id=%d size=%d:\n",
req->u.i2c_read.port_number,
req->u.i2c_read.num_transactions,
req->u.i2c_read.read_i2c_device_id,
req->u.i2c_read.num_bytes_read);
indent++;
for (i = 0; i < req->u.i2c_read.num_transactions; i++) {
const struct drm_dp_remote_i2c_read_tx *rtx =
&req->u.i2c_read.transactions[i];
P("%d: id=%03d size=%03d no_stop_bit=%d tx_delay=%03d: %*ph\n",
i, rtx->i2c_dev_id, rtx->num_bytes,
rtx->no_stop_bit, rtx->i2c_transaction_delay,
rtx->num_bytes, rtx->bytes);
}
break;
case DP_REMOTE_I2C_WRITE:
P("port=%d id=%d size=%d: %*ph\n",
req->u.i2c_write.port_number,
req->u.i2c_write.write_i2c_device_id,
req->u.i2c_write.num_bytes, req->u.i2c_write.num_bytes,
req->u.i2c_write.bytes);
break;
default:
P("???\n");
break;
}
#undef P
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_dump_sideband_msg_req_body);
static inline void
drm_dp_mst_dump_sideband_msg_tx(struct drm_printer *p,
const struct drm_dp_sideband_msg_tx *txmsg)
{
struct drm_dp_sideband_msg_req_body req;
char buf[64];
int ret;
int i;
drm_dp_mst_rad_to_str(txmsg->dst->rad, txmsg->dst->lct, buf,
sizeof(buf));
drm_printf(p, "txmsg cur_offset=%x cur_len=%x seqno=%x state=%s path_msg=%d dst=%s\n",
txmsg->cur_offset, txmsg->cur_len, txmsg->seqno,
drm_dp_mst_sideband_tx_state_str(txmsg->state),
txmsg->path_msg, buf);
ret = drm_dp_decode_sideband_req(txmsg, &req);
if (ret) {
drm_printf(p, "<failed to decode sideband req: %d>\n", ret);
return;
}
drm_dp_dump_sideband_msg_req_body(&req, 1, p);
switch (req.req_type) {
case DP_REMOTE_DPCD_WRITE:
kfree(req.u.dpcd_write.bytes);
break;
case DP_REMOTE_I2C_READ:
for (i = 0; i < req.u.i2c_read.num_transactions; i++)
kfree(req.u.i2c_read.transactions[i].bytes);
break;
case DP_REMOTE_I2C_WRITE:
kfree(req.u.i2c_write.bytes);
break;
}
}
static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len) static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
{ {
@ -842,8 +1122,8 @@ static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
clear_bit(vcpi - 1, &mgr->vcpi_mask); clear_bit(vcpi - 1, &mgr->vcpi_mask);
for (i = 0; i < mgr->max_payloads; i++) { for (i = 0; i < mgr->max_payloads; i++) {
if (mgr->proposed_vcpis[i]) if (mgr->proposed_vcpis[i] &&
if (mgr->proposed_vcpis[i]->vcpi == vcpi) { mgr->proposed_vcpis[i]->vcpi == vcpi) {
mgr->proposed_vcpis[i] = NULL; mgr->proposed_vcpis[i] = NULL;
clear_bit(i + 1, &mgr->payload_mask); clear_bit(i + 1, &mgr->payload_mask);
} }
@ -899,6 +1179,11 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
} }
} }
out: out:
if (unlikely(ret == -EIO) && drm_debug_enabled(DRM_UT_DP)) {
struct drm_printer p = drm_debug_printer(DBG_PREFIX);
drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
}
mutex_unlock(&mgr->qlock); mutex_unlock(&mgr->qlock);
return ret; return ret;
@ -1617,7 +1902,8 @@ void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
} }
EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister); EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister);
static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, static void
drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
struct drm_device *dev, struct drm_device *dev,
struct drm_dp_link_addr_reply_port *port_msg) struct drm_dp_link_addr_reply_port *port_msg)
{ {
@ -1722,7 +2008,8 @@ out:
drm_dp_mst_topology_put_port(port); drm_dp_mst_topology_put_port(port);
} }
static void drm_dp_update_port(struct drm_dp_mst_branch *mstb, static void
drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
struct drm_dp_connection_status_notify *conn_stat) struct drm_dp_connection_status_notify *conn_stat)
{ {
struct drm_dp_mst_port *port; struct drm_dp_mst_port *port;
@ -1800,7 +2087,7 @@ out:
static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper( static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *mstb,
uint8_t *guid) const uint8_t *guid)
{ {
struct drm_dp_mst_branch *found_mstb; struct drm_dp_mst_branch *found_mstb;
struct drm_dp_mst_port *port; struct drm_dp_mst_port *port;
@ -1824,7 +2111,7 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
static struct drm_dp_mst_branch * static struct drm_dp_mst_branch *
drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr, drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
uint8_t *guid) const uint8_t *guid)
{ {
struct drm_dp_mst_branch *mstb; struct drm_dp_mst_branch *mstb;
int ret; int ret;
@ -2035,8 +2322,11 @@ static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
idx += tosend + 1; idx += tosend + 1;
ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx); ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
if (ret) { if (unlikely(ret) && drm_debug_enabled(DRM_UT_DP)) {
DRM_DEBUG_KMS("sideband msg failed to send\n"); struct drm_printer p = drm_debug_printer(DBG_PREFIX);
drm_printf(&p, "sideband msg failed to send\n");
drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
return ret; return ret;
} }
@ -2098,17 +2388,46 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
{ {
mutex_lock(&mgr->qlock); mutex_lock(&mgr->qlock);
list_add_tail(&txmsg->next, &mgr->tx_msg_downq); list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
if (drm_debug_enabled(DRM_UT_DP)) {
struct drm_printer p = drm_debug_printer(DBG_PREFIX);
drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
}
if (list_is_singular(&mgr->tx_msg_downq)) if (list_is_singular(&mgr->tx_msg_downq))
process_single_down_tx_qlock(mgr); process_single_down_tx_qlock(mgr);
mutex_unlock(&mgr->qlock); mutex_unlock(&mgr->qlock);
} }
static void
drm_dp_dump_link_address(struct drm_dp_link_address_ack_reply *reply)
{
struct drm_dp_link_addr_reply_port *port_reply;
int i;
for (i = 0; i < reply->nports; i++) {
port_reply = &reply->ports[i];
DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n",
i,
port_reply->input_port,
port_reply->peer_device_type,
port_reply->port_number,
port_reply->dpcd_revision,
port_reply->mcs,
port_reply->ddps,
port_reply->legacy_device_plug_status,
port_reply->num_sdp_streams,
port_reply->num_sdp_stream_sinks);
}
}
static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_branch *mstb) struct drm_dp_mst_branch *mstb)
{ {
int len;
struct drm_dp_sideband_msg_tx *txmsg; struct drm_dp_sideband_msg_tx *txmsg;
int ret; struct drm_dp_link_address_ack_reply *reply;
int i, len, ret;
txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
if (!txmsg) if (!txmsg)
@ -2120,48 +2439,44 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
mstb->link_address_sent = true; mstb->link_address_sent = true;
drm_dp_queue_down_tx(mgr, txmsg); drm_dp_queue_down_tx(mgr, txmsg);
/* FIXME: Actually do some real error handling here */
ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
if (ret > 0) { if (ret <= 0) {
int i; DRM_ERROR("Sending link address failed with %d\n", ret);
goto out;
}
if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) { if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
DRM_DEBUG_KMS("link address nak received\n"); DRM_ERROR("link address NAK received\n");
} else { ret = -EIO;
DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports); goto out;
for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i,
txmsg->reply.u.link_addr.ports[i].input_port,
txmsg->reply.u.link_addr.ports[i].peer_device_type,
txmsg->reply.u.link_addr.ports[i].port_number,
txmsg->reply.u.link_addr.ports[i].dpcd_revision,
txmsg->reply.u.link_addr.ports[i].mcs,
txmsg->reply.u.link_addr.ports[i].ddps,
txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status,
txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
} }
drm_dp_check_mstb_guid(mstb, txmsg->reply.u.link_addr.guid); reply = &txmsg->reply.u.link_addr;
DRM_DEBUG_KMS("link address reply: %d\n", reply->nports);
drm_dp_dump_link_address(reply);
drm_dp_check_mstb_guid(mstb, reply->guid);
for (i = 0; i < reply->nports; i++)
drm_dp_mst_handle_link_address_port(mstb, mgr->dev,
&reply->ports[i]);
for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
}
drm_kms_helper_hotplug_event(mgr->dev); drm_kms_helper_hotplug_event(mgr->dev);
}
} else {
mstb->link_address_sent = false;
DRM_DEBUG_KMS("link address failed %d\n", ret);
}
out:
if (ret <= 0)
mstb->link_address_sent = false;
kfree(txmsg); kfree(txmsg);
} }
static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, static int
drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *mstb,
struct drm_dp_mst_port *port) struct drm_dp_mst_port *port)
{ {
int len; struct drm_dp_enum_path_resources_ack_reply *path_res;
struct drm_dp_sideband_msg_tx *txmsg; struct drm_dp_sideband_msg_tx *txmsg;
int len;
int ret; int ret;
txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
@ -2175,14 +2490,20 @@ static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
if (ret > 0) { if (ret > 0) {
path_res = &txmsg->reply.u.path_resources;
if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) { if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
DRM_DEBUG_KMS("enum path resources nak received\n"); DRM_DEBUG_KMS("enum path resources nak received\n");
} else { } else {
if (port->port_num != txmsg->reply.u.path_resources.port_number) if (port->port_num != path_res->port_number)
DRM_ERROR("got incorrect port in response\n"); DRM_ERROR("got incorrect port in response\n");
DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number,
txmsg->reply.u.path_resources.avail_payload_bw_number); DRM_DEBUG_KMS("enum path resources %d: %d %d\n",
port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number; path_res->port_number,
path_res->full_payload_bw_number,
path_res->avail_payload_bw_number);
port->available_pbn =
path_res->avail_payload_bw_number;
} }
} }
@ -2655,30 +2976,13 @@ static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
return 0; return 0;
} }
static bool drm_dp_get_vc_payload_bw(int dp_link_bw, static int drm_dp_get_vc_payload_bw(u8 dp_link_bw, u8 dp_link_count)
int dp_link_count,
int *out)
{ {
switch (dp_link_bw) { if (dp_link_bw == 0 || dp_link_count == 0)
default:
DRM_DEBUG_KMS("invalid link bandwidth in DPCD: %x (link count: %d)\n", DRM_DEBUG_KMS("invalid link bandwidth in DPCD: %x (link count: %d)\n",
dp_link_bw, dp_link_count); dp_link_bw, dp_link_count);
return false;
case DP_LINK_BW_1_62: return dp_link_bw * dp_link_count / 2;
*out = 3 * dp_link_count;
break;
case DP_LINK_BW_2_7:
*out = 5 * dp_link_count;
break;
case DP_LINK_BW_5_4:
*out = 10 * dp_link_count;
break;
case DP_LINK_BW_8_1:
*out = 15 * dp_link_count;
break;
}
return true;
} }
/** /**
@ -2710,9 +3014,9 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
goto out_unlock; goto out_unlock;
} }
if (!drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1],
mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK, mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK);
&mgr->pbn_div)) { if (mgr->pbn_div == 0) {
ret = -EINVAL; ret = -EINVAL;
goto out_unlock; goto out_unlock;
} }
@ -2890,30 +3194,26 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
{ {
int ret = 0;
if (!drm_dp_get_one_sb_msg(mgr, false)) {
memset(&mgr->down_rep_recv, 0,
sizeof(struct drm_dp_sideband_msg_rx));
return 0;
}
if (mgr->down_rep_recv.have_eomt) {
struct drm_dp_sideband_msg_tx *txmsg; struct drm_dp_sideband_msg_tx *txmsg;
struct drm_dp_mst_branch *mstb; struct drm_dp_mst_branch *mstb;
struct drm_dp_sideband_msg_hdr *hdr = &mgr->down_rep_recv.initial_hdr;
int slot = -1; int slot = -1;
mstb = drm_dp_get_mst_branch_device(mgr,
mgr->down_rep_recv.initial_hdr.lct,
mgr->down_rep_recv.initial_hdr.rad);
if (!mstb) { if (!drm_dp_get_one_sb_msg(mgr, false))
DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct); goto clear_down_rep_recv;
memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
if (!mgr->down_rep_recv.have_eomt)
return 0; return 0;
mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
if (!mstb) {
DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
hdr->lct);
goto clear_down_rep_recv;
} }
/* find the message */ /* find the message */
slot = mgr->down_rep_recv.initial_hdr.seqno; slot = hdr->seqno;
mutex_lock(&mgr->qlock); mutex_lock(&mgr->qlock);
txmsg = mstb->tx_slots[slot]; txmsg = mstb->tx_slots[slot];
/* remove from slots */ /* remove from slots */
@ -2921,14 +3221,9 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
if (!txmsg) { if (!txmsg) {
DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n", DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n",
mstb, mstb, hdr->seqno, hdr->lct, hdr->rad[0],
mgr->down_rep_recv.initial_hdr.seqno,
mgr->down_rep_recv.initial_hdr.lct,
mgr->down_rep_recv.initial_hdr.rad[0],
mgr->down_rep_recv.msg[0]); mgr->down_rep_recv.msg[0]);
drm_dp_mst_topology_put_mstb(mstb); goto no_msg;
memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
return 0;
} }
drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply); drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply);
@ -2950,76 +3245,84 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
mutex_unlock(&mgr->qlock); mutex_unlock(&mgr->qlock);
wake_up_all(&mgr->tx_waitq); wake_up_all(&mgr->tx_waitq);
}
return ret; return 0;
no_msg:
drm_dp_mst_topology_put_mstb(mstb);
clear_down_rep_recv:
memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
return 0;
} }
static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
{ {
int ret = 0;
if (!drm_dp_get_one_sb_msg(mgr, true)) {
memset(&mgr->up_req_recv, 0,
sizeof(struct drm_dp_sideband_msg_rx));
return 0;
}
if (mgr->up_req_recv.have_eomt) {
struct drm_dp_sideband_msg_req_body msg; struct drm_dp_sideband_msg_req_body msg;
struct drm_dp_sideband_msg_hdr *hdr = &mgr->up_req_recv.initial_hdr;
struct drm_dp_mst_branch *mstb = NULL; struct drm_dp_mst_branch *mstb = NULL;
const u8 *guid;
bool seqno; bool seqno;
if (!mgr->up_req_recv.initial_hdr.broadcast) { if (!drm_dp_get_one_sb_msg(mgr, true))
mstb = drm_dp_get_mst_branch_device(mgr, goto out;
mgr->up_req_recv.initial_hdr.lct,
mgr->up_req_recv.initial_hdr.rad); if (!mgr->up_req_recv.have_eomt)
if (!mstb) {
DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
return 0; return 0;
if (!hdr->broadcast) {
mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
if (!mstb) {
DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
hdr->lct);
goto out;
} }
} }
seqno = mgr->up_req_recv.initial_hdr.seqno; seqno = hdr->seqno;
drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg); drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY)
guid = msg.u.conn_stat.guid;
else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY)
guid = msg.u.resource_stat.guid;
else
goto out;
drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno,
false);
if (!mstb) {
mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
if (!mstb) {
DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
hdr->lct);
goto out;
}
}
if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) { if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false); drm_dp_mst_handle_conn_stat(mstb, &msg.u.conn_stat);
if (!mstb) DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.conn_stat.guid); msg.u.conn_stat.port_number,
msg.u.conn_stat.legacy_device_plug_status,
msg.u.conn_stat.displayport_device_plug_status,
msg.u.conn_stat.message_capability_status,
msg.u.conn_stat.input_port,
msg.u.conn_stat.peer_device_type);
if (!mstb) {
DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
return 0;
}
drm_dp_update_port(mstb, &msg.u.conn_stat);
DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
drm_kms_helper_hotplug_event(mgr->dev); drm_kms_helper_hotplug_event(mgr->dev);
} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) { } else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false); DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n",
if (!mstb) msg.u.resource_stat.port_number,
mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.resource_stat.guid); msg.u.resource_stat.available_pbn);
}
if (!mstb) { drm_dp_mst_topology_put_mstb(mstb);
DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct); out:
memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
return 0; return 0;
}
DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
}
if (mstb)
drm_dp_mst_topology_put_mstb(mstb);
memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
}
return ret;
} }
/** /**
@ -3539,13 +3842,6 @@ EXPORT_SYMBOL(drm_dp_check_act_status);
*/ */
int drm_dp_calc_pbn_mode(int clock, int bpp) int drm_dp_calc_pbn_mode(int clock, int bpp)
{ {
u64 kbps;
s64 peak_kbps;
u32 numerator;
u32 denominator;
kbps = clock * bpp;
/* /*
* margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006
* The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on
@ -3556,41 +3852,11 @@ int drm_dp_calc_pbn_mode(int clock, int bpp)
* peak_kbps *= (64/54) * peak_kbps *= (64/54)
* peak_kbps *= 8 convert to bytes * peak_kbps *= 8 convert to bytes
*/ */
return DIV_ROUND_UP_ULL(mul_u32_u32(clock * bpp, 64 * 1006),
numerator = 64 * 1006; 8 * 54 * 1000 * 1000);
denominator = 54 * 8 * 1000 * 1000;
kbps *= numerator;
peak_kbps = drm_fixp_from_fraction(kbps, denominator);
return drm_fixp2int_ceil(peak_kbps);
} }
EXPORT_SYMBOL(drm_dp_calc_pbn_mode); EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
static int test_calc_pbn_mode(void)
{
int ret;
ret = drm_dp_calc_pbn_mode(154000, 30);
if (ret != 689) {
DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
154000, 30, 689, ret);
return -EINVAL;
}
ret = drm_dp_calc_pbn_mode(234000, 30);
if (ret != 1047) {
DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
234000, 30, 1047, ret);
return -EINVAL;
}
ret = drm_dp_calc_pbn_mode(297000, 24);
if (ret != 1063) {
DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
297000, 24, 1063, ret);
return -EINVAL;
}
return 0;
}
/* we want to kick the TX after we've ack the up/down IRQs. */ /* we want to kick the TX after we've ack the up/down IRQs. */
static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr) static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
{ {
@ -3749,8 +4015,6 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
list_del(&port->next); list_del(&port->next);
mutex_unlock(&mgr->destroy_connector_lock); mutex_unlock(&mgr->destroy_connector_lock);
INIT_LIST_HEAD(&port->next);
mgr->cbs->destroy_connector(mgr, port->connector); mgr->cbs->destroy_connector(mgr, port->connector);
drm_dp_port_teardown_pdt(port, port->pdt); drm_dp_port_teardown_pdt(port, port->pdt);
@ -3970,8 +4234,6 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
if (!mgr->proposed_vcpis) if (!mgr->proposed_vcpis)
return -ENOMEM; return -ENOMEM;
set_bit(0, &mgr->payload_mask); set_bit(0, &mgr->payload_mask);
if (test_calc_pbn_mode() < 0)
DRM_ERROR("MST PBN self-test failed\n");
mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL); mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL);
if (mst_state == NULL) if (mst_state == NULL)
@ -4007,6 +4269,11 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
mgr->aux = NULL; mgr->aux = NULL;
drm_atomic_private_obj_fini(&mgr->base); drm_atomic_private_obj_fini(&mgr->base);
mgr->funcs = NULL; mgr->funcs = NULL;
mutex_destroy(&mgr->destroy_connector_lock);
mutex_destroy(&mgr->payload_lock);
mutex_destroy(&mgr->qlock);
mutex_destroy(&mgr->lock);
} }
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy); EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);

View File

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-only
*
* Declarations for DP MST related functions which are only used in selftests
*
* Copyright © 2018 Red Hat
* Authors:
* Lyude Paul <lyude@redhat.com>
*/
#ifndef _DRM_DP_MST_HELPER_INTERNAL_H_
#define _DRM_DP_MST_HELPER_INTERNAL_H_
#include <drm/drm_dp_mst_helper.h>
void
drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
struct drm_dp_sideband_msg_tx *raw);
int drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
struct drm_dp_sideband_msg_req_body *req);
void
drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
int indent, struct drm_printer *printer);
#endif /* !_DRM_DP_MST_HELPER_INTERNAL_H_ */

View File

@ -46,26 +46,9 @@
#include "drm_internal.h" #include "drm_internal.h"
#include "drm_legacy.h" #include "drm_legacy.h"
/*
* drm_debug: Enable debug output.
* Bitmask of DRM_UT_x. See include/drm/drm_print.h for details.
*/
unsigned int drm_debug = 0;
EXPORT_SYMBOL(drm_debug);
MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl"); MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl");
MODULE_DESCRIPTION("DRM shared core routines"); MODULE_DESCRIPTION("DRM shared core routines");
MODULE_LICENSE("GPL and additional rights"); MODULE_LICENSE("GPL and additional rights");
MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug category.\n"
"\t\tBit 0 (0x01) will enable CORE messages (drm core code)\n"
"\t\tBit 1 (0x02) will enable DRIVER messages (drm controller code)\n"
"\t\tBit 2 (0x04) will enable KMS messages (modesetting code)\n"
"\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n"
"\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n"
"\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n"
"\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n"
"\t\tBit 8 (0x100) will enable DP messages (displayport code)");
module_param_named(debug, drm_debug, int, 0600);
static DEFINE_SPINLOCK(drm_minor_lock); static DEFINE_SPINLOCK(drm_minor_lock);
static struct idr drm_minors_idr; static struct idr drm_minors_idr;

View File

@ -216,13 +216,11 @@ void drm_dsc_pps_payload_pack(struct drm_dsc_picture_parameter_set *pps_payload,
*/ */
for (i = 0; i < DSC_NUM_BUF_RANGES; i++) { for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
pps_payload->rc_range_parameters[i] = pps_payload->rc_range_parameters[i] =
((dsc_cfg->rc_range_params[i].range_min_qp << cpu_to_be16((dsc_cfg->rc_range_params[i].range_min_qp <<
DSC_PPS_RC_RANGE_MINQP_SHIFT) | DSC_PPS_RC_RANGE_MINQP_SHIFT) |
(dsc_cfg->rc_range_params[i].range_max_qp << (dsc_cfg->rc_range_params[i].range_max_qp <<
DSC_PPS_RC_RANGE_MAXQP_SHIFT) | DSC_PPS_RC_RANGE_MAXQP_SHIFT) |
(dsc_cfg->rc_range_params[i].range_bpg_offset)); (dsc_cfg->rc_range_params[i].range_bpg_offset));
pps_payload->rc_range_parameters[i] =
cpu_to_be16(pps_payload->rc_range_parameters[i]);
} }
/* PPS 88 */ /* PPS 88 */
@ -336,12 +334,6 @@ int drm_dsc_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg)
else else
vdsc_cfg->nfl_bpg_offset = 0; vdsc_cfg->nfl_bpg_offset = 0;
/* 2^16 - 1 */
if (vdsc_cfg->nfl_bpg_offset > 65535) {
DRM_DEBUG_KMS("NflBpgOffset is too large for this slice height\n");
return -ERANGE;
}
/* Number of groups used to code the entire slice */ /* Number of groups used to code the entire slice */
groups_total = groups_per_line * vdsc_cfg->slice_height; groups_total = groups_per_line * vdsc_cfg->slice_height;
@ -371,11 +363,6 @@ int drm_dsc_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg)
vdsc_cfg->scale_increment_interval = 0; vdsc_cfg->scale_increment_interval = 0;
} }
if (vdsc_cfg->scale_increment_interval > 65535) {
DRM_DEBUG_KMS("ScaleIncrementInterval is large for slice height\n");
return -ERANGE;
}
/* /*
* DSC spec mentions that bits_per_pixel specifies the target * DSC spec mentions that bits_per_pixel specifies the target
* bits/pixel (bpp) rate that is used by the encoder, * bits/pixel (bpp) rate that is used by the encoder,

View File

@ -1275,6 +1275,106 @@ static const struct drm_display_mode edid_cea_modes[] = {
4104, 4400, 0, 2160, 2168, 2178, 2250, 0, 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 108 - 1280x720@48Hz 16:9 */
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 90000, 1280, 2240,
2280, 2500, 0, 720, 725, 730, 750, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 109 - 1280x720@48Hz 64:27 */
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 90000, 1280, 2240,
2280, 2500, 0, 720, 725, 730, 750, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 110 - 1680x720@48Hz 64:27 */
{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 2490,
2530, 2750, 0, 720, 725, 730, 750, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 111 - 1920x1080@48Hz 16:9 */
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2558,
2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 112 - 1920x1080@48Hz 64:27 */
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2558,
2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 113 - 2560x1080@48Hz 64:27 */
{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 3558,
3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 114 - 3840x2160@48Hz 16:9 */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 5116,
5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 115 - 4096x2160@48Hz 256:135 */
{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5116,
5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
/* 116 - 3840x2160@48Hz 64:27 */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 5116,
5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 117 - 3840x2160@100Hz 16:9 */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4896,
4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 118 - 3840x2160@120Hz 16:9 */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4016,
4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 119 - 3840x2160@100Hz 64:27 */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4896,
4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 120 - 3840x2160@120Hz 64:27 */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4016,
4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 121 - 5120x2160@24Hz 64:27 */
{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 7116,
7204, 7500, 0, 2160, 2168, 2178, 2200, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 122 - 5120x2160@25Hz 64:27 */
{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 6816,
6904, 7200, 0, 2160, 2168, 2178, 2200, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 123 - 5120x2160@30Hz 64:27 */
{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 5784,
5872, 6000, 0, 2160, 2168, 2178, 2200, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 124 - 5120x2160@48Hz 64:27 */
{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 5866,
5954, 6250, 0, 2160, 2168, 2178, 2475, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 48, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 125 - 5120x2160@50Hz 64:27 */
{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 6216,
6304, 6600, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 126 - 5120x2160@60Hz 64:27 */
{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 5284,
5372, 5500, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
/* 127 - 5120x2160@100Hz 64:27 */
{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 1485000, 5120, 6216,
6304, 6600, 0, 2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
}; };
/* /*
@ -1551,7 +1651,7 @@ static void connector_bad_edid(struct drm_connector *connector,
{ {
int i; int i;
if (connector->bad_edid_counter++ && !(drm_debug & DRM_UT_KMS)) if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))
return; return;
dev_warn(connector->dev->dev, dev_warn(connector->dev->dev,
@ -3719,7 +3819,7 @@ cea_db_offsets(const u8 *cea, int *start, int *end)
if (*end < 4 || *end > 127) if (*end < 4 || *end > 127)
return -ERANGE; return -ERANGE;
} else { } else {
return -ENOTSUPP; return -EOPNOTSUPP;
} }
return 0; return 0;
@ -4188,7 +4288,7 @@ int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)
if (cea_revision(cea) < 3) { if (cea_revision(cea) < 3) {
DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
return -ENOTSUPP; return -EOPNOTSUPP;
} }
if (cea_db_offsets(cea, &start, &end)) { if (cea_db_offsets(cea, &start, &end)) {
@ -4249,7 +4349,7 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
if (cea_revision(cea) < 3) { if (cea_revision(cea) < 3) {
DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
return -ENOTSUPP; return -EOPNOTSUPP;
} }
if (cea_db_offsets(cea, &start, &end)) { if (cea_db_offsets(cea, &start, &end)) {

View File

@ -175,7 +175,7 @@ static void *edid_load(struct drm_connector *connector, const char *name,
u8 *edid; u8 *edid;
int fwsize, builtin; int fwsize, builtin;
int i, valid_extensions = 0; int i, valid_extensions = 0;
bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); bool print_bad_edid = !connector->bad_edid_counter || drm_debug_enabled(DRM_UT_KMS);
builtin = match_string(generic_edid_name, GENERIC_EDIDS, name); builtin = match_string(generic_edid_name, GENERIC_EDIDS, name);
if (builtin >= 0) { if (builtin >= 0) {

View File

@ -22,6 +22,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <drm/drm_bridge.h>
#include <drm/drm_device.h> #include <drm/drm_device.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>

View File

@ -46,6 +46,7 @@
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_vblank.h> #include <drm/drm_vblank.h>
#include "drm_crtc_helper_internal.h"
#include "drm_internal.h" #include "drm_internal.h"
static bool drm_fbdev_emulation = true; static bool drm_fbdev_emulation = true;

View File

@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/module.h>
#include <drm/drm_gem_ttm_helper.h>
/**
* DOC: overview
*
* This library provides helper functions for gem objects backed by
* ttm.
*/
/**
* drm_gem_ttm_print_info() - Print &ttm_buffer_object info for debugfs
* @p: DRM printer
* @indent: Tab indentation level
* @gem: GEM object
*
* This function can be used as &drm_gem_object_funcs.print_info
* callback.
*/
void drm_gem_ttm_print_info(struct drm_printer *p, unsigned int indent,
const struct drm_gem_object *gem)
{
static const char * const plname[] = {
[ TTM_PL_SYSTEM ] = "system",
[ TTM_PL_TT ] = "tt",
[ TTM_PL_VRAM ] = "vram",
[ TTM_PL_PRIV ] = "priv",
[ 16 ] = "cached",
[ 17 ] = "uncached",
[ 18 ] = "wc",
[ 19 ] = "contig",
[ 21 ] = "pinned", /* NO_EVICT */
[ 22 ] = "topdown",
};
const struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(gem);
drm_printf_indent(p, indent, "placement=");
drm_print_bits(p, bo->mem.placement, plname, ARRAY_SIZE(plname));
drm_printf(p, "\n");
if (bo->mem.bus.is_iomem) {
drm_printf_indent(p, indent, "bus.base=%lx\n",
(unsigned long)bo->mem.bus.base);
drm_printf_indent(p, indent, "bus.offset=%lx\n",
(unsigned long)bo->mem.bus.offset);
}
}
EXPORT_SYMBOL(drm_gem_ttm_print_info);
MODULE_DESCRIPTION("DRM gem ttm helpers");
MODULE_LICENSE("GPL");

View File

@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <drm/drm_gem_vram_helper.h> #include <drm/drm_debugfs.h>
#include <drm/drm_device.h> #include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_gem_ttm_helper.h>
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_mode.h> #include <drm/drm_mode.h>
#include <drm/drm_prime.h> #include <drm/drm_prime.h>
#include <drm/drm_vram_mm_helper.h>
#include <drm/ttm/ttm_page_alloc.h> #include <drm/ttm/ttm_page_alloc.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;
@ -14,6 +16,11 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs;
* *
* This library provides a GEM buffer object that is backed by video RAM * This library provides a GEM buffer object that is backed by video RAM
* (VRAM). It can be used for framebuffer devices with dedicated memory. * (VRAM). It can be used for framebuffer devices with dedicated memory.
*
* The data structure &struct drm_vram_mm and its helpers implement a memory
* manager for simple framebuffer devices with dedicated video memory. Buffer
* objects are either placed in video RAM or evicted to system memory. The rsp.
* buffer object is provided by &struct drm_gem_vram_object.
*/ */
/* /*
@ -26,6 +33,10 @@ static void drm_gem_vram_cleanup(struct drm_gem_vram_object *gbo)
* TTM buffer object in 'bo' has already been cleaned * TTM buffer object in 'bo' has already been cleaned
* up; only release the GEM object. * up; only release the GEM object.
*/ */
WARN_ON(gbo->kmap_use_count);
WARN_ON(gbo->kmap.virtual);
drm_gem_object_release(&gbo->bo.base); drm_gem_object_release(&gbo->bo.base);
} }
@ -47,6 +58,7 @@ static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo,
{ {
unsigned int i; unsigned int i;
unsigned int c = 0; unsigned int c = 0;
u32 invariant_flags = pl_flag & TTM_PL_FLAG_TOPDOWN;
gbo->placement.placement = gbo->placements; gbo->placement.placement = gbo->placements;
gbo->placement.busy_placement = gbo->placements; gbo->placement.busy_placement = gbo->placements;
@ -54,15 +66,18 @@ static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo,
if (pl_flag & TTM_PL_FLAG_VRAM) if (pl_flag & TTM_PL_FLAG_VRAM)
gbo->placements[c++].flags = TTM_PL_FLAG_WC | gbo->placements[c++].flags = TTM_PL_FLAG_WC |
TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_VRAM; TTM_PL_FLAG_VRAM |
invariant_flags;
if (pl_flag & TTM_PL_FLAG_SYSTEM) if (pl_flag & TTM_PL_FLAG_SYSTEM)
gbo->placements[c++].flags = TTM_PL_MASK_CACHING | gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
TTM_PL_FLAG_SYSTEM; TTM_PL_FLAG_SYSTEM |
invariant_flags;
if (!c) if (!c)
gbo->placements[c++].flags = TTM_PL_MASK_CACHING | gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
TTM_PL_FLAG_SYSTEM; TTM_PL_FLAG_SYSTEM |
invariant_flags;
gbo->placement.num_placement = c; gbo->placement.num_placement = c;
gbo->placement.num_busy_placement = c; gbo->placement.num_busy_placement = c;
@ -82,7 +97,6 @@ static int drm_gem_vram_init(struct drm_device *dev,
int ret; int ret;
size_t acc_size; size_t acc_size;
if (!gbo->bo.base.funcs)
gbo->bo.base.funcs = &drm_gem_vram_object_funcs; gbo->bo.base.funcs = &drm_gem_vram_object_funcs;
ret = drm_gem_object_init(dev, &gbo->bo.base, size); ret = drm_gem_object_init(dev, &gbo->bo.base, size);
@ -192,30 +206,12 @@ s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo)
} }
EXPORT_SYMBOL(drm_gem_vram_offset); EXPORT_SYMBOL(drm_gem_vram_offset);
/** static int drm_gem_vram_pin_locked(struct drm_gem_vram_object *gbo,
* drm_gem_vram_pin() - Pins a GEM VRAM object in a region. unsigned long pl_flag)
* @gbo: the GEM VRAM object
* @pl_flag: a bitmask of possible memory regions
*
* Pinning a buffer object ensures that it is not evicted from
* a memory region. A pinned buffer object has to be unpinned before
* it can be pinned to another region. If the pl_flag argument is 0,
* the buffer is pinned at its current location (video RAM or system
* memory).
*
* Returns:
* 0 on success, or
* a negative error code otherwise.
*/
int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag)
{ {
int i, ret; int i, ret;
struct ttm_operation_ctx ctx = { false, false }; struct ttm_operation_ctx ctx = { false, false };
ret = ttm_bo_reserve(&gbo->bo, true, false, NULL);
if (ret < 0)
return ret;
if (gbo->pin_count) if (gbo->pin_count)
goto out; goto out;
@ -227,20 +223,73 @@ int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag)
ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
if (ret < 0) if (ret < 0)
goto err_ttm_bo_unreserve; return ret;
out: out:
++gbo->pin_count; ++gbo->pin_count;
ttm_bo_unreserve(&gbo->bo);
return 0; return 0;
}
err_ttm_bo_unreserve: /**
* drm_gem_vram_pin() - Pins a GEM VRAM object in a region.
* @gbo: the GEM VRAM object
* @pl_flag: a bitmask of possible memory regions
*
* Pinning a buffer object ensures that it is not evicted from
* a memory region. A pinned buffer object has to be unpinned before
* it can be pinned to another region. If the pl_flag argument is 0,
* the buffer is pinned at its current location (video RAM or system
* memory).
*
* Small buffer objects, such as cursor images, can lead to memory
* fragmentation if they are pinned in the middle of video RAM. This
* is especially a problem on devices with only a small amount of
* video RAM. Fragmentation can prevent the primary framebuffer from
* fitting in, even though there's enough memory overall. The modifier
* DRM_GEM_VRAM_PL_FLAG_TOPDOWN marks the buffer object to be pinned
* at the high end of the memory region to avoid fragmentation.
*
* Returns:
* 0 on success, or
* a negative error code otherwise.
*/
int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag)
{
int ret;
ret = ttm_bo_reserve(&gbo->bo, true, false, NULL);
if (ret)
return ret;
ret = drm_gem_vram_pin_locked(gbo, pl_flag);
ttm_bo_unreserve(&gbo->bo); ttm_bo_unreserve(&gbo->bo);
return ret; return ret;
} }
EXPORT_SYMBOL(drm_gem_vram_pin); EXPORT_SYMBOL(drm_gem_vram_pin);
static int drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo)
{
int i, ret;
struct ttm_operation_ctx ctx = { false, false };
if (WARN_ON_ONCE(!gbo->pin_count))
return 0;
--gbo->pin_count;
if (gbo->pin_count)
return 0;
for (i = 0; i < gbo->placement.num_placement ; ++i)
gbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
if (ret < 0)
return ret;
return 0;
}
/** /**
* drm_gem_vram_unpin() - Unpins a GEM VRAM object * drm_gem_vram_unpin() - Unpins a GEM VRAM object
* @gbo: the GEM VRAM object * @gbo: the GEM VRAM object
@ -251,38 +300,46 @@ EXPORT_SYMBOL(drm_gem_vram_pin);
*/ */
int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo) int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo)
{ {
int i, ret; int ret;
struct ttm_operation_ctx ctx = { false, false };
ret = ttm_bo_reserve(&gbo->bo, true, false, NULL); ret = ttm_bo_reserve(&gbo->bo, true, false, NULL);
if (ret < 0) if (ret)
return ret; return ret;
ret = drm_gem_vram_unpin_locked(gbo);
if (WARN_ON_ONCE(!gbo->pin_count))
goto out;
--gbo->pin_count;
if (gbo->pin_count)
goto out;
for (i = 0; i < gbo->placement.num_placement ; ++i)
gbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
if (ret < 0)
goto err_ttm_bo_unreserve;
out:
ttm_bo_unreserve(&gbo->bo); ttm_bo_unreserve(&gbo->bo);
return 0;
err_ttm_bo_unreserve:
ttm_bo_unreserve(&gbo->bo);
return ret; return ret;
} }
EXPORT_SYMBOL(drm_gem_vram_unpin); EXPORT_SYMBOL(drm_gem_vram_unpin);
static void *drm_gem_vram_kmap_locked(struct drm_gem_vram_object *gbo,
bool map, bool *is_iomem)
{
int ret;
struct ttm_bo_kmap_obj *kmap = &gbo->kmap;
if (gbo->kmap_use_count > 0)
goto out;
if (kmap->virtual || !map)
goto out;
ret = ttm_bo_kmap(&gbo->bo, 0, gbo->bo.num_pages, kmap);
if (ret)
return ERR_PTR(ret);
out:
if (!kmap->virtual) {
if (is_iomem)
*is_iomem = false;
return NULL; /* not mapped; don't increment ref */
}
++gbo->kmap_use_count;
if (is_iomem)
return ttm_kmap_obj_virtual(kmap, is_iomem);
return kmap->virtual;
}
/** /**
* drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space * drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space
* @gbo: the GEM VRAM object * @gbo: the GEM VRAM object
@ -304,42 +361,120 @@ void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map,
bool *is_iomem) bool *is_iomem)
{ {
int ret; int ret;
struct ttm_bo_kmap_obj *kmap = &gbo->kmap; void *virtual;
if (kmap->virtual || !map) ret = ttm_bo_reserve(&gbo->bo, true, false, NULL);
goto out;
ret = ttm_bo_kmap(&gbo->bo, 0, gbo->bo.num_pages, kmap);
if (ret) if (ret)
return ERR_PTR(ret); return ERR_PTR(ret);
virtual = drm_gem_vram_kmap_locked(gbo, map, is_iomem);
ttm_bo_unreserve(&gbo->bo);
out: return virtual;
if (!is_iomem)
return kmap->virtual;
if (!kmap->virtual) {
*is_iomem = false;
return NULL;
}
return ttm_kmap_obj_virtual(kmap, is_iomem);
} }
EXPORT_SYMBOL(drm_gem_vram_kmap); EXPORT_SYMBOL(drm_gem_vram_kmap);
static void drm_gem_vram_kunmap_locked(struct drm_gem_vram_object *gbo)
{
if (WARN_ON_ONCE(!gbo->kmap_use_count))
return;
if (--gbo->kmap_use_count > 0)
return;
/*
* Permanently mapping and unmapping buffers adds overhead from
* updating the page tables and creates debugging output. Therefore,
* we delay the actual unmap operation until the BO gets evicted
* from memory. See drm_gem_vram_bo_driver_move_notify().
*/
}
/** /**
* drm_gem_vram_kunmap() - Unmaps a GEM VRAM object * drm_gem_vram_kunmap() - Unmaps a GEM VRAM object
* @gbo: the GEM VRAM object * @gbo: the GEM VRAM object
*/ */
void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo) void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo)
{ {
struct ttm_bo_kmap_obj *kmap = &gbo->kmap; int ret;
if (!kmap->virtual) ret = ttm_bo_reserve(&gbo->bo, false, false, NULL);
if (WARN_ONCE(ret, "ttm_bo_reserve_failed(): ret=%d\n", ret))
return; return;
drm_gem_vram_kunmap_locked(gbo);
ttm_bo_kunmap(kmap); ttm_bo_unreserve(&gbo->bo);
kmap->virtual = NULL;
} }
EXPORT_SYMBOL(drm_gem_vram_kunmap); EXPORT_SYMBOL(drm_gem_vram_kunmap);
/**
* drm_gem_vram_vmap() - Pins and maps a GEM VRAM object into kernel address
* space
* @gbo: The GEM VRAM object to map
*
* The vmap function pins a GEM VRAM object to its current location, either
* system or video memory, and maps its buffer into kernel address space.
* As pinned object cannot be relocated, you should avoid pinning objects
* permanently. Call drm_gem_vram_vunmap() with the returned address to
* unmap and unpin the GEM VRAM object.
*
* If you have special requirements for the pinning or mapping operations,
* call drm_gem_vram_pin() and drm_gem_vram_kmap() directly.
*
* Returns:
* The buffer's virtual address on success, or
* an ERR_PTR()-encoded error code otherwise.
*/
void *drm_gem_vram_vmap(struct drm_gem_vram_object *gbo)
{
int ret;
void *base;
ret = ttm_bo_reserve(&gbo->bo, true, false, NULL);
if (ret)
return ERR_PTR(ret);
ret = drm_gem_vram_pin_locked(gbo, 0);
if (ret)
goto err_ttm_bo_unreserve;
base = drm_gem_vram_kmap_locked(gbo, true, NULL);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
goto err_drm_gem_vram_unpin_locked;
}
ttm_bo_unreserve(&gbo->bo);
return base;
err_drm_gem_vram_unpin_locked:
drm_gem_vram_unpin_locked(gbo);
err_ttm_bo_unreserve:
ttm_bo_unreserve(&gbo->bo);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(drm_gem_vram_vmap);
/**
* drm_gem_vram_vunmap() - Unmaps and unpins a GEM VRAM object
* @gbo: The GEM VRAM object to unmap
* @vaddr: The mapping's base address as returned by drm_gem_vram_vmap()
*
* A call to drm_gem_vram_vunmap() unmaps and unpins a GEM VRAM buffer. See
* the documentation for drm_gem_vram_vmap() for more information.
*/
void drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo, void *vaddr)
{
int ret;
ret = ttm_bo_reserve(&gbo->bo, false, false, NULL);
if (WARN_ONCE(ret, "ttm_bo_reserve_failed(): ret=%d\n", ret))
return;
drm_gem_vram_kunmap_locked(gbo);
drm_gem_vram_unpin_locked(gbo);
ttm_bo_unreserve(&gbo->bo);
}
EXPORT_SYMBOL(drm_gem_vram_vunmap);
/** /**
* drm_gem_vram_fill_create_dumb() - \ * drm_gem_vram_fill_create_dumb() - \
Helper for implementing &struct drm_driver.dumb_create Helper for implementing &struct drm_driver.dumb_create
@ -410,59 +545,34 @@ static bool drm_is_gem_vram(struct ttm_buffer_object *bo)
return (bo->destroy == ttm_buffer_object_destroy); return (bo->destroy == ttm_buffer_object_destroy);
} }
/** static void drm_gem_vram_bo_driver_evict_flags(struct drm_gem_vram_object *gbo,
* drm_gem_vram_bo_driver_evict_flags() - \
Implements &struct ttm_bo_driver.evict_flags
* @bo: TTM buffer object. Refers to &struct drm_gem_vram_object.bo
* @pl: TTM placement information.
*/
void drm_gem_vram_bo_driver_evict_flags(struct ttm_buffer_object *bo,
struct ttm_placement *pl) struct ttm_placement *pl)
{ {
struct drm_gem_vram_object *gbo;
/* TTM may pass BOs that are not GEM VRAM BOs. */
if (!drm_is_gem_vram(bo))
return;
gbo = drm_gem_vram_of_bo(bo);
drm_gem_vram_placement(gbo, TTM_PL_FLAG_SYSTEM); drm_gem_vram_placement(gbo, TTM_PL_FLAG_SYSTEM);
*pl = gbo->placement; *pl = gbo->placement;
} }
EXPORT_SYMBOL(drm_gem_vram_bo_driver_evict_flags);
/** static int drm_gem_vram_bo_driver_verify_access(struct drm_gem_vram_object *gbo,
* drm_gem_vram_bo_driver_verify_access() - \
Implements &struct ttm_bo_driver.verify_access
* @bo: TTM buffer object. Refers to &struct drm_gem_vram_object.bo
* @filp: File pointer.
*
* Returns:
* 0 on success, or
* a negative errno code otherwise.
*/
int drm_gem_vram_bo_driver_verify_access(struct ttm_buffer_object *bo,
struct file *filp) struct file *filp)
{ {
struct drm_gem_vram_object *gbo = drm_gem_vram_of_bo(bo);
return drm_vma_node_verify_access(&gbo->bo.base.vma_node, return drm_vma_node_verify_access(&gbo->bo.base.vma_node,
filp->private_data); filp->private_data);
} }
EXPORT_SYMBOL(drm_gem_vram_bo_driver_verify_access);
/* static void drm_gem_vram_bo_driver_move_notify(struct drm_gem_vram_object *gbo,
* drm_gem_vram_mm_funcs - Functions for &struct drm_vram_mm bool evict,
* struct ttm_mem_reg *new_mem)
* Most users of @struct drm_gem_vram_object will also use {
* @struct drm_vram_mm. This instance of &struct drm_vram_mm_funcs struct ttm_bo_kmap_obj *kmap = &gbo->kmap;
* can be used to connect both.
*/ if (WARN_ON_ONCE(gbo->kmap_use_count))
const struct drm_vram_mm_funcs drm_gem_vram_mm_funcs = { return;
.evict_flags = drm_gem_vram_bo_driver_evict_flags,
.verify_access = drm_gem_vram_bo_driver_verify_access if (!kmap->virtual)
}; return;
EXPORT_SYMBOL(drm_gem_vram_mm_funcs); ttm_bo_kunmap(kmap);
kmap->virtual = NULL;
}
/* /*
* Helpers for struct drm_gem_object_funcs * Helpers for struct drm_gem_object_funcs
@ -595,17 +705,11 @@ static void drm_gem_vram_object_unpin(struct drm_gem_object *gem)
static void *drm_gem_vram_object_vmap(struct drm_gem_object *gem) static void *drm_gem_vram_object_vmap(struct drm_gem_object *gem)
{ {
struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
int ret;
void *base; void *base;
ret = drm_gem_vram_pin(gbo, 0); base = drm_gem_vram_vmap(gbo);
if (ret) if (IS_ERR(base))
return NULL; return NULL;
base = drm_gem_vram_kmap(gbo, true, NULL);
if (IS_ERR(base)) {
drm_gem_vram_unpin(gbo);
return NULL;
}
return base; return base;
} }
@ -620,8 +724,7 @@ static void drm_gem_vram_object_vunmap(struct drm_gem_object *gem,
{ {
struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
drm_gem_vram_kunmap(gbo); drm_gem_vram_vunmap(gbo, vaddr);
drm_gem_vram_unpin(gbo);
} }
/* /*
@ -633,5 +736,326 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs = {
.pin = drm_gem_vram_object_pin, .pin = drm_gem_vram_object_pin,
.unpin = drm_gem_vram_object_unpin, .unpin = drm_gem_vram_object_unpin,
.vmap = drm_gem_vram_object_vmap, .vmap = drm_gem_vram_object_vmap,
.vunmap = drm_gem_vram_object_vunmap .vunmap = drm_gem_vram_object_vunmap,
.print_info = drm_gem_ttm_print_info,
}; };
/*
* VRAM memory manager
*/
/*
* TTM TT
*/
static void backend_func_destroy(struct ttm_tt *tt)
{
ttm_tt_fini(tt);
kfree(tt);
}
static struct ttm_backend_func backend_func = {
.destroy = backend_func_destroy
};
/*
* TTM BO device
*/
static struct ttm_tt *bo_driver_ttm_tt_create(struct ttm_buffer_object *bo,
uint32_t page_flags)
{
struct ttm_tt *tt;
int ret;
tt = kzalloc(sizeof(*tt), GFP_KERNEL);
if (!tt)
return NULL;
tt->func = &backend_func;
ret = ttm_tt_init(tt, bo, page_flags);
if (ret < 0)
goto err_ttm_tt_init;
return tt;
err_ttm_tt_init:
kfree(tt);
return NULL;
}
static int bo_driver_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
{
switch (type) {
case TTM_PL_SYSTEM:
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
man->func = &ttm_bo_manager_func;
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_WC;
man->default_caching = TTM_PL_FLAG_WC;
break;
default:
return -EINVAL;
}
return 0;
}
static void bo_driver_evict_flags(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
struct drm_gem_vram_object *gbo;
/* TTM may pass BOs that are not GEM VRAM BOs. */
if (!drm_is_gem_vram(bo))
return;
gbo = drm_gem_vram_of_bo(bo);
drm_gem_vram_bo_driver_evict_flags(gbo, placement);
}
static int bo_driver_verify_access(struct ttm_buffer_object *bo,
struct file *filp)
{
struct drm_gem_vram_object *gbo;
/* TTM may pass BOs that are not GEM VRAM BOs. */
if (!drm_is_gem_vram(bo))
return -EINVAL;
gbo = drm_gem_vram_of_bo(bo);
return drm_gem_vram_bo_driver_verify_access(gbo, filp);
}
static void bo_driver_move_notify(struct ttm_buffer_object *bo,
bool evict,
struct ttm_mem_reg *new_mem)
{
struct drm_gem_vram_object *gbo;
/* TTM may pass BOs that are not GEM VRAM BOs. */
if (!drm_is_gem_vram(bo))
return;
gbo = drm_gem_vram_of_bo(bo);
drm_gem_vram_bo_driver_move_notify(gbo, evict, new_mem);
}
static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev,
struct ttm_mem_reg *mem)
{
struct ttm_mem_type_manager *man = bdev->man + mem->mem_type;
struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev);
if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
return -EINVAL;
mem->bus.addr = NULL;
mem->bus.size = mem->num_pages << PAGE_SHIFT;
switch (mem->mem_type) {
case TTM_PL_SYSTEM: /* nothing to do */
mem->bus.offset = 0;
mem->bus.base = 0;
mem->bus.is_iomem = false;
break;
case TTM_PL_VRAM:
mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = vmm->vram_base;
mem->bus.is_iomem = true;
break;
default:
return -EINVAL;
}
return 0;
}
static void bo_driver_io_mem_free(struct ttm_bo_device *bdev,
struct ttm_mem_reg *mem)
{ }
static struct ttm_bo_driver bo_driver = {
.ttm_tt_create = bo_driver_ttm_tt_create,
.ttm_tt_populate = ttm_pool_populate,
.ttm_tt_unpopulate = ttm_pool_unpopulate,
.init_mem_type = bo_driver_init_mem_type,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = bo_driver_evict_flags,
.verify_access = bo_driver_verify_access,
.move_notify = bo_driver_move_notify,
.io_mem_reserve = bo_driver_io_mem_reserve,
.io_mem_free = bo_driver_io_mem_free,
};
/*
* struct drm_vram_mm
*/
#if defined(CONFIG_DEBUG_FS)
static int drm_vram_mm_debugfs(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_vram_mm *vmm = node->minor->dev->vram_mm;
struct drm_mm *mm = vmm->bdev.man[TTM_PL_VRAM].priv;
struct ttm_bo_global *glob = vmm->bdev.glob;
struct drm_printer p = drm_seq_file_printer(m);
spin_lock(&glob->lru_lock);
drm_mm_print(mm, &p);
spin_unlock(&glob->lru_lock);
return 0;
}
static const struct drm_info_list drm_vram_mm_debugfs_list[] = {
{ "vram-mm", drm_vram_mm_debugfs, 0, NULL },
};
#endif
/**
* drm_vram_mm_debugfs_init() - Register VRAM MM debugfs file.
*
* @minor: drm minor device.
*
* Returns:
* 0 on success, or
* a negative error code otherwise.
*/
int drm_vram_mm_debugfs_init(struct drm_minor *minor)
{
int ret = 0;
#if defined(CONFIG_DEBUG_FS)
ret = drm_debugfs_create_files(drm_vram_mm_debugfs_list,
ARRAY_SIZE(drm_vram_mm_debugfs_list),
minor->debugfs_root, minor);
#endif
return ret;
}
EXPORT_SYMBOL(drm_vram_mm_debugfs_init);
static int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
uint64_t vram_base, size_t vram_size)
{
int ret;
vmm->vram_base = vram_base;
vmm->vram_size = vram_size;
ret = ttm_bo_device_init(&vmm->bdev, &bo_driver,
dev->anon_inode->i_mapping,
dev->vma_offset_manager,
true);
if (ret)
return ret;
ret = ttm_bo_init_mm(&vmm->bdev, TTM_PL_VRAM, vram_size >> PAGE_SHIFT);
if (ret)
return ret;
return 0;
}
static void drm_vram_mm_cleanup(struct drm_vram_mm *vmm)
{
ttm_bo_device_release(&vmm->bdev);
}
static int drm_vram_mm_mmap(struct file *filp, struct vm_area_struct *vma,
struct drm_vram_mm *vmm)
{
return ttm_bo_mmap(filp, vma, &vmm->bdev);
}
/*
* Helpers for integration with struct drm_device
*/
/**
* drm_vram_helper_alloc_mm - Allocates a device's instance of \
&struct drm_vram_mm
* @dev: the DRM device
* @vram_base: the base address of the video memory
* @vram_size: the size of the video memory in bytes
*
* Returns:
* The new instance of &struct drm_vram_mm on success, or
* an ERR_PTR()-encoded errno code otherwise.
*/
struct drm_vram_mm *drm_vram_helper_alloc_mm(
struct drm_device *dev, uint64_t vram_base, size_t vram_size)
{
int ret;
if (WARN_ON(dev->vram_mm))
return dev->vram_mm;
dev->vram_mm = kzalloc(sizeof(*dev->vram_mm), GFP_KERNEL);
if (!dev->vram_mm)
return ERR_PTR(-ENOMEM);
ret = drm_vram_mm_init(dev->vram_mm, dev, vram_base, vram_size);
if (ret)
goto err_kfree;
return dev->vram_mm;
err_kfree:
kfree(dev->vram_mm);
dev->vram_mm = NULL;
return ERR_PTR(ret);
}
EXPORT_SYMBOL(drm_vram_helper_alloc_mm);
/**
* drm_vram_helper_release_mm - Releases a device's instance of \
&struct drm_vram_mm
* @dev: the DRM device
*/
void drm_vram_helper_release_mm(struct drm_device *dev)
{
if (!dev->vram_mm)
return;
drm_vram_mm_cleanup(dev->vram_mm);
kfree(dev->vram_mm);
dev->vram_mm = NULL;
}
EXPORT_SYMBOL(drm_vram_helper_release_mm);
/*
* Helpers for &struct file_operations
*/
/**
* drm_vram_mm_file_operations_mmap() - \
Implements &struct file_operations.mmap()
* @filp: the mapping's file structure
* @vma: the mapping's memory area
*
* Returns:
* 0 on success, or
* a negative error code otherwise.
*/
int drm_vram_mm_file_operations_mmap(
struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *file_priv = filp->private_data;
struct drm_device *dev = file_priv->minor->dev;
if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized"))
return -EINVAL;
return drm_vram_mm_mmap(filp, vma, dev->vram_mm);
}
EXPORT_SYMBOL(drm_vram_mm_file_operations_mmap);

View File

@ -40,6 +40,7 @@
#include <xen/xen.h> #include <xen/xen.h>
#include <drm/drm_agpsupport.h> #include <drm/drm_agpsupport.h>
#include <drm/drm_cache.h>
#include <drm/drm_device.h> #include <drm/drm_device.h>
#include "drm_legacy.h" #include "drm_legacy.h"

View File

@ -783,7 +783,7 @@ static int mipi_dbi_spi1e_transfer(struct mipi_dbi *dbi, int dc,
int i, ret; int i, ret;
u8 *dst; u8 *dst;
if (drm_debug & DRM_UT_DRIVER) if (drm_debug_enabled(DRM_UT_DRIVER))
pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n", pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n",
__func__, dc, max_chunk); __func__, dc, max_chunk);
@ -907,7 +907,7 @@ static int mipi_dbi_spi1_transfer(struct mipi_dbi *dbi, int dc,
max_chunk = dbi->tx_buf9_len; max_chunk = dbi->tx_buf9_len;
dst16 = dbi->tx_buf9; dst16 = dbi->tx_buf9;
if (drm_debug & DRM_UT_DRIVER) if (drm_debug_enabled(DRM_UT_DRIVER))
pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n", pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n",
__func__, dc, max_chunk); __func__, dc, max_chunk);
@ -955,7 +955,7 @@ static int mipi_dbi_typec1_command(struct mipi_dbi *dbi, u8 *cmd,
int ret; int ret;
if (mipi_dbi_command_is_read(dbi, *cmd)) if (mipi_dbi_command_is_read(dbi, *cmd))
return -ENOTSUPP; return -EOPNOTSUPP;
MIPI_DBI_DEBUG_COMMAND(*cmd, parameters, num); MIPI_DBI_DEBUG_COMMAND(*cmd, parameters, num);
@ -1187,8 +1187,7 @@ static ssize_t mipi_dbi_debugfs_command_write(struct file *file,
struct mipi_dbi_dev *dbidev = m->private; struct mipi_dbi_dev *dbidev = m->private;
u8 val, cmd = 0, parameters[64]; u8 val, cmd = 0, parameters[64];
char *buf, *pos, *token; char *buf, *pos, *token;
unsigned int i; int i, ret, idx;
int ret, idx;
if (!drm_dev_enter(&dbidev->drm, &idx)) if (!drm_dev_enter(&dbidev->drm, &idx))
return -ENODEV; return -ENODEV;

View File

@ -174,7 +174,7 @@ static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
node->__subtree_last = LAST(node); node->__subtree_last = LAST(node);
if (hole_node->allocated) { if (drm_mm_node_allocated(hole_node)) {
rb = &hole_node->rb; rb = &hole_node->rb;
while (rb) { while (rb) {
parent = rb_entry(rb, struct drm_mm_node, rb); parent = rb_entry(rb, struct drm_mm_node, rb);
@ -424,9 +424,9 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
node->mm = mm; node->mm = mm;
__set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
list_add(&node->node_list, &hole->node_list); list_add(&node->node_list, &hole->node_list);
drm_mm_interval_tree_add_node(hole, node); drm_mm_interval_tree_add_node(hole, node);
node->allocated = true;
node->hole_size = 0; node->hole_size = 0;
rm_hole(hole); rm_hole(hole);
@ -543,9 +543,9 @@ int drm_mm_insert_node_in_range(struct drm_mm * const mm,
node->color = color; node->color = color;
node->hole_size = 0; node->hole_size = 0;
__set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
list_add(&node->node_list, &hole->node_list); list_add(&node->node_list, &hole->node_list);
drm_mm_interval_tree_add_node(hole, node); drm_mm_interval_tree_add_node(hole, node);
node->allocated = true;
rm_hole(hole); rm_hole(hole);
if (adj_start > hole_start) if (adj_start > hole_start)
@ -561,6 +561,11 @@ int drm_mm_insert_node_in_range(struct drm_mm * const mm,
} }
EXPORT_SYMBOL(drm_mm_insert_node_in_range); EXPORT_SYMBOL(drm_mm_insert_node_in_range);
static inline bool drm_mm_node_scanned_block(const struct drm_mm_node *node)
{
return test_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
}
/** /**
* drm_mm_remove_node - Remove a memory node from the allocator. * drm_mm_remove_node - Remove a memory node from the allocator.
* @node: drm_mm_node to remove * @node: drm_mm_node to remove
@ -574,8 +579,8 @@ void drm_mm_remove_node(struct drm_mm_node *node)
struct drm_mm *mm = node->mm; struct drm_mm *mm = node->mm;
struct drm_mm_node *prev_node; struct drm_mm_node *prev_node;
DRM_MM_BUG_ON(!node->allocated); DRM_MM_BUG_ON(!drm_mm_node_allocated(node));
DRM_MM_BUG_ON(node->scanned_block); DRM_MM_BUG_ON(drm_mm_node_scanned_block(node));
prev_node = list_prev_entry(node, node_list); prev_node = list_prev_entry(node, node_list);
@ -584,11 +589,12 @@ void drm_mm_remove_node(struct drm_mm_node *node)
drm_mm_interval_tree_remove(node, &mm->interval_tree); drm_mm_interval_tree_remove(node, &mm->interval_tree);
list_del(&node->node_list); list_del(&node->node_list);
node->allocated = false;
if (drm_mm_hole_follows(prev_node)) if (drm_mm_hole_follows(prev_node))
rm_hole(prev_node); rm_hole(prev_node);
add_hole(prev_node); add_hole(prev_node);
clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
} }
EXPORT_SYMBOL(drm_mm_remove_node); EXPORT_SYMBOL(drm_mm_remove_node);
@ -605,10 +611,11 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
{ {
struct drm_mm *mm = old->mm; struct drm_mm *mm = old->mm;
DRM_MM_BUG_ON(!old->allocated); DRM_MM_BUG_ON(!drm_mm_node_allocated(old));
*new = *old; *new = *old;
__set_bit(DRM_MM_NODE_ALLOCATED_BIT, &new->flags);
list_replace(&old->node_list, &new->node_list); list_replace(&old->node_list, &new->node_list);
rb_replace_node_cached(&old->rb, &new->rb, &mm->interval_tree); rb_replace_node_cached(&old->rb, &new->rb, &mm->interval_tree);
@ -622,8 +629,7 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
&mm->holes_addr); &mm->holes_addr);
} }
old->allocated = false; clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &old->flags);
new->allocated = true;
} }
EXPORT_SYMBOL(drm_mm_replace_node); EXPORT_SYMBOL(drm_mm_replace_node);
@ -731,9 +737,9 @@ bool drm_mm_scan_add_block(struct drm_mm_scan *scan,
u64 adj_start, adj_end; u64 adj_start, adj_end;
DRM_MM_BUG_ON(node->mm != mm); DRM_MM_BUG_ON(node->mm != mm);
DRM_MM_BUG_ON(!node->allocated); DRM_MM_BUG_ON(!drm_mm_node_allocated(node));
DRM_MM_BUG_ON(node->scanned_block); DRM_MM_BUG_ON(drm_mm_node_scanned_block(node));
node->scanned_block = true; __set_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
mm->scan_active++; mm->scan_active++;
/* Remove this block from the node_list so that we enlarge the hole /* Remove this block from the node_list so that we enlarge the hole
@ -818,8 +824,8 @@ bool drm_mm_scan_remove_block(struct drm_mm_scan *scan,
struct drm_mm_node *prev_node; struct drm_mm_node *prev_node;
DRM_MM_BUG_ON(node->mm != scan->mm); DRM_MM_BUG_ON(node->mm != scan->mm);
DRM_MM_BUG_ON(!node->scanned_block); DRM_MM_BUG_ON(!drm_mm_node_scanned_block(node));
node->scanned_block = false; __clear_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
DRM_MM_BUG_ON(!node->mm->scan_active); DRM_MM_BUG_ON(!node->mm->scan_active);
node->mm->scan_active--; node->mm->scan_active--;
@ -917,7 +923,7 @@ void drm_mm_init(struct drm_mm *mm, u64 start, u64 size)
/* Clever trick to avoid a special case in the free hole tracking. */ /* Clever trick to avoid a special case in the free hole tracking. */
INIT_LIST_HEAD(&mm->head_node.node_list); INIT_LIST_HEAD(&mm->head_node.node_list);
mm->head_node.allocated = false; mm->head_node.flags = 0;
mm->head_node.mm = mm; mm->head_node.mm = mm;
mm->head_node.start = start + size; mm->head_node.start = start + size;
mm->head_node.size = -size; mm->head_node.size = -size;

View File

@ -250,11 +250,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
if (!remote) if (!remote)
return -ENODEV; return -ENODEV;
if (!of_device_is_available(remote)) {
of_node_put(remote);
return -ENODEV;
}
if (panel) { if (panel) {
*panel = of_drm_find_panel(remote); *panel = of_drm_find_panel(remote);
if (!IS_ERR(*panel)) if (!IS_ERR(*panel))

View File

@ -44,13 +44,21 @@ static LIST_HEAD(panel_list);
/** /**
* drm_panel_init - initialize a panel * drm_panel_init - initialize a panel
* @panel: DRM panel * @panel: DRM panel
* @dev: parent device of the panel
* @funcs: panel operations
* @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to
* the panel interface
* *
* Sets up internal fields of the panel so that it can subsequently be added * Initialize the panel structure for subsequent registration with
* to the registry. * drm_panel_add().
*/ */
void drm_panel_init(struct drm_panel *panel) void drm_panel_init(struct drm_panel *panel, struct device *dev,
const struct drm_panel_funcs *funcs, int connector_type)
{ {
INIT_LIST_HEAD(&panel->list); INIT_LIST_HEAD(&panel->list);
panel->dev = dev;
panel->funcs = funcs;
panel->connector_type = connector_type;
} }
EXPORT_SYMBOL(drm_panel_init); EXPORT_SYMBOL(drm_panel_init);

View File

@ -28,6 +28,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/moduleparam.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -35,6 +36,24 @@
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
/*
* drm_debug: Enable debug output.
* Bitmask of DRM_UT_x. See include/drm/drm_print.h for details.
*/
unsigned int drm_debug;
EXPORT_SYMBOL(drm_debug);
MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug category.\n"
"\t\tBit 0 (0x01) will enable CORE messages (drm core code)\n"
"\t\tBit 1 (0x02) will enable DRIVER messages (drm controller code)\n"
"\t\tBit 2 (0x04) will enable KMS messages (modesetting code)\n"
"\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n"
"\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n"
"\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n"
"\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n"
"\t\tBit 8 (0x100) will enable DP messages (displayport code)");
module_param_named(debug, drm_debug, int, 0600);
void __drm_puts_coredump(struct drm_printer *p, const char *str) void __drm_puts_coredump(struct drm_printer *p, const char *str)
{ {
struct drm_print_iterator *iterator = p->arg; struct drm_print_iterator *iterator = p->arg;
@ -147,6 +166,12 @@ void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf)
} }
EXPORT_SYMBOL(__drm_printfn_debug); EXPORT_SYMBOL(__drm_printfn_debug);
void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf)
{
pr_err("*ERROR* %s %pV", p->prefix, vaf);
}
EXPORT_SYMBOL(__drm_printfn_err);
/** /**
* drm_puts - print a const string to a &drm_printer stream * drm_puts - print a const string to a &drm_printer stream
* @p: the &drm printer * @p: the &drm printer
@ -179,6 +204,37 @@ void drm_printf(struct drm_printer *p, const char *f, ...)
} }
EXPORT_SYMBOL(drm_printf); EXPORT_SYMBOL(drm_printf);
/**
* drm_print_bits - print bits to a &drm_printer stream
*
* Print bits (in flag fields for example) in human readable form.
*
* @p: the &drm_printer
* @value: field value.
* @bits: Array with bit names.
* @nbits: Size of bit names array.
*/
void drm_print_bits(struct drm_printer *p, unsigned long value,
const char * const bits[], unsigned int nbits)
{
bool first = true;
unsigned int i;
if (WARN_ON_ONCE(nbits > BITS_PER_TYPE(value)))
nbits = BITS_PER_TYPE(value);
for_each_set_bit(i, &value, nbits) {
if (WARN_ON_ONCE(!bits[i]))
continue;
drm_printf(p, "%s%s", first ? "" : ",",
bits[i]);
first = false;
}
if (first)
drm_printf(p, "(none)");
}
EXPORT_SYMBOL(drm_print_bits);
void drm_dev_printk(const struct device *dev, const char *level, void drm_dev_printk(const struct device *dev, const char *level,
const char *format, ...) const char *format, ...)
{ {
@ -206,7 +262,7 @@ void drm_dev_dbg(const struct device *dev, unsigned int category,
struct va_format vaf; struct va_format vaf;
va_list args; va_list args;
if (!(drm_debug & category)) if (!drm_debug_enabled(category))
return; return;
va_start(args, format); va_start(args, format);
@ -229,7 +285,7 @@ void drm_dbg(unsigned int category, const char *format, ...)
struct va_format vaf; struct va_format vaf;
va_list args; va_list args;
if (!(drm_debug & category)) if (!drm_debug_enabled(category))
return; return;
va_start(args, format); va_start(args, format);

View File

@ -32,6 +32,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <drm/drm_bridge.h>
#include <drm/drm_client.h> #include <drm/drm_client.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
@ -92,7 +93,6 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
enum drm_mode_status ret = MODE_OK; enum drm_mode_status ret = MODE_OK;
struct drm_encoder *encoder; struct drm_encoder *encoder;
int i;
/* Step 1: Validate against connector */ /* Step 1: Validate against connector */
ret = drm_connector_mode_valid(connector, mode); ret = drm_connector_mode_valid(connector, mode);
@ -100,7 +100,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
return ret; return ret;
/* Step 2: Validate against encoders and crtcs */ /* Step 2: Validate against encoders and crtcs */
drm_connector_for_each_possible_encoder(connector, encoder, i) { drm_connector_for_each_possible_encoder(connector, encoder) {
struct drm_crtc *crtc; struct drm_crtc *crtc;
ret = drm_encoder_mode_valid(encoder, mode); ret = drm_encoder_mode_valid(encoder, mode);

View File

@ -8,6 +8,7 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_plane_helper.h> #include <drm/drm_plane_helper.h>
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h> #include <drm/drm_simple_kms_helper.h>

View File

@ -135,6 +135,7 @@
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include <drm/drm_syncobj.h> #include <drm/drm_syncobj.h>
#include <drm/drm_utils.h>
#include "drm_internal.h" #include "drm_internal.h"

View File

@ -13,17 +13,23 @@ struct drm_file;
#define TRACE_INCLUDE_FILE drm_trace #define TRACE_INCLUDE_FILE drm_trace
TRACE_EVENT(drm_vblank_event, TRACE_EVENT(drm_vblank_event,
TP_PROTO(int crtc, unsigned int seq), TP_PROTO(int crtc, unsigned int seq, ktime_t time, bool high_prec),
TP_ARGS(crtc, seq), TP_ARGS(crtc, seq, time, high_prec),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(int, crtc) __field(int, crtc)
__field(unsigned int, seq) __field(unsigned int, seq)
__field(ktime_t, time)
__field(bool, high_prec)
), ),
TP_fast_assign( TP_fast_assign(
__entry->crtc = crtc; __entry->crtc = crtc;
__entry->seq = seq; __entry->seq = seq;
__entry->time = time;
__entry->high_prec = high_prec;
), ),
TP_printk("crtc=%d, seq=%u", __entry->crtc, __entry->seq) TP_printk("crtc=%d, seq=%u, time=%lld, high-prec=%s",
__entry->crtc, __entry->seq, __entry->time,
__entry->high_prec ? "true" : "false")
); );
TRACE_EVENT(drm_vblank_event_queued, TRACE_EVENT(drm_vblank_event_queued,

View File

@ -106,7 +106,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe,
write_seqlock(&vblank->seqlock); write_seqlock(&vblank->seqlock);
vblank->time = t_vblank; vblank->time = t_vblank;
vblank->count += vblank_count_inc; atomic64_add(vblank_count_inc, &vblank->count);
write_sequnlock(&vblank->seqlock); write_sequnlock(&vblank->seqlock);
} }
@ -272,7 +272,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
DRM_DEBUG_VBL("updating vblank count on crtc %u:" DRM_DEBUG_VBL("updating vblank count on crtc %u:"
" current=%llu, diff=%u, hw=%u hw_last=%u\n", " current=%llu, diff=%u, hw=%u hw_last=%u\n",
pipe, vblank->count, diff, cur_vblank, vblank->last); pipe, atomic64_read(&vblank->count), diff,
cur_vblank, vblank->last);
if (diff == 0) { if (diff == 0) {
WARN_ON_ONCE(cur_vblank != vblank->last); WARN_ON_ONCE(cur_vblank != vblank->last);
@ -294,11 +295,23 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
static u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) static u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
{ {
struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
u64 count;
if (WARN_ON(pipe >= dev->num_crtcs)) if (WARN_ON(pipe >= dev->num_crtcs))
return 0; return 0;
return vblank->count; count = atomic64_read(&vblank->count);
/*
* This read barrier corresponds to the implicit write barrier of the
* write seqlock in store_vblank(). Note that this is the only place
* where we need an explicit barrier, since all other access goes
* through drm_vblank_count_and_time(), which already has the required
* read barrier curtesy of the read seqlock.
*/
smp_rmb();
return count;
} }
/** /**
@ -319,7 +332,7 @@ u64 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc)
u64 vblank; u64 vblank;
unsigned long flags; unsigned long flags;
WARN_ONCE(drm_debug & DRM_UT_VBL && !dev->driver->get_vblank_timestamp, WARN_ONCE(drm_debug_enabled(DRM_UT_VBL) && !dev->driver->get_vblank_timestamp,
"This function requires support for accurate vblank timestamps."); "This function requires support for accurate vblank timestamps.");
spin_lock_irqsave(&dev->vblank_time_lock, flags); spin_lock_irqsave(&dev->vblank_time_lock, flags);
@ -693,7 +706,7 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
*/ */
*vblank_time = ktime_sub_ns(etime, delta_ns); *vblank_time = ktime_sub_ns(etime, delta_ns);
if ((drm_debug & DRM_UT_VBL) == 0) if (!drm_debug_enabled(DRM_UT_VBL))
return true; return true;
ts_etime = ktime_to_timespec64(etime); ts_etime = ktime_to_timespec64(etime);
@ -763,6 +776,14 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
* vblank interrupt (since it only reports the software vblank counter), see * vblank interrupt (since it only reports the software vblank counter), see
* drm_crtc_accurate_vblank_count() for such use-cases. * drm_crtc_accurate_vblank_count() for such use-cases.
* *
* Note that for a given vblank counter value drm_crtc_handle_vblank()
* and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
* provide a barrier: Any writes done before calling
* drm_crtc_handle_vblank() will be visible to callers of the later
* functions, iff the vblank count is the same or a later one.
*
* See also &drm_vblank_crtc.count.
*
* Returns: * Returns:
* The software vblank counter. * The software vblank counter.
*/ */
@ -800,7 +821,7 @@ static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
do { do {
seq = read_seqbegin(&vblank->seqlock); seq = read_seqbegin(&vblank->seqlock);
vblank_count = vblank->count; vblank_count = atomic64_read(&vblank->count);
*vblanktime = vblank->time; *vblanktime = vblank->time;
} while (read_seqretry(&vblank->seqlock, seq)); } while (read_seqretry(&vblank->seqlock, seq));
@ -817,6 +838,14 @@ static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
* vblank events since the system was booted, including lost events due to * vblank events since the system was booted, including lost events due to
* modesetting activity. Returns corresponding system timestamp of the time * modesetting activity. Returns corresponding system timestamp of the time
* of the vblank interval that corresponds to the current vblank counter value. * of the vblank interval that corresponds to the current vblank counter value.
*
* Note that for a given vblank counter value drm_crtc_handle_vblank()
* and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
* provide a barrier: Any writes done before calling
* drm_crtc_handle_vblank() will be visible to callers of the later
* functions, iff the vblank count is the same or a later one.
*
* See also &drm_vblank_crtc.count.
*/ */
u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
ktime_t *vblanktime) ktime_t *vblanktime)
@ -1323,7 +1352,7 @@ void drm_vblank_restore(struct drm_device *dev, unsigned int pipe)
assert_spin_locked(&dev->vblank_time_lock); assert_spin_locked(&dev->vblank_time_lock);
vblank = &dev->vblank[pipe]; vblank = &dev->vblank[pipe];
WARN_ONCE((drm_debug & DRM_UT_VBL) && !vblank->framedur_ns, WARN_ONCE(drm_debug_enabled(DRM_UT_VBL) && !vblank->framedur_ns,
"Cannot compute missed vblanks without frame duration\n"); "Cannot compute missed vblanks without frame duration\n");
framedur_ns = vblank->framedur_ns; framedur_ns = vblank->framedur_ns;
@ -1731,7 +1760,8 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
send_vblank_event(dev, e, seq, now); send_vblank_event(dev, e, seq, now);
} }
trace_drm_vblank_event(pipe, seq); trace_drm_vblank_event(pipe, seq, now,
dev->driver->get_vblank_timestamp != NULL);
} }
/** /**
@ -1806,6 +1836,14 @@ EXPORT_SYMBOL(drm_handle_vblank);
* *
* This is the native KMS version of drm_handle_vblank(). * This is the native KMS version of drm_handle_vblank().
* *
* Note that for a given vblank counter value drm_crtc_handle_vblank()
* and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
* provide a barrier: Any writes done before calling
* drm_crtc_handle_vblank() will be visible to callers of the later
* functions, iff the vblank count is the same or a later one.
*
* See also &drm_vblank_crtc.count.
*
* Returns: * Returns:
* True if the event was successfully handled, false on failure. * True if the event was successfully handled, false on failure.
*/ */

View File

@ -7,9 +7,8 @@
* *
* This library provides &struct drm_gem_vram_object (GEM VRAM), a GEM * This library provides &struct drm_gem_vram_object (GEM VRAM), a GEM
* buffer object that is backed by video RAM. It can be used for * buffer object that is backed by video RAM. It can be used for
* framebuffer devices with dedicated memory. The video RAM can be * framebuffer devices with dedicated memory. The video RAM is managed
* managed with &struct drm_vram_mm (VRAM MM). Both data structures are * by &struct drm_vram_mm (VRAM MM).
* supposed to be used together, but can also be used individually.
* *
* With the GEM interface userspace applications create, manage and destroy * With the GEM interface userspace applications create, manage and destroy
* graphics buffers, such as an on-screen framebuffer. GEM does not provide * graphics buffers, such as an on-screen framebuffer. GEM does not provide
@ -50,8 +49,7 @@
* // setup device, vram base and size * // setup device, vram base and size
* // ... * // ...
* *
* ret = drm_vram_helper_alloc_mm(dev, vram_base, vram_size, * ret = drm_vram_helper_alloc_mm(dev, vram_base, vram_size);
* &drm_gem_vram_mm_funcs);
* if (ret) * if (ret)
* return ret; * return ret;
* return 0; * return 0;

View File

@ -1,297 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_vram_mm_helper.h>
#include <drm/ttm/ttm_page_alloc.h>
/**
* DOC: overview
*
* The data structure &struct drm_vram_mm and its helpers implement a memory
* manager for simple framebuffer devices with dedicated video memory. Buffer
* objects are either placed in video RAM or evicted to system memory. These
* helper functions work well with &struct drm_gem_vram_object.
*/
/*
* TTM TT
*/
static void backend_func_destroy(struct ttm_tt *tt)
{
ttm_tt_fini(tt);
kfree(tt);
}
static struct ttm_backend_func backend_func = {
.destroy = backend_func_destroy
};
/*
* TTM BO device
*/
static struct ttm_tt *bo_driver_ttm_tt_create(struct ttm_buffer_object *bo,
uint32_t page_flags)
{
struct ttm_tt *tt;
int ret;
tt = kzalloc(sizeof(*tt), GFP_KERNEL);
if (!tt)
return NULL;
tt->func = &backend_func;
ret = ttm_tt_init(tt, bo, page_flags);
if (ret < 0)
goto err_ttm_tt_init;
return tt;
err_ttm_tt_init:
kfree(tt);
return NULL;
}
static int bo_driver_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
{
switch (type) {
case TTM_PL_SYSTEM:
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
man->func = &ttm_bo_manager_func;
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_WC;
man->default_caching = TTM_PL_FLAG_WC;
break;
default:
return -EINVAL;
}
return 0;
}
static void bo_driver_evict_flags(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bo->bdev);
if (vmm->funcs && vmm->funcs->evict_flags)
vmm->funcs->evict_flags(bo, placement);
}
static int bo_driver_verify_access(struct ttm_buffer_object *bo,
struct file *filp)
{
struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bo->bdev);
if (!vmm->funcs || !vmm->funcs->verify_access)
return 0;
return vmm->funcs->verify_access(bo, filp);
}
static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev,
struct ttm_mem_reg *mem)
{
struct ttm_mem_type_manager *man = bdev->man + mem->mem_type;
struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev);
if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
return -EINVAL;
mem->bus.addr = NULL;
mem->bus.size = mem->num_pages << PAGE_SHIFT;
switch (mem->mem_type) {
case TTM_PL_SYSTEM: /* nothing to do */
mem->bus.offset = 0;
mem->bus.base = 0;
mem->bus.is_iomem = false;
break;
case TTM_PL_VRAM:
mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = vmm->vram_base;
mem->bus.is_iomem = true;
break;
default:
return -EINVAL;
}
return 0;
}
static void bo_driver_io_mem_free(struct ttm_bo_device *bdev,
struct ttm_mem_reg *mem)
{ }
static struct ttm_bo_driver bo_driver = {
.ttm_tt_create = bo_driver_ttm_tt_create,
.ttm_tt_populate = ttm_pool_populate,
.ttm_tt_unpopulate = ttm_pool_unpopulate,
.init_mem_type = bo_driver_init_mem_type,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = bo_driver_evict_flags,
.verify_access = bo_driver_verify_access,
.io_mem_reserve = bo_driver_io_mem_reserve,
.io_mem_free = bo_driver_io_mem_free,
};
/*
* struct drm_vram_mm
*/
/**
* drm_vram_mm_init() - Initialize an instance of VRAM MM.
* @vmm: the VRAM MM instance to initialize
* @dev: the DRM device
* @vram_base: the base address of the video memory
* @vram_size: the size of the video memory in bytes
* @funcs: callback functions for buffer objects
*
* Returns:
* 0 on success, or
* a negative error code otherwise.
*/
int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
uint64_t vram_base, size_t vram_size,
const struct drm_vram_mm_funcs *funcs)
{
int ret;
vmm->vram_base = vram_base;
vmm->vram_size = vram_size;
vmm->funcs = funcs;
ret = ttm_bo_device_init(&vmm->bdev, &bo_driver,
dev->anon_inode->i_mapping,
true);
if (ret)
return ret;
ret = ttm_bo_init_mm(&vmm->bdev, TTM_PL_VRAM, vram_size >> PAGE_SHIFT);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL(drm_vram_mm_init);
/**
* drm_vram_mm_cleanup() - Cleans up an initialized instance of VRAM MM.
* @vmm: the VRAM MM instance to clean up
*/
void drm_vram_mm_cleanup(struct drm_vram_mm *vmm)
{
ttm_bo_device_release(&vmm->bdev);
}
EXPORT_SYMBOL(drm_vram_mm_cleanup);
/**
* drm_vram_mm_mmap() - Helper for implementing &struct file_operations.mmap()
* @filp: the mapping's file structure
* @vma: the mapping's memory area
* @vmm: the VRAM MM instance
*
* Returns:
* 0 on success, or
* a negative error code otherwise.
*/
int drm_vram_mm_mmap(struct file *filp, struct vm_area_struct *vma,
struct drm_vram_mm *vmm)
{
return ttm_bo_mmap(filp, vma, &vmm->bdev);
}
EXPORT_SYMBOL(drm_vram_mm_mmap);
/*
* Helpers for integration with struct drm_device
*/
/**
* drm_vram_helper_alloc_mm - Allocates a device's instance of \
&struct drm_vram_mm
* @dev: the DRM device
* @vram_base: the base address of the video memory
* @vram_size: the size of the video memory in bytes
* @funcs: callback functions for buffer objects
*
* Returns:
* The new instance of &struct drm_vram_mm on success, or
* an ERR_PTR()-encoded errno code otherwise.
*/
struct drm_vram_mm *drm_vram_helper_alloc_mm(
struct drm_device *dev, uint64_t vram_base, size_t vram_size,
const struct drm_vram_mm_funcs *funcs)
{
int ret;
if (WARN_ON(dev->vram_mm))
return dev->vram_mm;
dev->vram_mm = kzalloc(sizeof(*dev->vram_mm), GFP_KERNEL);
if (!dev->vram_mm)
return ERR_PTR(-ENOMEM);
ret = drm_vram_mm_init(dev->vram_mm, dev, vram_base, vram_size, funcs);
if (ret)
goto err_kfree;
return dev->vram_mm;
err_kfree:
kfree(dev->vram_mm);
dev->vram_mm = NULL;
return ERR_PTR(ret);
}
EXPORT_SYMBOL(drm_vram_helper_alloc_mm);
/**
* drm_vram_helper_release_mm - Releases a device's instance of \
&struct drm_vram_mm
* @dev: the DRM device
*/
void drm_vram_helper_release_mm(struct drm_device *dev)
{
if (!dev->vram_mm)
return;
drm_vram_mm_cleanup(dev->vram_mm);
kfree(dev->vram_mm);
dev->vram_mm = NULL;
}
EXPORT_SYMBOL(drm_vram_helper_release_mm);
/*
* Helpers for &struct file_operations
*/
/**
* drm_vram_mm_file_operations_mmap() - \
Implements &struct file_operations.mmap()
* @filp: the mapping's file structure
* @vma: the mapping's memory area
*
* Returns:
* 0 on success, or
* a negative error code otherwise.
*/
int drm_vram_mm_file_operations_mmap(
struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *file_priv = filp->private_data;
struct drm_device *dev = file_priv->minor->dev;
if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized"))
return -EINVAL;
return drm_vram_mm_mmap(filp, vma, dev->vram_mm);
}
EXPORT_SYMBOL(drm_vram_mm_file_operations_mmap);

View File

@ -326,7 +326,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
lockdep_assert_held(&gpu->lock); lockdep_assert_held(&gpu->lock);
if (drm_debug & DRM_UT_DRIVER) if (drm_debug_enabled(DRM_UT_DRIVER))
etnaviv_buffer_dump(gpu, buffer, 0, 0x50); etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
link_target = etnaviv_cmdbuf_get_va(cmdbuf, link_target = etnaviv_cmdbuf_get_va(cmdbuf,
@ -459,13 +459,13 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping) etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping)
+ buffer->user_size - 4); + buffer->user_size - 4);
if (drm_debug & DRM_UT_DRIVER) if (drm_debug_enabled(DRM_UT_DRIVER))
pr_info("stream link to 0x%08x @ 0x%08x %p\n", pr_info("stream link to 0x%08x @ 0x%08x %p\n",
return_target, return_target,
etnaviv_cmdbuf_get_va(cmdbuf, &gpu->mmu_context->cmdbuf_mapping), etnaviv_cmdbuf_get_va(cmdbuf, &gpu->mmu_context->cmdbuf_mapping),
cmdbuf->vaddr); cmdbuf->vaddr);
if (drm_debug & DRM_UT_DRIVER) { if (drm_debug_enabled(DRM_UT_DRIVER)) {
print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
cmdbuf->vaddr, cmdbuf->size, 0); cmdbuf->vaddr, cmdbuf->size, 0);
@ -484,6 +484,6 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
VIV_FE_LINK_HEADER_PREFETCH(link_dwords), VIV_FE_LINK_HEADER_PREFETCH(link_dwords),
link_target); link_target);
if (drm_debug & DRM_UT_DRIVER) if (drm_debug_enabled(DRM_UT_DRIVER))
etnaviv_buffer_dump(gpu, buffer, 0, 0x50); etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
} }

View File

@ -19,6 +19,7 @@
#include <drm/bridge/analogix_dp.h> #include <drm/bridge/analogix_dp.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include <drm/drm_panel.h> #include <drm/drm_panel.h>

View File

@ -24,6 +24,7 @@
#include <video/videomode.h> #include <video/videomode.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_fb_helper.h> #include <drm/drm_fb_helper.h>
#include <drm/drm_mipi_dsi.h> #include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h> #include <drm/drm_panel.h>

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