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:
Dave Airlie 2023-08-07 11:00:32 +10:00
commit ca9e70f527
124 changed files with 2782 additions and 549 deletions

View File

@ -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:

View File

@ -18,6 +18,7 @@ properties:
- enum:
- bananapi,lhr050h41
- feixin,k101-im2byl02
- tdo,tl050hdv35
- wanchanglong,w552946aba
- const: ilitek,ili9881c

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>;
};
};
};
};
...

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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,.*":

View File

@ -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
===========================

View File

@ -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>

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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,
};

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,
};

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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)
{

View File

@ -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");

View File

@ -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)

View File

@ -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)) {

View File

@ -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,
};

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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,
};

View File

@ -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

View File

@ -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,
};

View File

@ -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

View File

@ -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)
{

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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"),

View File

@ -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 },
{ }
};

View File

@ -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;

View File

@ -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,

View 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");

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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,
};

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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");

View File

@ -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,
};

View File

@ -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:

View File

@ -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:

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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
};

View File

@ -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,
};

View File

@ -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");

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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;

View File

@ -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 = {

View File

@ -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);

View File

@ -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");

View File

@ -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;

View File

@ -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,
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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[] = {

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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,
};

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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