drm-misc-next for v6.6:
UAPI Changes: * virtio: * Support sync objects Cross-subsystem Changes: * dt-bindings: * Move several panel bindings to the correct files * fbcon: * Cleanups * fbdev: * Use _IOMEM_, _SYSMEM_, _DMAMEM_ infixes for initializer macros and Kconfig tokens, update drivers accordingly * ps3fb: Build fix * hid/i2c: * Allow panels and touchscreens to power sequence together * host1x: * Fixes * video: * Fix Kconfig dependencies for boot-up logo Core Changes: * Documentation updates and fixes * Fixes * MIPI-DBI: * Allow using same the D/C GPIO for multiple displays plus driver updates * Tests: * Convert to kunit actions * Fix NULL-deref in drm_exec tests Driver Changes: * armada: * Fixes * ast: * Represent BMV as virtual connector * Report DP connection status * bridge: * dw-hdmi: Support CEC suspend/resume * Support debugfs for chains * Fixes * i915: * Fixes * imx: * Convert to dev_error_probe() * Cleanups * ipu-v3: * Convert to devm_platform_ioremap_resource() in several places * nouveau: * Workaround DPCD issues * panel: * Convert to of_device_get_match_data() * Fix Kconfig dependencies * simple: Set bpc value to fix warning; Set connector type for AUO T215HVN01; Support Innolux G156HCE-L01 plus DT bindings * ili9881: Support TDO TL050HDV35 LCD panel plus DT bindings * startek: Support KD070FHFID015 MIPI-DSI panel plus DT bindings * sitronix-st7789v: Support Inanbo T28CP45TN89 plus DT bindings; Support EDT ET028013DMA plus DT bindings; Various cleanups * edp: Add timings for N140HCA-EAC * Allow panels and touchscreens to power sequence together * Documentation fixes * qaic: * Cleanups * repaper: * Fixes * ssd130x * Fix shadow-plane allocation * Cleanups * tegra: * Convert to devm_platform_ioremap_resource() in several places * Support bridge/connector * Enable PM * Fixes * udl: * Cleanups * v3d: * Fixes * vc4: * Convert tests to kunit actions * virtio: * Support sync objects * vkms: * Support gamma LUT * Fixes -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmTLwPUACgkQaA3BHVML eiNRBwf8CTjJJpSppitI6YEDyjG5JjpJPOrw4gmyjPCLMRhIa+ddtz8c6eiAJQTX Q4RWz4LWF0j/aRdXzxbhCJxLmgMoSbcZYN+jDSoaNbX4Fyi1KXw9eum/HZeMODBO ScZQFC5iyiCeKHRXZU4+WefqIFTEkEJJll92g3JYlvy793S2TQsA9LB1RIkbwK6x 0R+TtKSxAq9Gtwn4H0z4ACIzBTuIACxwNQRd6FTIeT4yrd7t+JY3WiBz9M96S6dK npHyjvJ3Brb88rEzv2eZZUey3fxp7sO7U7DruQVOKkgi4FsltPWxs6Ze9iylXQZr KcKfW7sxlF2JZlJwT4u0Ur6DMl60eQ== =K1nU -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2023-08-03' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for v6.6: UAPI Changes: * virtio: * Support sync objects Cross-subsystem Changes: * dt-bindings: * Move several panel bindings to the correct files * fbcon: * Cleanups * fbdev: * Use _IOMEM_, _SYSMEM_, _DMAMEM_ infixes for initializer macros and Kconfig tokens, update drivers accordingly * ps3fb: Build fix * hid/i2c: * Allow panels and touchscreens to power sequence together * host1x: * Fixes * video: * Fix Kconfig dependencies for boot-up logo Core Changes: * Documentation updates and fixes * Fixes * MIPI-DBI: * Allow using same the D/C GPIO for multiple displays plus driver updates * Tests: * Convert to kunit actions * Fix NULL-deref in drm_exec tests Driver Changes: * armada: * Fixes * ast: * Represent BMV as virtual connector * Report DP connection status * bridge: * dw-hdmi: Support CEC suspend/resume * Support debugfs for chains * Fixes * i915: * Fixes * imx: * Convert to dev_error_probe() * Cleanups * ipu-v3: * Convert to devm_platform_ioremap_resource() in several places * nouveau: * Workaround DPCD issues * panel: * Convert to of_device_get_match_data() * Fix Kconfig dependencies * simple: Set bpc value to fix warning; Set connector type for AUO T215HVN01; Support Innolux G156HCE-L01 plus DT bindings * ili9881: Support TDO TL050HDV35 LCD panel plus DT bindings * startek: Support KD070FHFID015 MIPI-DSI panel plus DT bindings * sitronix-st7789v: Support Inanbo T28CP45TN89 plus DT bindings; Support EDT ET028013DMA plus DT bindings; Various cleanups * edp: Add timings for N140HCA-EAC * Allow panels and touchscreens to power sequence together * Documentation fixes * qaic: * Cleanups * repaper: * Fixes * ssd130x * Fix shadow-plane allocation * Cleanups * tegra: * Convert to devm_platform_ioremap_resource() in several places * Support bridge/connector * Enable PM * Fixes * udl: * Cleanups * v3d: * Fixes * vc4: * Convert tests to kunit actions * virtio: * Support sync objects * vkms: * Support gamma LUT * Fixes Signed-off-by: Dave Airlie <airlied@redhat.com> # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmTLwPUACgkQaA3BHVML # eiNRBwf8CTjJJpSppitI6YEDyjG5JjpJPOrw4gmyjPCLMRhIa+ddtz8c6eiAJQTX # Q4RWz4LWF0j/aRdXzxbhCJxLmgMoSbcZYN+jDSoaNbX4Fyi1KXw9eum/HZeMODBO # ScZQFC5iyiCeKHRXZU4+WefqIFTEkEJJll92g3JYlvy793S2TQsA9LB1RIkbwK6x # 0R+TtKSxAq9Gtwn4H0z4ACIzBTuIACxwNQRd6FTIeT4yrd7t+JY3WiBz9M96S6dK # npHyjvJ3Brb88rEzv2eZZUey3fxp7sO7U7DruQVOKkgi4FsltPWxs6Ze9iylXQZr # KcKfW7sxlF2JZlJwT4u0Ur6DMl60eQ== # =K1nU # -----END PGP SIGNATURE----- # gpg: Signature made Fri 04 Aug 2023 01:00:05 AEST # gpg: using RSA key 7217FBAC8CE9CF6344A168E5680DC11D530B7A23 # gpg: Can't check signature: No public key From: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20230803150149.GA16884@linux-uq9g
This commit is contained in:
commit
ca9e70f527
@ -49,6 +49,9 @@ properties:
|
||||
description: |
|
||||
OF device-tree gpio specification for RSTX pin(active low system reset)
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
toshiba,hpd-pin:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum:
|
||||
|
@ -18,6 +18,7 @@ properties:
|
||||
- enum:
|
||||
- bananapi,lhr050h41
|
||||
- feixin,k101-im2byl02
|
||||
- tdo,tl050hdv35
|
||||
- wanchanglong,w552946aba
|
||||
- const: ilitek,ili9881c
|
||||
|
||||
|
@ -40,6 +40,12 @@ properties:
|
||||
items:
|
||||
- enum:
|
||||
- auo,b101ew05
|
||||
# Chunghwa Picture Tubes Ltd. 7" WXGA (800x1280) TFT LCD LVDS panel
|
||||
- chunghwa,claa070wp03xg
|
||||
# HannStar Display Corp. HSD101PWW2 10.1" WXGA (1280x800) LVDS panel
|
||||
- hannstar,hsd101pww2
|
||||
# Hydis Technologies 7" WXGA (800x1280) TFT LCD LVDS panel
|
||||
- hydis,hv070wx2-1e0
|
||||
- tbs,a711-panel
|
||||
|
||||
- const: panel-lvds
|
||||
|
@ -103,8 +103,6 @@ properties:
|
||||
- cdtech,s070wv95-ct16
|
||||
# Chefree CH101OLHLWH-002 10.1" (1280x800) color TFT LCD panel
|
||||
- chefree,ch101olhlwh-002
|
||||
# Chunghwa Picture Tubes Ltd. 7" WXGA TFT LCD panel
|
||||
- chunghwa,claa070wp03xg
|
||||
# Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel
|
||||
- chunghwa,claa101wa01a
|
||||
# Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel
|
||||
@ -168,8 +166,6 @@ properties:
|
||||
- hannstar,hsd070pww1
|
||||
# HannStar Display Corp. HSD100PXN1 10.1" XGA LVDS panel
|
||||
- hannstar,hsd100pxn1
|
||||
# HannStar Display Corp. HSD101PWW2 10.1" WXGA (1280x800) LVDS panel
|
||||
- hannstar,hsd101pww2
|
||||
# Hitachi Ltd. Corporation 9" WVGA (800x480) TFT LCD panel
|
||||
- hit,tx23d38vm0caa
|
||||
# InfoVision Optoelectronics M133NWF4 R0 13.3" FHD (1920x1080) TFT LCD panel
|
||||
@ -196,6 +192,8 @@ properties:
|
||||
- innolux,n116bge
|
||||
# InnoLux 13.3" FHD (1920x1080) eDP TFT LCD panel
|
||||
- innolux,n125hce-gn1
|
||||
# InnoLux 15.6" FHD (1920x1080) TFT LCD panel
|
||||
- innolux,g156hce-l01
|
||||
# InnoLux 15.6" WXGA TFT LCD panel
|
||||
- innolux,n156bge-l21
|
||||
# Innolux P120ZDG-BF1 12.02 inch eDP 2K display panel
|
||||
|
@ -15,7 +15,10 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sitronix,st7789v
|
||||
enum:
|
||||
- edt,et028013dma
|
||||
- inanbo,t28cp45tn89-v17
|
||||
- sitronix,st7789v
|
||||
|
||||
reg: true
|
||||
reset-gpios: true
|
||||
@ -26,6 +29,10 @@ properties:
|
||||
spi-cpha: true
|
||||
spi-cpol: true
|
||||
|
||||
spi-rx-bus-width:
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
|
||||
dc-gpios:
|
||||
maxItems: 1
|
||||
description: DCX pin, Display data/command selection pin in parallel interface
|
||||
@ -33,7 +40,6 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reset-gpios
|
||||
- power-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
@ -0,0 +1,69 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/startek,kd070fhfid015.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Startek Electronic Technology Co. kd070fhfid015 7 inch TFT LCD panel
|
||||
|
||||
maintainers:
|
||||
- Alexandre Mergnat <amergnat@baylibre.com>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: startek,kd070fhfid015
|
||||
|
||||
enable-gpios: true
|
||||
|
||||
iovcc-supply:
|
||||
description: Reference to the regulator powering the panel IO pins.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: DSI virtual channel
|
||||
|
||||
reset-gpios: true
|
||||
|
||||
port: true
|
||||
|
||||
power-supply: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- enable-gpios
|
||||
- iovcc-supply
|
||||
- reg
|
||||
- reset-gpios
|
||||
- port
|
||||
- power-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "startek,kd070fhfid015";
|
||||
reg = <0>;
|
||||
enable-gpios = <&pio 67 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&pio 20 GPIO_ACTIVE_HIGH>;
|
||||
iovcc-supply = <&mt6357_vsim1_reg>;
|
||||
power-supply = <&vsys_lcm_reg>;
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&dsi_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -13,6 +13,9 @@ description:
|
||||
Supports the Elan eKTH6915 touchscreen controller.
|
||||
This touchscreen controller uses the i2c-hid protocol with a reset GPIO.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/input/touchscreen/touchscreen.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
@ -24,6 +27,8 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
panel: true
|
||||
|
||||
reset-gpios:
|
||||
description: Reset GPIO; not all touchscreens using eKTH6915 hook this up.
|
||||
|
||||
|
@ -14,6 +14,9 @@ description:
|
||||
This touchscreen uses the i2c-hid protocol but has some non-standard
|
||||
power sequencing required.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/input/touchscreen/touchscreen.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@ -30,6 +33,8 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
panel: true
|
||||
|
||||
reset-gpios:
|
||||
true
|
||||
|
||||
|
@ -44,6 +44,8 @@ properties:
|
||||
description: HID descriptor address
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
panel: true
|
||||
|
||||
post-power-on-delay-ms:
|
||||
description: Time required by the device after enabling its regulators
|
||||
or powering it on, before it is ready for communication.
|
||||
|
@ -10,6 +10,13 @@ maintainers:
|
||||
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
||||
|
||||
properties:
|
||||
panel:
|
||||
description: If this touchscreen is integrally connected to a panel, this
|
||||
is a reference to that panel. The presence of this reference indicates
|
||||
that the touchscreen should be power sequenced together with the panel
|
||||
and that they may share power and/or reset signals.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
touchscreen-min-x:
|
||||
description: minimum x coordinate reported
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
@ -617,6 +617,8 @@ patternProperties:
|
||||
description: Imagination Technologies Ltd.
|
||||
"^imi,.*":
|
||||
description: Integrated Micro-Electronics Inc.
|
||||
"^inanbo,.*":
|
||||
description: Shenzhen INANBO Electronic Technology Co., Ltd.
|
||||
"^incircuit,.*":
|
||||
description: In-Circuit GmbH
|
||||
"^indiedroid,.*":
|
||||
|
@ -517,6 +517,8 @@ DRM Cache Handling and Fast WC memcpy()
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_cache.c
|
||||
:export:
|
||||
|
||||
.. _drm_sync_objects:
|
||||
|
||||
DRM Sync Objects
|
||||
===========================
|
||||
|
||||
|
@ -49,14 +49,18 @@ converted over. Modern compositors like Wayland or Surfaceflinger on Android
|
||||
really want an atomic modeset interface, so this is all about the bright
|
||||
future.
|
||||
|
||||
There is a conversion guide for atomic and all you need is a GPU for a
|
||||
non-converted driver (again virtual HW drivers for KVM are still all
|
||||
suitable).
|
||||
There is a conversion guide for atomic [1]_ and all you need is a GPU for a
|
||||
non-converted driver. The "Atomic mode setting design overview" series [2]_
|
||||
[3]_ at LWN.net can also be helpful.
|
||||
|
||||
As part of this drivers also need to convert to universal plane (which means
|
||||
exposing primary & cursor as proper plane objects). But that's much easier to
|
||||
do by directly using the new atomic helper driver callbacks.
|
||||
|
||||
.. [1] https://blog.ffwll.ch/2014/11/atomic-modeset-support-for-kms-drivers.html
|
||||
.. [2] https://lwn.net/Articles/653071/
|
||||
.. [3] https://lwn.net/Articles/653466/
|
||||
|
||||
Contact: Daniel Vetter, respective driver maintainers
|
||||
|
||||
Level: Advanced
|
||||
@ -456,6 +460,31 @@ Contact: Thomas Zimmermann <tzimmermann@suse.de>
|
||||
|
||||
Level: Starter
|
||||
|
||||
Clean up checks for already prepared/enabled in panels
|
||||
------------------------------------------------------
|
||||
|
||||
In a whole pile of panel drivers, we have code to make the
|
||||
prepare/unprepare/enable/disable callbacks behave as no-ops if they've already
|
||||
been called. To get some idea of the duplicated code, try::
|
||||
|
||||
git grep 'if.*>prepared' -- drivers/gpu/drm/panel
|
||||
git grep 'if.*>enabled' -- drivers/gpu/drm/panel
|
||||
|
||||
In the patch ("drm/panel: Check for already prepared/enabled in drm_panel")
|
||||
we've moved this check to the core. Now we can most definitely remove the
|
||||
check from the individual panels and save a pile of code.
|
||||
|
||||
In adition to removing the check from the individual panels, it is believed
|
||||
that even the core shouldn't need this check and that should be considered
|
||||
an error if other code ever relies on this check. The check in the core
|
||||
currently prints a warning whenever something is relying on this check with
|
||||
dev_warn(). After a little while, we likely want to promote this to a
|
||||
WARN(1) to help encourage folks not to rely on this behavior.
|
||||
|
||||
Contact: Douglas Anderson <dianders@chromium.org>
|
||||
|
||||
Level: Starter/Intermediate
|
||||
|
||||
|
||||
Core refactorings
|
||||
=================
|
||||
@ -753,16 +782,16 @@ existing hardware. The new driver's call-back functions are filled from
|
||||
existing fbdev code.
|
||||
|
||||
More complex fbdev drivers can be refactored step-by-step into a DRM
|
||||
driver with the help of the DRM fbconv helpers. [1] These helpers provide
|
||||
driver with the help of the DRM fbconv helpers [4]_. These helpers provide
|
||||
the transition layer between the DRM core infrastructure and the fbdev
|
||||
driver interface. Create a new DRM driver on top of the fbconv helpers,
|
||||
copy over the fbdev driver, and hook it up to the DRM code. Examples for
|
||||
several fbdev drivers are available at [1] and a tutorial of this process
|
||||
available at [2]. The result is a primitive DRM driver that can run X11
|
||||
and Weston.
|
||||
several fbdev drivers are available in Thomas Zimmermann's fbconv tree
|
||||
[4]_, as well as a tutorial of this process [5]_. The result is a primitive
|
||||
DRM driver that can run X11 and Weston.
|
||||
|
||||
- [1] https://gitlab.freedesktop.org/tzimmermann/linux/tree/fbconv
|
||||
- [2] https://gitlab.freedesktop.org/tzimmermann/linux/blob/fbconv/drivers/gpu/drm/drm_fbconv_helper.c
|
||||
.. [4] https://gitlab.freedesktop.org/tzimmermann/linux/tree/fbconv
|
||||
.. [5] https://gitlab.freedesktop.org/tzimmermann/linux/blob/fbconv/drivers/gpu/drm/drm_fbconv_helper.c
|
||||
|
||||
Contact: Thomas Zimmermann <tzimmermann@suse.de>
|
||||
|
||||
|
@ -1292,7 +1292,6 @@ static void update_profiling_data(struct drm_file *file_priv,
|
||||
static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv,
|
||||
bool is_partial)
|
||||
{
|
||||
struct qaic_partial_execute_entry *pexec;
|
||||
struct qaic_execute *args = data;
|
||||
struct qaic_execute_entry *exec;
|
||||
struct dma_bridge_chan *dbc;
|
||||
@ -1312,7 +1311,7 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr
|
||||
|
||||
received_ts = ktime_get_ns();
|
||||
|
||||
size = is_partial ? sizeof(*pexec) : sizeof(*exec);
|
||||
size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec);
|
||||
n = (unsigned long)size * args->hdr.count;
|
||||
if (args->hdr.count == 0 || n / args->hdr.count != size)
|
||||
return -EINVAL;
|
||||
@ -1320,7 +1319,6 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr
|
||||
user_data = u64_to_user_ptr(args->data);
|
||||
|
||||
exec = kcalloc(args->hdr.count, size, GFP_KERNEL);
|
||||
pexec = (struct qaic_partial_execute_entry *)exec;
|
||||
if (!exec)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -11,7 +11,7 @@ menuconfig DRM
|
||||
select DRM_PANEL_ORIENTATION_QUIRKS
|
||||
select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
|
||||
select FB_CORE if DRM_FBDEV_EMULATION
|
||||
select FB_SYS_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
|
||||
select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
|
||||
select HDMI
|
||||
select I2C
|
||||
select DMA_SHARED_BUFFER
|
||||
@ -224,7 +224,7 @@ config DRM_TTM_HELPER
|
||||
config DRM_GEM_DMA_HELPER
|
||||
tristate
|
||||
depends on DRM
|
||||
select FB_DMA_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
help
|
||||
Choose this if you need the GEM DMA helper functions
|
||||
|
||||
|
@ -3,7 +3,7 @@ config DRM_ARMADA
|
||||
tristate "DRM support for Marvell Armada SoCs"
|
||||
depends on DRM && HAVE_CLK && ARM && MMU
|
||||
select DRM_KMS_HELPER
|
||||
select FB_IO_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
help
|
||||
Support the "LCD" controllers found on the Marvell Armada 510
|
||||
devices. There are two controllers on the device, each controller
|
||||
|
@ -34,7 +34,7 @@ static void armada_fbdev_fb_destroy(struct fb_info *info)
|
||||
|
||||
static const struct fb_ops armada_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
FB_DEFAULT_IO_OPS,
|
||||
FB_DEFAULT_IOMEM_OPS,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_destroy = armada_fbdev_fb_destroy,
|
||||
};
|
||||
|
@ -4,6 +4,8 @@
|
||||
* Rewritten from the dovefb driver, and Armada510 manuals.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include <drm/armada_drm.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
@ -445,8 +447,8 @@ static int armada_overlay_get_property(struct drm_plane *plane,
|
||||
drm_to_overlay_state(state)->colorkey_ug,
|
||||
drm_to_overlay_state(state)->colorkey_vb, 0);
|
||||
} else if (property == priv->colorkey_mode_prop) {
|
||||
*val = (drm_to_overlay_state(state)->colorkey_mode &
|
||||
CFG_CKMODE_MASK) >> ffs(CFG_CKMODE_MASK);
|
||||
*val = FIELD_GET(CFG_CKMODE_MASK,
|
||||
drm_to_overlay_state(state)->colorkey_mode);
|
||||
} else if (property == priv->brightness_prop) {
|
||||
*val = drm_to_overlay_state(state)->brightness + 256;
|
||||
} else if (property == priv->contrast_prop) {
|
||||
|
@ -7,6 +7,17 @@
|
||||
#include <drm/drm_print.h>
|
||||
#include "ast_drv.h"
|
||||
|
||||
bool ast_astdp_is_connected(struct ast_device *ast)
|
||||
{
|
||||
if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING))
|
||||
return false;
|
||||
if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD))
|
||||
return false;
|
||||
if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata)
|
||||
{
|
||||
struct ast_device *ast = to_ast_device(dev);
|
||||
|
@ -272,11 +272,9 @@ static bool ast_launch_m68k(struct drm_device *dev)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
|
||||
bool ast_dp501_is_connected(struct ast_device *ast)
|
||||
{
|
||||
struct ast_device *ast = to_ast_device(dev);
|
||||
u32 i, boot_address, offset, data;
|
||||
u32 *pEDIDidx;
|
||||
u32 boot_address, offset, data;
|
||||
|
||||
if (ast->config_mode == ast_use_p2a) {
|
||||
boot_address = get_fw_base(ast);
|
||||
@ -292,14 +290,6 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
|
||||
data = ast_mindwm(ast, boot_address + offset);
|
||||
if (!(data & AST_DP501_PNP_CONNECTED))
|
||||
return false;
|
||||
|
||||
/* Read EDID */
|
||||
offset = AST_DP501_EDID_DATA;
|
||||
for (i = 0; i < 128; i += 4) {
|
||||
data = ast_mindwm(ast, boot_address + offset + i);
|
||||
pEDIDidx = (u32 *)(ediddata + i);
|
||||
*pEDIDidx = data;
|
||||
}
|
||||
} else {
|
||||
if (!ast->dp501_fw_buf)
|
||||
return false;
|
||||
@ -319,7 +309,30 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
|
||||
data = readl(ast->dp501_fw_buf + offset);
|
||||
if (!(data & AST_DP501_PNP_CONNECTED))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
|
||||
{
|
||||
struct ast_device *ast = to_ast_device(dev);
|
||||
u32 i, boot_address, offset, data;
|
||||
u32 *pEDIDidx;
|
||||
|
||||
if (!ast_dp501_is_connected(ast))
|
||||
return false;
|
||||
|
||||
if (ast->config_mode == ast_use_p2a) {
|
||||
boot_address = get_fw_base(ast);
|
||||
|
||||
/* Read EDID */
|
||||
offset = AST_DP501_EDID_DATA;
|
||||
for (i = 0; i < 128; i += 4) {
|
||||
data = ast_mindwm(ast, boot_address + offset + i);
|
||||
pEDIDidx = (u32 *)(ediddata + i);
|
||||
*pEDIDidx = data;
|
||||
}
|
||||
} else {
|
||||
/* Read EDID */
|
||||
offset = AST_DP501_EDID_DATA;
|
||||
for (i = 0; i < 128; i += 4) {
|
||||
|
@ -214,6 +214,10 @@ struct ast_device {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
} astdp;
|
||||
struct {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
} bmc;
|
||||
} output;
|
||||
|
||||
bool support_wide_screen;
|
||||
@ -510,6 +514,7 @@ void ast_patch_ahb_2500(struct ast_device *ast);
|
||||
/* ast dp501 */
|
||||
void ast_set_dp501_video_output(struct drm_device *dev, u8 mode);
|
||||
bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size);
|
||||
bool ast_dp501_is_connected(struct ast_device *ast);
|
||||
bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata);
|
||||
u8 ast_get_dp501_max_clk(struct drm_device *dev);
|
||||
void ast_init_3rdtx(struct drm_device *dev);
|
||||
@ -518,6 +523,7 @@ void ast_init_3rdtx(struct drm_device *dev);
|
||||
struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev);
|
||||
|
||||
/* aspeed DP */
|
||||
bool ast_astdp_is_connected(struct ast_device *ast);
|
||||
int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata);
|
||||
void ast_dp_launch(struct drm_device *dev);
|
||||
void ast_dp_power_on_off(struct drm_device *dev, bool no);
|
||||
|
@ -1582,8 +1582,20 @@ err_drm_connector_update_edid_property:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_dp501_connector_helper_detect_ctx(struct drm_connector *connector,
|
||||
struct drm_modeset_acquire_ctx *ctx,
|
||||
bool force)
|
||||
{
|
||||
struct ast_device *ast = to_ast_device(connector->dev);
|
||||
|
||||
if (ast_dp501_is_connected(ast))
|
||||
return connector_status_connected;
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs ast_dp501_connector_helper_funcs = {
|
||||
.get_modes = ast_dp501_connector_helper_get_modes,
|
||||
.detect_ctx = ast_dp501_connector_helper_detect_ctx,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs ast_dp501_connector_funcs = {
|
||||
@ -1608,7 +1620,7 @@ static int ast_dp501_connector_init(struct drm_device *dev, struct drm_connector
|
||||
connector->interlace_allowed = 0;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1680,8 +1692,20 @@ err_drm_connector_update_edid_property:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_astdp_connector_helper_detect_ctx(struct drm_connector *connector,
|
||||
struct drm_modeset_acquire_ctx *ctx,
|
||||
bool force)
|
||||
{
|
||||
struct ast_device *ast = to_ast_device(connector->dev);
|
||||
|
||||
if (ast_astdp_is_connected(ast))
|
||||
return connector_status_connected;
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs ast_astdp_connector_helper_funcs = {
|
||||
.get_modes = ast_astdp_connector_helper_get_modes,
|
||||
.detect_ctx = ast_astdp_connector_helper_detect_ctx,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs ast_astdp_connector_funcs = {
|
||||
@ -1706,7 +1730,7 @@ static int ast_astdp_connector_init(struct drm_device *dev, struct drm_connector
|
||||
connector->interlace_allowed = 0;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1735,6 +1759,60 @@ static int ast_astdp_output_init(struct ast_device *ast)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* BMC virtual Connector
|
||||
*/
|
||||
|
||||
static const struct drm_encoder_funcs ast_bmc_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static int ast_bmc_connector_helper_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
return drm_add_modes_noedid(connector, 4096, 4096);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs ast_bmc_connector_helper_funcs = {
|
||||
.get_modes = ast_bmc_connector_helper_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs ast_bmc_connector_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int ast_bmc_output_init(struct ast_device *ast)
|
||||
{
|
||||
struct drm_device *dev = &ast->base;
|
||||
struct drm_crtc *crtc = &ast->crtc;
|
||||
struct drm_encoder *encoder = &ast->output.bmc.encoder;
|
||||
struct drm_connector *connector = &ast->output.bmc.connector;
|
||||
int ret;
|
||||
|
||||
ret = drm_encoder_init(dev, encoder,
|
||||
&ast_bmc_encoder_funcs,
|
||||
DRM_MODE_ENCODER_VIRTUAL, "ast_bmc");
|
||||
if (ret)
|
||||
return ret;
|
||||
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
||||
|
||||
ret = drm_connector_init(dev, connector, &ast_bmc_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VIRTUAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &ast_bmc_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_attach_encoder(connector, encoder);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mode config
|
||||
*/
|
||||
@ -1842,8 +1920,13 @@ int ast_mode_config_init(struct ast_device *ast)
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = ast_bmc_output_init(ast);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_mode_config_reset(dev);
|
||||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -813,7 +813,7 @@ MODULE_DEVICE_TABLE(of, anx6345_match_table);
|
||||
static struct i2c_driver anx6345_driver = {
|
||||
.driver = {
|
||||
.name = "anx6345",
|
||||
.of_match_table = of_match_ptr(anx6345_match_table),
|
||||
.of_match_table = anx6345_match_table,
|
||||
},
|
||||
.probe = anx6345_i2c_probe,
|
||||
.remove = anx6345_i2c_remove,
|
||||
|
@ -1373,7 +1373,6 @@ static const struct i2c_device_id anx78xx_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, anx78xx_id);
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
static const struct of_device_id anx78xx_match_table[] = {
|
||||
{ .compatible = "analogix,anx7808", .data = anx7808_i2c_addresses },
|
||||
{ .compatible = "analogix,anx7812", .data = anx781x_i2c_addresses },
|
||||
@ -1382,12 +1381,11 @@ static const struct of_device_id anx78xx_match_table[] = {
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, anx78xx_match_table);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver anx78xx_driver = {
|
||||
.driver = {
|
||||
.name = "anx7814",
|
||||
.of_match_table = of_match_ptr(anx78xx_match_table),
|
||||
.of_match_table = anx78xx_match_table,
|
||||
},
|
||||
.probe = anx78xx_i2c_probe,
|
||||
.remove = anx78xx_i2c_remove,
|
||||
|
@ -2655,7 +2655,7 @@ MODULE_DEVICE_TABLE(of, mhdp_ids);
|
||||
static struct platform_driver mhdp_driver = {
|
||||
.driver = {
|
||||
.name = "cdns-mhdp8546",
|
||||
.of_match_table = of_match_ptr(mhdp_ids),
|
||||
.of_match_table = mhdp_ids,
|
||||
},
|
||||
.probe = cdns_mhdp_probe,
|
||||
.remove = cdns_mhdp_remove,
|
||||
|
@ -607,7 +607,7 @@ static struct i2c_driver ch7033_driver = {
|
||||
.remove = ch7033_remove,
|
||||
.driver = {
|
||||
.name = "ch7033",
|
||||
.of_match_table = of_match_ptr(ch7033_dt_ids),
|
||||
.of_match_table = ch7033_dt_ids,
|
||||
},
|
||||
.id_table = ch7033_ids,
|
||||
};
|
||||
|
@ -2376,7 +2376,7 @@ MODULE_DEVICE_TABLE(i2c, sii8620_id);
|
||||
static struct i2c_driver sii8620_driver = {
|
||||
.driver = {
|
||||
.name = "sii8620",
|
||||
.of_match_table = of_match_ptr(sii8620_dt_match),
|
||||
.of_match_table = sii8620_dt_match,
|
||||
},
|
||||
.probe = sii8620_probe,
|
||||
.remove = sii8620_remove,
|
||||
|
@ -62,6 +62,10 @@ struct dw_hdmi_cec {
|
||||
bool rx_done;
|
||||
struct cec_notifier *notify;
|
||||
int irq;
|
||||
|
||||
u8 regs_polarity;
|
||||
u8 regs_mask;
|
||||
u8 regs_mute_stat0;
|
||||
};
|
||||
|
||||
static void dw_hdmi_write(struct dw_hdmi_cec *cec, u8 val, int offset)
|
||||
@ -304,11 +308,44 @@ static void dw_hdmi_cec_remove(struct platform_device *pdev)
|
||||
cec_unregister_adapter(cec->adap);
|
||||
}
|
||||
|
||||
static int __maybe_unused dw_hdmi_cec_resume(struct device *dev)
|
||||
{
|
||||
struct dw_hdmi_cec *cec = dev_get_drvdata(dev);
|
||||
|
||||
/* Restore logical address */
|
||||
dw_hdmi_write(cec, cec->addresses & 255, HDMI_CEC_ADDR_L);
|
||||
dw_hdmi_write(cec, cec->addresses >> 8, HDMI_CEC_ADDR_H);
|
||||
|
||||
/* Restore interrupt status/mask registers */
|
||||
dw_hdmi_write(cec, cec->regs_polarity, HDMI_CEC_POLARITY);
|
||||
dw_hdmi_write(cec, cec->regs_mask, HDMI_CEC_MASK);
|
||||
dw_hdmi_write(cec, cec->regs_mute_stat0, HDMI_IH_MUTE_CEC_STAT0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dw_hdmi_cec_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_hdmi_cec *cec = dev_get_drvdata(dev);
|
||||
|
||||
/* store interrupt status/mask registers */
|
||||
cec->regs_polarity = dw_hdmi_read(cec, HDMI_CEC_POLARITY);
|
||||
cec->regs_mask = dw_hdmi_read(cec, HDMI_CEC_MASK);
|
||||
cec->regs_mute_stat0 = dw_hdmi_read(cec, HDMI_IH_MUTE_CEC_STAT0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_hdmi_cec_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_cec_suspend, dw_hdmi_cec_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver dw_hdmi_cec_driver = {
|
||||
.probe = dw_hdmi_cec_probe,
|
||||
.remove_new = dw_hdmi_cec_remove,
|
||||
.driver = {
|
||||
.name = "dw-hdmi-cec",
|
||||
.pm = &dw_hdmi_cec_pm,
|
||||
},
|
||||
};
|
||||
module_platform_driver(dw_hdmi_cec_driver);
|
||||
|
@ -448,7 +448,7 @@ MODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids);
|
||||
static struct i2c_driver tfp410_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tfp410",
|
||||
.of_match_table = of_match_ptr(tfp410_match),
|
||||
.of_match_table = tfp410_match,
|
||||
},
|
||||
.id_table = tfp410_i2c_ids,
|
||||
.probe = tfp410_i2c_probe,
|
||||
|
@ -3332,7 +3332,7 @@ EXPORT_SYMBOL(drm_atomic_helper_disable_all);
|
||||
* that also takes a snapshot of the modeset state to be restored on resume.
|
||||
*
|
||||
* This is just a convenience wrapper around drm_atomic_helper_disable_all(),
|
||||
* and it is the atomic version of drm_crtc_force_disable_all().
|
||||
* and it is the atomic version of drm_helper_force_disable_all().
|
||||
*/
|
||||
void drm_atomic_helper_shutdown(struct drm_device *dev)
|
||||
{
|
||||
|
@ -27,8 +27,10 @@
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <drm/drm_atomic_state_helper.h>
|
||||
#include <drm/drm_debugfs.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
@ -1345,6 +1347,50 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np)
|
||||
EXPORT_SYMBOL(of_drm_find_bridge);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int drm_bridge_chains_info(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_debugfs_entry *entry = m->private;
|
||||
struct drm_device *dev = entry->dev;
|
||||
struct drm_printer p = drm_seq_file_printer(m);
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
struct drm_encoder *encoder;
|
||||
unsigned int bridge_idx = 0;
|
||||
|
||||
list_for_each_entry(encoder, &config->encoder_list, head) {
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
drm_printf(&p, "encoder[%u]\n", encoder->base.id);
|
||||
|
||||
drm_for_each_bridge_in_chain(encoder, bridge) {
|
||||
drm_printf(&p, "\tbridge[%u] type: %u, ops: %#x",
|
||||
bridge_idx, bridge->type, bridge->ops);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
if (bridge->of_node)
|
||||
drm_printf(&p, ", OF: %pOFfc", bridge->of_node);
|
||||
#endif
|
||||
|
||||
drm_printf(&p, "\n");
|
||||
|
||||
bridge_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_debugfs_info drm_bridge_debugfs_list[] = {
|
||||
{ "bridge_chains", drm_bridge_chains_info, 0 },
|
||||
};
|
||||
|
||||
void drm_bridge_debugfs_init(struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_add_files(minor->dev, drm_bridge_debugfs_list,
|
||||
ARRAY_SIZE(drm_bridge_debugfs_list));
|
||||
}
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>");
|
||||
MODULE_DESCRIPTION("DRM bridge infrastructure");
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
@ -318,6 +318,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
struct i2c_adapter *ddc = NULL;
|
||||
struct drm_bridge *bridge, *panel_bridge = NULL;
|
||||
int connector_type;
|
||||
int ret;
|
||||
|
||||
bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL);
|
||||
if (!bridge_connector)
|
||||
@ -368,8 +369,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
drm_connector_init_with_ddc(drm, connector, &drm_bridge_connector_funcs,
|
||||
connector_type, ddc);
|
||||
ret = drm_connector_init_with_ddc(drm, connector,
|
||||
&drm_bridge_connector_funcs,
|
||||
connector_type, ddc);
|
||||
if (ret) {
|
||||
kfree(bridge_connector);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
|
||||
|
||||
if (bridge_connector->bridge_hpd)
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_auth.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_debugfs.h>
|
||||
#include <drm/drm_device.h>
|
||||
@ -274,6 +275,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
|
||||
|
||||
if (drm_drv_uses_atomic_modeset(dev)) {
|
||||
drm_atomic_debugfs_init(minor);
|
||||
drm_bridge_debugfs_init(minor);
|
||||
}
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
|
@ -62,9 +62,9 @@ static const struct fb_ops drm_fbdev_dma_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_open = drm_fbdev_dma_fb_open,
|
||||
.fb_release = drm_fbdev_dma_fb_release,
|
||||
__FB_DEFAULT_DMA_OPS_RDWR,
|
||||
__FB_DEFAULT_DMAMEM_OPS_RDWR,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
__FB_DEFAULT_DMA_OPS_DRAW,
|
||||
__FB_DEFAULT_DMAMEM_OPS_DRAW,
|
||||
.fb_mmap = drm_fbdev_dma_fb_mmap,
|
||||
.fb_destroy = drm_fbdev_dma_fb_destroy,
|
||||
};
|
||||
|
@ -34,9 +34,9 @@ static int drm_fbdev_generic_fb_release(struct fb_info *info, int user)
|
||||
return 0;
|
||||
}
|
||||
|
||||
FB_GEN_DEFAULT_DEFERRED_SYS_OPS(drm_fbdev_generic,
|
||||
drm_fb_helper_damage_range,
|
||||
drm_fb_helper_damage_area);
|
||||
FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(drm_fbdev_generic,
|
||||
drm_fb_helper_damage_range,
|
||||
drm_fb_helper_damage_area);
|
||||
|
||||
static void drm_fbdev_generic_fb_destroy(struct fb_info *info)
|
||||
{
|
||||
|
@ -168,8 +168,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev,
|
||||
if (drm_drv_uses_atomic_modeset(dev) &&
|
||||
!drm_any_plane_has_format(dev, mode_cmd->pixel_format,
|
||||
mode_cmd->modifier[0])) {
|
||||
drm_dbg(dev, "Unsupported pixel format %p4cc / modifier 0x%llx\n",
|
||||
&mode_cmd->pixel_format, mode_cmd->modifier[0]);
|
||||
drm_dbg_kms(dev, "Unsupported pixel format %p4cc / modifier 0x%llx\n",
|
||||
&mode_cmd->pixel_format, mode_cmd->modifier[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1140,10 +1140,13 @@ static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd,
|
||||
return -ENOMEM;
|
||||
|
||||
tr[1].rx_buf = buf;
|
||||
|
||||
spi_bus_lock(spi->controller);
|
||||
gpiod_set_value_cansleep(dbi->dc, 0);
|
||||
|
||||
spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr));
|
||||
ret = spi_sync(spi, &m);
|
||||
ret = spi_sync_locked(spi, &m);
|
||||
spi_bus_unlock(spi->controller);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
@ -1177,19 +1180,24 @@ static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd,
|
||||
|
||||
MIPI_DBI_DEBUG_COMMAND(*cmd, par, num);
|
||||
|
||||
spi_bus_lock(spi->controller);
|
||||
gpiod_set_value_cansleep(dbi->dc, 0);
|
||||
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
|
||||
ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
|
||||
spi_bus_unlock(spi->controller);
|
||||
if (ret || !num)
|
||||
return ret;
|
||||
|
||||
if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes)
|
||||
bpw = 16;
|
||||
|
||||
spi_bus_lock(spi->controller);
|
||||
gpiod_set_value_cansleep(dbi->dc, 1);
|
||||
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
|
||||
ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
|
||||
spi_bus_unlock(spi->controller);
|
||||
|
||||
return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1271,7 +1279,8 @@ EXPORT_SYMBOL(mipi_dbi_spi_init);
|
||||
* @len: Buffer length
|
||||
*
|
||||
* This SPI transfer helper breaks up the transfer of @buf into chunks which
|
||||
* the SPI controller driver can handle.
|
||||
* the SPI controller driver can handle. The SPI bus must be locked when
|
||||
* calling this.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
@ -1305,7 +1314,7 @@ int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
|
||||
buf += chunk;
|
||||
len -= chunk;
|
||||
|
||||
ret = spi_sync(spi, &m);
|
||||
ret = spi_sync_locked(spi, &m);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -58,6 +58,8 @@ 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->followers);
|
||||
mutex_init(&panel->follower_lock);
|
||||
panel->dev = dev;
|
||||
panel->funcs = funcs;
|
||||
panel->connector_type = connector_type;
|
||||
@ -105,13 +107,38 @@ EXPORT_SYMBOL(drm_panel_remove);
|
||||
*/
|
||||
int drm_panel_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct drm_panel_follower *follower;
|
||||
int ret;
|
||||
|
||||
if (!panel)
|
||||
return -EINVAL;
|
||||
|
||||
if (panel->funcs && panel->funcs->prepare)
|
||||
return panel->funcs->prepare(panel);
|
||||
if (panel->prepared) {
|
||||
dev_warn(panel->dev, "Skipping prepare of already prepared panel\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
mutex_lock(&panel->follower_lock);
|
||||
|
||||
if (panel->funcs && panel->funcs->prepare) {
|
||||
ret = panel->funcs->prepare(panel);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
}
|
||||
panel->prepared = true;
|
||||
|
||||
list_for_each_entry(follower, &panel->followers, list) {
|
||||
ret = follower->funcs->panel_prepared(follower);
|
||||
if (ret < 0)
|
||||
dev_info(panel->dev, "%ps failed: %d\n",
|
||||
follower->funcs->panel_prepared, ret);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
mutex_unlock(&panel->follower_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_prepare);
|
||||
|
||||
@ -128,13 +155,38 @@ EXPORT_SYMBOL(drm_panel_prepare);
|
||||
*/
|
||||
int drm_panel_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct drm_panel_follower *follower;
|
||||
int ret;
|
||||
|
||||
if (!panel)
|
||||
return -EINVAL;
|
||||
|
||||
if (panel->funcs && panel->funcs->unprepare)
|
||||
return panel->funcs->unprepare(panel);
|
||||
if (!panel->prepared) {
|
||||
dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
mutex_lock(&panel->follower_lock);
|
||||
|
||||
list_for_each_entry(follower, &panel->followers, list) {
|
||||
ret = follower->funcs->panel_unpreparing(follower);
|
||||
if (ret < 0)
|
||||
dev_info(panel->dev, "%ps failed: %d\n",
|
||||
follower->funcs->panel_unpreparing, ret);
|
||||
}
|
||||
|
||||
if (panel->funcs && panel->funcs->unprepare) {
|
||||
ret = panel->funcs->unprepare(panel);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
}
|
||||
panel->prepared = false;
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
mutex_unlock(&panel->follower_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_unprepare);
|
||||
|
||||
@ -155,11 +207,17 @@ int drm_panel_enable(struct drm_panel *panel)
|
||||
if (!panel)
|
||||
return -EINVAL;
|
||||
|
||||
if (panel->enabled) {
|
||||
dev_warn(panel->dev, "Skipping enable of already enabled panel\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (panel->funcs && panel->funcs->enable) {
|
||||
ret = panel->funcs->enable(panel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
panel->enabled = true;
|
||||
|
||||
ret = backlight_enable(panel->backlight);
|
||||
if (ret < 0)
|
||||
@ -187,13 +245,22 @@ int drm_panel_disable(struct drm_panel *panel)
|
||||
if (!panel)
|
||||
return -EINVAL;
|
||||
|
||||
if (!panel->enabled) {
|
||||
dev_warn(panel->dev, "Skipping disable of already disabled panel\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = backlight_disable(panel->backlight);
|
||||
if (ret < 0)
|
||||
DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
|
||||
ret);
|
||||
|
||||
if (panel->funcs && panel->funcs->disable)
|
||||
return panel->funcs->disable(panel);
|
||||
if (panel->funcs && panel->funcs->disable) {
|
||||
ret = panel->funcs->disable(panel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
panel->enabled = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -305,6 +372,141 @@ int of_drm_get_panel_orientation(const struct device_node *np,
|
||||
EXPORT_SYMBOL(of_drm_get_panel_orientation);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* drm_is_panel_follower() - Check if the device is a panel follower
|
||||
* @dev: The 'struct device' to check
|
||||
*
|
||||
* This checks to see if a device needs to be power sequenced together with
|
||||
* a panel using the panel follower API.
|
||||
* At the moment panels can only be followed on device tree enabled systems.
|
||||
* The "panel" property of the follower points to the panel to be followed.
|
||||
*
|
||||
* Return: true if we should be power sequenced with a panel; false otherwise.
|
||||
*/
|
||||
bool drm_is_panel_follower(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* The "panel" property is actually a phandle, but for simplicity we
|
||||
* don't bother trying to parse it here. We just need to know if the
|
||||
* property is there.
|
||||
*/
|
||||
return of_property_read_bool(dev->of_node, "panel");
|
||||
}
|
||||
EXPORT_SYMBOL(drm_is_panel_follower);
|
||||
|
||||
/**
|
||||
* drm_panel_add_follower() - Register something to follow panel state.
|
||||
* @follower_dev: The 'struct device' for the follower.
|
||||
* @follower: The panel follower descriptor for the follower.
|
||||
*
|
||||
* A panel follower is called right after preparing the panel and right before
|
||||
* unpreparing the panel. It's primary intention is to power on an associated
|
||||
* touchscreen, though it could be used for any similar devices. Multiple
|
||||
* devices are allowed the follow the same panel.
|
||||
*
|
||||
* If a follower is added to a panel that's already been turned on, the
|
||||
* follower's prepare callback is called right away.
|
||||
*
|
||||
* At the moment panels can only be followed on device tree enabled systems.
|
||||
* The "panel" property of the follower points to the panel to be followed.
|
||||
*
|
||||
* Return: 0 or an error code. Note that -ENODEV means that we detected that
|
||||
* follower_dev is not actually following a panel. The caller may
|
||||
* choose to ignore this return value if following a panel is optional.
|
||||
*/
|
||||
int drm_panel_add_follower(struct device *follower_dev,
|
||||
struct drm_panel_follower *follower)
|
||||
{
|
||||
struct device_node *panel_np;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0);
|
||||
if (!panel_np)
|
||||
return -ENODEV;
|
||||
|
||||
panel = of_drm_find_panel(panel_np);
|
||||
of_node_put(panel_np);
|
||||
if (IS_ERR(panel))
|
||||
return PTR_ERR(panel);
|
||||
|
||||
get_device(panel->dev);
|
||||
follower->panel = panel;
|
||||
|
||||
mutex_lock(&panel->follower_lock);
|
||||
|
||||
list_add_tail(&follower->list, &panel->followers);
|
||||
if (panel->prepared) {
|
||||
ret = follower->funcs->panel_prepared(follower);
|
||||
if (ret < 0)
|
||||
dev_info(panel->dev, "%ps failed: %d\n",
|
||||
follower->funcs->panel_prepared, ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&panel->follower_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_add_follower);
|
||||
|
||||
/**
|
||||
* drm_panel_remove_follower() - Reverse drm_panel_add_follower().
|
||||
* @follower: The panel follower descriptor for the follower.
|
||||
*
|
||||
* Undo drm_panel_add_follower(). This includes calling the follower's
|
||||
* unprepare function if we're removed from a panel that's currently prepared.
|
||||
*
|
||||
* Return: 0 or an error code.
|
||||
*/
|
||||
void drm_panel_remove_follower(struct drm_panel_follower *follower)
|
||||
{
|
||||
struct drm_panel *panel = follower->panel;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&panel->follower_lock);
|
||||
|
||||
if (panel->prepared) {
|
||||
ret = follower->funcs->panel_unpreparing(follower);
|
||||
if (ret < 0)
|
||||
dev_info(panel->dev, "%ps failed: %d\n",
|
||||
follower->funcs->panel_unpreparing, ret);
|
||||
}
|
||||
list_del_init(&follower->list);
|
||||
|
||||
mutex_unlock(&panel->follower_lock);
|
||||
|
||||
put_device(panel->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_panel_remove_follower);
|
||||
|
||||
static void drm_panel_remove_follower_void(void *follower)
|
||||
{
|
||||
drm_panel_remove_follower(follower);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_drm_panel_add_follower() - devm version of drm_panel_add_follower()
|
||||
* @follower_dev: The 'struct device' for the follower.
|
||||
* @follower: The panel follower descriptor for the follower.
|
||||
*
|
||||
* Handles calling drm_panel_remove_follower() using devm on the follower_dev.
|
||||
*
|
||||
* Return: 0 or an error code.
|
||||
*/
|
||||
int devm_drm_panel_add_follower(struct device *follower_dev,
|
||||
struct drm_panel_follower *follower)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_panel_add_follower(follower_dev, follower);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(follower_dev,
|
||||
drm_panel_remove_follower_void, follower);
|
||||
}
|
||||
EXPORT_SYMBOL(devm_drm_panel_add_follower);
|
||||
|
||||
#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
|
||||
/**
|
||||
* drm_panel_of_backlight - use backlight device node for backlight
|
||||
|
@ -40,8 +40,8 @@
|
||||
/**
|
||||
* DOC: overview
|
||||
*
|
||||
* This helper library has two parts. The first part has support to implement
|
||||
* primary plane support on top of the normal CRTC configuration interface.
|
||||
* This helper library contains helpers to implement primary plane support on
|
||||
* top of the normal CRTC configuration interface.
|
||||
* Since the legacy &drm_mode_config_funcs.set_config interface ties the primary
|
||||
* plane together with the CRTC state this does not allow userspace to disable
|
||||
* the primary plane itself. The default primary plane only expose XRBG8888 and
|
||||
@ -51,14 +51,6 @@
|
||||
* planes, and newly merged drivers must not rely upon these transitional
|
||||
* helpers.
|
||||
*
|
||||
* The second part also implements transitional helpers which allow drivers to
|
||||
* gradually switch to the atomic helper infrastructure for plane updates. Once
|
||||
* that switch is complete drivers shouldn't use these any longer, instead using
|
||||
* the proper legacy implementations for update and disable plane hooks provided
|
||||
* by the atomic helpers.
|
||||
*
|
||||
* Again drivers are strongly urged to switch to the new interfaces.
|
||||
*
|
||||
* The plane helpers share the function table structures with other helpers,
|
||||
* specifically also the atomic helpers. See &struct drm_plane_helper_funcs for
|
||||
* the details.
|
||||
|
@ -7,7 +7,7 @@ config DRM_EXYNOS
|
||||
select DRM_DISPLAY_HELPER if DRM_EXYNOS_DP
|
||||
select DRM_KMS_HELPER
|
||||
select VIDEOMODE_HELPERS
|
||||
select FB_DMA_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
select SND_SOC_HDMI_CODEC if SND_SOC
|
||||
help
|
||||
Choose this option if you have a Samsung SoC Exynos chipset.
|
||||
|
@ -49,9 +49,9 @@ static void exynos_drm_fb_destroy(struct fb_info *info)
|
||||
|
||||
static const struct fb_ops exynos_drm_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
__FB_DEFAULT_DMA_OPS_RDWR,
|
||||
__FB_DEFAULT_DMAMEM_OPS_RDWR,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
__FB_DEFAULT_DMA_OPS_DRAW,
|
||||
__FB_DEFAULT_DMAMEM_OPS_DRAW,
|
||||
.fb_mmap = exynos_drm_fb_mmap,
|
||||
.fb_destroy = exynos_drm_fb_destroy,
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ config DRM_GMA500
|
||||
tristate "Intel GMA500/600/3600/3650 KMS Framebuffer"
|
||||
depends on DRM && PCI && X86 && MMU
|
||||
select DRM_KMS_HELPER
|
||||
select FB_IO_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
select I2C
|
||||
select I2C_ALGOBIT
|
||||
# GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915
|
||||
|
@ -135,10 +135,10 @@ static void psb_fbdev_fb_destroy(struct fb_info *info)
|
||||
|
||||
static const struct fb_ops psb_fbdev_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
__FB_DEFAULT_IO_OPS_RDWR,
|
||||
__FB_DEFAULT_IOMEM_OPS_RDWR,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_setcolreg = psb_fbdev_fb_setcolreg,
|
||||
__FB_DEFAULT_IO_OPS_DRAW,
|
||||
__FB_DEFAULT_IOMEM_OPS_DRAW,
|
||||
.fb_mmap = psb_fbdev_fb_mmap,
|
||||
.fb_destroy = psb_fbdev_fb_destroy,
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ config DRM_I915
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_PANEL
|
||||
select DRM_MIPI_DSI
|
||||
select FB_IO_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
select RELAY
|
||||
select I2C
|
||||
select I2C_ALGOBIT
|
||||
|
@ -85,9 +85,9 @@ static void intel_fbdev_invalidate(struct intel_fbdev *ifbdev)
|
||||
intel_frontbuffer_invalidate(to_frontbuffer(ifbdev), ORIGIN_CPU);
|
||||
}
|
||||
|
||||
FB_GEN_DEFAULT_DEFERRED_IO_OPS(intel_fbdev,
|
||||
drm_fb_helper_damage_range,
|
||||
drm_fb_helper_damage_area)
|
||||
FB_GEN_DEFAULT_DEFERRED_IOMEM_OPS(intel_fbdev,
|
||||
drm_fb_helper_damage_range,
|
||||
drm_fb_helper_damage_area)
|
||||
|
||||
static int intel_fbdev_set_par(struct fb_info *info)
|
||||
{
|
||||
|
@ -186,7 +186,7 @@ i915_gem_object_wait(struct drm_i915_gem_object *obj,
|
||||
static inline unsigned long nsecs_to_jiffies_timeout(const u64 n)
|
||||
{
|
||||
/* nsecs_to_jiffies64() does not guard against overflow */
|
||||
if (NSEC_PER_SEC % HZ &&
|
||||
if ((NSEC_PER_SEC % HZ) != 0 &&
|
||||
div_u64(n, NSEC_PER_SEC) >= MAX_JIFFY_OFFSET / HZ)
|
||||
return MAX_JIFFY_OFFSET;
|
||||
|
||||
|
@ -1220,7 +1220,7 @@ emit_semaphore_wait(struct i915_request *to,
|
||||
/*
|
||||
* If this or its dependents are waiting on an external fence
|
||||
* that may fail catastrophically, then we want to avoid using
|
||||
* sempahores as they bypass the fence signaling metadata, and we
|
||||
* semaphores as they bypass the fence signaling metadata, and we
|
||||
* lose the fence->error propagation.
|
||||
*/
|
||||
if (from->sched.flags & I915_SCHED_HAS_EXTERNAL_CHAIN)
|
||||
|
@ -66,6 +66,7 @@ static int dcss_drv_platform_probe(struct platform_device *pdev)
|
||||
mdrv->kms = dcss_kms_attach(mdrv->dcss);
|
||||
if (IS_ERR(mdrv->kms)) {
|
||||
err = PTR_ERR(mdrv->kms);
|
||||
dev_err_probe(dev, err, "Failed to initialize KMS\n");
|
||||
goto dcss_shutoff;
|
||||
}
|
||||
|
||||
|
@ -618,6 +618,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
||||
width = ipu_src_rect_width(new_state);
|
||||
else
|
||||
width = drm_rect_width(&new_state->src) >> 16;
|
||||
height = drm_rect_height(&new_state->src) >> 16;
|
||||
|
||||
eba = drm_plane_state_to_eba(new_state, 0);
|
||||
|
||||
@ -628,9 +629,9 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
||||
if (ipu_state->use_pre) {
|
||||
axi_id = ipu_chan_assign_axi_id(ipu_plane->dma);
|
||||
ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, width,
|
||||
drm_rect_height(&new_state->src) >> 16,
|
||||
fb->pitches[0], fb->format->format,
|
||||
fb->modifier, &eba);
|
||||
height, fb->pitches[0],
|
||||
fb->format->format, fb->modifier,
|
||||
&eba);
|
||||
}
|
||||
|
||||
if (!old_state->fb ||
|
||||
@ -684,7 +685,6 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
||||
|
||||
ipu_dmfc_config_wait4eot(ipu_plane->dmfc, width);
|
||||
|
||||
height = drm_rect_height(&new_state->src) >> 16;
|
||||
info = drm_format_info(fb->format->format);
|
||||
ipu_calculate_bursts(width, info->cpp[0], fb->pitches[0],
|
||||
&burstsize, &num_bursts);
|
||||
@ -747,8 +747,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
||||
ipu_cpmem_set_burstsize(ipu_plane->ipu_ch, 16);
|
||||
|
||||
ipu_cpmem_zero(ipu_plane->alpha_ch);
|
||||
ipu_cpmem_set_resolution(ipu_plane->alpha_ch, width,
|
||||
drm_rect_height(&new_state->src) >> 16);
|
||||
ipu_cpmem_set_resolution(ipu_plane->alpha_ch, width, height);
|
||||
ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8);
|
||||
ipu_cpmem_set_high_priority(ipu_plane->alpha_ch);
|
||||
ipu_idmac_set_double_buffer(ipu_plane->alpha_ch, 1);
|
||||
|
@ -21,7 +21,7 @@ config DRM_MSM
|
||||
select DRM_BRIDGE
|
||||
select DRM_PANEL_BRIDGE
|
||||
select DRM_SCHED
|
||||
select FB_SYS_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_SYSMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
select SHMEM
|
||||
select TMPFS
|
||||
select QCOM_SCM
|
||||
|
@ -25,9 +25,9 @@ module_param(fbdev, bool, 0600);
|
||||
* fbdev funcs, to implement legacy fbdev interface on top of drm driver
|
||||
*/
|
||||
|
||||
FB_GEN_DEFAULT_DEFERRED_SYS_OPS(msm_fbdev,
|
||||
drm_fb_helper_damage_range,
|
||||
drm_fb_helper_damage_area)
|
||||
FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(msm_fbdev,
|
||||
drm_fb_helper_damage_range,
|
||||
drm_fb_helper_damage_area)
|
||||
|
||||
static int msm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <drm/display/drm_dp.h>
|
||||
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/init.h>
|
||||
#include <subdev/gpio.h>
|
||||
@ -634,6 +636,50 @@ nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp)
|
||||
return outp->dp.rates != 0;
|
||||
}
|
||||
|
||||
/* XXX: This is a big fat hack, and this is just drm_dp_read_dpcd_caps()
|
||||
* converted to work inside nvkm. This is a temporary holdover until we start
|
||||
* passing the drm_dp_aux device through NVKM
|
||||
*/
|
||||
static int
|
||||
nvkm_dp_read_dpcd_caps(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_i2c_aux *aux = outp->dp.aux;
|
||||
u8 dpcd_ext[DP_RECEIVER_CAP_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, DP_RECEIVER_CAP_SIZE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Prior to DP1.3 the bit represented by
|
||||
* DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
|
||||
* If it is set DP_DPCD_REV at 0000h could be at a value less than
|
||||
* the true capability of the panel. The only way to check is to
|
||||
* then compare 0000h and 2200h.
|
||||
*/
|
||||
if (!(outp->dp.dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
|
||||
DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
|
||||
return 0;
|
||||
|
||||
ret = nvkm_rdaux(aux, DP_DP13_DPCD_REV, dpcd_ext, sizeof(dpcd_ext));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (outp->dp.dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
|
||||
OUTP_DBG(outp, "Extended DPCD rev less than base DPCD rev (%d > %d)\n",
|
||||
outp->dp.dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memcmp(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext)))
|
||||
return 0;
|
||||
|
||||
memcpy(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr)
|
||||
{
|
||||
@ -689,7 +735,7 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr)
|
||||
memset(outp->dp.lttpr, 0x00, sizeof(outp->dp.lttpr));
|
||||
}
|
||||
|
||||
if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, sizeof(outp->dp.dpcd))) {
|
||||
if (!nvkm_dp_read_dpcd_caps(outp)) {
|
||||
const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 };
|
||||
const u8 *rate;
|
||||
int rate_max;
|
||||
|
@ -4,7 +4,7 @@ config DRM_OMAP
|
||||
depends on DRM && OF
|
||||
depends on ARCH_OMAP2PLUS
|
||||
select DRM_KMS_HELPER
|
||||
select FB_DMA_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
select VIDEOMODE_HELPERS
|
||||
select HDMI
|
||||
default n
|
||||
|
@ -106,13 +106,13 @@ static void omap_fbdev_fb_destroy(struct fb_info *info)
|
||||
|
||||
static const struct fb_ops omap_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
__FB_DEFAULT_DMA_OPS_RDWR,
|
||||
__FB_DEFAULT_DMAMEM_OPS_RDWR,
|
||||
.fb_check_var = drm_fb_helper_check_var,
|
||||
.fb_set_par = drm_fb_helper_set_par,
|
||||
.fb_setcmap = drm_fb_helper_setcmap,
|
||||
.fb_blank = drm_fb_helper_blank,
|
||||
.fb_pan_display = omap_fbdev_pan_display,
|
||||
__FB_DEFAULT_DMA_OPS_DRAW,
|
||||
__FB_DEFAULT_DMAMEM_OPS_DRAW,
|
||||
.fb_ioctl = drm_fb_helper_ioctl,
|
||||
.fb_mmap = omap_fbdev_fb_mmap,
|
||||
.fb_destroy = omap_fbdev_fb_destroy,
|
||||
|
@ -734,6 +734,17 @@ config DRM_PANEL_SONY_TULIP_TRULY_NT35521
|
||||
NT35521 1280x720 video mode panel as found on Sony Xperia M4
|
||||
Aqua phone.
|
||||
|
||||
config DRM_PANEL_STARTEK_KD070FHFID015
|
||||
tristate "STARTEK KD070FHFID015 panel"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Say Y here if you want to enable support for STARTEK KD070FHFID015 DSI panel
|
||||
based on RENESAS-R69429 controller. The pannel is a 7-inch TFT LCD display
|
||||
with a resolution of 1024 x 600 pixels. It provides a MIPI DSI interface to
|
||||
the host, a built-in LED backlight and touch controller.
|
||||
|
||||
config DRM_PANEL_TDO_TL070WSH30
|
||||
tristate "TDO TL070WSH30 DSI panel"
|
||||
depends on OF
|
||||
@ -799,6 +810,8 @@ config DRM_PANEL_VISIONOX_R66451
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select DRM_DISPLAY_DP_HELPER
|
||||
select DRM_DISPLAY_HELPER
|
||||
help
|
||||
Say Y here if you want to enable support for Visionox
|
||||
R66451 1080x2340 AMOLED DSI panel.
|
||||
|
@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
|
||||
obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
|
||||
obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
|
||||
obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
|
||||
obj-$(CONFIG_DRM_PANEL_STARTEK_KD070FHFID015) += panel-startek-kd070fhfid015.o
|
||||
obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
|
||||
obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
|
||||
obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
|
||||
|
@ -1890,6 +1890,7 @@ static const struct edp_panel_entry edp_panels[] = {
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1153, &delay_200_500_e80_d50, "N116BGE-EA2"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1154, &delay_200_500_e80_d50, "N116BCA-EA2"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d4, &delay_200_500_e80_d50, "N140HCA-EAC"),
|
||||
|
||||
EDP_PANEL_ENTRY('I', 'V', 'O', 0x057d, &delay_200_500_e200, "R140NWF5 RH"),
|
||||
EDP_PANEL_ENTRY('I', 'V', 'O', 0x854a, &delay_200_500_p2e100, "M133NW4J"),
|
||||
|
@ -455,6 +455,174 @@ static const struct ili9881c_instr k101_im2byl02_init[] = {
|
||||
ILI9881C_COMMAND_INSTR(0xD3, 0x3F), /* VN0 */
|
||||
};
|
||||
|
||||
static const struct ili9881c_instr tl050hdv35_init[] = {
|
||||
ILI9881C_SWITCH_PAGE_INSTR(3),
|
||||
ILI9881C_COMMAND_INSTR(0x01, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x02, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x03, 0x73),
|
||||
ILI9881C_COMMAND_INSTR(0x04, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x05, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x06, 0x0a),
|
||||
ILI9881C_COMMAND_INSTR(0x07, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x08, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x09, 0x01),
|
||||
ILI9881C_COMMAND_INSTR(0x0a, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x0b, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x0c, 0x01),
|
||||
ILI9881C_COMMAND_INSTR(0x0d, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x0e, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x0f, 0x1d),
|
||||
ILI9881C_COMMAND_INSTR(0x10, 0x1d),
|
||||
ILI9881C_COMMAND_INSTR(0x15, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x16, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x17, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x18, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x19, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x1a, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x1b, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x1c, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x1d, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x1e, 0x40),
|
||||
ILI9881C_COMMAND_INSTR(0x1f, 0x80),
|
||||
ILI9881C_COMMAND_INSTR(0x20, 0x06),
|
||||
ILI9881C_COMMAND_INSTR(0x21, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x28, 0x33),
|
||||
ILI9881C_COMMAND_INSTR(0x29, 0x03),
|
||||
ILI9881C_COMMAND_INSTR(0x2a, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x2b, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x2c, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x2d, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x2e, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x2f, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x35, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x36, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x37, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x38, 0x3C),
|
||||
ILI9881C_COMMAND_INSTR(0x39, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x3a, 0x40),
|
||||
ILI9881C_COMMAND_INSTR(0x3b, 0x40),
|
||||
ILI9881C_COMMAND_INSTR(0x3c, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x3d, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x3e, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x3f, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x40, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x41, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x42, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x43, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x44, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x55, 0xab),
|
||||
ILI9881C_COMMAND_INSTR(0x5a, 0x89),
|
||||
ILI9881C_COMMAND_INSTR(0x5b, 0xab),
|
||||
ILI9881C_COMMAND_INSTR(0x5c, 0xcd),
|
||||
ILI9881C_COMMAND_INSTR(0x5d, 0xef),
|
||||
ILI9881C_COMMAND_INSTR(0x5e, 0x11),
|
||||
ILI9881C_COMMAND_INSTR(0x5f, 0x01),
|
||||
ILI9881C_COMMAND_INSTR(0x60, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x61, 0x15),
|
||||
ILI9881C_COMMAND_INSTR(0x62, 0x14),
|
||||
ILI9881C_COMMAND_INSTR(0x63, 0x0e),
|
||||
ILI9881C_COMMAND_INSTR(0x64, 0x0f),
|
||||
ILI9881C_COMMAND_INSTR(0x65, 0x0c),
|
||||
ILI9881C_COMMAND_INSTR(0x66, 0x0d),
|
||||
ILI9881C_COMMAND_INSTR(0x67, 0x06),
|
||||
ILI9881C_COMMAND_INSTR(0x68, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x69, 0x07),
|
||||
ILI9881C_COMMAND_INSTR(0x6a, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x6b, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x6c, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x6d, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x6e, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x6f, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x70, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x71, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x72, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x73, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x74, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x75, 0x01),
|
||||
ILI9881C_COMMAND_INSTR(0x76, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x77, 0x14),
|
||||
ILI9881C_COMMAND_INSTR(0x78, 0x15),
|
||||
ILI9881C_COMMAND_INSTR(0x79, 0x0e),
|
||||
ILI9881C_COMMAND_INSTR(0x7a, 0x0f),
|
||||
ILI9881C_COMMAND_INSTR(0x7b, 0x0c),
|
||||
ILI9881C_COMMAND_INSTR(0x7c, 0x0d),
|
||||
ILI9881C_COMMAND_INSTR(0x7d, 0x06),
|
||||
ILI9881C_COMMAND_INSTR(0x7e, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x7f, 0x07),
|
||||
ILI9881C_COMMAND_INSTR(0x88, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x89, 0x02),
|
||||
ILI9881C_COMMAND_INSTR(0x8A, 0x02),
|
||||
ILI9881C_SWITCH_PAGE_INSTR(4),
|
||||
ILI9881C_COMMAND_INSTR(0x38, 0x01),
|
||||
ILI9881C_COMMAND_INSTR(0x39, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x6c, 0x15),
|
||||
ILI9881C_COMMAND_INSTR(0x6e, 0x2b),
|
||||
ILI9881C_COMMAND_INSTR(0x6f, 0x33),
|
||||
ILI9881C_COMMAND_INSTR(0x8d, 0x18),
|
||||
ILI9881C_COMMAND_INSTR(0x87, 0xba),
|
||||
ILI9881C_COMMAND_INSTR(0x26, 0x76),
|
||||
ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
|
||||
ILI9881C_COMMAND_INSTR(0xb5, 0x06),
|
||||
ILI9881C_COMMAND_INSTR(0x3a, 0x24),
|
||||
ILI9881C_COMMAND_INSTR(0x35, 0x1f),
|
||||
ILI9881C_COMMAND_INSTR(0x33, 0x14),
|
||||
ILI9881C_COMMAND_INSTR(0x3b, 0x98),
|
||||
ILI9881C_SWITCH_PAGE_INSTR(1),
|
||||
ILI9881C_COMMAND_INSTR(0x22, 0x0a),
|
||||
ILI9881C_COMMAND_INSTR(0x31, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x40, 0x33),
|
||||
ILI9881C_COMMAND_INSTR(0x53, 0xa2),
|
||||
ILI9881C_COMMAND_INSTR(0x55, 0x92),
|
||||
ILI9881C_COMMAND_INSTR(0x50, 0x96),
|
||||
ILI9881C_COMMAND_INSTR(0x51, 0x96),
|
||||
ILI9881C_COMMAND_INSTR(0x60, 0x22),
|
||||
ILI9881C_COMMAND_INSTR(0x61, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0x62, 0x19),
|
||||
ILI9881C_COMMAND_INSTR(0x63, 0x00),
|
||||
ILI9881C_COMMAND_INSTR(0xa0, 0x08),
|
||||
ILI9881C_COMMAND_INSTR(0xa1, 0x11),
|
||||
ILI9881C_COMMAND_INSTR(0xa2, 0x19),
|
||||
ILI9881C_COMMAND_INSTR(0xa3, 0x0d),
|
||||
ILI9881C_COMMAND_INSTR(0xa4, 0x0d),
|
||||
ILI9881C_COMMAND_INSTR(0xa5, 0x1e),
|
||||
ILI9881C_COMMAND_INSTR(0xa6, 0x14),
|
||||
ILI9881C_COMMAND_INSTR(0xa7, 0x17),
|
||||
ILI9881C_COMMAND_INSTR(0xa8, 0x4f),
|
||||
ILI9881C_COMMAND_INSTR(0xa9, 0x1a),
|
||||
ILI9881C_COMMAND_INSTR(0xaa, 0x27),
|
||||
ILI9881C_COMMAND_INSTR(0xab, 0x49),
|
||||
ILI9881C_COMMAND_INSTR(0xac, 0x1a),
|
||||
ILI9881C_COMMAND_INSTR(0xad, 0x18),
|
||||
ILI9881C_COMMAND_INSTR(0xae, 0x4c),
|
||||
ILI9881C_COMMAND_INSTR(0xaf, 0x22),
|
||||
ILI9881C_COMMAND_INSTR(0xb0, 0x27),
|
||||
ILI9881C_COMMAND_INSTR(0xb1, 0x4b),
|
||||
ILI9881C_COMMAND_INSTR(0xb2, 0x60),
|
||||
ILI9881C_COMMAND_INSTR(0xb3, 0x39),
|
||||
ILI9881C_COMMAND_INSTR(0xc0, 0x08),
|
||||
ILI9881C_COMMAND_INSTR(0xc1, 0x11),
|
||||
ILI9881C_COMMAND_INSTR(0xc2, 0x19),
|
||||
ILI9881C_COMMAND_INSTR(0xc3, 0x0d),
|
||||
ILI9881C_COMMAND_INSTR(0xc4, 0x0d),
|
||||
ILI9881C_COMMAND_INSTR(0xc5, 0x1e),
|
||||
ILI9881C_COMMAND_INSTR(0xc6, 0x14),
|
||||
ILI9881C_COMMAND_INSTR(0xc7, 0x17),
|
||||
ILI9881C_COMMAND_INSTR(0xc8, 0x4f),
|
||||
ILI9881C_COMMAND_INSTR(0xc9, 0x1a),
|
||||
ILI9881C_COMMAND_INSTR(0xca, 0x27),
|
||||
ILI9881C_COMMAND_INSTR(0xcb, 0x49),
|
||||
ILI9881C_COMMAND_INSTR(0xcc, 0x1a),
|
||||
ILI9881C_COMMAND_INSTR(0xcd, 0x18),
|
||||
ILI9881C_COMMAND_INSTR(0xce, 0x4c),
|
||||
ILI9881C_COMMAND_INSTR(0xcf, 0x33),
|
||||
ILI9881C_COMMAND_INSTR(0xd0, 0x27),
|
||||
ILI9881C_COMMAND_INSTR(0xd1, 0x4b),
|
||||
ILI9881C_COMMAND_INSTR(0xd2, 0x60),
|
||||
ILI9881C_COMMAND_INSTR(0xd3, 0x39),
|
||||
ILI9881C_SWITCH_PAGE_INSTR(0),
|
||||
ILI9881C_COMMAND_INSTR(0x36, 0x03),
|
||||
};
|
||||
|
||||
static const struct ili9881c_instr w552946ab_init[] = {
|
||||
ILI9881C_SWITCH_PAGE_INSTR(3),
|
||||
ILI9881C_COMMAND_INSTR(0x01, 0x00),
|
||||
@ -812,6 +980,23 @@ static const struct drm_display_mode k101_im2byl02_default_mode = {
|
||||
.height_mm = 217,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode tl050hdv35_default_mode = {
|
||||
.clock = 59400,
|
||||
|
||||
.hdisplay = 720,
|
||||
.hsync_start = 720 + 18,
|
||||
.hsync_end = 720 + 18 + 3,
|
||||
.htotal = 720 + 18 + 3 + 20,
|
||||
|
||||
.vdisplay = 1280,
|
||||
.vsync_start = 1280 + 26,
|
||||
.vsync_end = 1280 + 26 + 6,
|
||||
.vtotal = 1280 + 26 + 6 + 28,
|
||||
|
||||
.width_mm = 62,
|
||||
.height_mm = 110,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode w552946aba_default_mode = {
|
||||
.clock = 64000,
|
||||
|
||||
@ -944,6 +1129,14 @@ static const struct ili9881c_desc k101_im2byl02_desc = {
|
||||
.mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
|
||||
};
|
||||
|
||||
static const struct ili9881c_desc tl050hdv35_desc = {
|
||||
.init = tl050hdv35_init,
|
||||
.init_length = ARRAY_SIZE(tl050hdv35_init),
|
||||
.mode = &tl050hdv35_default_mode,
|
||||
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
|
||||
MIPI_DSI_MODE_LPM,
|
||||
};
|
||||
|
||||
static const struct ili9881c_desc w552946aba_desc = {
|
||||
.init = w552946ab_init,
|
||||
.init_length = ARRAY_SIZE(w552946ab_init),
|
||||
@ -955,6 +1148,7 @@ static const struct ili9881c_desc w552946aba_desc = {
|
||||
static const struct of_device_id ili9881c_of_match[] = {
|
||||
{ .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc },
|
||||
{ .compatible = "feixin,k101-im2byl02", .data = &k101_im2byl02_desc },
|
||||
{ .compatible = "tdo,tl050hdv35", .data = &tl050hdv35_desc },
|
||||
{ .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc },
|
||||
{ }
|
||||
};
|
||||
|
@ -1185,7 +1185,9 @@ static const struct panel_desc auo_t215hvn01 = {
|
||||
.delay = {
|
||||
.disable = 5,
|
||||
.unprepare = 1000,
|
||||
}
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode avic_tm070ddh03_mode = {
|
||||
@ -2374,6 +2376,37 @@ static const struct panel_desc innolux_g121x1_l03 = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct display_timing innolux_g156hce_l01_timings = {
|
||||
.pixelclock = { 120000000, 144000000, 150000000 },
|
||||
.hactive = { 1920, 1920, 1920 },
|
||||
.hfront_porch = { 80, 90, 100 },
|
||||
.hback_porch = { 80, 90, 100 },
|
||||
.hsync_len = { 20, 30, 30 },
|
||||
.vactive = { 1080, 1080, 1080 },
|
||||
.vfront_porch = { 3, 10, 20 },
|
||||
.vback_porch = { 3, 10, 20 },
|
||||
.vsync_len = { 4, 10, 10 },
|
||||
};
|
||||
|
||||
static const struct panel_desc innolux_g156hce_l01 = {
|
||||
.timings = &innolux_g156hce_l01_timings,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 344,
|
||||
.height = 194,
|
||||
},
|
||||
.delay = {
|
||||
.prepare = 1, /* T1+T2 */
|
||||
.enable = 450, /* T5 */
|
||||
.disable = 200, /* T6 */
|
||||
.unprepare = 10, /* T3+T7 */
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode innolux_n156bge_l21_mode = {
|
||||
.clock = 69300,
|
||||
.hdisplay = 1366,
|
||||
@ -3205,6 +3238,7 @@ static const struct drm_display_mode powertip_ph800480t013_idf02_mode = {
|
||||
static const struct panel_desc powertip_ph800480t013_idf02 = {
|
||||
.modes = &powertip_ph800480t013_idf02_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 152,
|
||||
.height = 91,
|
||||
@ -4239,6 +4273,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "innolux,g121x1-l03",
|
||||
.data = &innolux_g121x1_l03,
|
||||
}, {
|
||||
.compatible = "innolux,g156hce-l01",
|
||||
.data = &innolux_g156hce_l01,
|
||||
}, {
|
||||
.compatible = "innolux,n156bge-l21",
|
||||
.data = &innolux_n156bge_l21,
|
||||
@ -4455,13 +4492,13 @@ MODULE_DEVICE_TABLE(of, platform_of_match);
|
||||
|
||||
static int panel_simple_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *id;
|
||||
const struct panel_desc *desc;
|
||||
|
||||
id = of_match_node(platform_of_match, pdev->dev.of_node);
|
||||
if (!id)
|
||||
desc = of_device_get_match_data(&pdev->dev);
|
||||
if (!desc)
|
||||
return -ENODEV;
|
||||
|
||||
return panel_simple_probe(&pdev->dev, id->data);
|
||||
return panel_simple_probe(&pdev->dev, desc);
|
||||
}
|
||||
|
||||
static void panel_simple_platform_remove(struct platform_device *pdev)
|
||||
@ -4732,15 +4769,12 @@ MODULE_DEVICE_TABLE(of, dsi_of_match);
|
||||
static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
const struct panel_desc_dsi *desc;
|
||||
const struct of_device_id *id;
|
||||
int err;
|
||||
|
||||
id = of_match_node(dsi_of_match, dsi->dev.of_node);
|
||||
if (!id)
|
||||
desc = of_device_get_match_data(&dsi->dev);
|
||||
if (!desc)
|
||||
return -ENODEV;
|
||||
|
||||
desc = id->data;
|
||||
|
||||
err = panel_simple_probe(&dsi->dev, &desc->desc);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -10,14 +10,12 @@
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#define ST7789V_COLMOD_RGB_FMT_18BITS (6 << 4)
|
||||
#define ST7789V_COLMOD_CTRL_FMT_18BITS (6 << 0)
|
||||
|
||||
#define ST7789V_RAMCTRL_CMD 0xb0
|
||||
#define ST7789V_RAMCTRL_RM_RGB BIT(4)
|
||||
#define ST7789V_RAMCTRL_DM_RGB BIT(0)
|
||||
@ -29,7 +27,8 @@
|
||||
#define ST7789V_RGBCTRL_RCM(n) (((n) & 3) << 5)
|
||||
#define ST7789V_RGBCTRL_VSYNC_HIGH BIT(3)
|
||||
#define ST7789V_RGBCTRL_HSYNC_HIGH BIT(2)
|
||||
#define ST7789V_RGBCTRL_PCLK_HIGH BIT(1)
|
||||
#define ST7789V_RGBCTRL_PCLK_FALLING BIT(1)
|
||||
#define ST7789V_RGBCTRL_DE_LOW BIT(0)
|
||||
#define ST7789V_RGBCTRL_VBP(n) ((n) & 0x7f)
|
||||
#define ST7789V_RGBCTRL_HBP(n) ((n) & 0x1f)
|
||||
|
||||
@ -111,8 +110,19 @@
|
||||
return val; \
|
||||
} while (0)
|
||||
|
||||
#define ST7789V_IDS { 0x85, 0x85, 0x52 }
|
||||
#define ST7789V_IDS_SIZE 3
|
||||
|
||||
struct st7789_panel_info {
|
||||
const struct drm_display_mode *mode;
|
||||
u32 bus_format;
|
||||
u32 bus_flags;
|
||||
bool invert_mode;
|
||||
};
|
||||
|
||||
struct st7789v {
|
||||
struct drm_panel panel;
|
||||
const struct st7789_panel_info *info;
|
||||
struct spi_device *spi;
|
||||
struct gpio_desc *reset;
|
||||
struct regulator *power;
|
||||
@ -132,17 +142,12 @@ static int st7789v_spi_write(struct st7789v *ctx, enum st7789v_prefix prefix,
|
||||
u8 data)
|
||||
{
|
||||
struct spi_transfer xfer = { };
|
||||
struct spi_message msg;
|
||||
u16 txbuf = ((prefix & 1) << 8) | data;
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
xfer.tx_buf = &txbuf;
|
||||
xfer.bits_per_word = 9;
|
||||
xfer.len = sizeof(txbuf);
|
||||
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
return spi_sync(ctx->spi, &msg);
|
||||
return spi_sync_transfer(ctx->spi, &xfer, 1);
|
||||
}
|
||||
|
||||
static int st7789v_write_command(struct st7789v *ctx, u8 cmd)
|
||||
@ -155,6 +160,76 @@ static int st7789v_write_data(struct st7789v *ctx, u8 cmd)
|
||||
return st7789v_spi_write(ctx, ST7789V_DATA, cmd);
|
||||
}
|
||||
|
||||
static int st7789v_read_data(struct st7789v *ctx, u8 cmd, u8 *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
struct spi_transfer xfer[2] = { };
|
||||
struct spi_message msg;
|
||||
u16 txbuf = ((ST7789V_COMMAND & 1) << 8) | cmd;
|
||||
u16 rxbuf[4] = {};
|
||||
u8 bit9 = 0;
|
||||
int ret, i;
|
||||
|
||||
switch (len) {
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
xfer[0].tx_buf = &txbuf;
|
||||
xfer[0].len = sizeof(txbuf);
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
|
||||
xfer[1].rx_buf = rxbuf;
|
||||
xfer[1].len = len * 2;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
|
||||
ret = spi_sync(ctx->spi, &msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = rxbuf[i] >> i | (bit9 << (9 - i));
|
||||
if (i)
|
||||
bit9 = rxbuf[i] & GENMASK(i - 1, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7789v_check_id(struct drm_panel *panel)
|
||||
{
|
||||
const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS;
|
||||
struct st7789v *ctx = panel_to_st7789v(panel);
|
||||
bool invalid_ids = false;
|
||||
int ret, i;
|
||||
u8 ids[3];
|
||||
|
||||
if (ctx->spi->mode & SPI_NO_RX)
|
||||
return 0;
|
||||
|
||||
ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ST7789V_IDS_SIZE; i++) {
|
||||
if (ids[i] != st7789v_ids[i]) {
|
||||
invalid_ids = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid_ids)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode default_mode = {
|
||||
.clock = 7000,
|
||||
.hdisplay = 240,
|
||||
@ -165,18 +240,76 @@ static const struct drm_display_mode default_mode = {
|
||||
.vsync_start = 320 + 8,
|
||||
.vsync_end = 320 + 8 + 4,
|
||||
.vtotal = 320 + 8 + 4 + 4,
|
||||
.width_mm = 61,
|
||||
.height_mm = 103,
|
||||
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode t28cp45tn89_mode = {
|
||||
.clock = 6008,
|
||||
.hdisplay = 240,
|
||||
.hsync_start = 240 + 38,
|
||||
.hsync_end = 240 + 38 + 10,
|
||||
.htotal = 240 + 38 + 10 + 10,
|
||||
.vdisplay = 320,
|
||||
.vsync_start = 320 + 8,
|
||||
.vsync_end = 320 + 8 + 4,
|
||||
.vtotal = 320 + 8 + 4 + 4,
|
||||
.width_mm = 43,
|
||||
.height_mm = 57,
|
||||
.flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode et028013dma_mode = {
|
||||
.clock = 3000,
|
||||
.hdisplay = 240,
|
||||
.hsync_start = 240 + 38,
|
||||
.hsync_end = 240 + 38 + 10,
|
||||
.htotal = 240 + 38 + 10 + 10,
|
||||
.vdisplay = 320,
|
||||
.vsync_start = 320 + 8,
|
||||
.vsync_end = 320 + 8 + 4,
|
||||
.vtotal = 320 + 8 + 4 + 4,
|
||||
.width_mm = 43,
|
||||
.height_mm = 58,
|
||||
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
|
||||
};
|
||||
|
||||
static const struct st7789_panel_info default_panel = {
|
||||
.mode = &default_mode,
|
||||
.invert_mode = true,
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH |
|
||||
DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
|
||||
};
|
||||
|
||||
static const struct st7789_panel_info t28cp45tn89_panel = {
|
||||
.mode = &t28cp45tn89_mode,
|
||||
.invert_mode = false,
|
||||
.bus_format = MEDIA_BUS_FMT_RGB565_1X16,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH |
|
||||
DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
|
||||
};
|
||||
|
||||
static const struct st7789_panel_info et028013dma_panel = {
|
||||
.mode = &et028013dma_mode,
|
||||
.invert_mode = true,
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH |
|
||||
DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
|
||||
};
|
||||
|
||||
static int st7789v_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct st7789v *ctx = panel_to_st7789v(panel);
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, &default_mode);
|
||||
mode = drm_mode_duplicate(connector->dev, ctx->info->mode);
|
||||
if (!mode) {
|
||||
dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
|
||||
default_mode.hdisplay, default_mode.vdisplay,
|
||||
drm_mode_vrefresh(&default_mode));
|
||||
dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
|
||||
ctx->info->mode->hdisplay, ctx->info->mode->vdisplay,
|
||||
drm_mode_vrefresh(ctx->info->mode));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -185,8 +318,12 @@ static int st7789v_get_modes(struct drm_panel *panel,
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
connector->display_info.width_mm = 61;
|
||||
connector->display_info.height_mm = 103;
|
||||
connector->display_info.bpc = 6;
|
||||
connector->display_info.width_mm = ctx->info->mode->width_mm;
|
||||
connector->display_info.height_mm = ctx->info->mode->height_mm;
|
||||
connector->display_info.bus_flags = ctx->info->bus_flags;
|
||||
drm_display_info_set_bus_formats(&connector->display_info,
|
||||
&ctx->info->bus_format, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -194,8 +331,34 @@ static int st7789v_get_modes(struct drm_panel *panel,
|
||||
static int st7789v_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct st7789v *ctx = panel_to_st7789v(panel);
|
||||
u8 pixel_fmt, polarity;
|
||||
int ret;
|
||||
|
||||
switch (ctx->info->bus_format) {
|
||||
case MEDIA_BUS_FMT_RGB666_1X18:
|
||||
pixel_fmt = MIPI_DCS_PIXEL_FMT_18BIT;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB565_1X16:
|
||||
pixel_fmt = MIPI_DCS_PIXEL_FMT_16BIT;
|
||||
break;
|
||||
default:
|
||||
dev_err(panel->dev, "unsupported bus format: %d\n",
|
||||
ctx->info->bus_format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pixel_fmt = (pixel_fmt << 4) | pixel_fmt;
|
||||
|
||||
polarity = 0;
|
||||
if (ctx->info->mode->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
polarity |= ST7789V_RGBCTRL_VSYNC_HIGH;
|
||||
if (ctx->info->mode->flags & DRM_MODE_FLAG_PHSYNC)
|
||||
polarity |= ST7789V_RGBCTRL_HSYNC_HIGH;
|
||||
if (ctx->info->bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE)
|
||||
polarity |= ST7789V_RGBCTRL_PCLK_FALLING;
|
||||
if (ctx->info->bus_flags & DRM_BUS_FLAG_DE_LOW)
|
||||
polarity |= ST7789V_RGBCTRL_DE_LOW;
|
||||
|
||||
ret = regulator_enable(ctx->power);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -205,6 +368,14 @@ static int st7789v_prepare(struct drm_panel *panel)
|
||||
gpiod_set_value(ctx->reset, 0);
|
||||
msleep(120);
|
||||
|
||||
/*
|
||||
* Avoid failing if the IDs are invalid in case the Rx bus width
|
||||
* description is missing.
|
||||
*/
|
||||
ret = st7789v_check_id(panel);
|
||||
if (ret)
|
||||
dev_warn(panel->dev, "Unrecognized panel IDs");
|
||||
|
||||
ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
|
||||
|
||||
/* We need to wait 120ms after a sleep out command */
|
||||
@ -216,9 +387,7 @@ static int st7789v_prepare(struct drm_panel *panel)
|
||||
|
||||
ST7789V_TEST(ret, st7789v_write_command(ctx,
|
||||
MIPI_DCS_SET_PIXEL_FORMAT));
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx,
|
||||
(MIPI_DCS_PIXEL_FMT_18BIT << 4) |
|
||||
(MIPI_DCS_PIXEL_FMT_18BIT)));
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx, pixel_fmt));
|
||||
|
||||
ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PORCTRL_CMD));
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc));
|
||||
@ -296,7 +465,13 @@ static int st7789v_prepare(struct drm_panel *panel)
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN61(0x1b)));
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN62(0x28)));
|
||||
|
||||
ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_ENTER_INVERT_MODE));
|
||||
if (ctx->info->invert_mode) {
|
||||
ST7789V_TEST(ret, st7789v_write_command(ctx,
|
||||
MIPI_DCS_ENTER_INVERT_MODE));
|
||||
} else {
|
||||
ST7789V_TEST(ret, st7789v_write_command(ctx,
|
||||
MIPI_DCS_EXIT_INVERT_MODE));
|
||||
}
|
||||
|
||||
ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RAMCTRL_CMD));
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_DM_RGB |
|
||||
@ -307,9 +482,7 @@ static int st7789v_prepare(struct drm_panel *panel)
|
||||
ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RGBCTRL_CMD));
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_WO |
|
||||
ST7789V_RGBCTRL_RCM(2) |
|
||||
ST7789V_RGBCTRL_VSYNC_HIGH |
|
||||
ST7789V_RGBCTRL_HSYNC_HIGH |
|
||||
ST7789V_RGBCTRL_PCLK_HIGH));
|
||||
polarity));
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8)));
|
||||
ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20)));
|
||||
|
||||
@ -355,32 +528,40 @@ static const struct drm_panel_funcs st7789v_drm_funcs = {
|
||||
|
||||
static int st7789v_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct st7789v *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, ctx);
|
||||
ctx->spi = spi;
|
||||
|
||||
drm_panel_init(&ctx->panel, &spi->dev, &st7789v_drm_funcs,
|
||||
spi->bits_per_word = 9;
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&spi->dev, ret, "Failed to setup spi\n");
|
||||
|
||||
ctx->info = device_get_match_data(&spi->dev);
|
||||
|
||||
drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs,
|
||||
DRM_MODE_CONNECTOR_DPI);
|
||||
|
||||
ctx->power = devm_regulator_get(&spi->dev, "power");
|
||||
if (IS_ERR(ctx->power))
|
||||
return PTR_ERR(ctx->power);
|
||||
ctx->power = devm_regulator_get(dev, "power");
|
||||
ret = PTR_ERR_OR_ZERO(ctx->power);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get regulator\n");
|
||||
|
||||
ctx->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->reset)) {
|
||||
dev_err(&spi->dev, "Couldn't get our reset line\n");
|
||||
return PTR_ERR(ctx->reset);
|
||||
}
|
||||
ctx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
|
||||
ret = PTR_ERR_OR_ZERO(ctx->reset);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get reset line\n");
|
||||
|
||||
ret = drm_panel_of_backlight(&ctx->panel);
|
||||
if (ret)
|
||||
return ret;
|
||||
return dev_err_probe(dev, ret, "Failed to get backlight\n");
|
||||
|
||||
drm_panel_add(&ctx->panel);
|
||||
|
||||
@ -394,8 +575,18 @@ static void st7789v_remove(struct spi_device *spi)
|
||||
drm_panel_remove(&ctx->panel);
|
||||
}
|
||||
|
||||
static const struct spi_device_id st7789v_spi_id[] = {
|
||||
{ "st7789v", (unsigned long) &default_panel },
|
||||
{ "t28cp45tn89-v17", (unsigned long) &t28cp45tn89_panel },
|
||||
{ "et028013dma", (unsigned long) &et028013dma_panel },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st7789v_spi_id);
|
||||
|
||||
static const struct of_device_id st7789v_of_match[] = {
|
||||
{ .compatible = "sitronix,st7789v" },
|
||||
{ .compatible = "sitronix,st7789v", .data = &default_panel },
|
||||
{ .compatible = "inanbo,t28cp45tn89-v17", .data = &t28cp45tn89_panel },
|
||||
{ .compatible = "edt,et028013dma", .data = &et028013dma_panel },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st7789v_of_match);
|
||||
@ -403,6 +594,7 @@ MODULE_DEVICE_TABLE(of, st7789v_of_match);
|
||||
static struct spi_driver st7789v_driver = {
|
||||
.probe = st7789v_probe,
|
||||
.remove = st7789v_remove,
|
||||
.id_table = st7789v_spi_id,
|
||||
.driver = {
|
||||
.name = "st7789v",
|
||||
.of_match_table = st7789v_of_match,
|
||||
|
406
drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c
Normal file
406
drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c
Normal file
@ -0,0 +1,406 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2016 InforceComputing
|
||||
* Copyright (C) 2016 Linaro Ltd
|
||||
* Copyright (C) 2023 BayLibre, SAS
|
||||
*
|
||||
* Authors:
|
||||
* - Vinay Simha BN <simhavcs@gmail.com>
|
||||
* - Sumit Semwal <sumit.semwal@linaro.org>
|
||||
* - Guillaume La Roque <glaroque@baylibre.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#define DSI_REG_MCAP 0xB0
|
||||
#define DSI_REG_IS 0xB3 /* Interface Setting */
|
||||
#define DSI_REG_IIS 0xB4 /* Interface ID Setting */
|
||||
#define DSI_REG_CTRL 0xB6
|
||||
|
||||
enum {
|
||||
IOVCC = 0,
|
||||
POWER = 1
|
||||
};
|
||||
|
||||
struct stk_panel {
|
||||
bool prepared;
|
||||
const struct drm_display_mode *mode;
|
||||
struct backlight_device *backlight;
|
||||
struct drm_panel base;
|
||||
struct gpio_desc *enable_gpio; /* Power IC supply enable */
|
||||
struct gpio_desc *reset_gpio; /* External reset */
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct regulator_bulk_data supplies[2];
|
||||
};
|
||||
|
||||
static inline struct stk_panel *to_stk_panel(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct stk_panel, base);
|
||||
}
|
||||
|
||||
static int stk_panel_init(struct stk_panel *stk)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = stk->dsi;
|
||||
struct device *dev = &stk->dsi->dev;
|
||||
int ret;
|
||||
|
||||
ret = mipi_dsi_dcs_soft_reset(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to mipi_dsi_dcs_soft_reset: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
mdelay(5);
|
||||
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set exit sleep mode: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
msleep(120);
|
||||
|
||||
mipi_dsi_generic_write_seq(dsi, DSI_REG_MCAP, 0x04);
|
||||
|
||||
/* Interface setting, video mode */
|
||||
mipi_dsi_generic_write_seq(dsi, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00);
|
||||
mipi_dsi_generic_write_seq(dsi, DSI_REG_IIS, 0x0C, 0x00);
|
||||
mipi_dsi_generic_write_seq(dsi, DSI_REG_CTRL, 0x3A, 0xD3);
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x77);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to write display brightness: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
|
||||
MIPI_DCS_WRITE_MEMORY_START);
|
||||
|
||||
ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set pixel format: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mipi_dsi_dcs_set_column_address(dsi, 0, stk->mode->hdisplay - 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set column address: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mipi_dsi_dcs_set_page_address(dsi, 0, stk->mode->vdisplay - 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set page address: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stk_panel_on(struct stk_panel *stk)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = stk->dsi;
|
||||
struct device *dev = &stk->dsi->dev;
|
||||
int ret;
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_on(dsi);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to set display on: %d\n", ret);
|
||||
|
||||
mdelay(20);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stk_panel_off(struct stk_panel *stk)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = stk->dsi;
|
||||
struct device *dev = &stk->dsi->dev;
|
||||
int ret;
|
||||
|
||||
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_off(dsi);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to set display off: %d\n", ret);
|
||||
|
||||
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to enter sleep mode: %d\n", ret);
|
||||
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
static int stk_panel_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct stk_panel *stk = to_stk_panel(panel);
|
||||
|
||||
if (!stk->prepared)
|
||||
return 0;
|
||||
|
||||
stk_panel_off(stk);
|
||||
regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies);
|
||||
gpiod_set_value(stk->reset_gpio, 0);
|
||||
gpiod_set_value(stk->enable_gpio, 1);
|
||||
|
||||
stk->prepared = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stk_panel_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct stk_panel *stk = to_stk_panel(panel);
|
||||
struct device *dev = &stk->dsi->dev;
|
||||
int ret;
|
||||
|
||||
if (stk->prepared)
|
||||
return 0;
|
||||
|
||||
gpiod_set_value(stk->reset_gpio, 0);
|
||||
gpiod_set_value(stk->enable_gpio, 0);
|
||||
ret = regulator_enable(stk->supplies[IOVCC].consumer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mdelay(8);
|
||||
ret = regulator_enable(stk->supplies[POWER].consumer);
|
||||
if (ret < 0)
|
||||
goto iovccoff;
|
||||
|
||||
mdelay(20);
|
||||
gpiod_set_value(stk->enable_gpio, 1);
|
||||
mdelay(20);
|
||||
gpiod_set_value(stk->reset_gpio, 1);
|
||||
mdelay(10);
|
||||
ret = stk_panel_init(stk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to init panel: %d\n", ret);
|
||||
goto poweroff;
|
||||
}
|
||||
|
||||
ret = stk_panel_on(stk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set panel on: %d\n", ret);
|
||||
goto poweroff;
|
||||
}
|
||||
|
||||
stk->prepared = true;
|
||||
|
||||
return 0;
|
||||
|
||||
poweroff:
|
||||
regulator_disable(stk->supplies[POWER].consumer);
|
||||
iovccoff:
|
||||
regulator_disable(stk->supplies[IOVCC].consumer);
|
||||
gpiod_set_value(stk->reset_gpio, 0);
|
||||
gpiod_set_value(stk->enable_gpio, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode default_mode = {
|
||||
.clock = 163204,
|
||||
.hdisplay = 1200,
|
||||
.hsync_start = 1200 + 144,
|
||||
.hsync_end = 1200 + 144 + 16,
|
||||
.htotal = 1200 + 144 + 16 + 45,
|
||||
.vdisplay = 1920,
|
||||
.vsync_start = 1920 + 8,
|
||||
.vsync_end = 1920 + 8 + 4,
|
||||
.vtotal = 1920 + 8 + 4 + 4,
|
||||
.width_mm = 95,
|
||||
.height_mm = 151,
|
||||
};
|
||||
|
||||
static int stk_panel_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, &default_mode);
|
||||
if (!mode) {
|
||||
dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
|
||||
default_mode.hdisplay, default_mode.vdisplay,
|
||||
drm_mode_vrefresh(&default_mode));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
drm_mode_probed_add(connector, mode);
|
||||
connector->display_info.width_mm = default_mode.width_mm;
|
||||
connector->display_info.height_mm = default_mode.height_mm;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = bl_get_data(bl);
|
||||
int ret;
|
||||
u16 brightness;
|
||||
|
||||
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
|
||||
ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
|
||||
return brightness & 0xff;
|
||||
}
|
||||
|
||||
static int dsi_dcs_bl_update_status(struct backlight_device *bl)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = bl_get_data(bl);
|
||||
struct device *dev = &dsi->dev;
|
||||
int ret;
|
||||
|
||||
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
|
||||
ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set DSI control: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops dsi_bl_ops = {
|
||||
.update_status = dsi_dcs_bl_update_status,
|
||||
.get_brightness = dsi_dcs_bl_get_brightness,
|
||||
};
|
||||
|
||||
static struct backlight_device *
|
||||
drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct device *dev = &dsi->dev;
|
||||
struct backlight_properties props = {
|
||||
.type = BACKLIGHT_RAW,
|
||||
.brightness = 255,
|
||||
.max_brightness = 255,
|
||||
};
|
||||
|
||||
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
|
||||
&dsi_bl_ops, &props);
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs stk_panel_funcs = {
|
||||
.unprepare = stk_panel_unprepare,
|
||||
.prepare = stk_panel_prepare,
|
||||
.get_modes = stk_panel_get_modes,
|
||||
};
|
||||
|
||||
static const struct of_device_id stk_of_match[] = {
|
||||
{ .compatible = "startek,kd070fhfid015", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stk_of_match);
|
||||
|
||||
static int stk_panel_add(struct stk_panel *stk)
|
||||
{
|
||||
struct device *dev = &stk->dsi->dev;
|
||||
int ret;
|
||||
|
||||
stk->mode = &default_mode;
|
||||
|
||||
stk->supplies[IOVCC].supply = "iovcc";
|
||||
stk->supplies[POWER].supply = "power";
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "regulator_bulk failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(stk->reset_gpio)) {
|
||||
ret = PTR_ERR(stk->reset_gpio);
|
||||
dev_err(dev, "cannot get reset-gpios %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(stk->enable_gpio)) {
|
||||
ret = PTR_ERR(stk->enable_gpio);
|
||||
dev_err(dev, "cannot get enable-gpio %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
stk->backlight = drm_panel_create_dsi_backlight(stk->dsi);
|
||||
if (IS_ERR(stk->backlight)) {
|
||||
ret = PTR_ERR(stk->backlight);
|
||||
dev_err(dev, "failed to register backlight %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs,
|
||||
DRM_MODE_CONNECTOR_DSI);
|
||||
|
||||
drm_panel_add(&stk->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stk_panel_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct stk_panel *stk;
|
||||
int ret;
|
||||
|
||||
dsi->lanes = 4;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM);
|
||||
|
||||
stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL);
|
||||
if (!stk)
|
||||
return -ENOMEM;
|
||||
|
||||
mipi_dsi_set_drvdata(dsi, stk);
|
||||
|
||||
stk->dsi = dsi;
|
||||
|
||||
ret = stk_panel_add(stk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0)
|
||||
drm_panel_remove(&stk->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stk_panel_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct stk_panel *stk = mipi_dsi_get_drvdata(dsi);
|
||||
int err;
|
||||
|
||||
err = mipi_dsi_detach(dsi);
|
||||
if (err < 0)
|
||||
dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
|
||||
err);
|
||||
|
||||
drm_panel_remove(&stk->base);
|
||||
}
|
||||
|
||||
static struct mipi_dsi_driver stk_panel_driver = {
|
||||
.driver = {
|
||||
.name = "panel-startek-kd070fhfid015",
|
||||
.of_match_table = stk_of_match,
|
||||
},
|
||||
.probe = stk_panel_probe,
|
||||
.remove = stk_panel_remove,
|
||||
};
|
||||
module_mipi_dsi_driver(stk_panel_driver);
|
||||
|
||||
MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
|
||||
MODULE_DESCRIPTION("STARTEK KD070FHFID015");
|
||||
MODULE_LICENSE("GPL");
|
@ -11,7 +11,7 @@ config DRM_RADEON
|
||||
select DRM_SUBALLOC_HELPER
|
||||
select DRM_TTM
|
||||
select DRM_TTM_HELPER
|
||||
select FB_IO_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
select SND_HDA_COMPONENT if SND_HDA_CORE
|
||||
select POWER_SUPPLY
|
||||
select HWMON
|
||||
|
@ -3603,7 +3603,7 @@ void cik_fence_compute_ring_emit(struct radeon_device *rdev,
|
||||
* @rdev: radeon_device pointer
|
||||
* @ring: radeon ring buffer object
|
||||
* @semaphore: radeon semaphore object
|
||||
* @emit_wait: Is this a sempahore wait?
|
||||
* @emit_wait: Is this a semaphore wait?
|
||||
*
|
||||
* Emits a semaphore signal/wait packet to the CP ring and prevents the PFP
|
||||
* from running ahead of semaphore waits.
|
||||
|
@ -2918,7 +2918,7 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
|
||||
* @rdev: radeon_device pointer
|
||||
* @ring: radeon ring buffer object
|
||||
* @semaphore: radeon semaphore object
|
||||
* @emit_wait: Is this a sempahore wait?
|
||||
* @emit_wait: Is this a semaphore wait?
|
||||
*
|
||||
* Emits a semaphore signal/wait packet to the CP ring and prevents the PFP
|
||||
* from running ahead of semaphore waits.
|
||||
|
@ -193,7 +193,7 @@ static const struct fb_ops radeon_fbdev_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_open = radeon_fbdev_fb_open,
|
||||
.fb_release = radeon_fbdev_fb_release,
|
||||
FB_DEFAULT_IO_OPS,
|
||||
FB_DEFAULT_IOMEM_OPS,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
.fb_destroy = radeon_fbdev_fb_destroy,
|
||||
};
|
||||
|
@ -600,7 +600,8 @@ int __rcar_du_plane_atomic_check(struct drm_plane *plane,
|
||||
if (!state->crtc) {
|
||||
/*
|
||||
* The visible field is not reset by the DRM core but only
|
||||
* updated by drm_plane_helper_check_state(), set it manually.
|
||||
* updated by drm_atomic_helper_check_plane_state(), set it
|
||||
* manually.
|
||||
*/
|
||||
state->visible = false;
|
||||
*format = NULL;
|
||||
|
@ -142,7 +142,7 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = {
|
||||
EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X);
|
||||
|
||||
struct ssd130x_plane_state {
|
||||
struct drm_plane_state base;
|
||||
struct drm_shadow_plane_state base;
|
||||
/* Intermediate buffer to convert pixels from XRGB8888 to HW format */
|
||||
u8 *buffer;
|
||||
/* Buffer to store pixels in HW format and written to the panel */
|
||||
@ -151,7 +151,7 @@ struct ssd130x_plane_state {
|
||||
|
||||
static inline struct ssd130x_plane_state *to_ssd130x_plane_state(struct drm_plane_state *state)
|
||||
{
|
||||
return container_of(state, struct ssd130x_plane_state, base);
|
||||
return container_of(state, struct ssd130x_plane_state, base.base);
|
||||
}
|
||||
|
||||
static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm)
|
||||
@ -689,11 +689,12 @@ static void ssd130x_primary_plane_reset(struct drm_plane *plane)
|
||||
if (!ssd130x_state)
|
||||
return;
|
||||
|
||||
__drm_atomic_helper_plane_reset(plane, &ssd130x_state->base);
|
||||
__drm_gem_reset_shadow_plane(plane, &ssd130x_state->base);
|
||||
}
|
||||
|
||||
static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_shadow_plane_state *new_shadow_plane_state;
|
||||
struct ssd130x_plane_state *old_ssd130x_state;
|
||||
struct ssd130x_plane_state *ssd130x_state;
|
||||
|
||||
@ -709,9 +710,11 @@ static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_
|
||||
ssd130x_state->buffer = NULL;
|
||||
ssd130x_state->data_array = NULL;
|
||||
|
||||
__drm_atomic_helper_plane_duplicate_state(plane, &ssd130x_state->base);
|
||||
new_shadow_plane_state = &ssd130x_state->base;
|
||||
|
||||
return &ssd130x_state->base;
|
||||
__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
|
||||
|
||||
return &new_shadow_plane_state->base;
|
||||
}
|
||||
|
||||
static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane,
|
||||
@ -722,7 +725,7 @@ static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane,
|
||||
kfree(ssd130x_state->data_array);
|
||||
kfree(ssd130x_state->buffer);
|
||||
|
||||
__drm_atomic_helper_plane_destroy_state(&ssd130x_state->base);
|
||||
__drm_gem_destroy_shadow_plane_state(&ssd130x_state->base);
|
||||
|
||||
kfree(ssd130x_state);
|
||||
}
|
||||
@ -741,7 +744,6 @@ static const struct drm_plane_funcs ssd130x_primary_plane_funcs = {
|
||||
.atomic_duplicate_state = ssd130x_primary_plane_duplicate_state,
|
||||
.atomic_destroy_state = ssd130x_primary_plane_destroy_state,
|
||||
.destroy = drm_plane_cleanup,
|
||||
DRM_GEM_SHADOW_PLANE_FUNCS,
|
||||
};
|
||||
|
||||
static enum drm_mode_status ssd130x_crtc_helper_mode_valid(struct drm_crtc *crtc,
|
||||
|
@ -12,7 +12,7 @@ config DRM_TEGRA
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_MIPI_DSI
|
||||
select DRM_PANEL
|
||||
select FB_DMA_HELPERS if DRM_FBDEV_EMULATION
|
||||
select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION
|
||||
select TEGRA_HOST1X
|
||||
select INTERCONNECT
|
||||
select IOMMU_IOVA
|
||||
|
@ -447,7 +447,6 @@ static const struct pinmux_ops tegra_dpaux_pinmux_ops = {
|
||||
static int tegra_dpaux_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_dpaux *dpaux;
|
||||
struct resource *regs;
|
||||
u32 value;
|
||||
int err;
|
||||
|
||||
@ -461,14 +460,13 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
|
||||
INIT_LIST_HEAD(&dpaux->list);
|
||||
dpaux->dev = &pdev->dev;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dpaux->regs = devm_ioremap_resource(&pdev->dev, regs);
|
||||
dpaux->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(dpaux->regs))
|
||||
return PTR_ERR(dpaux->regs);
|
||||
|
||||
dpaux->irq = platform_get_irq(pdev, 0);
|
||||
if (dpaux->irq < 0)
|
||||
return -ENXIO;
|
||||
return dpaux->irq;
|
||||
|
||||
if (!pdev->dev.pm_domain) {
|
||||
dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux");
|
||||
|
@ -59,9 +59,9 @@ static void tegra_fbdev_fb_destroy(struct fb_info *info)
|
||||
|
||||
static const struct fb_ops tegra_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
__FB_DEFAULT_DMA_OPS_RDWR,
|
||||
__FB_DEFAULT_DMAMEM_OPS_RDWR,
|
||||
DRM_FB_HELPER_DEFAULT_OPS,
|
||||
__FB_DEFAULT_DMA_OPS_DRAW,
|
||||
__FB_DEFAULT_DMAMEM_OPS_DRAW,
|
||||
.fb_mmap = tegra_fb_mmap,
|
||||
.fb_destroy = tegra_fbdev_fb_destroy,
|
||||
};
|
||||
|
@ -71,22 +71,15 @@ static int gr2d_init(struct host1x_client *client)
|
||||
goto free;
|
||||
}
|
||||
|
||||
pm_runtime_enable(client->dev);
|
||||
pm_runtime_use_autosuspend(client->dev);
|
||||
pm_runtime_set_autosuspend_delay(client->dev, 200);
|
||||
|
||||
err = tegra_drm_register_client(dev->dev_private, drm);
|
||||
if (err < 0) {
|
||||
dev_err(client->dev, "failed to register client: %d\n", err);
|
||||
goto disable_rpm;
|
||||
goto detach_iommu;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
disable_rpm:
|
||||
pm_runtime_dont_use_autosuspend(client->dev);
|
||||
pm_runtime_force_suspend(client->dev);
|
||||
|
||||
detach_iommu:
|
||||
host1x_client_iommu_detach(client);
|
||||
free:
|
||||
host1x_syncpt_put(client->syncpts[0]);
|
||||
@ -300,6 +293,7 @@ static void gr2d_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gr2d *gr2d = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
host1x_client_unregister(&gr2d->client.base);
|
||||
}
|
||||
|
||||
@ -373,6 +367,10 @@ static int __maybe_unused gr2d_runtime_resume(struct device *dev)
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 500);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
|
@ -80,22 +80,15 @@ static int gr3d_init(struct host1x_client *client)
|
||||
goto free;
|
||||
}
|
||||
|
||||
pm_runtime_enable(client->dev);
|
||||
pm_runtime_use_autosuspend(client->dev);
|
||||
pm_runtime_set_autosuspend_delay(client->dev, 200);
|
||||
|
||||
err = tegra_drm_register_client(dev->dev_private, drm);
|
||||
if (err < 0) {
|
||||
dev_err(client->dev, "failed to register client: %d\n", err);
|
||||
goto disable_rpm;
|
||||
goto detach_iommu;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
disable_rpm:
|
||||
pm_runtime_dont_use_autosuspend(client->dev);
|
||||
pm_runtime_force_suspend(client->dev);
|
||||
|
||||
detach_iommu:
|
||||
host1x_client_iommu_detach(client);
|
||||
free:
|
||||
host1x_syncpt_put(client->syncpts[0]);
|
||||
@ -554,6 +547,7 @@ static void gr3d_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gr3d *gr3d = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
host1x_client_unregister(&gr3d->client.base);
|
||||
}
|
||||
|
||||
@ -607,6 +601,10 @@ static int __maybe_unused gr3d_runtime_resume(struct device *dev)
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 500);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <soc/tegra/common.h>
|
||||
#include <sound/hdmi-codec.h>
|
||||
|
||||
#include <drm/drm_bridge_connector.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_debugfs.h>
|
||||
@ -1545,26 +1546,47 @@ static int tegra_hdmi_init(struct host1x_client *client)
|
||||
{
|
||||
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
|
||||
struct drm_device *drm = dev_get_drvdata(client->host);
|
||||
struct drm_connector *connector;
|
||||
int err;
|
||||
|
||||
hdmi->output.dev = client->dev;
|
||||
|
||||
drm_connector_init_with_ddc(drm, &hdmi->output.connector,
|
||||
&tegra_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA,
|
||||
hdmi->output.ddc);
|
||||
drm_connector_helper_add(&hdmi->output.connector,
|
||||
&tegra_hdmi_connector_helper_funcs);
|
||||
hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
drm_simple_encoder_init(drm, &hdmi->output.encoder,
|
||||
DRM_MODE_ENCODER_TMDS);
|
||||
drm_encoder_helper_add(&hdmi->output.encoder,
|
||||
&tegra_hdmi_encoder_helper_funcs);
|
||||
|
||||
drm_connector_attach_encoder(&hdmi->output.connector,
|
||||
&hdmi->output.encoder);
|
||||
drm_connector_register(&hdmi->output.connector);
|
||||
if (hdmi->output.bridge) {
|
||||
err = drm_bridge_attach(&hdmi->output.encoder, hdmi->output.bridge,
|
||||
NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (err) {
|
||||
dev_err(client->dev, "failed to attach bridge: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
connector = drm_bridge_connector_init(drm, &hdmi->output.encoder);
|
||||
if (IS_ERR(connector)) {
|
||||
dev_err(client->dev,
|
||||
"failed to initialize bridge connector: %pe\n",
|
||||
connector);
|
||||
return PTR_ERR(connector);
|
||||
}
|
||||
|
||||
drm_connector_attach_encoder(connector, &hdmi->output.encoder);
|
||||
} else {
|
||||
drm_connector_init_with_ddc(drm, &hdmi->output.connector,
|
||||
&tegra_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA,
|
||||
hdmi->output.ddc);
|
||||
drm_connector_helper_add(&hdmi->output.connector,
|
||||
&tegra_hdmi_connector_helper_funcs);
|
||||
hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
drm_connector_attach_encoder(&hdmi->output.connector,
|
||||
&hdmi->output.encoder);
|
||||
drm_connector_register(&hdmi->output.connector);
|
||||
}
|
||||
|
||||
err = tegra_output_init(drm, &hdmi->output);
|
||||
if (err < 0) {
|
||||
@ -1770,7 +1792,6 @@ static irqreturn_t tegra_hdmi_irq(int irq, void *data)
|
||||
static int tegra_hdmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_hdmi *hdmi;
|
||||
struct resource *regs;
|
||||
int err;
|
||||
|
||||
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
|
||||
@ -1832,8 +1853,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hdmi->regs = devm_ioremap_resource(&pdev->dev, regs);
|
||||
hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(hdmi->regs))
|
||||
return PTR_ERR(hdmi->regs);
|
||||
|
||||
|
@ -175,13 +175,9 @@ static int nvdec_init(struct host1x_client *client)
|
||||
goto free_channel;
|
||||
}
|
||||
|
||||
pm_runtime_enable(client->dev);
|
||||
pm_runtime_use_autosuspend(client->dev);
|
||||
pm_runtime_set_autosuspend_delay(client->dev, 500);
|
||||
|
||||
err = tegra_drm_register_client(tegra, drm);
|
||||
if (err < 0)
|
||||
goto disable_rpm;
|
||||
goto free_syncpt;
|
||||
|
||||
/*
|
||||
* Inherit the DMA parameters (such as maximum segment size) from the
|
||||
@ -191,10 +187,7 @@ static int nvdec_init(struct host1x_client *client)
|
||||
|
||||
return 0;
|
||||
|
||||
disable_rpm:
|
||||
pm_runtime_dont_use_autosuspend(client->dev);
|
||||
pm_runtime_force_suspend(client->dev);
|
||||
|
||||
free_syncpt:
|
||||
host1x_syncpt_put(client->syncpts[0]);
|
||||
free_channel:
|
||||
host1x_channel_put(nvdec->channel);
|
||||
@ -274,6 +267,8 @@ static int nvdec_load_falcon_firmware(struct nvdec *nvdec)
|
||||
return err;
|
||||
} else {
|
||||
virt = tegra_drm_alloc(tegra, size, &iova);
|
||||
if (IS_ERR(virt))
|
||||
return PTR_ERR(virt);
|
||||
}
|
||||
|
||||
nvdec->falcon.firmware.virt = virt;
|
||||
@ -537,6 +532,10 @@ static int nvdec_probe(struct platform_device *pdev)
|
||||
goto exit_falcon;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 500);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_falcon:
|
||||
@ -549,8 +548,8 @@ static void nvdec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct nvdec *nvdec = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
host1x_client_unregister(&nvdec->client.base);
|
||||
|
||||
falcon_exit(&nvdec->falcon);
|
||||
}
|
||||
|
||||
|
@ -3708,7 +3708,6 @@ static int tegra_sor_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct tegra_sor *sor;
|
||||
struct resource *regs;
|
||||
int err;
|
||||
|
||||
sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL);
|
||||
@ -3781,8 +3780,7 @@ static int tegra_sor_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sor->regs = devm_ioremap_resource(&pdev->dev, regs);
|
||||
sor->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(sor->regs)) {
|
||||
err = PTR_ERR(sor->regs);
|
||||
goto remove;
|
||||
|
@ -141,13 +141,9 @@ static int vic_init(struct host1x_client *client)
|
||||
goto free_channel;
|
||||
}
|
||||
|
||||
pm_runtime_enable(client->dev);
|
||||
pm_runtime_use_autosuspend(client->dev);
|
||||
pm_runtime_set_autosuspend_delay(client->dev, 500);
|
||||
|
||||
err = tegra_drm_register_client(tegra, drm);
|
||||
if (err < 0)
|
||||
goto disable_rpm;
|
||||
goto free_syncpt;
|
||||
|
||||
/*
|
||||
* Inherit the DMA parameters (such as maximum segment size) from the
|
||||
@ -157,10 +153,7 @@ static int vic_init(struct host1x_client *client)
|
||||
|
||||
return 0;
|
||||
|
||||
disable_rpm:
|
||||
pm_runtime_dont_use_autosuspend(client->dev);
|
||||
pm_runtime_force_suspend(client->dev);
|
||||
|
||||
free_syncpt:
|
||||
host1x_syncpt_put(client->syncpts[0]);
|
||||
free_channel:
|
||||
host1x_channel_put(vic->channel);
|
||||
@ -527,6 +520,10 @@ static int vic_probe(struct platform_device *pdev)
|
||||
goto exit_falcon;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 500);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_falcon:
|
||||
@ -539,8 +536,8 @@ static void vic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vic *vic = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
host1x_client_unregister(&vic->client.base);
|
||||
|
||||
falcon_exit(&vic->falcon);
|
||||
}
|
||||
|
||||
|
@ -82,13 +82,6 @@ static int drm_client_modeset_test_init(struct kunit *test)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drm_client_modeset_test_exit(struct kunit *test)
|
||||
{
|
||||
struct drm_client_modeset_test_priv *priv = test->priv;
|
||||
|
||||
drm_kunit_helper_free_device(test, priv->dev);
|
||||
}
|
||||
|
||||
static void drm_test_pick_cmdline_res_1920_1080_60(struct kunit *test)
|
||||
{
|
||||
struct drm_client_modeset_test_priv *priv = test->priv;
|
||||
@ -188,7 +181,6 @@ static struct kunit_case drm_test_pick_cmdline_tests[] = {
|
||||
static struct kunit_suite drm_test_pick_cmdline_test_suite = {
|
||||
.name = "drm_test_pick_cmdline",
|
||||
.init = drm_client_modeset_test_init,
|
||||
.exit = drm_client_modeset_test_exit,
|
||||
.test_cases = drm_test_pick_cmdline_tests
|
||||
};
|
||||
|
||||
|
@ -12,11 +12,35 @@
|
||||
|
||||
#include <drm/drm_exec.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
|
||||
#include "../lib/drm_random.h"
|
||||
|
||||
static struct drm_device dev;
|
||||
struct drm_exec_priv {
|
||||
struct device *dev;
|
||||
struct drm_device *drm;
|
||||
};
|
||||
|
||||
static int drm_exec_test_init(struct kunit *test)
|
||||
{
|
||||
struct drm_exec_priv *priv;
|
||||
|
||||
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
|
||||
|
||||
test->priv = priv;
|
||||
|
||||
priv->dev = drm_kunit_helper_alloc_device(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
|
||||
|
||||
priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, sizeof(*priv->drm), 0,
|
||||
DRIVER_MODESET);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sanitycheck(struct kunit *test)
|
||||
{
|
||||
@ -29,11 +53,12 @@ static void sanitycheck(struct kunit *test)
|
||||
|
||||
static void test_lock(struct kunit *test)
|
||||
{
|
||||
struct drm_exec_priv *priv = test->priv;
|
||||
struct drm_gem_object gobj = { };
|
||||
struct drm_exec exec;
|
||||
int ret;
|
||||
|
||||
drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE);
|
||||
drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE);
|
||||
|
||||
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
|
||||
drm_exec_until_all_locked(&exec) {
|
||||
@ -48,11 +73,12 @@ static void test_lock(struct kunit *test)
|
||||
|
||||
static void test_lock_unlock(struct kunit *test)
|
||||
{
|
||||
struct drm_exec_priv *priv = test->priv;
|
||||
struct drm_gem_object gobj = { };
|
||||
struct drm_exec exec;
|
||||
int ret;
|
||||
|
||||
drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE);
|
||||
drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE);
|
||||
|
||||
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
|
||||
drm_exec_until_all_locked(&exec) {
|
||||
@ -74,11 +100,12 @@ static void test_lock_unlock(struct kunit *test)
|
||||
|
||||
static void test_duplicates(struct kunit *test)
|
||||
{
|
||||
struct drm_exec_priv *priv = test->priv;
|
||||
struct drm_gem_object gobj = { };
|
||||
struct drm_exec exec;
|
||||
int ret;
|
||||
|
||||
drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE);
|
||||
drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE);
|
||||
|
||||
drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES);
|
||||
drm_exec_until_all_locked(&exec) {
|
||||
@ -102,11 +129,12 @@ static void test_duplicates(struct kunit *test)
|
||||
|
||||
static void test_prepare(struct kunit *test)
|
||||
{
|
||||
struct drm_exec_priv *priv = test->priv;
|
||||
struct drm_gem_object gobj = { };
|
||||
struct drm_exec exec;
|
||||
int ret;
|
||||
|
||||
drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE);
|
||||
drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE);
|
||||
|
||||
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
|
||||
drm_exec_until_all_locked(&exec) {
|
||||
@ -121,14 +149,15 @@ static void test_prepare(struct kunit *test)
|
||||
|
||||
static void test_prepare_array(struct kunit *test)
|
||||
{
|
||||
struct drm_exec_priv *priv = test->priv;
|
||||
struct drm_gem_object gobj1 = { };
|
||||
struct drm_gem_object gobj2 = { };
|
||||
struct drm_gem_object *array[] = { &gobj1, &gobj2 };
|
||||
struct drm_exec exec;
|
||||
int ret;
|
||||
|
||||
drm_gem_private_object_init(&dev, &gobj1, PAGE_SIZE);
|
||||
drm_gem_private_object_init(&dev, &gobj2, PAGE_SIZE);
|
||||
drm_gem_private_object_init(priv->drm, &gobj1, PAGE_SIZE);
|
||||
drm_gem_private_object_init(priv->drm, &gobj2, PAGE_SIZE);
|
||||
|
||||
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
|
||||
drm_exec_until_all_locked(&exec)
|
||||
@ -150,6 +179,7 @@ static struct kunit_case drm_exec_tests[] = {
|
||||
|
||||
static struct kunit_suite drm_exec_test_suite = {
|
||||
.name = "drm_exec",
|
||||
.init = drm_exec_test_init,
|
||||
.test_cases = drm_exec_tests,
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
#include <drm/drm_managed.h>
|
||||
@ -26,6 +27,28 @@ static struct platform_driver fake_platform_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static void kunit_action_platform_driver_unregister(void *ptr)
|
||||
{
|
||||
struct platform_driver *drv = ptr;
|
||||
|
||||
platform_driver_unregister(drv);
|
||||
|
||||
}
|
||||
|
||||
static void kunit_action_platform_device_put(void *ptr)
|
||||
{
|
||||
struct platform_device *pdev = ptr;
|
||||
|
||||
platform_device_put(pdev);
|
||||
}
|
||||
|
||||
static void kunit_action_platform_device_del(void *ptr)
|
||||
{
|
||||
struct platform_device *pdev = ptr;
|
||||
|
||||
platform_device_del(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test
|
||||
* @test: The test context object
|
||||
@ -35,8 +58,8 @@ static struct platform_driver fake_platform_driver = {
|
||||
* able to leverage the usual infrastructure and most notably the
|
||||
* device-managed resources just like a "real" device.
|
||||
*
|
||||
* Callers need to make sure drm_kunit_helper_free_device() on the
|
||||
* device when done.
|
||||
* Resources will be cleaned up automatically, but the removal can be
|
||||
* forced using @drm_kunit_helper_free_device.
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to the new device, or an ERR_PTR() otherwise.
|
||||
@ -49,12 +72,27 @@ struct device *drm_kunit_helper_alloc_device(struct kunit *test)
|
||||
ret = platform_driver_register(&fake_platform_driver);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
kunit_action_platform_driver_unregister,
|
||||
&fake_platform_driver);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
kunit_action_platform_device_put,
|
||||
pdev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = platform_device_add(pdev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
kunit_action_platform_device_del,
|
||||
pdev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
return &pdev->dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);
|
||||
@ -70,8 +108,17 @@ void drm_kunit_helper_free_device(struct kunit *test, struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
platform_driver_unregister(&fake_platform_driver);
|
||||
kunit_release_action(test,
|
||||
kunit_action_platform_device_del,
|
||||
pdev);
|
||||
|
||||
kunit_release_action(test,
|
||||
kunit_action_platform_device_put,
|
||||
pdev);
|
||||
|
||||
kunit_release_action(test,
|
||||
kunit_action_platform_driver_unregister,
|
||||
pdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
|
||||
|
||||
@ -100,5 +147,91 @@ __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver);
|
||||
|
||||
static void action_drm_release_context(void *ptr)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx *ctx = ptr;
|
||||
|
||||
drm_modeset_drop_locks(ctx);
|
||||
drm_modeset_acquire_fini(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_kunit_helper_context_alloc - Allocates an acquire context
|
||||
* @test: The test context object
|
||||
*
|
||||
* Allocates and initializes a modeset acquire context.
|
||||
*
|
||||
* The context is tied to the kunit test context, so we must not call
|
||||
* drm_modeset_acquire_fini() on it, it will be done so automatically.
|
||||
*
|
||||
* Returns:
|
||||
* An ERR_PTR on error, a pointer to the newly allocated context otherwise
|
||||
*/
|
||||
struct drm_modeset_acquire_ctx *
|
||||
drm_kunit_helper_acquire_ctx_alloc(struct kunit *test)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx);
|
||||
|
||||
drm_modeset_acquire_init(ctx, 0);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
action_drm_release_context,
|
||||
ctx);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc);
|
||||
|
||||
static void kunit_action_drm_atomic_state_put(void *ptr)
|
||||
{
|
||||
struct drm_atomic_state *state = ptr;
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_kunit_helper_atomic_state_alloc - Allocates an atomic state
|
||||
* @test: The test context object
|
||||
* @drm: The device to alloc the state for
|
||||
* @ctx: Locking context for that atomic update
|
||||
*
|
||||
* Allocates a empty atomic state.
|
||||
*
|
||||
* The state is tied to the kunit test context, so we must not call
|
||||
* drm_atomic_state_put() on it, it will be done so automatically.
|
||||
*
|
||||
* Returns:
|
||||
* An ERR_PTR on error, a pointer to the newly allocated state otherwise
|
||||
*/
|
||||
struct drm_atomic_state *
|
||||
drm_kunit_helper_atomic_state_alloc(struct kunit *test,
|
||||
struct drm_device *drm,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
struct drm_atomic_state *state;
|
||||
int ret;
|
||||
|
||||
state = drm_atomic_state_alloc(drm);
|
||||
if (!state)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
kunit_action_drm_atomic_state_put,
|
||||
state);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
state->acquire_ctx = ctx;
|
||||
|
||||
return state;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc);
|
||||
|
||||
MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -36,13 +36,6 @@ static int drm_test_modes_init(struct kunit *test)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drm_test_modes_exit(struct kunit *test)
|
||||
{
|
||||
struct drm_test_modes_priv *priv = test->priv;
|
||||
|
||||
drm_kunit_helper_free_device(test, priv->dev);
|
||||
}
|
||||
|
||||
static void drm_test_modes_analog_tv_ntsc_480i(struct kunit *test)
|
||||
{
|
||||
struct drm_test_modes_priv *priv = test->priv;
|
||||
@ -148,7 +141,6 @@ static struct kunit_case drm_modes_analog_tv_tests[] = {
|
||||
static struct kunit_suite drm_modes_analog_tv_test_suite = {
|
||||
.name = "drm_modes_analog_tv",
|
||||
.init = drm_test_modes_init,
|
||||
.exit = drm_test_modes_exit,
|
||||
.test_cases = drm_modes_analog_tv_tests,
|
||||
};
|
||||
|
||||
|
@ -60,13 +60,6 @@ static int drm_probe_helper_test_init(struct kunit *test)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drm_probe_helper_test_exit(struct kunit *test)
|
||||
{
|
||||
struct drm_probe_helper_test_priv *priv = test->priv;
|
||||
|
||||
drm_kunit_helper_free_device(test, priv->dev);
|
||||
}
|
||||
|
||||
typedef struct drm_display_mode *(*expected_mode_func_t)(struct drm_device *);
|
||||
|
||||
struct drm_connector_helper_tv_get_modes_test {
|
||||
@ -208,7 +201,6 @@ static struct kunit_case drm_test_connector_helper_tv_get_modes_tests[] = {
|
||||
static struct kunit_suite drm_test_connector_helper_tv_get_modes_suite = {
|
||||
.name = "drm_connector_helper_tv_get_modes",
|
||||
.init = drm_probe_helper_test_init,
|
||||
.exit = drm_probe_helper_test_exit,
|
||||
.test_cases = drm_test_connector_helper_tv_get_modes_tests,
|
||||
};
|
||||
|
||||
|
@ -38,7 +38,8 @@ static int tidss_plane_atomic_check(struct drm_plane *plane,
|
||||
if (!new_plane_state->crtc) {
|
||||
/*
|
||||
* The visible field is not reset by the DRM core but only
|
||||
* updated by drm_plane_helper_check_state(), set it manually.
|
||||
* updated by drm_atomic_helper_check_plane_state(), set it
|
||||
* manually.
|
||||
*/
|
||||
new_plane_state->visible = false;
|
||||
return 0;
|
||||
|
@ -316,19 +316,24 @@ static int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par,
|
||||
u32 speed_hz;
|
||||
int ret;
|
||||
|
||||
spi_bus_lock(spi->controller);
|
||||
gpiod_set_value_cansleep(dbi->dc, 0);
|
||||
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
|
||||
ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
|
||||
spi_bus_unlock(spi->controller);
|
||||
if (ret || !num)
|
||||
return ret;
|
||||
|
||||
if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !dbi->swap_bytes)
|
||||
bpw = 16;
|
||||
|
||||
spi_bus_lock(spi->controller);
|
||||
gpiod_set_value_cansleep(dbi->dc, 1);
|
||||
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
|
||||
ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
|
||||
spi_bus_unlock(spi->controller);
|
||||
|
||||
return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
|
||||
|
@ -59,9 +59,11 @@ static int waveshare_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par,
|
||||
* before being transferred as 8-bit on the big endian SPI bus.
|
||||
*/
|
||||
buf[0] = cpu_to_be16(*cmd);
|
||||
spi_bus_lock(spi->controller);
|
||||
gpiod_set_value_cansleep(mipi->dc, 0);
|
||||
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 2);
|
||||
ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, buf, 2);
|
||||
spi_bus_unlock(spi->controller);
|
||||
if (ret || !num)
|
||||
goto free;
|
||||
|
||||
@ -79,9 +81,11 @@ static int waveshare_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par,
|
||||
if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !mipi->swap_bytes)
|
||||
bpw = 16;
|
||||
|
||||
spi_bus_lock(spi->controller);
|
||||
gpiod_set_value_cansleep(mipi->dc, 1);
|
||||
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
|
||||
ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, data, num);
|
||||
spi_bus_unlock(spi->controller);
|
||||
free:
|
||||
kfree(buf);
|
||||
|
||||
|
@ -307,7 +307,8 @@ static int panel_mipi_dbi_spi_probe(struct spi_device *spi)
|
||||
if (IS_ERR(dbi->reset))
|
||||
return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
|
||||
|
||||
dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
|
||||
/* Multiple panels can share the "dc" GPIO, but only if they are on the same SPI bus! */
|
||||
dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
|
||||
if (IS_ERR(dc))
|
||||
return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n");
|
||||
|
||||
|
@ -533,7 +533,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
|
||||
DRM_DEBUG("Flushing [FB:%d] st=%ums\n", fb->base.id,
|
||||
epd->factored_stage_time);
|
||||
|
||||
buf = kmalloc_array(fb->width, fb->height, GFP_KERNEL);
|
||||
buf = kmalloc(fb->width * fb->height / 8, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto out_exit;
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_edid.h>
|
||||
@ -310,16 +311,6 @@ static const struct drm_plane_funcs udl_primary_plane_funcs = {
|
||||
* CRTC
|
||||
*/
|
||||
|
||||
static int udl_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
|
||||
if (!new_crtc_state->enable)
|
||||
return 0;
|
||||
|
||||
return drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
|
||||
}
|
||||
|
||||
static void udl_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
@ -381,7 +372,7 @@ out:
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs udl_crtc_helper_funcs = {
|
||||
.atomic_check = udl_crtc_helper_atomic_check,
|
||||
.atomic_check = drm_crtc_helper_atomic_check,
|
||||
.atomic_enable = udl_crtc_helper_atomic_enable,
|
||||
.atomic_disable = udl_crtc_helper_atomic_disable,
|
||||
};
|
||||
|
@ -340,7 +340,7 @@ struct v3d_submit_ext {
|
||||
static inline unsigned long nsecs_to_jiffies_timeout(const u64 n)
|
||||
{
|
||||
/* nsecs_to_jiffies64() does not guard against overflow */
|
||||
if (NSEC_PER_SEC % HZ &&
|
||||
if ((NSEC_PER_SEC % HZ) != 0 &&
|
||||
div_u64(n, NSEC_PER_SEC) >= MAX_JIFFY_OFFSET / HZ)
|
||||
return MAX_JIFFY_OFFSET;
|
||||
|
||||
|
@ -153,6 +153,13 @@ static int __build_mock(struct kunit *test, struct drm_device *drm,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kunit_action_drm_dev_unregister(void *ptr)
|
||||
{
|
||||
struct drm_device *drm = ptr;
|
||||
|
||||
drm_dev_unregister(drm);
|
||||
}
|
||||
|
||||
static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
|
||||
{
|
||||
struct drm_device *drm;
|
||||
@ -186,6 +193,11 @@ static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
|
||||
ret = drm_dev_register(drm, 0);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
kunit_action_drm_dev_unregister,
|
||||
drm);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
return vc4;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
struct pv_muxing_priv {
|
||||
struct vc4_dev *vc4;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_atomic_state *state;
|
||||
};
|
||||
|
||||
@ -725,7 +724,7 @@ static void drm_vc4_test_pv_muxing_invalid(struct kunit *test)
|
||||
static int vc4_pv_muxing_test_init(struct kunit *test)
|
||||
{
|
||||
const struct pv_muxing_param *params = test->param_value;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct pv_muxing_priv *priv;
|
||||
struct drm_device *drm;
|
||||
struct vc4_dev *vc4;
|
||||
@ -738,33 +737,16 @@ static int vc4_pv_muxing_test_init(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
|
||||
priv->vc4 = vc4;
|
||||
|
||||
drm_modeset_acquire_init(&priv->ctx, 0);
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
drm = &vc4->base;
|
||||
state = drm_atomic_state_alloc(drm);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
state->acquire_ctx = &priv->ctx;
|
||||
|
||||
priv->state = state;
|
||||
priv->state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vc4_pv_muxing_test_exit(struct kunit *test)
|
||||
{
|
||||
struct pv_muxing_priv *priv = test->priv;
|
||||
struct vc4_dev *vc4 = priv->vc4;
|
||||
struct drm_device *drm = &vc4->base;
|
||||
struct drm_atomic_state *state = priv->state;
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
drm_modeset_drop_locks(&priv->ctx);
|
||||
drm_modeset_acquire_fini(&priv->ctx);
|
||||
drm_dev_unregister(drm);
|
||||
drm_kunit_helper_free_device(test, vc4->dev);
|
||||
}
|
||||
|
||||
static struct kunit_case vc4_pv_muxing_tests[] = {
|
||||
KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing,
|
||||
vc4_test_pv_muxing_gen_params),
|
||||
@ -776,7 +758,6 @@ static struct kunit_case vc4_pv_muxing_tests[] = {
|
||||
static struct kunit_suite vc4_pv_muxing_test_suite = {
|
||||
.name = "vc4-pv-muxing-combinations",
|
||||
.init = vc4_pv_muxing_test_init,
|
||||
.exit = vc4_pv_muxing_test_exit,
|
||||
.test_cases = vc4_pv_muxing_tests,
|
||||
};
|
||||
|
||||
@ -791,7 +772,6 @@ static struct kunit_case vc5_pv_muxing_tests[] = {
|
||||
static struct kunit_suite vc5_pv_muxing_test_suite = {
|
||||
.name = "vc5-pv-muxing-combinations",
|
||||
.init = vc4_pv_muxing_test_init,
|
||||
.exit = vc4_pv_muxing_test_exit,
|
||||
.test_cases = vc5_pv_muxing_tests,
|
||||
};
|
||||
|
||||
@ -802,7 +782,7 @@ static struct kunit_suite vc5_pv_muxing_test_suite = {
|
||||
*/
|
||||
static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *test)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_atomic_state *state;
|
||||
struct vc4_crtc_state *new_vc4_crtc_state;
|
||||
struct vc4_hvs_state *new_hvs_state;
|
||||
@ -815,14 +795,13 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes
|
||||
vc4 = vc5_mock_device(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
drm = &vc4->base;
|
||||
state = drm_atomic_state_alloc(drm);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
state->acquire_ctx = &ctx;
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
@ -843,13 +822,9 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes
|
||||
ret = drm_atomic_helper_swap_state(state, false);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
|
||||
state = drm_atomic_state_alloc(drm);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
state->acquire_ctx = &ctx;
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
@ -868,17 +843,18 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes
|
||||
KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use);
|
||||
|
||||
KUNIT_EXPECT_NE(test, hdmi0_channel, hdmi1_channel);
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
drm_dev_unregister(drm);
|
||||
drm_kunit_helper_free_device(test, vc4->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test makes sure that we never change the FIFO of an active HVS
|
||||
* channel if we disable a FIFO with a lower index.
|
||||
*
|
||||
* Doing so would result in a FIFO stall and would disrupt an output
|
||||
* supposed to be unaffected by the commit.
|
||||
*/
|
||||
static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_atomic_state *state;
|
||||
struct vc4_crtc_state *new_vc4_crtc_state;
|
||||
struct vc4_hvs_state *new_hvs_state;
|
||||
@ -891,14 +867,13 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
vc4 = vc5_mock_device(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
drm = &vc4->base;
|
||||
state = drm_atomic_state_alloc(drm);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
state->acquire_ctx = &ctx;
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
@ -930,13 +905,9 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
ret = drm_atomic_helper_swap_state(state, false);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
|
||||
state = drm_atomic_state_alloc(drm);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
state->acquire_ctx = &ctx;
|
||||
|
||||
ret = vc4_mock_atomic_del_output(test, state, VC4_ENCODER_TYPE_HDMI0);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
@ -958,18 +929,27 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
|
||||
KUNIT_EXPECT_EQ(test, old_hdmi1_channel, hdmi1_channel);
|
||||
}
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
drm_dev_unregister(drm);
|
||||
drm_kunit_helper_free_device(test, vc4->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that if we affect a single output, only the CRTC state of that
|
||||
* output will be pulled in the global atomic state.
|
||||
*
|
||||
* This is relevant for two things:
|
||||
*
|
||||
* - If we don't have that state at all, we are unlikely to affect the
|
||||
* FIFO muxing. This is somewhat redundant with
|
||||
* drm_test_vc5_pv_muxing_bugs_stable_fifo()
|
||||
*
|
||||
* - KMS waits for page flips to occur on all the CRTC found in the
|
||||
* CRTC state. Since the CRTC is unaffected, we would over-wait, but
|
||||
* most importantly run into corner cases like waiting on an
|
||||
* inactive CRTC that never completes.
|
||||
*/
|
||||
static void
|
||||
drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct kunit *test)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_atomic_state *state;
|
||||
struct vc4_crtc_state *new_vc4_crtc_state;
|
||||
struct drm_device *drm;
|
||||
@ -979,14 +959,13 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku
|
||||
vc4 = vc5_mock_device(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
drm = &vc4->base;
|
||||
state = drm_atomic_state_alloc(drm);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
state->acquire_ctx = &ctx;
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
@ -996,13 +975,9 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku
|
||||
ret = drm_atomic_helper_swap_state(state, false);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
|
||||
state = drm_atomic_state_alloc(drm);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
state->acquire_ctx = &ctx;
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
@ -1012,12 +987,6 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku
|
||||
new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
|
||||
VC4_ENCODER_TYPE_HDMI0);
|
||||
KUNIT_EXPECT_NULL(test, new_vc4_crtc_state);
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
drm_dev_unregister(drm);
|
||||
drm_kunit_helper_free_device(test, vc4->dev);
|
||||
}
|
||||
|
||||
static struct kunit_case vc5_pv_muxing_bugs_tests[] = {
|
||||
|
@ -176,7 +176,8 @@ static const struct drm_driver driver = {
|
||||
* If KMS is disabled DRIVER_MODESET and DRIVER_ATOMIC are masked
|
||||
* out via drm_device::driver_features:
|
||||
*/
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_RENDER | DRIVER_ATOMIC,
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_RENDER | DRIVER_ATOMIC |
|
||||
DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE,
|
||||
.open = virtio_gpu_driver_open,
|
||||
.postclose = virtio_gpu_driver_postclose,
|
||||
|
||||
|
@ -14,11 +14,24 @@
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_syncobj.h>
|
||||
#include <drm/virtgpu_drm.h>
|
||||
|
||||
#include "virtgpu_drv.h"
|
||||
|
||||
struct virtio_gpu_submit_post_dep {
|
||||
struct drm_syncobj *syncobj;
|
||||
struct dma_fence_chain *chain;
|
||||
u64 point;
|
||||
};
|
||||
|
||||
struct virtio_gpu_submit {
|
||||
struct virtio_gpu_submit_post_dep *post_deps;
|
||||
unsigned int num_out_syncobjs;
|
||||
|
||||
struct drm_syncobj **in_syncobjs;
|
||||
unsigned int num_in_syncobjs;
|
||||
|
||||
struct virtio_gpu_object_array *buflist;
|
||||
struct drm_virtgpu_execbuffer *exbuf;
|
||||
struct virtio_gpu_fence *out_fence;
|
||||
@ -59,6 +72,203 @@ static int virtio_gpu_dma_fence_wait(struct virtio_gpu_submit *submit,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtio_gpu_free_syncobjs(struct drm_syncobj **syncobjs,
|
||||
u32 nr_syncobjs)
|
||||
{
|
||||
u32 i = nr_syncobjs;
|
||||
|
||||
while (i--) {
|
||||
if (syncobjs[i])
|
||||
drm_syncobj_put(syncobjs[i]);
|
||||
}
|
||||
|
||||
kvfree(syncobjs);
|
||||
}
|
||||
|
||||
static int
|
||||
virtio_gpu_parse_deps(struct virtio_gpu_submit *submit)
|
||||
{
|
||||
struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
|
||||
struct drm_virtgpu_execbuffer_syncobj syncobj_desc;
|
||||
size_t syncobj_stride = exbuf->syncobj_stride;
|
||||
u32 num_in_syncobjs = exbuf->num_in_syncobjs;
|
||||
struct drm_syncobj **syncobjs;
|
||||
int ret = 0, i;
|
||||
|
||||
if (!num_in_syncobjs)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* kvalloc at first tries to allocate memory using kmalloc and
|
||||
* falls back to vmalloc only on failure. It also uses __GFP_NOWARN
|
||||
* internally for allocations larger than a page size, preventing
|
||||
* storm of KMSG warnings.
|
||||
*/
|
||||
syncobjs = kvcalloc(num_in_syncobjs, sizeof(*syncobjs), GFP_KERNEL);
|
||||
if (!syncobjs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_in_syncobjs; i++) {
|
||||
u64 address = exbuf->in_syncobjs + i * syncobj_stride;
|
||||
struct dma_fence *fence;
|
||||
|
||||
memset(&syncobj_desc, 0, sizeof(syncobj_desc));
|
||||
|
||||
if (copy_from_user(&syncobj_desc,
|
||||
u64_to_user_ptr(address),
|
||||
min(syncobj_stride, sizeof(syncobj_desc)))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (syncobj_desc.flags & ~VIRTGPU_EXECBUF_SYNCOBJ_FLAGS) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = drm_syncobj_find_fence(submit->file, syncobj_desc.handle,
|
||||
syncobj_desc.point, 0, &fence);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = virtio_gpu_dma_fence_wait(submit, fence);
|
||||
|
||||
dma_fence_put(fence);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (syncobj_desc.flags & VIRTGPU_EXECBUF_SYNCOBJ_RESET) {
|
||||
syncobjs[i] = drm_syncobj_find(submit->file,
|
||||
syncobj_desc.handle);
|
||||
if (!syncobjs[i]) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
virtio_gpu_free_syncobjs(syncobjs, i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
submit->num_in_syncobjs = num_in_syncobjs;
|
||||
submit->in_syncobjs = syncobjs;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void virtio_gpu_reset_syncobjs(struct drm_syncobj **syncobjs,
|
||||
u32 nr_syncobjs)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < nr_syncobjs; i++) {
|
||||
if (syncobjs[i])
|
||||
drm_syncobj_replace_fence(syncobjs[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
virtio_gpu_free_post_deps(struct virtio_gpu_submit_post_dep *post_deps,
|
||||
u32 nr_syncobjs)
|
||||
{
|
||||
u32 i = nr_syncobjs;
|
||||
|
||||
while (i--) {
|
||||
kfree(post_deps[i].chain);
|
||||
drm_syncobj_put(post_deps[i].syncobj);
|
||||
}
|
||||
|
||||
kvfree(post_deps);
|
||||
}
|
||||
|
||||
static int virtio_gpu_parse_post_deps(struct virtio_gpu_submit *submit)
|
||||
{
|
||||
struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
|
||||
struct drm_virtgpu_execbuffer_syncobj syncobj_desc;
|
||||
struct virtio_gpu_submit_post_dep *post_deps;
|
||||
u32 num_out_syncobjs = exbuf->num_out_syncobjs;
|
||||
size_t syncobj_stride = exbuf->syncobj_stride;
|
||||
int ret = 0, i;
|
||||
|
||||
if (!num_out_syncobjs)
|
||||
return 0;
|
||||
|
||||
post_deps = kvcalloc(num_out_syncobjs, sizeof(*post_deps), GFP_KERNEL);
|
||||
if (!post_deps)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_out_syncobjs; i++) {
|
||||
u64 address = exbuf->out_syncobjs + i * syncobj_stride;
|
||||
|
||||
memset(&syncobj_desc, 0, sizeof(syncobj_desc));
|
||||
|
||||
if (copy_from_user(&syncobj_desc,
|
||||
u64_to_user_ptr(address),
|
||||
min(syncobj_stride, sizeof(syncobj_desc)))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
post_deps[i].point = syncobj_desc.point;
|
||||
|
||||
if (syncobj_desc.flags) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (syncobj_desc.point) {
|
||||
post_deps[i].chain = dma_fence_chain_alloc();
|
||||
if (!post_deps[i].chain) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
post_deps[i].syncobj = drm_syncobj_find(submit->file,
|
||||
syncobj_desc.handle);
|
||||
if (!post_deps[i].syncobj) {
|
||||
kfree(post_deps[i].chain);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
virtio_gpu_free_post_deps(post_deps, i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
submit->num_out_syncobjs = num_out_syncobjs;
|
||||
submit->post_deps = post_deps;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
virtio_gpu_process_post_deps(struct virtio_gpu_submit *submit)
|
||||
{
|
||||
struct virtio_gpu_submit_post_dep *post_deps = submit->post_deps;
|
||||
|
||||
if (post_deps) {
|
||||
struct dma_fence *fence = &submit->out_fence->f;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < submit->num_out_syncobjs; i++) {
|
||||
if (post_deps[i].chain) {
|
||||
drm_syncobj_add_point(post_deps[i].syncobj,
|
||||
post_deps[i].chain,
|
||||
fence, post_deps[i].point);
|
||||
post_deps[i].chain = NULL;
|
||||
} else {
|
||||
drm_syncobj_replace_fence(post_deps[i].syncobj,
|
||||
fence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int virtio_gpu_fence_event_create(struct drm_device *dev,
|
||||
struct drm_file *file,
|
||||
struct virtio_gpu_fence *fence,
|
||||
@ -118,6 +328,10 @@ static int virtio_gpu_init_submit_buflist(struct virtio_gpu_submit *submit)
|
||||
|
||||
static void virtio_gpu_cleanup_submit(struct virtio_gpu_submit *submit)
|
||||
{
|
||||
virtio_gpu_reset_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs);
|
||||
virtio_gpu_free_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs);
|
||||
virtio_gpu_free_post_deps(submit->post_deps, submit->num_out_syncobjs);
|
||||
|
||||
if (!IS_ERR(submit->buf))
|
||||
kvfree(submit->buf);
|
||||
|
||||
@ -172,6 +386,7 @@ static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit,
|
||||
drm_fence_event = false;
|
||||
|
||||
if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) ||
|
||||
exbuf->num_out_syncobjs ||
|
||||
exbuf->num_bo_handles ||
|
||||
drm_fence_event)
|
||||
out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx);
|
||||
@ -291,6 +506,14 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
ret = virtio_gpu_parse_post_deps(&submit);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
ret = virtio_gpu_parse_deps(&submit);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
/*
|
||||
* Await in-fences in the end of the job submission path to
|
||||
* optimize the path by proceeding directly to the submission
|
||||
@ -311,6 +534,7 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
|
||||
* the job submission path.
|
||||
*/
|
||||
virtio_gpu_install_out_fence_fd(&submit);
|
||||
virtio_gpu_process_post_deps(&submit);
|
||||
virtio_gpu_complete_submit(&submit);
|
||||
cleanup:
|
||||
virtio_gpu_cleanup_submit(&submit);
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_blend.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_fixed.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
#include <linux/minmax.h>
|
||||
@ -89,6 +90,73 @@ static void fill_background(const struct pixel_argb_u16 *background_color,
|
||||
output_buffer->pixels[i] = *background_color;
|
||||
}
|
||||
|
||||
// lerp(a, b, t) = a + (b - a) * t
|
||||
static u16 lerp_u16(u16 a, u16 b, s64 t)
|
||||
{
|
||||
s64 a_fp = drm_int2fixp(a);
|
||||
s64 b_fp = drm_int2fixp(b);
|
||||
|
||||
s64 delta = drm_fixp_mul(b_fp - a_fp, t);
|
||||
|
||||
return drm_fixp2int(a_fp + delta);
|
||||
}
|
||||
|
||||
static s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
|
||||
{
|
||||
s64 color_channel_fp = drm_int2fixp(channel_value);
|
||||
|
||||
return drm_fixp_mul(color_channel_fp, lut->channel_value2index_ratio);
|
||||
}
|
||||
|
||||
/*
|
||||
* This enum is related to the positions of the variables inside
|
||||
* `struct drm_color_lut`, so the order of both needs to be the same.
|
||||
*/
|
||||
enum lut_channel {
|
||||
LUT_RED = 0,
|
||||
LUT_GREEN,
|
||||
LUT_BLUE,
|
||||
LUT_RESERVED
|
||||
};
|
||||
|
||||
static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
|
||||
enum lut_channel channel)
|
||||
{
|
||||
s64 lut_index = get_lut_index(lut, channel_value);
|
||||
|
||||
/*
|
||||
* This checks if `struct drm_color_lut` has any gap added by the compiler
|
||||
* between the struct fields.
|
||||
*/
|
||||
static_assert(sizeof(struct drm_color_lut) == sizeof(__u16) * 4);
|
||||
|
||||
u16 *floor_lut_value = (__u16 *)&lut->base[drm_fixp2int(lut_index)];
|
||||
u16 *ceil_lut_value = (__u16 *)&lut->base[drm_fixp2int_ceil(lut_index)];
|
||||
|
||||
u16 floor_channel_value = floor_lut_value[channel];
|
||||
u16 ceil_channel_value = ceil_lut_value[channel];
|
||||
|
||||
return lerp_u16(floor_channel_value, ceil_channel_value,
|
||||
lut_index & DRM_FIXED_DECIMAL_MASK);
|
||||
}
|
||||
|
||||
static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buffer *output_buffer)
|
||||
{
|
||||
if (!crtc_state->gamma_lut.base)
|
||||
return;
|
||||
|
||||
if (!crtc_state->gamma_lut.lut_length)
|
||||
return;
|
||||
|
||||
for (size_t x = 0; x < output_buffer->n_pixels; x++) {
|
||||
struct pixel_argb_u16 *pixel = &output_buffer->pixels[x];
|
||||
|
||||
pixel->r = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->r, LUT_RED);
|
||||
pixel->g = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->g, LUT_GREEN);
|
||||
pixel->b = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->b, LUT_BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* blend - blend the pixels from all planes and compute crc
|
||||
* @wb: The writeback frame buffer metadata
|
||||
@ -130,6 +198,8 @@ static void blend(struct vkms_writeback_job *wb,
|
||||
output_buffer);
|
||||
}
|
||||
|
||||
apply_lut(crtc_state, output_buffer);
|
||||
|
||||
*crc32 = crc32_le(*crc32, (void *)output_buffer->pixels, row_size);
|
||||
|
||||
if (wb)
|
||||
@ -244,6 +314,22 @@ void vkms_composer_worker(struct work_struct *work)
|
||||
crtc_state->frame_start = 0;
|
||||
crtc_state->frame_end = 0;
|
||||
crtc_state->crc_pending = false;
|
||||
|
||||
if (crtc->state->gamma_lut) {
|
||||
s64 max_lut_index_fp;
|
||||
s64 u16_max_fp = drm_int2fixp(0xffff);
|
||||
|
||||
crtc_state->gamma_lut.base = (struct drm_color_lut *)crtc->state->gamma_lut->data;
|
||||
crtc_state->gamma_lut.lut_length =
|
||||
crtc->state->gamma_lut->length / sizeof(struct drm_color_lut);
|
||||
max_lut_index_fp = drm_int2fixp(crtc_state->gamma_lut.lut_length - 1);
|
||||
crtc_state->gamma_lut.channel_value2index_ratio = drm_fixp_div(max_lut_index_fp,
|
||||
u16_max_fp);
|
||||
|
||||
} else {
|
||||
crtc_state->gamma_lut.base = NULL;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&out->composer_lock);
|
||||
|
||||
/*
|
||||
@ -322,10 +408,15 @@ void vkms_set_composer(struct vkms_output *out, bool enabled)
|
||||
if (enabled)
|
||||
drm_crtc_vblank_get(&out->crtc);
|
||||
|
||||
spin_lock_irq(&out->lock);
|
||||
mutex_lock(&out->enabled_lock);
|
||||
old_enabled = out->composer_enabled;
|
||||
out->composer_enabled = enabled;
|
||||
spin_unlock_irq(&out->lock);
|
||||
|
||||
/* the composition wasn't enabled, so unlock the lock to make sure the lock
|
||||
* will be balanced even if we have a failed commit
|
||||
*/
|
||||
if (!out->composer_enabled)
|
||||
mutex_unlock(&out->enabled_lock);
|
||||
|
||||
if (old_enabled)
|
||||
drm_crtc_vblank_put(&out->crtc);
|
||||
|
@ -16,7 +16,7 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
|
||||
struct drm_crtc *crtc = &output->crtc;
|
||||
struct vkms_crtc_state *state;
|
||||
u64 ret_overrun;
|
||||
bool ret, fence_cookie;
|
||||
bool ret, fence_cookie, composer_enabled;
|
||||
|
||||
fence_cookie = dma_fence_begin_signalling();
|
||||
|
||||
@ -25,15 +25,15 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
|
||||
if (ret_overrun != 1)
|
||||
pr_warn("%s: vblank timer overrun\n", __func__);
|
||||
|
||||
spin_lock(&output->lock);
|
||||
ret = drm_crtc_handle_vblank(crtc);
|
||||
if (!ret)
|
||||
DRM_ERROR("vkms failure on handling vblank");
|
||||
|
||||
state = output->composer_state;
|
||||
spin_unlock(&output->lock);
|
||||
composer_enabled = output->composer_enabled;
|
||||
mutex_unlock(&output->enabled_lock);
|
||||
|
||||
if (state && output->composer_enabled) {
|
||||
if (state && composer_enabled) {
|
||||
u64 frame = drm_crtc_accurate_vblank_count(crtc);
|
||||
|
||||
/* update frame_start only if a queued vkms_composer_worker()
|
||||
@ -290,8 +290,12 @@ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
|
||||
|
||||
drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs);
|
||||
|
||||
drm_mode_crtc_set_gamma_size(crtc, VKMS_LUT_SIZE);
|
||||
drm_crtc_enable_color_mgmt(crtc, 0, false, VKMS_LUT_SIZE);
|
||||
|
||||
spin_lock_init(&vkms_out->lock);
|
||||
spin_lock_init(&vkms_out->composer_lock);
|
||||
mutex_init(&vkms_out->enabled_lock);
|
||||
|
||||
vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0);
|
||||
if (!vkms_out->composer_workq)
|
||||
|
@ -120,9 +120,27 @@ static const struct drm_driver vkms_driver = {
|
||||
.minor = DRIVER_MINOR,
|
||||
};
|
||||
|
||||
static int vkms_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
int i;
|
||||
|
||||
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||
if (!new_crtc_state->gamma_lut || !new_crtc_state->color_mgmt_changed)
|
||||
continue;
|
||||
|
||||
if (new_crtc_state->gamma_lut->length / sizeof(struct drm_color_lut *)
|
||||
> VKMS_LUT_SIZE)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return drm_atomic_helper_check(dev, state);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs vkms_mode_funcs = {
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_check = vkms_atomic_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#define NUM_OVERLAY_PLANES 8
|
||||
|
||||
#define VKMS_LUT_SIZE 256
|
||||
|
||||
struct vkms_frame_info {
|
||||
struct drm_framebuffer *fb;
|
||||
struct drm_rect src, dst;
|
||||
@ -64,6 +66,12 @@ struct vkms_plane {
|
||||
struct drm_plane base;
|
||||
};
|
||||
|
||||
struct vkms_color_lut {
|
||||
struct drm_color_lut *base;
|
||||
size_t lut_length;
|
||||
s64 channel_value2index_ratio;
|
||||
};
|
||||
|
||||
/**
|
||||
* vkms_crtc_state - Driver specific CRTC state
|
||||
* @base: base CRTC state
|
||||
@ -79,6 +87,7 @@ struct vkms_crtc_state {
|
||||
/* stack of active planes for crc computation, should be in z order */
|
||||
struct vkms_plane_state **active_planes;
|
||||
struct vkms_writeback_job *active_writeback;
|
||||
struct vkms_color_lut gamma_lut;
|
||||
|
||||
/* below four are protected by vkms_output.composer_lock */
|
||||
bool crc_pending;
|
||||
@ -99,8 +108,10 @@ struct vkms_output {
|
||||
struct workqueue_struct *composer_workq;
|
||||
/* protects concurrent access to composer */
|
||||
spinlock_t lock;
|
||||
/* guarantees that if the composer is enabled, a job will be queued */
|
||||
struct mutex enabled_lock;
|
||||
|
||||
/* protected by @lock */
|
||||
/* protected by @enabled_lock */
|
||||
bool composer_enabled;
|
||||
struct vkms_crtc_state *composer_state;
|
||||
|
||||
|
@ -79,6 +79,14 @@ int host1x_memory_context_list_init(struct host1x *host1x)
|
||||
!device_iommu_mapped(&ctx->dev)) {
|
||||
dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i);
|
||||
device_unregister(&ctx->dev);
|
||||
|
||||
/*
|
||||
* This means that if IOMMU is disabled but context devices
|
||||
* are defined in the device tree, Host1x will fail to probe.
|
||||
* That's probably OK in this time and age.
|
||||
*/
|
||||
err = -EINVAL;
|
||||
|
||||
goto unreg_devices;
|
||||
}
|
||||
}
|
||||
|
@ -271,15 +271,13 @@ u32 ipu_pre_get_baddr(struct ipu_pre *pre)
|
||||
static int ipu_pre_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct ipu_pre *pre;
|
||||
|
||||
pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL);
|
||||
if (!pre)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pre->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
pre->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(pre->regs))
|
||||
return PTR_ERR(pre->regs);
|
||||
|
||||
|
@ -358,7 +358,6 @@ EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending);
|
||||
static int ipu_prg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct ipu_prg *prg;
|
||||
u32 val;
|
||||
int i, ret;
|
||||
@ -367,12 +366,10 @@ static int ipu_prg_probe(struct platform_device *pdev)
|
||||
if (!prg)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
prg->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
prg->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(prg->regs))
|
||||
return PTR_ERR(prg->regs);
|
||||
|
||||
|
||||
prg->clk_ipg = devm_clk_get(dev, "ipg");
|
||||
if (IS_ERR(prg->clk_ipg))
|
||||
return PTR_ERR(prg->clk_ipg);
|
||||
|
@ -9,6 +9,7 @@ if I2C_HID
|
||||
config I2C_HID_ACPI
|
||||
tristate "HID over I2C transport layer ACPI driver"
|
||||
depends on ACPI
|
||||
depends on DRM || !DRM
|
||||
select I2C_HID_CORE
|
||||
help
|
||||
Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
|
||||
@ -25,6 +26,7 @@ config I2C_HID_OF
|
||||
tristate "HID over I2C transport layer Open Firmware driver"
|
||||
# No "depends on OF" because this can also be used for manually
|
||||
# (board-file) instantiated "hid-over-i2c" type i2c-clients.
|
||||
depends on DRM || !DRM
|
||||
select I2C_HID_CORE
|
||||
help
|
||||
Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
|
||||
@ -41,6 +43,7 @@ config I2C_HID_OF
|
||||
config I2C_HID_OF_ELAN
|
||||
tristate "Driver for Elan hid-i2c based devices on OF systems"
|
||||
depends on OF
|
||||
depends on DRM || !DRM
|
||||
select I2C_HID_CORE
|
||||
help
|
||||
Say Y here if you want support for Elan i2c devices that use
|
||||
@ -56,6 +59,7 @@ config I2C_HID_OF_ELAN
|
||||
config I2C_HID_OF_GOODIX
|
||||
tristate "Driver for Goodix hid-i2c based devices on OF systems"
|
||||
depends on OF
|
||||
depends on DRM || !DRM
|
||||
select I2C_HID_CORE
|
||||
help
|
||||
Say Y here if you want support for Goodix i2c devices that use
|
||||
@ -70,5 +74,7 @@ config I2C_HID_OF_GOODIX
|
||||
|
||||
config I2C_HID_CORE
|
||||
tristate
|
||||
# We need to call into panel code so if DRM=m, this can't be 'y'
|
||||
depends on DRM || !DRM
|
||||
endif
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user