drm-misc-next for v5.20:

UAPI Changes:
 
  * fourcc: Update documentation
 
 Cross-subsystem Changes:
 
  * iosys-map: Rework iosys_map_{rd,wr} for improved performance
 
  * vfio: Use aperture helpers
 
 Core Changes:
 
  * aperture: Export for use with other subsystems
 
  * connector: Remove deprecated ida_simple_get()
 
  * crtc: Add helper with general state checks, convert drivers
 
  * format-helper: Add Kunit tests for RGB32 to RGB8
 
 Driver Changes:
 
  * ast: Fix black screen on resume
 
  * bridge: tc358767: Simplify DSI lane handling
 
  * mcde: Fix ref-count leak
 
  * mxsfb/lcdif: Support i.MX8MP LCD controller
 
  * stm/ltdc: Support dynamic Z order; Support mirroring; Fixes and cleanups
 
  * vc4: Many small fixes throughout the driver
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmK9TnYACgkQaA3BHVML
 eiMJcgf+JsGWlFutkxlJCEUDKTXk6BYHQL4czyskDvpBoLrdU1tyrAfKKtqP5k+0
 SMvS6h1CFa/fSUCYpbdpJ6ER1fZ9r19WdgoPTBc4b97/uQTOJDzd5zuHDiJZquwC
 O6HD/rptUzPFe6HJuY2cYVtwMlWb2NhITMHfctgyeQJSMK8TwoU1bDVFftwxaWFt
 ISscTz0enn38sCjEarSpyKkBCinuaWDcpe5BI2Dp3imkDWR3ktzuh4B11QWS0DKs
 Q/FLGTEl1sDrV7r93WiA5BIAPVwNMm1Pl0syd1p42SNLNnv0gcap4GL6qni4h9Ev
 P/3fIInor/Sht8fyhlFsOUA8k2x7MA==
 =6NoJ
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2022-06-30' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for v5.20:

UAPI Changes:

 * fourcc: Update documentation

Cross-subsystem Changes:

 * iosys-map: Rework iosys_map_{rd,wr} for improved performance

 * vfio: Use aperture helpers

Core Changes:

 * aperture: Export for use with other subsystems

 * connector: Remove deprecated ida_simple_get()

 * crtc: Add helper with general state checks, convert drivers

 * format-helper: Add Kunit tests for RGB32 to RGB8

Driver Changes:

 * ast: Fix black screen on resume

 * bridge: tc358767: Simplify DSI lane handling

 * mcde: Fix ref-count leak

 * mxsfb/lcdif: Support i.MX8MP LCD controller

 * stm/ltdc: Support dynamic Z order; Support mirroring; Fixes and cleanups

 * vc4: Many small fixes throughout the driver

Signed-off-by: Dave Airlie <airlied@redhat.com>

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmK9TnYACgkQaA3BHVML
# eiMJcgf+JsGWlFutkxlJCEUDKTXk6BYHQL4czyskDvpBoLrdU1tyrAfKKtqP5k+0
# SMvS6h1CFa/fSUCYpbdpJ6ER1fZ9r19WdgoPTBc4b97/uQTOJDzd5zuHDiJZquwC
# O6HD/rptUzPFe6HJuY2cYVtwMlWb2NhITMHfctgyeQJSMK8TwoU1bDVFftwxaWFt
# ISscTz0enn38sCjEarSpyKkBCinuaWDcpe5BI2Dp3imkDWR3ktzuh4B11QWS0DKs
# Q/FLGTEl1sDrV7r93WiA5BIAPVwNMm1Pl0syd1p42SNLNnv0gcap4GL6qni4h9Ev
# P/3fIInor/Sht8fyhlFsOUA8k2x7MA==
# =6NoJ
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 30 Jun 2022 17:19:18 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/Yr1On+eT1mCvIMzW@linux-uq9g
This commit is contained in:
Dave Airlie 2022-07-01 09:52:36 +10:00
commit f929217499
75 changed files with 3244 additions and 564 deletions

View File

@ -20,6 +20,7 @@ properties:
- fsl,imx23-lcdif
- fsl,imx28-lcdif
- fsl,imx6sx-lcdif
- fsl,imx8mp-lcdif
- items:
- enum:
- fsl,imx6sl-lcdif

View File

@ -0,0 +1,74 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/ebbg,ft8719.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: EBBG FT8719 MIPI-DSI LCD panel
maintainers:
- Joel Selvaraj <jo@jsfamily.in>
description: |
The FT8719 panel from EBBG is a FHD+ LCD display panel with a resolution
of 1080x2246. It is a video mode DSI panel. The backlight is managed
through the QCOM WLED driver.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
const: ebbg,ft8719
reg:
maxItems: 1
description: DSI virtual channel of the peripheral
vddio-supply:
description: power IC supply regulator
vddpos-supply:
description: positive boost supply regulator
vddneg-supply:
description: negative boost supply regulator
required:
- compatible
- reg
- vddio-supply
- vddpos-supply
- vddneg-supply
- reset-gpios
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "ebbg,ft8719";
reg = <0>;
vddio-supply = <&vreg_l14a_1p88>;
vddpos-supply = <&lab>;
vddneg-supply = <&ibb>;
reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>;
backlight = <&pmi8998_wled>;
port {
ebbg_ft8719_in_0: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};

View File

@ -35,6 +35,8 @@ properties:
- ampire,am-480272h3tmqw-t01h
# Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel
- ampire,am800480r3tmqwa1h
# Ampire AM-800600P5TMQW-TB8H 8.0" SVGA TFT LCD panel
- ampire,am800600p5tmqw-tb8h
# AU Optronics Corporation 10.1" WSVGA TFT LCD panel
- auo,b101aw03
# AU Optronics Corporation 10.1" WSVGA TFT LCD panel
@ -107,6 +109,8 @@ properties:
- chunghwa,claa101wb03
# DataImage, Inc. 4.3" WQVGA (480x272) TFT LCD panel with 24-bit parallel interface.
- dataimage,fg040346dsswbg04
# DataImage, Inc. 10.1" WXGA (1280×800) TFT LCD panel
- dataimage,fg1001l0dsswmg01
# DataImage, Inc. 7" WVGA (800x480) TFT LCD panel with 24-bit parallel interface.
- dataimage,scf0700c48ggu18
# DLC Display Co. DLC1010GIG 10.1" WXGA TFT LCD Panel

View File

@ -350,6 +350,8 @@ patternProperties:
description: Embedded Artists AB
"^ebang,.*":
description: Zhejiang Ebang Communication Co., Ltd
"^ebbg,.*":
description: EBBG
"^ebs-systart,.*":
description: EBS-SYSTART GmbH
"^ebv,.*":

View File

@ -0,0 +1,13 @@
.. SPDX-License-Identifier: GPL-2.0
Managing Ownership of the Framebuffer Aperture
==============================================
.. kernel-doc:: drivers/video/aperture.c
:doc: overview
.. kernel-doc:: include/linux/aperture.h
:internal:
.. kernel-doc:: drivers/video/aperture.c
:export:

View File

@ -27,6 +27,7 @@ available subsections can be seen below.
component
message-based
infiniband
aperture
frame-buffer
regulator
reset

View File

@ -207,6 +207,38 @@ Utilities
:internal:
Unit testing
============
KUnit
-----
KUnit (Kernel unit testing framework) provides a common framework for unit tests
within the Linux kernel.
This section covers the specifics for the DRM subsystem. For general information
about KUnit, please refer to Documentation/dev-tools/kunit/start.rst.
How to run the tests?
~~~~~~~~~~~~~~~~~~~~~
In order to facilitate running the test suite, a configuration file is present
in ``drivers/gpu/drm/tests/.kunitconfig``. It can be used by ``kunit.py`` as
follows:
.. code-block:: bash
$ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/gpu/drm/tests \
--kconfig_add CONFIG_VIRTIO_UML=y \
--kconfig_add CONFIG_UML_PCI_OVER_VIRTIO=y
.. note::
The configuration included in ``.kunitconfig`` should be as generic as
possible.
``CONFIG_VIRTIO_UML`` and ``CONFIG_UML_PCI_OVER_VIRTIO`` are not
included in it because they are only required for User Mode Linux.
Legacy Support Code
===================

View File

@ -6226,6 +6226,13 @@ S: Maintained
F: Documentation/devicetree/bindings/display/bridge/chipone,icn6211.yaml
F: drivers/gpu/drm/bridge/chipone-icn6211.c
DRM DRIVER FOR EBBG FT8719 PANEL
M: Joel Selvaraj <jo@jsfamily.in>
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/panel/ebbg,ft8719.yaml
F: drivers/gpu/drm/panel/panel-ebbg-ft8719.c
DRM DRIVER FOR FARADAY TVE200 TV ENCODER
M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
@ -6466,13 +6473,17 @@ S: Orphan / Obsolete
F: drivers/gpu/drm/savage/
F: include/uapi/drm/savage_drm.h
DRM DRIVER FOR SIMPLE FRAMEBUFFERS
DRM DRIVER FOR FIRMWARE FRAMEBUFFERS
M: Thomas Zimmermann <tzimmermann@suse.de>
M: Javier Martinez Canillas <javierm@redhat.com>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
F: drivers/gpu/drm/drm_aperture.c
F: drivers/gpu/drm/tiny/simpledrm.c
F: drivers/video/aperture.c
F: include/drm/drm_aperture.h
F: include/linux/aperture.h
DRM DRIVER FOR SIS VIDEO CARDS
S: Orphan / Obsolete

View File

@ -75,7 +75,7 @@ menuconfig DMABUF_HEAPS
between drivers.
menuconfig DMABUF_SYSFS_STATS
bool "DMA-BUF sysfs statistics"
bool "DMA-BUF sysfs statistics (DEPRECATED)"
depends on DMA_SHARED_BUFFER
help
Choose this option to enable DMA-BUF sysfs statistics
@ -85,6 +85,10 @@ menuconfig DMABUF_SYSFS_STATS
statistics for the DMA-BUF with the unique inode number
<inode_number>.
This option is deprecated and should sooner or later be removed.
Android is the only user of this and it turned out that this resulted
in quite some performance problems.
source "drivers/dma-buf/heaps/Kconfig"
endmenu

View File

@ -70,6 +70,22 @@ config DRM_DEBUG_SELFTEST
If in doubt, say "N".
config DRM_KUNIT_TEST
tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS
depends on DRM && KUNIT=y
select DRM_KMS_HELPER
default KUNIT_ALL_TESTS
help
This builds unit tests for DRM. This option is not useful for
distributions or general kernels, but only for kernel
developers working on DRM and associated drivers.
For more information on KUnit and unit tests in general,
please refer to the KUnit documentation in
Documentation/dev-tools/kunit/.
If in doubt, say "N".
config DRM_KMS_HELPER
tristate
depends on DRM

View File

@ -76,6 +76,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
#
obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/
obj-$(CONFIG_DRM_KUNIT_TEST) += tests/
obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o
obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
@ -130,7 +131,7 @@ obj-y += bridge/
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
obj-y += hisilicon/
obj-$(CONFIG_DRM_MXSFB) += mxsfb/
obj-y += mxsfb/
obj-y += tiny/
obj-$(CONFIG_DRM_PL111) += pl111/
obj-$(CONFIG_DRM_TVE200) += tve200/

View File

@ -34,7 +34,7 @@ int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata)
* CRE4[7:0]: Read-Pointer for EDID (Unit: 4bytes); valid range: 0~64
*/
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE4,
(u8) ~ASTDP_EDID_READ_POINTER_MASK, (u8) i);
ASTDP_AND_CLEAR_MASK, (u8)i);
j = 0;
/*
@ -274,8 +274,8 @@ void ast_dp_set_mode(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mo
* CRE1[7:0]: MISC1 (default: 0x00)
* CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50)
*/
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE0, (u8) ~ASTDP_CLEAR_MASK,
ASTDP_MISC0_24bpp);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE1, (u8) ~ASTDP_CLEAR_MASK, ASTDP_MISC1);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE2, (u8) ~ASTDP_CLEAR_MASK, ModeIdx);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE0, ASTDP_AND_CLEAR_MASK,
ASTDP_MISC0_24bpp);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE1, ASTDP_AND_CLEAR_MASK, ASTDP_MISC1);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE2, ASTDP_AND_CLEAR_MASK, ModeIdx);
}

View File

@ -433,7 +433,7 @@ int ast_mode_config_init(struct ast_private *ast);
*/
#define ASTDP_MISC0_24bpp BIT(5)
#define ASTDP_MISC1 0
#define ASTDP_CLEAR_MASK GENMASK(7, 0)
#define ASTDP_AND_CLEAR_MASK 0x00
/*
* ASTDP resoultion table:

View File

@ -990,6 +990,9 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct ast_private *ast = to_ast_private(crtc->dev);
u8 ch = AST_DPMS_VSYNC_OFF | AST_DPMS_HSYNC_OFF;
struct ast_crtc_state *ast_state;
const struct drm_format_info *format;
struct ast_vbios_mode_info *vbios_mode_info;
/* TODO: Maybe control display signal generation with
* Sync Enable (bit CR17.7).
@ -1007,6 +1010,16 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)
ast_dp_set_on_off(crtc->dev, 1);
}
ast_state = to_ast_crtc_state(crtc->state);
format = ast_state->format;
if (format) {
vbios_mode_info = &ast_state->vbios_mode_info;
ast_set_color_reg(ast, format);
ast_set_vbios_color_reg(ast, format, vbios_mode_info);
}
ast_crtc_load_lut(ast, crtc);
break;
case DRM_MODE_DPMS_STANDBY:
@ -1095,15 +1108,19 @@ ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode
static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
crtc);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
struct drm_device *dev = crtc->dev;
struct ast_crtc_state *ast_state;
const struct drm_format_info *format;
bool succ;
int ret;
ret = drm_atomic_helper_check_crtc_state(crtc_state, false);
if (ret)
return ret;
if (!crtc_state->enable)
return 0; /* no mode checks if CRTC is being disabled */
goto out;
ast_state = to_ast_crtc_state(crtc_state);
@ -1117,7 +1134,8 @@ static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
if (!succ)
return -EINVAL;
return 0;
out:
return drm_atomic_add_affected_planes(state, crtc);
}
static void ast_crtc_helper_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state)

View File

@ -1393,10 +1393,21 @@ static struct i2c_driver adv7511_driver = {
static int __init adv7511_init(void)
{
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
mipi_dsi_driver_register(&adv7533_dsi_driver);
int ret;
return i2c_add_driver(&adv7511_driver);
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
ret = mipi_dsi_driver_register(&adv7533_dsi_driver);
if (ret)
return ret;
}
ret = i2c_add_driver(&adv7511_driver);
if (ret) {
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
mipi_dsi_driver_unregister(&adv7533_dsi_driver);
}
return ret;
}
module_init(adv7511_init);

View File

@ -324,11 +324,7 @@ error:
static int ps8622_backlight_update(struct backlight_device *bl)
{
struct ps8622_bridge *ps8622 = dev_get_drvdata(&bl->dev);
int ret, brightness = bl->props.brightness;
if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
brightness = 0;
int ret, brightness = backlight_get_brightness(bl);
if (!ps8622->enabled)
return -EINVAL;

View File

@ -288,7 +288,6 @@ struct tc_data {
struct drm_connector connector;
struct mipi_dsi_device *dsi;
u8 dsi_lanes;
/* link settings */
struct tc_edp_link link;
@ -1261,7 +1260,7 @@ static int tc_dsi_rx_enable(struct tc_data *tc)
regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE);
regmap_write(tc->regmap, PPI_LPTXTIMECNT, LPX_PERIOD);
value = ((LANEENABLE_L0EN << tc->dsi_lanes) - LANEENABLE_L0EN) |
value = ((LANEENABLE_L0EN << tc->dsi->lanes) - LANEENABLE_L0EN) |
LANEENABLE_CLEN;
regmap_write(tc->regmap, PPI_LANEENABLE, value);
regmap_write(tc->regmap, DSI_LANEENABLE, value);
@ -1909,8 +1908,7 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc)
tc->dsi = dsi;
tc->dsi_lanes = dsi_lanes;
dsi->lanes = tc->dsi_lanes;
dsi->lanes = dsi_lanes;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;

View File

@ -3,7 +3,7 @@
config DRM_DP_AUX_BUS
tristate
depends on DRM
depends on OF
depends on OF || COMPILE_TEST
config DRM_DISPLAY_HELPER
tristate

View File

@ -1,14 +1,7 @@
// SPDX-License-Identifier: MIT
#include <linux/device.h>
#include <linux/fb.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/platform_device.h> /* for firmware helpers */
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/vgaarb.h>
#include <linux/aperture.h>
#include <linux/platform_device.h>
#include <drm/drm_aperture.h>
#include <drm/drm_drv.h>
@ -126,92 +119,6 @@
* afterwards.
*/
struct drm_aperture {
struct drm_device *dev;
resource_size_t base;
resource_size_t size;
struct list_head lh;
void (*detach)(struct drm_device *dev);
};
static LIST_HEAD(drm_apertures);
static DEFINE_MUTEX(drm_apertures_lock);
static bool overlap(resource_size_t base1, resource_size_t end1,
resource_size_t base2, resource_size_t end2)
{
return (base1 < end2) && (end1 > base2);
}
static void devm_aperture_acquire_release(void *data)
{
struct drm_aperture *ap = data;
bool detached = !ap->dev;
if (detached)
return;
mutex_lock(&drm_apertures_lock);
list_del(&ap->lh);
mutex_unlock(&drm_apertures_lock);
}
static int devm_aperture_acquire(struct drm_device *dev,
resource_size_t base, resource_size_t size,
void (*detach)(struct drm_device *))
{
size_t end = base + size;
struct list_head *pos;
struct drm_aperture *ap;
mutex_lock(&drm_apertures_lock);
list_for_each(pos, &drm_apertures) {
ap = container_of(pos, struct drm_aperture, lh);
if (overlap(base, end, ap->base, ap->base + ap->size)) {
mutex_unlock(&drm_apertures_lock);
return -EBUSY;
}
}
ap = devm_kzalloc(dev->dev, sizeof(*ap), GFP_KERNEL);
if (!ap) {
mutex_unlock(&drm_apertures_lock);
return -ENOMEM;
}
ap->dev = dev;
ap->base = base;
ap->size = size;
ap->detach = detach;
INIT_LIST_HEAD(&ap->lh);
list_add(&ap->lh, &drm_apertures);
mutex_unlock(&drm_apertures_lock);
return devm_add_action_or_reset(dev->dev, devm_aperture_acquire_release, ap);
}
static void drm_aperture_detach_firmware(struct drm_device *dev)
{
struct platform_device *pdev = to_platform_device(dev->dev);
/*
* Remove the device from the device hierarchy. This is the right thing
* to do for firmware-based DRM drivers, such as EFI, VESA or VGA. After
* the new driver takes over the hardware, the firmware device's state
* will be lost.
*
* For non-platform devices, a new callback would be required.
*
* If the aperture helpers ever need to handle native drivers, this call
* would only have to unplug the DRM device, so that the hardware device
* stays around after detachment.
*/
platform_device_unregister(pdev);
}
/**
* devm_aperture_acquire_from_firmware - Acquires ownership of a firmware framebuffer
* on behalf of a DRM driver.
@ -239,40 +146,17 @@ static void drm_aperture_detach_firmware(struct drm_device *dev)
int devm_aperture_acquire_from_firmware(struct drm_device *dev, resource_size_t base,
resource_size_t size)
{
struct platform_device *pdev;
if (drm_WARN_ON(dev, !dev_is_platform(dev->dev)))
return -EINVAL;
return devm_aperture_acquire(dev, base, size, drm_aperture_detach_firmware);
pdev = to_platform_device(dev->dev);
return devm_aperture_acquire_for_platform_device(pdev, base, size);
}
EXPORT_SYMBOL(devm_aperture_acquire_from_firmware);
static void drm_aperture_detach_drivers(resource_size_t base, resource_size_t size)
{
resource_size_t end = base + size;
struct list_head *pos, *n;
mutex_lock(&drm_apertures_lock);
list_for_each_safe(pos, n, &drm_apertures) {
struct drm_aperture *ap =
container_of(pos, struct drm_aperture, lh);
struct drm_device *dev = ap->dev;
if (WARN_ON_ONCE(!dev))
continue;
if (!overlap(base, end, ap->base, ap->base + ap->size))
continue;
ap->dev = NULL; /* detach from device */
list_del(&ap->lh);
ap->detach(dev);
}
mutex_unlock(&drm_apertures_lock);
}
/**
* drm_aperture_remove_conflicting_framebuffers - remove existing framebuffers in the given range
* @base: the aperture's base address in physical memory
@ -289,27 +173,7 @@ static void drm_aperture_detach_drivers(resource_size_t base, resource_size_t si
int drm_aperture_remove_conflicting_framebuffers(resource_size_t base, resource_size_t size,
bool primary, const struct drm_driver *req_driver)
{
#if IS_REACHABLE(CONFIG_FB)
struct apertures_struct *a;
int ret;
a = alloc_apertures(1);
if (!a)
return -ENOMEM;
a->ranges[0].base = base;
a->ranges[0].size = size;
ret = remove_conflicting_framebuffers(a, req_driver->name, primary);
kfree(a);
if (ret)
return ret;
#endif
drm_aperture_detach_drivers(base, size);
return 0;
return aperture_remove_conflicting_devices(base, size, primary, req_driver->name);
}
EXPORT_SYMBOL(drm_aperture_remove_conflicting_framebuffers);
@ -328,30 +192,6 @@ EXPORT_SYMBOL(drm_aperture_remove_conflicting_framebuffers);
int drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev,
const struct drm_driver *req_driver)
{
resource_size_t base, size;
int bar, ret;
/*
* WARNING: Apparently we must kick fbdev drivers before vgacon,
* otherwise the vga fbdev driver falls over.
*/
#if IS_REACHABLE(CONFIG_FB)
ret = remove_conflicting_pci_framebuffers(pdev, req_driver->name);
if (ret)
return ret;
#endif
ret = vga_remove_vgacon(pdev);
if (ret)
return ret;
for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) {
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
continue;
base = pci_resource_start(pdev, bar);
size = pci_resource_len(pdev, bar);
drm_aperture_detach_drivers(base, size);
}
return 0;
return aperture_remove_conflicting_pci_devices(pdev, req_driver->name);
}
EXPORT_SYMBOL(drm_aperture_remove_conflicting_pci_framebuffers);

View File

@ -877,6 +877,61 @@ int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state,
}
EXPORT_SYMBOL(drm_atomic_helper_check_plane_state);
/**
* drm_atomic_helper_check_crtc_state() - Check CRTC state for validity
* @crtc_state: CRTC state to check
* @can_disable_primary_planes: can the CRTC be enabled without a primary plane?
*
* Checks that a desired CRTC update is valid. Drivers that provide
* their own CRTC handling rather than helper-provided implementations may
* still wish to call this function to avoid duplication of error checking
* code.
*
* Note that @can_disable_primary_planes only tests if the CRTC can be
* enabled without a primary plane. To test if a primary plane can be updated
* without a CRTC, use drm_atomic_helper_check_plane_state() in the plane's
* atomic check.
*
* RETURNS:
* Zero if update appears valid, error code on failure
*/
int drm_atomic_helper_check_crtc_state(struct drm_crtc_state *crtc_state,
bool can_disable_primary_planes)
{
struct drm_device *dev = crtc_state->crtc->dev;
struct drm_atomic_state *state = crtc_state->state;
if (!crtc_state->enable)
return 0;
/* needs at least one primary plane to be enabled */
if (!can_disable_primary_planes) {
bool has_primary_plane = false;
struct drm_plane *plane;
drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) {
struct drm_plane_state *plane_state;
if (plane->type != DRM_PLANE_TYPE_PRIMARY)
continue;
plane_state = drm_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state))
return PTR_ERR(plane_state);
if (plane_state->fb && plane_state->crtc) {
has_primary_plane = true;
break;
}
}
if (!has_primary_plane) {
drm_dbg_kms(dev, "Cannot enable CRTC without a primary plane.\n");
return -EINVAL;
}
}
return 0;
}
EXPORT_SYMBOL(drm_atomic_helper_check_crtc_state);
/**
* drm_atomic_helper_check_planes - validate state object for planes changes
* @dev: DRM device

View File

@ -80,7 +80,7 @@
*
* Note that the source rectangle must fully lie within the bounds of the
* &drm_framebuffer. The destination rectangle can lie outside of the visible
* area of the current mode of the CRTC. It must be apprpriately clipped by the
* area of the current mode of the CRTC. It must be appropriately clipped by the
* driver, which can be done by calling drm_plane_helper_check_update(). Drivers
* are also allowed to round the subpixel sampling positions appropriately, but
* only to the next full pixel. No pixel outside of the source rectangle may

View File

@ -251,7 +251,7 @@ int drm_connector_init(struct drm_device *dev,
connector->funcs = funcs;
/* connector index is used with 32bit bitmasks */
ret = ida_simple_get(&config->connector_ida, 0, 32, GFP_KERNEL);
ret = ida_alloc_max(&config->connector_ida, 31, GFP_KERNEL);
if (ret < 0) {
DRM_DEBUG_KMS("Failed to allocate %s connector index: %d\n",
drm_connector_enum_list[connector_type].name,
@ -263,7 +263,7 @@ int drm_connector_init(struct drm_device *dev,
connector->connector_type = connector_type;
connector->connector_type_id =
ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);
ida_alloc_min(connector_ida, 1, GFP_KERNEL);
if (connector->connector_type_id < 0) {
ret = connector->connector_type_id;
goto out_put_id;
@ -323,10 +323,10 @@ int drm_connector_init(struct drm_device *dev,
connector->debugfs_entry = NULL;
out_put_type_id:
if (ret)
ida_simple_remove(connector_ida, connector->connector_type_id);
ida_free(connector_ida, connector->connector_type_id);
out_put_id:
if (ret)
ida_simple_remove(&config->connector_ida, connector->index);
ida_free(&config->connector_ida, connector->index);
out_put:
if (ret)
drm_mode_object_unregister(dev, &connector->base);
@ -480,11 +480,10 @@ void drm_connector_cleanup(struct drm_connector *connector)
list_for_each_entry_safe(mode, t, &connector->modes, head)
drm_mode_remove(connector, mode);
ida_simple_remove(&drm_connector_enum_list[connector->connector_type].ida,
ida_free(&drm_connector_enum_list[connector->connector_type].ida,
connector->connector_type_id);
ida_simple_remove(&dev->mode_config.connector_ida,
connector->index);
ida_free(&dev->mode_config.connector_ida, connector->index);
kfree(connector->display_info.bus_formats);
drm_mode_object_unregister(dev, &connector->base);

View File

@ -681,7 +681,11 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
schedule_work(&helper->damage_work);
}
/* Convert memory region into area of scanlines and pixels per scanline */
/*
* Convert memory region into area of scanlines and pixels per
* scanline. The parameters off and len must not reach beyond
* the end of the framebuffer.
*/
static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, size_t len,
struct drm_rect *clip)
{
@ -716,22 +720,29 @@ static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off,
*/
void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist)
{
unsigned long start, end, min, max;
unsigned long start, end, min_off, max_off;
struct fb_deferred_io_pageref *pageref;
struct drm_rect damage_area;
min = ULONG_MAX;
max = 0;
min_off = ULONG_MAX;
max_off = 0;
list_for_each_entry(pageref, pagereflist, list) {
start = pageref->offset;
end = start + PAGE_SIZE;
min = min(min, start);
max = max(max, end);
min_off = min(min_off, start);
max_off = max(max_off, end);
}
if (min >= max)
if (min_off >= max_off)
return;
drm_fb_helper_memory_range_to_clip(info, min, max - min, &damage_area);
/*
* As we can only track pages, we might reach beyond the end
* of the screen and account for non-existing scanlines. Hence,
* keep the covered memory area within the screen buffer.
*/
max_off = min(max_off, info->screen_size);
drm_fb_helper_memory_range_to_clip(info, min_off, max_off - min_off, &damage_area);
drm_fb_helper_damage(info, damage_area.x1, damage_area.y1,
drm_rect_width(&damage_area),
drm_rect_height(&damage_area));

View File

@ -100,14 +100,12 @@ drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
crtc);
bool has_primary = crtc_state->plane_mask &
drm_plane_mask(crtc->primary);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
int ret;
/* We always want to have an active plane with an active CRTC */
if (has_primary != crtc_state->enable)
return -EINVAL;
ret = drm_atomic_helper_check_crtc_state(crtc_state, false);
if (ret)
return ret;
return drm_atomic_add_affected_planes(state, crtc);
}
@ -227,7 +225,7 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
false, true);
false, false);
if (ret)
return ret;

View File

@ -298,7 +298,7 @@ static int logicvc_drm_probe(struct platform_device *pdev)
struct logicvc_drm *logicvc;
struct device *dev = &pdev->dev;
struct drm_device *drm_dev;
struct regmap *regmap;
struct regmap *regmap = NULL;
struct resource res;
void __iomem *base;
int irq;
@ -349,7 +349,6 @@ static int logicvc_drm_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "Failed to get IRQ\n");
ret = -ENODEV;
goto error_reserved_mem;
}

View File

@ -491,6 +491,7 @@ static int logicvc_layer_init(struct logicvc_drm *logicvc,
if (!formats) {
drm_err(drm_dev, "Failed to lookup formats for layer #%d\n",
index);
ret = -EINVAL;
goto error;
}
@ -612,10 +613,10 @@ int logicvc_layers_init(struct logicvc_drm *logicvc)
}
ret = logicvc_layer_init(logicvc, layer_node, index);
if (ret)
if (ret) {
of_node_put(layers_node);
goto error;
of_node_put(layer_node);
}
}
of_node_put(layers_node);

View File

@ -1111,6 +1111,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
bridge = of_drm_find_bridge(child);
if (!bridge) {
dev_err(dev, "failed to find bridge\n");
of_node_put(child);
return -EINVAL;
}
}

View File

@ -19,3 +19,19 @@ config DRM_MXSFB
i.MX28, i.MX6SX, i.MX7 and i.MX8M).
If M is selected the module will be called mxsfb.
config DRM_IMX_LCDIF
tristate "i.MX LCDIFv3 LCD controller"
depends on DRM && OF
depends on COMMON_CLK
select DRM_MXS
select DRM_KMS_HELPER
select DRM_GEM_CMA_HELPER
select DRM_PANEL
select DRM_PANEL_BRIDGE
help
Choose this option if you have an LCDIFv3 LCD controller.
Those devices are found in various i.MX SoC (i.MX8MP,
i.MXRT).
If M is selected the module will be called imx-lcdif.

View File

@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
mxsfb-y := mxsfb_drv.o mxsfb_kms.o
obj-$(CONFIG_DRM_MXSFB) += mxsfb.o
imx-lcdif-y := lcdif_drv.o lcdif_kms.o
obj-$(CONFIG_DRM_IMX_LCDIF) += imx-lcdif.o

View File

@ -0,0 +1,340 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
*
* This code is based on drivers/gpu/drm/mxsfb/mxsfb*
*/
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include "lcdif_drv.h"
#include "lcdif_regs.h"
static const struct drm_mode_config_funcs lcdif_mode_config_funcs = {
.fb_create = drm_gem_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static const struct drm_mode_config_helper_funcs lcdif_mode_config_helpers = {
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
};
static int lcdif_attach_bridge(struct lcdif_drm_private *lcdif)
{
struct drm_device *drm = lcdif->drm;
struct drm_bridge *bridge;
struct drm_panel *panel;
int ret;
ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
&bridge);
if (ret)
return ret;
if (panel) {
bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
DRM_MODE_CONNECTOR_DPI);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
}
if (!bridge)
return -ENODEV;
ret = drm_bridge_attach(&lcdif->encoder, bridge, NULL, 0);
if (ret)
return dev_err_probe(drm->dev, ret, "Failed to attach bridge\n");
lcdif->bridge = bridge;
return 0;
}
static irqreturn_t lcdif_irq_handler(int irq, void *data)
{
struct drm_device *drm = data;
struct lcdif_drm_private *lcdif = drm->dev_private;
u32 reg, stat;
stat = readl(lcdif->base + LCDC_V8_INT_STATUS_D0);
if (!stat)
return IRQ_NONE;
if (stat & INT_STATUS_D0_VS_BLANK) {
reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
if (!(reg & CTRLDESCL0_5_SHADOW_LOAD_EN))
drm_crtc_handle_vblank(&lcdif->crtc);
}
writel(stat, lcdif->base + LCDC_V8_INT_STATUS_D0);
return IRQ_HANDLED;
}
static int lcdif_load(struct drm_device *drm)
{
struct platform_device *pdev = to_platform_device(drm->dev);
struct lcdif_drm_private *lcdif;
struct resource *res;
int ret;
lcdif = devm_kzalloc(&pdev->dev, sizeof(*lcdif), GFP_KERNEL);
if (!lcdif)
return -ENOMEM;
lcdif->drm = drm;
drm->dev_private = lcdif;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
lcdif->base = devm_ioremap_resource(drm->dev, res);
if (IS_ERR(lcdif->base))
return PTR_ERR(lcdif->base);
lcdif->clk = devm_clk_get(drm->dev, "pix");
if (IS_ERR(lcdif->clk))
return PTR_ERR(lcdif->clk);
lcdif->clk_axi = devm_clk_get(drm->dev, "axi");
if (IS_ERR(lcdif->clk_axi))
return PTR_ERR(lcdif->clk_axi);
lcdif->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi");
if (IS_ERR(lcdif->clk_disp_axi))
return PTR_ERR(lcdif->clk_disp_axi);
platform_set_drvdata(pdev, drm);
ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(36));
if (ret)
return ret;
/* Modeset init */
drm_mode_config_init(drm);
ret = lcdif_kms_init(lcdif);
if (ret < 0) {
dev_err(drm->dev, "Failed to initialize KMS pipeline\n");
return ret;
}
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (ret < 0) {
dev_err(drm->dev, "Failed to initialise vblank\n");
return ret;
}
/* Start with vertical blanking interrupt reporting disabled. */
drm_crtc_vblank_off(&lcdif->crtc);
ret = lcdif_attach_bridge(lcdif);
if (ret)
return dev_err_probe(drm->dev, ret, "Cannot connect bridge\n");
drm->mode_config.min_width = LCDIF_MIN_XRES;
drm->mode_config.min_height = LCDIF_MIN_YRES;
drm->mode_config.max_width = LCDIF_MAX_XRES;
drm->mode_config.max_height = LCDIF_MAX_YRES;
drm->mode_config.funcs = &lcdif_mode_config_funcs;
drm->mode_config.helper_private = &lcdif_mode_config_helpers;
drm_mode_config_reset(drm);
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
lcdif->irq = ret;
ret = devm_request_irq(drm->dev, lcdif->irq, lcdif_irq_handler, 0,
drm->driver->name, drm);
if (ret < 0) {
dev_err(drm->dev, "Failed to install IRQ handler\n");
return ret;
}
drm_kms_helper_poll_init(drm);
drm_helper_hpd_irq_event(drm);
pm_runtime_enable(drm->dev);
return 0;
}
static void lcdif_unload(struct drm_device *drm)
{
struct lcdif_drm_private *lcdif = drm->dev_private;
pm_runtime_get_sync(drm->dev);
drm_crtc_vblank_off(&lcdif->crtc);
drm_kms_helper_poll_fini(drm);
drm_mode_config_cleanup(drm);
pm_runtime_put_sync(drm->dev);
pm_runtime_disable(drm->dev);
drm->dev_private = NULL;
}
DEFINE_DRM_GEM_CMA_FOPS(fops);
static const struct drm_driver lcdif_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
DRM_GEM_CMA_DRIVER_OPS,
.fops = &fops,
.name = "imx-lcdif",
.desc = "i.MX LCDIF Controller DRM",
.date = "20220417",
.major = 1,
.minor = 0,
};
static const struct of_device_id lcdif_dt_ids[] = {
{ .compatible = "fsl,imx8mp-lcdif" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, lcdif_dt_ids);
static int lcdif_probe(struct platform_device *pdev)
{
struct drm_device *drm;
int ret;
drm = drm_dev_alloc(&lcdif_driver, &pdev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
ret = lcdif_load(drm);
if (ret)
goto err_free;
ret = drm_dev_register(drm, 0);
if (ret)
goto err_unload;
drm_fbdev_generic_setup(drm, 32);
return 0;
err_unload:
lcdif_unload(drm);
err_free:
drm_dev_put(drm);
return ret;
}
static int lcdif_remove(struct platform_device *pdev)
{
struct drm_device *drm = platform_get_drvdata(pdev);
drm_dev_unregister(drm);
drm_atomic_helper_shutdown(drm);
lcdif_unload(drm);
drm_dev_put(drm);
return 0;
}
static void lcdif_shutdown(struct platform_device *pdev)
{
struct drm_device *drm = platform_get_drvdata(pdev);
drm_atomic_helper_shutdown(drm);
}
static int __maybe_unused lcdif_rpm_suspend(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct lcdif_drm_private *lcdif = drm->dev_private;
/* These clock supply the DISPLAY CLOCK Domain */
clk_disable_unprepare(lcdif->clk);
/* These clock supply the System Bus, AXI, Write Path, LFIFO */
clk_disable_unprepare(lcdif->clk_disp_axi);
/* These clock supply the Control Bus, APB, APBH Ctrl Registers */
clk_disable_unprepare(lcdif->clk_axi);
return 0;
}
static int __maybe_unused lcdif_rpm_resume(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct lcdif_drm_private *lcdif = drm->dev_private;
/* These clock supply the Control Bus, APB, APBH Ctrl Registers */
clk_prepare_enable(lcdif->clk_axi);
/* These clock supply the System Bus, AXI, Write Path, LFIFO */
clk_prepare_enable(lcdif->clk_disp_axi);
/* These clock supply the DISPLAY CLOCK Domain */
clk_prepare_enable(lcdif->clk);
return 0;
}
static int __maybe_unused lcdif_suspend(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
int ret;
ret = drm_mode_config_helper_suspend(drm);
if (ret)
return ret;
return lcdif_rpm_suspend(dev);
}
static int __maybe_unused lcdif_resume(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
lcdif_rpm_resume(dev);
return drm_mode_config_helper_resume(drm);
}
static const struct dev_pm_ops lcdif_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(lcdif_suspend, lcdif_resume)
SET_RUNTIME_PM_OPS(lcdif_rpm_suspend, lcdif_rpm_resume, NULL)
};
static struct platform_driver lcdif_platform_driver = {
.probe = lcdif_probe,
.remove = lcdif_remove,
.shutdown = lcdif_shutdown,
.driver = {
.name = "imx-lcdif",
.of_match_table = lcdif_dt_ids,
.pm = &lcdif_pm_ops,
},
};
drm_module_platform_driver(lcdif_platform_driver);
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("Freescale LCDIF DRM/KMS driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
*
* i.MX8MP/i.MXRT LCDIFv3 LCD controller driver.
*/
#ifndef __LCDIF_DRV_H__
#define __LCDIF_DRV_H__
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_encoder.h>
#include <drm/drm_plane.h>
struct clk;
struct lcdif_drm_private {
void __iomem *base; /* registers */
struct clk *clk;
struct clk *clk_axi;
struct clk *clk_disp_axi;
unsigned int irq;
struct drm_device *drm;
struct {
struct drm_plane primary;
/* i.MXRT does support overlay planes, add them here. */
} planes;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_bridge *bridge;
};
static inline struct lcdif_drm_private *
to_lcdif_drm_private(struct drm_device *drm)
{
return drm->dev_private;
}
int lcdif_kms_init(struct lcdif_drm_private *lcdif);
#endif /* __LCDIF_DRV_H__ */

View File

@ -0,0 +1,484 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
*
* This code is based on drivers/gpu/drm/mxsfb/mxsfb*
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_encoder.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_vblank.h>
#include "lcdif_drv.h"
#include "lcdif_regs.h"
/* -----------------------------------------------------------------------------
* CRTC
*/
static void lcdif_set_formats(struct lcdif_drm_private *lcdif,
const u32 bus_format)
{
struct drm_device *drm = lcdif->drm;
const u32 format = lcdif->crtc.primary->state->fb->format->format;
writel(CSC0_CTRL_BYPASS, lcdif->base + LCDC_V8_CSC0_CTRL);
switch (bus_format) {
case MEDIA_BUS_FMT_RGB565_1X16:
writel(DISP_PARA_LINE_PATTERN_RGB565,
lcdif->base + LCDC_V8_DISP_PARA);
break;
case MEDIA_BUS_FMT_RGB888_1X24:
writel(DISP_PARA_LINE_PATTERN_RGB888,
lcdif->base + LCDC_V8_DISP_PARA);
break;
case MEDIA_BUS_FMT_UYVY8_1X16:
writel(DISP_PARA_LINE_PATTERN_UYVY_H,
lcdif->base + LCDC_V8_DISP_PARA);
/* CSC: BT.601 Full Range RGB to YCbCr coefficients. */
writel(CSC0_COEF0_A2(0x096) | CSC0_COEF0_A1(0x04c),
lcdif->base + LCDC_V8_CSC0_COEF0);
writel(CSC0_COEF1_B1(0x7d5) | CSC0_COEF1_A3(0x01d),
lcdif->base + LCDC_V8_CSC0_COEF1);
writel(CSC0_COEF2_B3(0x080) | CSC0_COEF2_B2(0x7ac),
lcdif->base + LCDC_V8_CSC0_COEF2);
writel(CSC0_COEF3_C2(0x795) | CSC0_COEF3_C1(0x080),
lcdif->base + LCDC_V8_CSC0_COEF3);
writel(CSC0_COEF4_D1(0x000) | CSC0_COEF4_C3(0x7ec),
lcdif->base + LCDC_V8_CSC0_COEF4);
writel(CSC0_COEF5_D3(0x080) | CSC0_COEF5_D2(0x080),
lcdif->base + LCDC_V8_CSC0_COEF5);
writel(CSC0_CTRL_CSC_MODE_RGB2YCbCr,
lcdif->base + LCDC_V8_CSC0_CTRL);
break;
default:
dev_err(drm->dev, "Unknown media bus format 0x%x\n", bus_format);
break;
}
switch (format) {
case DRM_FORMAT_RGB565:
writel(CTRLDESCL0_5_BPP_16_RGB565,
lcdif->base + LCDC_V8_CTRLDESCL0_5);
break;
case DRM_FORMAT_RGB888:
writel(CTRLDESCL0_5_BPP_24_RGB888,
lcdif->base + LCDC_V8_CTRLDESCL0_5);
break;
case DRM_FORMAT_XRGB1555:
writel(CTRLDESCL0_5_BPP_16_ARGB1555,
lcdif->base + LCDC_V8_CTRLDESCL0_5);
break;
case DRM_FORMAT_XRGB4444:
writel(CTRLDESCL0_5_BPP_16_ARGB4444,
lcdif->base + LCDC_V8_CTRLDESCL0_5);
break;
case DRM_FORMAT_XBGR8888:
writel(CTRLDESCL0_5_BPP_32_ABGR8888,
lcdif->base + LCDC_V8_CTRLDESCL0_5);
break;
case DRM_FORMAT_XRGB8888:
writel(CTRLDESCL0_5_BPP_32_ARGB8888,
lcdif->base + LCDC_V8_CTRLDESCL0_5);
break;
default:
dev_err(drm->dev, "Unknown pixel format 0x%x\n", format);
break;
}
}
static void lcdif_set_mode(struct lcdif_drm_private *lcdif, u32 bus_flags)
{
struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode;
u32 ctrl = 0;
if (m->flags & DRM_MODE_FLAG_NHSYNC)
ctrl |= CTRL_INV_HS;
if (m->flags & DRM_MODE_FLAG_NVSYNC)
ctrl |= CTRL_INV_VS;
if (bus_flags & DRM_BUS_FLAG_DE_LOW)
ctrl |= CTRL_INV_DE;
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
ctrl |= CTRL_INV_PXCK;
writel(ctrl, lcdif->base + LCDC_V8_CTRL);
writel(DISP_SIZE_DELTA_Y(m->crtc_vdisplay) |
DISP_SIZE_DELTA_X(m->crtc_hdisplay),
lcdif->base + LCDC_V8_DISP_SIZE);
writel(HSYN_PARA_BP_H(m->htotal - m->hsync_end) |
HSYN_PARA_FP_H(m->hsync_start - m->hdisplay),
lcdif->base + LCDC_V8_HSYN_PARA);
writel(VSYN_PARA_BP_V(m->vtotal - m->vsync_end) |
VSYN_PARA_FP_V(m->vsync_start - m->vdisplay),
lcdif->base + LCDC_V8_VSYN_PARA);
writel(VSYN_HSYN_WIDTH_PW_V(m->vsync_end - m->vsync_start) |
VSYN_HSYN_WIDTH_PW_H(m->hsync_end - m->hsync_start),
lcdif->base + LCDC_V8_VSYN_HSYN_WIDTH);
writel(CTRLDESCL0_1_HEIGHT(m->crtc_vdisplay) |
CTRLDESCL0_1_WIDTH(m->crtc_hdisplay),
lcdif->base + LCDC_V8_CTRLDESCL0_1);
writel(CTRLDESCL0_3_PITCH(lcdif->crtc.primary->state->fb->pitches[0]),
lcdif->base + LCDC_V8_CTRLDESCL0_3);
}
static void lcdif_enable_controller(struct lcdif_drm_private *lcdif)
{
u32 reg;
reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
reg |= DISP_PARA_DISP_ON;
writel(reg, lcdif->base + LCDC_V8_DISP_PARA);
reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
reg |= CTRLDESCL0_5_EN;
writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
}
static void lcdif_disable_controller(struct lcdif_drm_private *lcdif)
{
u32 reg;
int ret;
reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
reg &= ~CTRLDESCL0_5_EN;
writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5,
reg, !(reg & CTRLDESCL0_5_EN),
0, 36000); /* Wait ~2 frame times max */
if (ret)
drm_err(lcdif->drm, "Failed to disable controller!\n");
reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
reg &= ~DISP_PARA_DISP_ON;
writel(reg, lcdif->base + LCDC_V8_DISP_PARA);
}
static void lcdif_reset_block(struct lcdif_drm_private *lcdif)
{
writel(CTRL_SW_RESET, lcdif->base + LCDC_V8_CTRL + REG_SET);
readl(lcdif->base + LCDC_V8_CTRL);
writel(CTRL_SW_RESET, lcdif->base + LCDC_V8_CTRL + REG_CLR);
readl(lcdif->base + LCDC_V8_CTRL);
}
static void lcdif_crtc_mode_set_nofb(struct lcdif_drm_private *lcdif,
struct drm_bridge_state *bridge_state,
const u32 bus_format)
{
struct drm_device *drm = lcdif->crtc.dev;
struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode;
u32 bus_flags = 0;
if (lcdif->bridge && lcdif->bridge->timings)
bus_flags = lcdif->bridge->timings->input_bus_flags;
else if (bridge_state)
bus_flags = bridge_state->input_bus_cfg.flags;
DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
m->crtc_clock,
(int)(clk_get_rate(lcdif->clk) / 1000));
DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
bus_flags);
DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
/* Mandatory eLCDIF reset as per the Reference Manual */
lcdif_reset_block(lcdif);
lcdif_set_formats(lcdif, bus_format);
lcdif_set_mode(lcdif, bus_flags);
}
static int lcdif_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
crtc);
bool has_primary = crtc_state->plane_mask &
drm_plane_mask(crtc->primary);
/* The primary plane has to be enabled when the CRTC is active. */
if (crtc_state->active && !has_primary)
return -EINVAL;
return drm_atomic_add_affected_planes(state, crtc);
}
static void lcdif_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
struct drm_pending_vblank_event *event;
u32 reg;
reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
event = crtc->state->event;
crtc->state->event = NULL;
if (!event)
return;
spin_lock_irq(&crtc->dev->event_lock);
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
}
static void lcdif_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
crtc->primary);
struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode;
struct drm_bridge_state *bridge_state = NULL;
struct drm_device *drm = lcdif->drm;
u32 bus_format = 0;
dma_addr_t paddr;
/* If there is a bridge attached to the LCDIF, use its bus format */
if (lcdif->bridge) {
bridge_state =
drm_atomic_get_new_bridge_state(state,
lcdif->bridge);
if (!bridge_state)
bus_format = MEDIA_BUS_FMT_FIXED;
else
bus_format = bridge_state->input_bus_cfg.format;
if (bus_format == MEDIA_BUS_FMT_FIXED) {
dev_warn_once(drm->dev,
"Bridge does not provide bus format, assuming MEDIA_BUS_FMT_RGB888_1X24.\n"
"Please fix bridge driver by handling atomic_get_input_bus_fmts.\n");
bus_format = MEDIA_BUS_FMT_RGB888_1X24;
}
}
/* If all else fails, default to RGB888_1X24 */
if (!bus_format)
bus_format = MEDIA_BUS_FMT_RGB888_1X24;
clk_set_rate(lcdif->clk, m->crtc_clock * 1000);
pm_runtime_get_sync(drm->dev);
lcdif_crtc_mode_set_nofb(lcdif, bridge_state, bus_format);
/* Write cur_buf as well to avoid an initial corrupt frame */
paddr = drm_fb_cma_get_gem_addr(new_pstate->fb, new_pstate, 0);
if (paddr) {
writel(lower_32_bits(paddr),
lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4);
writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)),
lcdif->base + LCDC_V8_CTRLDESCL_HIGH0_4);
}
lcdif_enable_controller(lcdif);
drm_crtc_vblank_on(crtc);
}
static void lcdif_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
struct drm_device *drm = lcdif->drm;
struct drm_pending_vblank_event *event;
drm_crtc_vblank_off(crtc);
lcdif_disable_controller(lcdif);
spin_lock_irq(&drm->event_lock);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
drm_crtc_send_vblank_event(crtc, event);
}
spin_unlock_irq(&drm->event_lock);
pm_runtime_put_sync(drm->dev);
}
static int lcdif_crtc_enable_vblank(struct drm_crtc *crtc)
{
struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
/* Clear and enable VBLANK IRQ */
writel(INT_STATUS_D0_VS_BLANK, lcdif->base + LCDC_V8_INT_STATUS_D0);
writel(INT_ENABLE_D0_VS_BLANK_EN, lcdif->base + LCDC_V8_INT_ENABLE_D0);
return 0;
}
static void lcdif_crtc_disable_vblank(struct drm_crtc *crtc)
{
struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
/* Disable and clear VBLANK IRQ */
writel(0, lcdif->base + LCDC_V8_INT_ENABLE_D0);
writel(INT_STATUS_D0_VS_BLANK, lcdif->base + LCDC_V8_INT_STATUS_D0);
}
static const struct drm_crtc_helper_funcs lcdif_crtc_helper_funcs = {
.atomic_check = lcdif_crtc_atomic_check,
.atomic_flush = lcdif_crtc_atomic_flush,
.atomic_enable = lcdif_crtc_atomic_enable,
.atomic_disable = lcdif_crtc_atomic_disable,
};
static const struct drm_crtc_funcs lcdif_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = lcdif_crtc_enable_vblank,
.disable_vblank = lcdif_crtc_disable_vblank,
};
/* -----------------------------------------------------------------------------
* Encoder
*/
static const struct drm_encoder_funcs lcdif_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
/* -----------------------------------------------------------------------------
* Planes
*/
static int lcdif_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state,
plane);
struct lcdif_drm_private *lcdif = to_lcdif_drm_private(plane->dev);
struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_new_crtc_state(state,
&lcdif->crtc);
return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
false, true);
}
static void lcdif_plane_primary_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct lcdif_drm_private *lcdif = to_lcdif_drm_private(plane->dev);
struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
plane);
dma_addr_t paddr;
paddr = drm_fb_cma_get_gem_addr(new_pstate->fb, new_pstate, 0);
if (paddr) {
writel(lower_32_bits(paddr),
lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4);
writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)),
lcdif->base + LCDC_V8_CTRLDESCL_HIGH0_4);
}
}
static bool lcdif_format_mod_supported(struct drm_plane *plane,
uint32_t format,
uint64_t modifier)
{
return modifier == DRM_FORMAT_MOD_LINEAR;
}
static const struct drm_plane_helper_funcs lcdif_plane_primary_helper_funcs = {
.atomic_check = lcdif_plane_atomic_check,
.atomic_update = lcdif_plane_primary_atomic_update,
};
static const struct drm_plane_funcs lcdif_plane_funcs = {
.format_mod_supported = lcdif_format_mod_supported,
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static const u32 lcdif_primary_plane_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_RGB888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_XRGB4444,
DRM_FORMAT_XRGB8888,
};
static const u64 lcdif_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
/* -----------------------------------------------------------------------------
* Initialization
*/
int lcdif_kms_init(struct lcdif_drm_private *lcdif)
{
struct drm_encoder *encoder = &lcdif->encoder;
struct drm_crtc *crtc = &lcdif->crtc;
int ret;
drm_plane_helper_add(&lcdif->planes.primary,
&lcdif_plane_primary_helper_funcs);
ret = drm_universal_plane_init(lcdif->drm, &lcdif->planes.primary, 1,
&lcdif_plane_funcs,
lcdif_primary_plane_formats,
ARRAY_SIZE(lcdif_primary_plane_formats),
lcdif_modifiers, DRM_PLANE_TYPE_PRIMARY,
NULL);
if (ret)
return ret;
drm_crtc_helper_add(crtc, &lcdif_crtc_helper_funcs);
ret = drm_crtc_init_with_planes(lcdif->drm, crtc,
&lcdif->planes.primary, NULL,
&lcdif_crtc_funcs, NULL);
if (ret)
return ret;
encoder->possible_crtcs = drm_crtc_mask(crtc);
return drm_encoder_init(lcdif->drm, encoder, &lcdif_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL);
}

View File

@ -0,0 +1,257 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
*
* i.MX8MP/i.MXRT LCDIF LCD controller driver.
*/
#ifndef __LCDIF_REGS_H__
#define __LCDIF_REGS_H__
#define REG_SET 4
#define REG_CLR 8
/* V8 register set */
#define LCDC_V8_CTRL 0x00
#define LCDC_V8_DISP_PARA 0x10
#define LCDC_V8_DISP_SIZE 0x14
#define LCDC_V8_HSYN_PARA 0x18
#define LCDC_V8_VSYN_PARA 0x1c
#define LCDC_V8_VSYN_HSYN_WIDTH 0x20
#define LCDC_V8_INT_STATUS_D0 0x24
#define LCDC_V8_INT_ENABLE_D0 0x28
#define LCDC_V8_INT_STATUS_D1 0x30
#define LCDC_V8_INT_ENABLE_D1 0x34
#define LCDC_V8_CTRLDESCL0_1 0x200
#define LCDC_V8_CTRLDESCL0_3 0x208
#define LCDC_V8_CTRLDESCL_LOW0_4 0x20c
#define LCDC_V8_CTRLDESCL_HIGH0_4 0x210
#define LCDC_V8_CTRLDESCL0_5 0x214
#define LCDC_V8_CSC0_CTRL 0x21c
#define LCDC_V8_CSC0_COEF0 0x220
#define LCDC_V8_CSC0_COEF1 0x224
#define LCDC_V8_CSC0_COEF2 0x228
#define LCDC_V8_CSC0_COEF3 0x22c
#define LCDC_V8_CSC0_COEF4 0x230
#define LCDC_V8_CSC0_COEF5 0x234
#define LCDC_V8_PANIC0_THRES 0x238
#define CTRL_SFTRST BIT(31)
#define CTRL_CLKGATE BIT(30)
#define CTRL_BYPASS_COUNT BIT(19)
#define CTRL_VSYNC_MODE BIT(18)
#define CTRL_DOTCLK_MODE BIT(17)
#define CTRL_DATA_SELECT BIT(16)
#define CTRL_BUS_WIDTH_16 (0 << 10)
#define CTRL_BUS_WIDTH_8 (1 << 10)
#define CTRL_BUS_WIDTH_18 (2 << 10)
#define CTRL_BUS_WIDTH_24 (3 << 10)
#define CTRL_BUS_WIDTH_MASK (0x3 << 10)
#define CTRL_WORD_LENGTH_16 (0 << 8)
#define CTRL_WORD_LENGTH_8 (1 << 8)
#define CTRL_WORD_LENGTH_18 (2 << 8)
#define CTRL_WORD_LENGTH_24 (3 << 8)
#define CTRL_MASTER BIT(5)
#define CTRL_DF16 BIT(3)
#define CTRL_DF18 BIT(2)
#define CTRL_DF24 BIT(1)
#define CTRL_RUN BIT(0)
#define CTRL1_RECOVER_ON_UNDERFLOW BIT(24)
#define CTRL1_FIFO_CLEAR BIT(21)
#define CTRL1_SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16)
#define CTRL1_GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf)
#define CTRL1_CUR_FRAME_DONE_IRQ_EN BIT(13)
#define CTRL1_CUR_FRAME_DONE_IRQ BIT(9)
#define CTRL2_SET_OUTSTANDING_REQS_1 0
#define CTRL2_SET_OUTSTANDING_REQS_2 (0x1 << 21)
#define CTRL2_SET_OUTSTANDING_REQS_4 (0x2 << 21)
#define CTRL2_SET_OUTSTANDING_REQS_8 (0x3 << 21)
#define CTRL2_SET_OUTSTANDING_REQS_16 (0x4 << 21)
#define CTRL2_SET_OUTSTANDING_REQS_MASK (0x7 << 21)
#define TRANSFER_COUNT_SET_VCOUNT(x) (((x) & 0xffff) << 16)
#define TRANSFER_COUNT_GET_VCOUNT(x) (((x) >> 16) & 0xffff)
#define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff)
#define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff)
#define VDCTRL0_ENABLE_PRESENT BIT(28)
#define VDCTRL0_VSYNC_ACT_HIGH BIT(27)
#define VDCTRL0_HSYNC_ACT_HIGH BIT(26)
#define VDCTRL0_DOTCLK_ACT_FALLING BIT(25)
#define VDCTRL0_ENABLE_ACT_HIGH BIT(24)
#define VDCTRL0_VSYNC_PERIOD_UNIT BIT(21)
#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT BIT(20)
#define VDCTRL0_HALF_LINE BIT(19)
#define VDCTRL0_HALF_LINE_MODE BIT(18)
#define VDCTRL0_SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
#define VDCTRL0_GET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
#define VDCTRL2_SET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
#define VDCTRL2_GET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
#define VDCTRL3_MUX_SYNC_SIGNALS BIT(29)
#define VDCTRL3_VSYNC_ONLY BIT(28)
#define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16)
#define GET_HOR_WAIT_CNT(x) (((x) >> 16) & 0xfff)
#define SET_VERT_WAIT_CNT(x) ((x) & 0xffff)
#define GET_VERT_WAIT_CNT(x) ((x) & 0xffff)
#define VDCTRL4_SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) /* v4 only */
#define VDCTRL4_GET_DOTCLK_DLY(x) (((x) >> 29) & 0x7) /* v4 only */
#define VDCTRL4_SYNC_SIGNALS_ON BIT(18)
#define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff)
#define DEBUG0_HSYNC BIT(26)
#define DEBUG0_VSYNC BIT(25)
#define AS_CTRL_PS_DISABLE BIT(23)
#define AS_CTRL_ALPHA_INVERT BIT(20)
#define AS_CTRL_ALPHA(a) (((a) & 0xff) << 8)
#define AS_CTRL_FORMAT_RGB565 (0xe << 4)
#define AS_CTRL_FORMAT_RGB444 (0xd << 4)
#define AS_CTRL_FORMAT_RGB555 (0xc << 4)
#define AS_CTRL_FORMAT_ARGB4444 (0x9 << 4)
#define AS_CTRL_FORMAT_ARGB1555 (0x8 << 4)
#define AS_CTRL_FORMAT_RGB888 (0x4 << 4)
#define AS_CTRL_FORMAT_ARGB8888 (0x0 << 4)
#define AS_CTRL_ENABLE_COLORKEY BIT(3)
#define AS_CTRL_ALPHA_CTRL_ROP (3 << 1)
#define AS_CTRL_ALPHA_CTRL_MULTIPLY (2 << 1)
#define AS_CTRL_ALPHA_CTRL_OVERRIDE (1 << 1)
#define AS_CTRL_ALPHA_CTRL_EMBEDDED (0 << 1)
#define AS_CTRL_AS_ENABLE BIT(0)
/* V8 register set */
#define CTRL_SW_RESET BIT(31)
#define CTRL_FETCH_START_OPTION_FPV 0
#define CTRL_FETCH_START_OPTION_PWV BIT(8)
#define CTRL_FETCH_START_OPTION_BPV BIT(9)
#define CTRL_FETCH_START_OPTION_RESV GENMASK(9, 8)
#define CTRL_FETCH_START_OPTION_MASK GENMASK(9, 8)
#define CTRL_NEG BIT(4)
#define CTRL_INV_PXCK BIT(3)
#define CTRL_INV_DE BIT(2)
#define CTRL_INV_VS BIT(1)
#define CTRL_INV_HS BIT(0)
#define DISP_PARA_DISP_ON BIT(31)
#define DISP_PARA_SWAP_EN BIT(30)
#define DISP_PARA_LINE_PATTERN_UYVY_H (GENMASK(29, 28) | BIT(26))
#define DISP_PARA_LINE_PATTERN_RGB565 GENMASK(28, 26)
#define DISP_PARA_LINE_PATTERN_RGB888 0
#define DISP_PARA_LINE_PATTERN_MASK GENMASK(29, 26)
#define DISP_PARA_DISP_MODE_MASK GENMASK(25, 24)
#define DISP_PARA_BGND_R_MASK GENMASK(23, 16)
#define DISP_PARA_BGND_G_MASK GENMASK(15, 8)
#define DISP_PARA_BGND_B_MASK GENMASK(7, 0)
#define DISP_SIZE_DELTA_Y(n) (((n) & 0xffff) << 16)
#define DISP_SIZE_DELTA_Y_MASK GENMASK(31, 16)
#define DISP_SIZE_DELTA_X(n) ((n) & 0xffff)
#define DISP_SIZE_DELTA_X_MASK GENMASK(15, 0)
#define HSYN_PARA_BP_H(n) (((n) & 0xffff) << 16)
#define HSYN_PARA_BP_H_MASK GENMASK(31, 16)
#define HSYN_PARA_FP_H(n) ((n) & 0xffff)
#define HSYN_PARA_FP_H_MASK GENMASK(15, 0)
#define VSYN_PARA_BP_V(n) (((n) & 0xffff) << 16)
#define VSYN_PARA_BP_V_MASK GENMASK(31, 16)
#define VSYN_PARA_FP_V(n) ((n) & 0xffff)
#define VSYN_PARA_FP_V_MASK GENMASK(15, 0)
#define VSYN_HSYN_WIDTH_PW_V(n) (((n) & 0xffff) << 16)
#define VSYN_HSYN_WIDTH_PW_V_MASK GENMASK(31, 16)
#define VSYN_HSYN_WIDTH_PW_H(n) ((n) & 0xffff)
#define VSYN_HSYN_WIDTH_PW_H_MASK GENMASK(15, 0)
#define INT_STATUS_D0_FIFO_EMPTY BIT(24)
#define INT_STATUS_D0_DMA_DONE BIT(16)
#define INT_STATUS_D0_DMA_ERR BIT(8)
#define INT_STATUS_D0_VS_BLANK BIT(2)
#define INT_STATUS_D0_UNDERRUN BIT(1)
#define INT_STATUS_D0_VSYNC BIT(0)
#define INT_ENABLE_D0_FIFO_EMPTY_EN BIT(24)
#define INT_ENABLE_D0_DMA_DONE_EN BIT(16)
#define INT_ENABLE_D0_DMA_ERR_EN BIT(8)
#define INT_ENABLE_D0_VS_BLANK_EN BIT(2)
#define INT_ENABLE_D0_UNDERRUN_EN BIT(1)
#define INT_ENABLE_D0_VSYNC_EN BIT(0)
#define INT_STATUS_D1_PLANE_PANIC BIT(0)
#define INT_ENABLE_D1_PLANE_PANIC_EN BIT(0)
#define CTRLDESCL0_1_HEIGHT(n) (((n) & 0xffff) << 16)
#define CTRLDESCL0_1_HEIGHT_MASK GENMASK(31, 16)
#define CTRLDESCL0_1_WIDTH(n) ((n) & 0xffff)
#define CTRLDESCL0_1_WIDTH_MASK GENMASK(15, 0)
#define CTRLDESCL0_3_PITCH(n) ((n) & 0xffff)
#define CTRLDESCL0_3_PITCH_MASK GENMASK(15, 0)
#define CTRLDESCL_HIGH0_4_ADDR_HIGH(n) ((n) & 0xf)
#define CTRLDESCL_HIGH0_4_ADDR_HIGH_MASK GENMASK(3, 0)
#define CTRLDESCL0_5_EN BIT(31)
#define CTRLDESCL0_5_SHADOW_LOAD_EN BIT(30)
#define CTRLDESCL0_5_BPP_16_RGB565 BIT(26)
#define CTRLDESCL0_5_BPP_16_ARGB1555 (BIT(26) | BIT(24))
#define CTRLDESCL0_5_BPP_16_ARGB4444 (BIT(26) | BIT(25))
#define CTRLDESCL0_5_BPP_YCbCr422 (BIT(26) | BIT(25) | BIT(24))
#define CTRLDESCL0_5_BPP_24_RGB888 BIT(27)
#define CTRLDESCL0_5_BPP_32_ARGB8888 (BIT(27) | BIT(24))
#define CTRLDESCL0_5_BPP_32_ABGR8888 (BIT(27) | BIT(25))
#define CTRLDESCL0_5_BPP_MASK GENMASK(27, 24)
#define CTRLDESCL0_5_YUV_FORMAT_Y2VY1U 0
#define CTRLDESCL0_5_YUV_FORMAT_Y2UY1V BIT(14)
#define CTRLDESCL0_5_YUV_FORMAT_VY2UY1 BIT(15)
#define CTRLDESCL0_5_YUV_FORMAT_UY2VY1 (BIT(15) | BIT(14))
#define CTRLDESCL0_5_YUV_FORMAT_MASK GENMASK(15, 14)
#define CSC0_CTRL_CSC_MODE_RGB2YCbCr GENMASK(2, 1)
#define CSC0_CTRL_CSC_MODE_MASK GENMASK(2, 1)
#define CSC0_CTRL_BYPASS BIT(0)
#define CSC0_COEF0_A2(n) (((n) << 16) & CSC0_COEF0_A2_MASK)
#define CSC0_COEF0_A2_MASK GENMASK(26, 16)
#define CSC0_COEF0_A1(n) ((n) & CSC0_COEF0_A1_MASK)
#define CSC0_COEF0_A1_MASK GENMASK(10, 0)
#define CSC0_COEF1_B1(n) (((n) << 16) & CSC0_COEF1_B1_MASK)
#define CSC0_COEF1_B1_MASK GENMASK(26, 16)
#define CSC0_COEF1_A3(n) ((n) & CSC0_COEF1_A3_MASK)
#define CSC0_COEF1_A3_MASK GENMASK(10, 0)
#define CSC0_COEF2_B3(n) (((n) << 16) & CSC0_COEF2_B3_MASK)
#define CSC0_COEF2_B3_MASK GENMASK(26, 16)
#define CSC0_COEF2_B2(n) ((n) & CSC0_COEF2_B2_MASK)
#define CSC0_COEF2_B2_MASK GENMASK(10, 0)
#define CSC0_COEF3_C2(n) (((n) << 16) & CSC0_COEF3_C2_MASK)
#define CSC0_COEF3_C2_MASK GENMASK(26, 16)
#define CSC0_COEF3_C1(n) ((n) & CSC0_COEF3_C1_MASK)
#define CSC0_COEF3_C1_MASK GENMASK(10, 0)
#define CSC0_COEF4_D1(n) (((n) << 16) & CSC0_COEF4_D1_MASK)
#define CSC0_COEF4_D1_MASK GENMASK(24, 16)
#define CSC0_COEF4_C3(n) ((n) & CSC0_COEF4_C3_MASK)
#define CSC0_COEF4_C3_MASK GENMASK(10, 0)
#define CSC0_COEF5_D3(n) (((n) << 16) & CSC0_COEF5_D3_MASK)
#define CSC0_COEF5_D3_MASK GENMASK(24, 16)
#define CSC0_COEF5_D2(n) ((n) & CSC0_COEF5_D2_MASK)
#define CSC0_COEF5_D2_MASK GENMASK(8, 0)
#define PANIC0_THRES_LOW_MASK GENMASK(24, 16)
#define PANIC0_THRES_HIGH_MASK GENMASK(8, 0)
#define LCDIF_MIN_XRES 120
#define LCDIF_MIN_YRES 120
#define LCDIF_MAX_XRES 0xffff
#define LCDIF_MAX_YRES 0xffff
#endif /* __LCDIF_REGS_H__ */

View File

@ -11,7 +11,6 @@ config DRM_NOUVEAU
select DRM_TTM
select DRM_TTM_HELPER
select BACKLIGHT_CLASS_DEVICE if DRM_NOUVEAU_BACKLIGHT
select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && INPUT
select X86_PLATFORM_DEVICES if ACPI && X86
select ACPI_WMI if ACPI && X86
select MXM_WMI if ACPI && X86

View File

@ -115,6 +115,17 @@ config DRM_PANEL_EDP
that it can be automatically turned off when the panel goes into a
low power state.
config DRM_PANEL_EBBG_FT8719
tristate "EBBG FT8719 panel driver"
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
help
Say Y here if you want to enable support for the EBBG FT8719
video mode panel. Mainly found on Xiaomi Poco F1 mobile phone.
The panel has a resolution of 1080x2246. It provides a MIPI DSI
interface to the host.
config DRM_PANEL_ELIDA_KD35T133
tristate "Elida KD35T133 panel driver"
depends on OF

View File

@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
obj-$(CONFIG_DRM_PANEL_EDP) += panel-edp.o
obj-$(CONFIG_DRM_PANEL_EBBG_FT8719) += panel-ebbg-ft8719.o
obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o
obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o

View File

@ -215,14 +215,9 @@ static const struct drm_panel_funcs tm5p5_nt35596_panel_funcs = {
static int tm5p5_nt35596_bl_update_status(struct backlight_device *bl)
{
struct mipi_dsi_device *dsi = bl_get_data(bl);
u16 brightness = bl->props.brightness;
u16 brightness = backlight_get_brightness(bl);
int ret;
if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK ||
bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
brightness = 0;
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);

View File

@ -85,17 +85,10 @@ static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable)
else
return;
if (enable) {
backlight->props.fb_blank = FB_BLANK_UNBLANK;
backlight->props.state = ~(BL_CORE_FBBLANK | BL_CORE_SUSPENDED);
backlight->props.power = FB_BLANK_UNBLANK;
} else {
backlight->props.fb_blank = FB_BLANK_NORMAL;
backlight->props.power = FB_BLANK_POWERDOWN;
backlight->props.state |= BL_CORE_FBBLANK | BL_CORE_SUSPENDED;
}
backlight_update_status(backlight);
if (enable)
backlight_enable(backlight);
else
backlight_disable(backlight);
}
static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
@ -196,13 +189,7 @@ static int dsicm_bl_update_status(struct backlight_device *dev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
int r = 0;
int level;
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
level = dev->props.brightness;
else
level = 0;
int level = backlight_get_brightness(dev);
dev_dbg(&ddata->dsi->dev, "update brightness to %d\n", level);
@ -219,11 +206,7 @@ static int dsicm_bl_update_status(struct backlight_device *dev)
static int dsicm_bl_get_intensity(struct backlight_device *dev)
{
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
return dev->props.brightness;
return 0;
return backlight_get_brightness(dev);
}
static const struct backlight_ops dsicm_bl_ops = {

View File

@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 Joel Selvaraj <jo@jsfamily.in>
* Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <video/mipi_display.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
static const char * const regulator_names[] = {
"vddio",
"vddpos",
"vddneg",
};
static const unsigned long regulator_enable_loads[] = {
62000,
100000,
100000
};
struct ebbg_ft8719 {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
struct gpio_desc *reset_gpio;
};
static inline
struct ebbg_ft8719 *to_ebbg_ft8719(struct drm_panel *panel)
{
return container_of(panel, struct ebbg_ft8719, panel);
}
static void ebbg_ft8719_reset(struct ebbg_ft8719 *ctx)
{
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(4000, 5000);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(15000, 16000);
}
static int ebbg_ft8719_on(struct ebbg_ft8719 *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
if (ret < 0) {
dev_err(dev, "Failed to set display brightness: %d\n", ret);
return ret;
}
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret < 0) {
dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
return ret;
}
msleep(90);
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display on: %d\n", ret);
return ret;
}
return 0;
}
static int ebbg_ft8719_off(struct ebbg_ft8719 *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &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);
return ret;
}
usleep_range(10000, 11000);
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
if (ret < 0) {
dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
return ret;
}
msleep(90);
return 0;
}
static int ebbg_ft8719_prepare(struct drm_panel *panel)
{
struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret < 0)
return ret;
ebbg_ft8719_reset(ctx);
ret = ebbg_ft8719_on(ctx);
if (ret < 0) {
dev_err(dev, "Failed to initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
return ret;
}
return 0;
}
static int ebbg_ft8719_unprepare(struct drm_panel *panel)
{
struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
ret = ebbg_ft8719_off(ctx);
if (ret < 0)
dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret)
dev_err(panel->dev, "Failed to disable regulators: %d\n", ret);
return 0;
}
static const struct drm_display_mode ebbg_ft8719_mode = {
.clock = (1080 + 28 + 4 + 16) * (2246 + 120 + 4 + 12) * 60 / 1000,
.hdisplay = 1080,
.hsync_start = 1080 + 28,
.hsync_end = 1080 + 28 + 4,
.htotal = 1080 + 28 + 4 + 16,
.vdisplay = 2246,
.vsync_start = 2246 + 120,
.vsync_end = 2246 + 120 + 4,
.vtotal = 2246 + 120 + 4 + 12,
.width_mm = 68,
.height_mm = 141,
};
static int ebbg_ft8719_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, &ebbg_ft8719_mode);
if (!mode)
return -ENOMEM;
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
drm_mode_probed_add(connector, mode);
return 1;
}
static const struct drm_panel_funcs ebbg_ft8719_panel_funcs = {
.prepare = ebbg_ft8719_prepare,
.unprepare = ebbg_ft8719_unprepare,
.get_modes = ebbg_ft8719_get_modes,
};
static int ebbg_ft8719_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct ebbg_ft8719 *ctx;
int i, ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
ctx->supplies[i].supply = regulator_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
ctx->supplies);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get regulators\n");
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
ret = regulator_set_load(ctx->supplies[i].consumer,
regulator_enable_loads[i]);
if (ret)
return dev_err_probe(dev, ret,
"Failed to set regulator load\n");
}
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->reset_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
"Failed to get reset-gpios\n");
ctx->dsi = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_CLOCK_NON_CONTINUOUS;
drm_panel_init(&ctx->panel, dev, &ebbg_ft8719_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
drm_panel_add(&ctx->panel);
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
static int ebbg_ft8719_remove(struct mipi_dsi_device *dsi)
{
struct ebbg_ft8719 *ctx = mipi_dsi_get_drvdata(dsi);
int ret;
ret = mipi_dsi_detach(dsi);
if (ret < 0)
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
drm_panel_remove(&ctx->panel);
return 0;
}
static const struct of_device_id ebbg_ft8719_of_match[] = {
{ .compatible = "ebbg,ft8719" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ebbg_ft8719_of_match);
static struct mipi_dsi_driver ebbg_ft8719_driver = {
.probe = ebbg_ft8719_probe,
.remove = ebbg_ft8719_remove,
.driver = {
.name = "panel-ebbg-ft8719",
.of_match_table = ebbg_ft8719_of_match,
},
};
module_mipi_dsi_driver(ebbg_ft8719_driver);
MODULE_AUTHOR("Joel Selvaraj <jo@jsfamily.in>");
MODULE_DESCRIPTION("DRM driver for EBBG FT8719 video dsi panel");
MODULE_LICENSE("GPL v2");

View File

@ -1885,6 +1885,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1511, &delay_200_500_e50, "LQ140M1JW48"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1523, &sharp_lq140m1jw46.delay, "LQ140M1JW46"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"),

View File

@ -190,7 +190,7 @@ struct nt35510_config {
* 6 = Hsync x 2
* 7 = Hsync x 4
* bits 4..6 in the upper nibble controls BTP, the boosting
* amplification for the the step-up circuit:
* amplification for the step-up circuit:
* 0 = Disable
* 1 = 1.5 x VDDB
* 2 = 1.66 x VDDB
@ -211,7 +211,7 @@ struct nt35510_config {
* bits 0..2 in the lower nibble controls NCK, the booster clock
* frequency, the values are the same as for PCK in @bt1ctr.
* bits 4..5 in the upper nibble controls BTN, the boosting
* amplification for the the step-up circuit.
* amplification for the step-up circuit.
* 0 = Disable
* 1 = -1.5 x VDDB
* 2 = -2 x VDDB
@ -250,7 +250,7 @@ struct nt35510_config {
* bits 0..2 in the lower nibble controls LCK, the booster clock
* frequency, the values are the same as for PCK in @bt1ctr.
* bits 4..5 in the upper nibble controls BTL, the boosting
* amplification for the the step-up circuit.
* amplification for the step-up circuit.
* 0 = AVEE + VCL
* 1 = AVEE - AVDD
* 2 = AVEE + VCL - AVDD

View File

@ -675,8 +675,10 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
drm_panel_init(&panel->base, dev, &panel_simple_funcs, connector_type);
err = drm_panel_of_backlight(&panel->base);
if (err)
if (err) {
dev_err_probe(dev, err, "Could not find backlight\n");
goto disable_pm_runtime;
}
drm_panel_add(&panel->base);
@ -790,6 +792,36 @@ static const struct panel_desc ampire_am800480r3tmqwa1h = {
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
};
static const struct display_timing ampire_am800600p5tmqw_tb8h_timing = {
.pixelclock = { 34500000, 39600000, 50400000 },
.hactive = { 800, 800, 800 },
.hfront_porch = { 12, 112, 312 },
.hback_porch = { 87, 87, 48 },
.hsync_len = { 1, 1, 40 },
.vactive = { 600, 600, 600 },
.vfront_porch = { 1, 21, 61 },
.vback_porch = { 38, 38, 19 },
.vsync_len = { 1, 1, 20 },
.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE |
DISPLAY_FLAGS_SYNC_POSEDGE,
};
static const struct panel_desc ampire_am800600p5tmqwtb8h = {
.timings = &ampire_am800600p5tmqw_tb8h_timing,
.num_timings = 1,
.bpc = 6,
.size = {
.width = 162,
.height = 122,
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
.bus_flags = DRM_BUS_FLAG_DE_HIGH |
DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE,
.connector_type = DRM_MODE_CONNECTOR_DPI,
};
static const struct display_timing santek_st0700i5y_rbslw_f_timing = {
.pixelclock = { 26400000, 33300000, 46800000 },
.hactive = { 800, 800, 800 },
@ -1439,6 +1471,30 @@ static const struct panel_desc dataimage_fg040346dsswbg04 = {
.connector_type = DRM_MODE_CONNECTOR_DPI,
};
static const struct display_timing dataimage_fg1001l0dsswmg01_timing = {
.pixelclock = { 68900000, 71110000, 73400000 },
.hactive = { 1280, 1280, 1280 },
.vactive = { 800, 800, 800 },
.hback_porch = { 100, 100, 100 },
.hfront_porch = { 100, 100, 100 },
.vback_porch = { 5, 5, 5 },
.vfront_porch = { 5, 5, 5 },
.hsync_len = { 24, 24, 24 },
.vsync_len = { 3, 3, 3 },
.flags = DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE |
DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
};
static const struct panel_desc dataimage_fg1001l0dsswmg01 = {
.timings = &dataimage_fg1001l0dsswmg01_timing,
.num_timings = 1,
.bpc = 8,
.size = {
.width = 217,
.height = 136,
},
};
static const struct drm_display_mode dataimage_scf0700c48ggu18_mode = {
.clock = 33260,
.hdisplay = 800,
@ -3766,6 +3822,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "ampire,am800480r3tmqwa1h",
.data = &ampire_am800480r3tmqwa1h,
}, {
.compatible = "ampire,am800600p5tmqw-tb8h",
.data = &ampire_am800600p5tmqwtb8h,
}, {
.compatible = "arm,rtsm-display",
.data = &arm_rtsm,
@ -3844,6 +3903,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "dataimage,fg040346dsswbg04",
.data = &dataimage_fg040346dsswbg04,
}, {
.compatible = "dataimage,fg1001l0dsswmg01",
.data = &dataimage_fg1001l0dsswmg01,
}, {
.compatible = "dataimage,scf0700c48ggu18",
.data = &dataimage_scf0700c48ggu18,

View File

@ -298,13 +298,7 @@ static void acx565akm_set_brightness(struct acx565akm_panel *lcd, int level)
static int acx565akm_bl_update_status_locked(struct backlight_device *dev)
{
struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
int level;
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
level = dev->props.brightness;
else
level = 0;
int level = backlight_get_brightness(dev);
acx565akm_set_brightness(lcd, level);
@ -330,8 +324,7 @@ static int acx565akm_bl_get_intensity(struct backlight_device *dev)
mutex_lock(&lcd->mutex);
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
if (!backlight_is_blank(dev))
intensity = acx565akm_get_actual_brightness(lcd);
else
intensity = 0;
@ -349,7 +342,6 @@ static const struct backlight_ops acx565akm_bl_ops = {
static int acx565akm_backlight_init(struct acx565akm_panel *lcd)
{
struct backlight_properties props = {
.fb_blank = FB_BLANK_UNBLANK,
.power = FB_BLANK_UNBLANK,
.type = BACKLIGHT_RAW,
};

View File

@ -249,7 +249,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
cntl |= CNTL_ST_CDWID_24;
/*
* Note that the the ARM hardware's format reader takes 'r' from
* Note that the ARM hardware's format reader takes 'r' from
* the low bit, while DRM formats list channels from high bit
* to low bit as you read left to right. The ST Micro version of
* the PL110 (LCDC) however uses the standard DRM format.

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Test cases for for the DRM DP MST helpers
* Test cases for the DRM DP MST helpers
*/
#define PREFIX_STR "[drm_dp_mst_helper]"

View File

@ -18,11 +18,7 @@ static int shmob_drm_backlight_update(struct backlight_device *bdev)
struct shmob_drm_connector *scon = bl_get_data(bdev);
struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
int brightness = bdev->props.brightness;
if (bdev->props.power != FB_BLANK_UNBLANK ||
bdev->props.state & BL_CORE_SUSPENDED)
brightness = 0;
int brightness = backlight_get_brightness(bdev);
return bdata->set_brightness(brightness);
}

View File

@ -95,6 +95,7 @@ static int drv_load(struct drm_device *ddev)
ddev->mode_config.max_width = STM_MAX_FB_WIDTH;
ddev->mode_config.max_height = STM_MAX_FB_HEIGHT;
ddev->mode_config.funcs = &drv_mode_config_funcs;
ddev->mode_config.normalize_zpos = true;
ret = ltdc_load(ddev);
if (ret)

View File

@ -165,16 +165,20 @@
#define BCCR_BCWHITE GENMASK(23, 0) /* Background Color WHITE */
#define IER_LIE BIT(0) /* Line Interrupt Enable */
#define IER_FUIE BIT(1) /* Fifo Underrun Interrupt Enable */
#define IER_FUWIE BIT(1) /* Fifo Underrun Warning Interrupt Enable */
#define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */
#define IER_RRIE BIT(3) /* Register Reload Interrupt enable */
#define IER_RRIE BIT(3) /* Register Reload Interrupt Enable */
#define IER_FUEIE BIT(6) /* Fifo Underrun Error Interrupt Enable */
#define IER_CRCIE BIT(7) /* CRC Error Interrupt Enable */
#define CPSR_CYPOS GENMASK(15, 0) /* Current Y position */
#define ISR_LIF BIT(0) /* Line Interrupt Flag */
#define ISR_FUIF BIT(1) /* Fifo Underrun Interrupt Flag */
#define ISR_FUWIF BIT(1) /* Fifo Underrun Warning Interrupt Flag */
#define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */
#define ISR_RRIF BIT(3) /* Register Reload Interrupt Flag */
#define ISR_FUEIF BIT(6) /* Fifo Underrun Error Interrupt Flag */
#define ISR_CRCIF BIT(7) /* CRC Error Interrupt Flag */
#define EDCR_OCYEN BIT(25) /* Output Conversion to YCbCr 422: ENable */
#define EDCR_OCYSEL BIT(26) /* Output Conversion to YCbCr 422: SELection of the CCIR */
@ -183,6 +187,7 @@
#define LXCR_LEN BIT(0) /* Layer ENable */
#define LXCR_COLKEN BIT(1) /* Color Keying Enable */
#define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */
#define LXCR_HMEN BIT(8) /* Horizontal Mirroring ENable */
#define LXWHPCR_WHSTPOS GENMASK(11, 0) /* Window Horizontal StarT POSition */
#define LXWHPCR_WHSPPOS GENMASK(27, 16) /* Window Horizontal StoP POSition */
@ -197,9 +202,10 @@
#define LXBFCR_BF2 GENMASK(2, 0) /* Blending Factor 2 */
#define LXBFCR_BF1 GENMASK(10, 8) /* Blending Factor 1 */
#define LXBFCR_BOR GENMASK(18, 16) /* Blending ORder */
#define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */
#define LXCFBLR_CFBP GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */
#define LXCFBLR_CFBP GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */
#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
@ -232,6 +238,8 @@
#define NB_PF 8 /* Max nb of HW pixel format */
#define FUT_DFT 128 /* Default value of fifo underrun threshold */
/*
* Skip the first value and the second in case CRC was enabled during
* the thread irq. This is to be sure CRC value is relevant for the
@ -712,12 +720,13 @@ static irqreturn_t ltdc_irq_thread(int irq, void *arg)
ltdc_irq_crc_handle(ldev, crtc);
}
/* Save FIFO Underrun & Transfer Error status */
mutex_lock(&ldev->err_lock);
if (ldev->irq_status & ISR_FUIF)
ldev->error_status |= ISR_FUIF;
if (ldev->irq_status & ISR_TERRIF)
ldev->error_status |= ISR_TERRIF;
ldev->transfer_err++;
if (ldev->irq_status & ISR_FUEIF)
ldev->fifo_err++;
if (ldev->irq_status & ISR_FUWIF)
ldev->fifo_warn++;
mutex_unlock(&ldev->err_lock);
return IRQ_HANDLED;
@ -776,7 +785,7 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
/* Enable IRQ */
regmap_set_bits(ldev->regmap, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
regmap_set_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
/* Commit shadow registers = update planes at next vblank */
if (!ldev->caps.plane_reg_shadow)
@ -790,19 +799,32 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
{
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
struct drm_device *ddev = crtc->dev;
int layer_index = 0;
DRM_DEBUG_DRIVER("\n");
drm_crtc_vblank_off(crtc);
/* Disable all layers */
for (layer_index = 0; layer_index < ldev->caps.nb_layers; layer_index++)
regmap_write_bits(ldev->regmap, LTDC_L1CR + layer_index * LAY_OFS,
LXCR_CLUTEN | LXCR_LEN, 0);
/* disable IRQ */
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
/* immediately commit disable of layers before switching off LTDC */
if (!ldev->caps.plane_reg_shadow)
regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
pm_runtime_put_sync(ddev->dev);
/* clear interrupt error counters */
mutex_lock(&ldev->err_lock);
ldev->transfer_err = 0;
ldev->fifo_err = 0;
ldev->fifo_warn = 0;
mutex_unlock(&ldev->err_lock);
}
#define CLK_TOLERANCE_HZ 50
@ -905,9 +927,9 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
drm_connector_list_iter_end(&iter);
}
if (bridge && bridge->timings)
if (bridge && bridge->timings) {
bus_flags = bridge->timings->input_bus_flags;
else if (connector) {
} else if (connector) {
bus_flags = connector->display_info.bus_flags;
if (connector->display_info.num_bus_formats)
bus_formats = connector->display_info.bus_formats[0];
@ -1163,6 +1185,18 @@ static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
return 0;
}
static void ltdc_crtc_atomic_print_state(struct drm_printer *p,
const struct drm_crtc_state *state)
{
struct drm_crtc *crtc = state->crtc;
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
drm_printf(p, "\ttransfer_error=%d\n", ldev->transfer_err);
drm_printf(p, "\tfifo_underrun_error=%d\n", ldev->fifo_err);
drm_printf(p, "\tfifo_underrun_warning=%d\n", ldev->fifo_warn);
drm_printf(p, "\tfifo_underrun_threshold=%d\n", ldev->fifo_threshold);
}
static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
@ -1173,6 +1207,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.enable_vblank = ltdc_crtc_enable_vblank,
.disable_vblank = ltdc_crtc_disable_vblank,
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
.atomic_print_state = ltdc_crtc_atomic_print_state,
};
static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
@ -1187,6 +1222,7 @@ static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
.set_crc_source = ltdc_crtc_set_crc_source,
.verify_crc_source = ltdc_crtc_verify_crc_source,
.atomic_print_state = ltdc_crtc_atomic_print_state,
};
/*
@ -1212,7 +1248,8 @@ static int ltdc_plane_atomic_check(struct drm_plane *plane,
/* Reject scaling */
if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
DRM_ERROR("Scaling is not supported");
DRM_DEBUG_DRIVER("Scaling is not supported");
return -EINVAL;
}
@ -1232,7 +1269,8 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
u32 y0 = newstate->crtc_y;
u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
u32 src_x, src_y, src_w, src_h;
u32 val, pitch_in_bytes, line_length, line_number, paddr, ahbp, avbp, bpcr;
u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
u32 paddr, paddr1, paddr2;
enum ltdc_pix_fmt pf;
if (!newstate->crtc || !fb) {
@ -1284,13 +1322,6 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
}
regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
/* Configures the color frame buffer pitch in bytes & line length */
pitch_in_bytes = fb->pitches[0];
line_length = fb->format->cpp[0] *
(x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
val = ((pitch_in_bytes << 16) | line_length);
regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
/* Specifies the constant alpha value */
val = newstate->alpha >> 8;
regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
@ -1305,78 +1336,124 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
plane->type != DRM_PLANE_TYPE_PRIMARY)
val = BF1_PAXCA | BF2_1PAXCA;
regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs, LXBFCR_BF2 | LXBFCR_BF1, val);
/* Configures the frame buffer line number */
line_number = y1 - y0 + 1;
regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
if (ldev->caps.dynamic_zorder) {
val |= (newstate->normalized_zpos << 16);
regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
LXBFCR_BF2 | LXBFCR_BF1 | LXBFCR_BOR, val);
} else {
regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
LXBFCR_BF2 | LXBFCR_BF1, val);
}
/* Sets the FB address */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 0);
if (newstate->rotation & DRM_MODE_REFLECT_X)
paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;
if (newstate->rotation & DRM_MODE_REFLECT_Y)
paddr += (fb->pitches[0] * (y1 - y0));
DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
/* Configures the color frame buffer pitch in bytes & line length */
line_length = fb->format->cpp[0] *
(x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
if (newstate->rotation & DRM_MODE_REFLECT_Y)
/* Compute negative value (signed on 16 bits) for the picth */
pitch_in_bytes = 0x10000 - fb->pitches[0];
else
pitch_in_bytes = fb->pitches[0];
val = (pitch_in_bytes << 16) | line_length;
regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
/* Configures the frame buffer line number */
line_number = y1 - y0 + 1;
regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
if (ldev->caps.ycbcr_input) {
if (fb->format->is_yuv) {
switch (fb->format->format) {
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
/* Configure the auxiliary frame buffer address 0 & 1 */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr + 1);
/* Configure the auxiliary frame buffer address 0 */
paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
/* Configure the buffer length */
val = ((pitch_in_bytes << 16) | line_length);
regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
if (newstate->rotation & DRM_MODE_REFLECT_X)
paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
/* Configure the frame buffer line number */
val = (line_number >> 1);
regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
if (newstate->rotation & DRM_MODE_REFLECT_Y)
paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
break;
case DRM_FORMAT_YUV420:
/* Configure the auxiliary frame buffer address 0 */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
/* Configure the auxiliary frame buffer address 0 & 1 */
paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
/* Configure the auxiliary frame buffer address 1 */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
if (newstate->rotation & DRM_MODE_REFLECT_X) {
paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
}
line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
(ldev->caps.bus_width >> 3) - 1;
if (newstate->rotation & DRM_MODE_REFLECT_Y) {
paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
}
/* Configure the buffer length */
val = (((pitch_in_bytes >> 1) << 16) | line_length);
regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
/* Configure the frame buffer line number */
val = (line_number >> 1);
regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
break;
case DRM_FORMAT_YVU420:
/* Configure the auxiliary frame buffer address 0 */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
/* Configure the auxiliary frame buffer address 0 & 1 */
paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
/* Configure the auxiliary frame buffer address 1 */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
if (newstate->rotation & DRM_MODE_REFLECT_X) {
paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
}
line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
(ldev->caps.bus_width >> 3) - 1;
if (newstate->rotation & DRM_MODE_REFLECT_Y) {
paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
}
/* Configure the buffer length */
val = (((pitch_in_bytes >> 1) << 16) | line_length);
regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
/* Configure the frame buffer line number */
val = (line_number >> 1);
regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
break;
}
/*
* Set the length and the number of lines of the auxiliary
* buffers if the framebuffer contains more than one plane.
*/
if (fb->format->num_planes > 1) {
if (newstate->rotation & DRM_MODE_REFLECT_Y)
/*
* Compute negative value (signed on 16 bits)
* for the picth
*/
pitch_in_bytes = 0x10000 - fb->pitches[1];
else
pitch_in_bytes = fb->pitches[1];
line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
(ldev->caps.bus_width >> 3) - 1;
/* Configure the auxiliary buffer length */
val = (pitch_in_bytes << 16) | line_length;
regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
/* Configure the auxiliary frame buffer line number */
val = line_number >> 1;
regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
}
/* Configure YCbC conversion coefficient */
ltdc_set_ycbcr_coeffs(plane);
@ -1391,7 +1468,12 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
/* Enable layer and CLUT if needed */
val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
val |= LXCR_LEN;
regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN, val);
/* Enable horizontal mirroring if requested */
if (newstate->rotation & DRM_MODE_REFLECT_X)
val |= LXCR_HMEN;
regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);
/* Commit shadow registers = update plane at next vblank */
if (ldev->caps.plane_reg_shadow)
@ -1401,13 +1483,21 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
ldev->plane_fpsi[plane->index].counter++;
mutex_lock(&ldev->err_lock);
if (ldev->error_status & ISR_FUIF) {
DRM_WARN("ltdc fifo underrun: please verify display mode\n");
ldev->error_status &= ~ISR_FUIF;
if (ldev->transfer_err) {
DRM_WARN("ltdc transfer error: %d\n", ldev->transfer_err);
ldev->transfer_err = 0;
}
if (ldev->error_status & ISR_TERRIF) {
DRM_WARN("ltdc transfer error\n");
ldev->error_status &= ~ISR_TERRIF;
if (ldev->caps.fifo_threshold) {
if (ldev->fifo_err) {
DRM_WARN("ltdc fifo underrun: please verify display mode\n");
ldev->fifo_err = 0;
}
} else {
if (ldev->fifo_warn >= ldev->fifo_threshold) {
DRM_WARN("ltdc fifo underrun: please verify display mode\n");
ldev->fifo_warn = 0;
}
}
mutex_unlock(&ldev->err_lock);
}
@ -1420,8 +1510,8 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
struct ltdc_device *ldev = plane_to_ltdc(plane);
u32 lofs = plane->index * LAY_OFS;
/* disable layer */
regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN, 0);
/* Disable layer */
regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, 0);
/* Commit shadow registers = update plane at next vblank */
if (ldev->caps.plane_reg_shadow)
@ -1565,6 +1655,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
{
struct ltdc_device *ldev = ddev->dev_private;
struct drm_plane *primary, *overlay;
int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
unsigned int i;
int ret;
@ -1574,7 +1665,14 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
return -EINVAL;
}
drm_plane_create_zpos_immutable_property(primary, 0);
if (ldev->caps.dynamic_zorder)
drm_plane_create_zpos_property(primary, 0, 0, ldev->caps.nb_layers - 1);
else
drm_plane_create_zpos_immutable_property(primary, 0);
if (ldev->caps.plane_rotation)
drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
supported_rotations);
/* Init CRTC according to its hardware features */
if (ldev->caps.crc)
@ -1603,7 +1701,14 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
DRM_ERROR("Can not create overlay plane %d\n", i);
goto cleanup;
}
drm_plane_create_zpos_immutable_property(overlay, i);
if (ldev->caps.dynamic_zorder)
drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
else
drm_plane_create_zpos_immutable_property(overlay, i);
if (ldev->caps.plane_rotation)
drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
supported_rotations);
}
return 0;
@ -1634,6 +1739,10 @@ static void ltdc_encoder_enable(struct drm_encoder *encoder)
DRM_DEBUG_DRIVER("\n");
/* set fifo underrun threshold register */
if (ldev->caps.fifo_threshold)
regmap_write(ldev->regmap, LTDC_FUT, ldev->fifo_threshold);
/* Enable LTDC */
regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
}
@ -1733,6 +1842,9 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_output = false;
ldev->caps.plane_reg_shadow = false;
ldev->caps.crc = false;
ldev->caps.dynamic_zorder = false;
ldev->caps.plane_rotation = false;
ldev->caps.fifo_threshold = false;
break;
case HWVER_20101:
ldev->caps.layer_ofs = LAY_OFS_0;
@ -1748,6 +1860,9 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_output = false;
ldev->caps.plane_reg_shadow = false;
ldev->caps.crc = false;
ldev->caps.dynamic_zorder = false;
ldev->caps.plane_rotation = false;
ldev->caps.fifo_threshold = false;
break;
case HWVER_40100:
ldev->caps.layer_ofs = LAY_OFS_1;
@ -1763,6 +1878,9 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_output = true;
ldev->caps.plane_reg_shadow = true;
ldev->caps.crc = true;
ldev->caps.dynamic_zorder = true;
ldev->caps.plane_rotation = true;
ldev->caps.fifo_threshold = true;
break;
default:
return -ENODEV;
@ -1887,9 +2005,6 @@ int ltdc_load(struct drm_device *ddev)
goto err;
}
/* Disable interrupts */
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
ret = ltdc_get_caps(ddev);
if (ret) {
DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
@ -1897,8 +2012,22 @@ int ltdc_load(struct drm_device *ddev)
goto err;
}
/* Disable interrupts */
if (ldev->caps.fifo_threshold)
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
IER_TERRIE);
else
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
IER_TERRIE | IER_FUEIE);
DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version);
/* initialize default value for fifo underrun threshold & clear interrupt error counters */
ldev->transfer_err = 0;
ldev->fifo_err = 0;
ldev->fifo_warn = 0;
ldev->fifo_threshold = FUT_DFT;
for (i = 0; i < ldev->caps.nb_irq; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0) {
@ -1913,7 +2042,6 @@ int ltdc_load(struct drm_device *ddev)
DRM_ERROR("Failed to register LTDC interrupt\n");
goto err;
}
}
crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);

View File

@ -28,6 +28,9 @@ struct ltdc_caps {
bool ycbcr_output; /* ycbcr output converter supported */
bool plane_reg_shadow; /* plane shadow registers ability */
bool crc; /* cyclic redundancy check supported */
bool dynamic_zorder; /* dynamic z-order */
bool plane_rotation; /* plane rotation */
bool fifo_threshold; /* fifo underrun threshold supported */
};
#define LTDC_MAX_LAYER 4
@ -43,8 +46,11 @@ struct ltdc_device {
struct clk *pixel_clk; /* lcd pixel clock */
struct mutex err_lock; /* protecting error_status */
struct ltdc_caps caps;
u32 error_status;
u32 irq_status;
u32 fifo_err; /* fifo underrun error counter */
u32 fifo_warn; /* fifo underrun warning counter */
u32 fifo_threshold; /* fifo underrun threshold */
u32 transfer_err; /* transfer error counter */
struct fps_info plane_fpsi[LTDC_MAX_LAYER];
struct drm_atomic_state *suspend_state;
int crc_skip_count;

View File

@ -0,0 +1,3 @@
CONFIG_KUNIT=y
CONFIG_DRM=y
CONFIG_DRM_KUNIT_TEST=y

View File

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o

View File

@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0+
#include <kunit/test.h>
#include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mode.h>
#include <drm/drm_print.h>
#include <drm/drm_rect.h>
#include "../drm_crtc_internal.h"
#define TEST_BUF_SIZE 50
struct xrgb8888_to_rgb332_case {
const char *name;
unsigned int pitch;
unsigned int dst_pitch;
struct drm_rect clip;
const u32 xrgb8888[TEST_BUF_SIZE];
const u8 expected[4 * TEST_BUF_SIZE];
};
static struct xrgb8888_to_rgb332_case xrgb8888_to_rgb332_cases[] = {
{
.name = "single_pixel_source_buffer",
.pitch = 1 * 4,
.dst_pitch = 0,
.clip = DRM_RECT_INIT(0, 0, 1, 1),
.xrgb8888 = { 0x01FF0000 },
.expected = { 0xE0 },
},
{
.name = "single_pixel_clip_rectangle",
.pitch = 2 * 4,
.dst_pitch = 0,
.clip = DRM_RECT_INIT(1, 1, 1, 1),
.xrgb8888 = {
0x00000000, 0x00000000,
0x00000000, 0x10FF0000,
},
.expected = { 0xE0 },
},
{
/* Well known colors: White, black, red, green, blue, magenta,
* yellow and cyan. Different values for the X in XRGB8888 to
* make sure it is ignored. Partial clip area.
*/
.name = "well_known_colors",
.pitch = 4 * 4,
.dst_pitch = 0,
.clip = DRM_RECT_INIT(1, 1, 2, 4),
.xrgb8888 = {
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x11FFFFFF, 0x22000000, 0x00000000,
0x00000000, 0x33FF0000, 0x4400FF00, 0x00000000,
0x00000000, 0x550000FF, 0x66FF00FF, 0x00000000,
0x00000000, 0x77FFFF00, 0x8800FFFF, 0x00000000,
},
.expected = {
0xFF, 0x00,
0xE0, 0x1C,
0x03, 0xE3,
0xFC, 0x1F,
},
},
{
/* Randomly picked colors. Full buffer within the clip area. */
.name = "destination_pitch",
.pitch = 3 * 4,
.dst_pitch = 5,
.clip = DRM_RECT_INIT(0, 0, 3, 3),
.xrgb8888 = {
0xA10E449C, 0xB1114D05, 0xC1A80303,
0xD16C7073, 0xA20E449C, 0xB2114D05,
0xC2A80303, 0xD26C7073, 0xA30E449C,
},
.expected = {
0x0A, 0x08, 0xA0, 0x00, 0x00,
0x6D, 0x0A, 0x08, 0x00, 0x00,
0xA0, 0x6D, 0x0A, 0x00, 0x00,
},
},
};
/*
* conversion_buf_size - Return the destination buffer size required to convert
* between formats.
* @dst_format: destination buffer pixel format (DRM_FORMAT_*)
* @dst_pitch: Number of bytes between two consecutive scanlines within dst
* @clip: Clip rectangle area to convert
*
* Returns:
* The size of the destination buffer or negative value on error.
*/
static size_t conversion_buf_size(u32 dst_format, unsigned int dst_pitch,
const struct drm_rect *clip)
{
const struct drm_format_info *dst_fi = drm_format_info(dst_format);
if (!dst_fi)
return -EINVAL;
if (!dst_pitch)
dst_pitch = drm_rect_width(clip) * dst_fi->cpp[0];
return dst_pitch * drm_rect_height(clip);
}
static void xrgb8888_to_rgb332_case_desc(struct xrgb8888_to_rgb332_case *t,
char *desc)
{
strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(xrgb8888_to_rgb332, xrgb8888_to_rgb332_cases,
xrgb8888_to_rgb332_case_desc);
static void xrgb8888_to_rgb332_test(struct kunit *test)
{
const struct xrgb8888_to_rgb332_case *params = test->param_value;
size_t dst_size;
__u8 *dst = NULL;
struct drm_framebuffer fb = {
.format = drm_format_info(DRM_FORMAT_XRGB8888),
.pitches = { params->pitch, 0, 0 },
};
dst_size = conversion_buf_size(DRM_FORMAT_RGB332, params->dst_pitch,
&params->clip);
KUNIT_ASSERT_GT(test, dst_size, 0);
dst = kunit_kzalloc(test, dst_size, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dst);
drm_fb_xrgb8888_to_rgb332(dst, params->dst_pitch, params->xrgb8888,
&fb, &params->clip);
KUNIT_EXPECT_EQ(test, memcmp(dst, params->expected, dst_size), 0);
}
static struct kunit_case drm_format_helper_test_cases[] = {
KUNIT_CASE_PARAM(xrgb8888_to_rgb332_test,
xrgb8888_to_rgb332_gen_params),
{}
};
static struct kunit_suite drm_format_helper_test_suite = {
.name = "drm_format_helper_test",
.test_cases = drm_format_helper_test_cases,
};
kunit_test_suite(drm_format_helper_test_suite);
MODULE_DESCRIPTION("KUnit tests for the drm_format_helper APIs");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>");

View File

@ -69,6 +69,7 @@ config DRM_PANEL_MIPI_DBI
config DRM_SIMPLEDRM
tristate "Simple framebuffer driver"
depends on DRM && MMU
select APERTURE_HELPERS
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
help

View File

@ -317,10 +317,13 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
struct drm_crtc_state *crtc_state = crtc->state;
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1;
bool is_hdmi = vc4_encoder->type == VC4_ENCODER_TYPE_HDMI0 ||
vc4_encoder->type == VC4_ENCODER_TYPE_HDMI1;
u32 pixel_rep = ((mode->flags & DRM_MODE_FLAG_DBLCLK) && !is_hdmi) ? 2 : 1;
bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 ||
vc4_encoder->type == VC4_ENCODER_TYPE_DSI1);
u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
bool is_dsi1 = vc4_encoder->type == VC4_ENCODER_TYPE_DSI1;
u32 format = is_dsi1 ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
u8 ppc = pv_data->pixels_per_clock;
bool debug_dump_regs = false;
@ -346,7 +349,8 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
PV_HORZB_HACTIVE));
CRTC_WRITE(PV_VERTA,
VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end +
interlace,
PV_VERTA_VBP) |
VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
PV_VERTA_VSYNC));
@ -358,7 +362,7 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
if (interlace) {
CRTC_WRITE(PV_VERTA_EVEN,
VC4_SET_FIELD(mode->crtc_vtotal -
mode->crtc_vsync_end - 1,
mode->crtc_vsync_end,
PV_VERTA_VBP) |
VC4_SET_FIELD(mode->crtc_vsync_end -
mode->crtc_vsync_start,
@ -378,7 +382,7 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
PV_VCONTROL_CONTINUOUS |
(is_dsi ? PV_VCONTROL_DSI : 0) |
PV_VCONTROL_INTERLACE |
VC4_SET_FIELD(mode->htotal * pixel_rep / 2,
VC4_SET_FIELD(mode->htotal * pixel_rep / (2 * ppc),
PV_VCONTROL_ODD_DELAY));
CRTC_WRITE(PV_VSYNCD_EVEN, 0);
} else {

View File

@ -131,7 +131,7 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
struct vc4_dpi *dpi = vc4_encoder->dpi;
struct drm_connector_list_iter conn_iter;
struct drm_connector *connector = NULL, *connector_scan;
u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
u32 dpi_c = DPI_ENABLE;
int ret;
/* Look up the connector attached to DPI so we can get the
@ -148,49 +148,68 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
}
drm_connector_list_iter_end(&conn_iter);
if (connector && connector->display_info.num_bus_formats) {
u32 bus_format = connector->display_info.bus_formats[0];
/* Default to 24bit if no connector or format found. */
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, DPI_FORMAT);
switch (bus_format) {
case MEDIA_BUS_FMT_RGB888_1X24:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB,
DPI_FORMAT);
break;
case MEDIA_BUS_FMT_BGR888_1X24:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB,
DPI_FORMAT);
dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);
break;
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_2,
DPI_FORMAT);
break;
case MEDIA_BUS_FMT_RGB666_1X18:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1,
DPI_FORMAT);
break;
case MEDIA_BUS_FMT_RGB565_1X16:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_3,
DPI_FORMAT);
break;
default:
DRM_ERROR("Unknown media bus format %d\n", bus_format);
break;
if (connector) {
if (connector->display_info.num_bus_formats) {
u32 bus_format = connector->display_info.bus_formats[0];
dpi_c &= ~DPI_FORMAT_MASK;
switch (bus_format) {
case MEDIA_BUS_FMT_RGB888_1X24:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB,
DPI_FORMAT);
break;
case MEDIA_BUS_FMT_BGR888_1X24:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB,
DPI_FORMAT);
dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR,
DPI_ORDER);
break;
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_2,
DPI_FORMAT);
break;
case MEDIA_BUS_FMT_RGB666_1X18:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1,
DPI_FORMAT);
break;
case MEDIA_BUS_FMT_RGB565_1X16:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_3,
DPI_FORMAT);
break;
default:
DRM_ERROR("Unknown media bus format %d\n",
bus_format);
break;
}
}
} else {
/* Default to 24bit if no connector found. */
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, DPI_FORMAT);
if (connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
dpi_c |= DPI_PIXEL_CLK_INVERT;
if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW)
dpi_c |= DPI_OUTPUT_ENABLE_INVERT;
}
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
dpi_c |= DPI_HSYNC_INVERT;
else if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
dpi_c |= DPI_HSYNC_DISABLE;
if (mode->flags & DRM_MODE_FLAG_CSYNC) {
if (mode->flags & DRM_MODE_FLAG_NCSYNC)
dpi_c |= DPI_OUTPUT_ENABLE_INVERT;
} else {
dpi_c |= DPI_OUTPUT_ENABLE_MODE;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
dpi_c |= DPI_VSYNC_INVERT;
else if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
dpi_c |= DPI_VSYNC_DISABLE;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
dpi_c |= DPI_HSYNC_INVERT;
else if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
dpi_c |= DPI_HSYNC_DISABLE;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
dpi_c |= DPI_VSYNC_INVERT;
else if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
dpi_c |= DPI_VSYNC_DISABLE;
}
DPI_WRITE(DPI_C, dpi_c);

View File

@ -209,6 +209,15 @@ static void vc4_match_add_drivers(struct device *dev,
}
}
const struct of_device_id vc4_dma_range_matches[] = {
{ .compatible = "brcm,bcm2711-hvs" },
{ .compatible = "brcm,bcm2835-hvs" },
{ .compatible = "brcm,bcm2835-v3d" },
{ .compatible = "brcm,cygnus-v3d" },
{ .compatible = "brcm,vc4-v3d" },
{}
};
static int vc4_drm_bind(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@ -227,6 +236,16 @@ static int vc4_drm_bind(struct device *dev)
vc4_drm_driver.driver_features &= ~DRIVER_RENDER;
of_node_put(node);
node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches,
NULL);
if (node) {
ret = of_dma_configure(dev, node, true);
of_node_put(node);
if (ret)
return ret;
}
vc4 = devm_drm_dev_alloc(dev, &vc4_drm_driver, struct vc4_dev, base);
if (IS_ERR(vc4))
return PTR_ERR(vc4);

View File

@ -181,8 +181,50 @@
#define DSI0_TXPKT_PIX_FIFO 0x20 /* AKA PIX_FIFO */
#define DSI0_INT_STAT 0x24
#define DSI0_INT_EN 0x28
#define DSI0_INT_STAT 0x24
#define DSI0_INT_EN 0x28
# define DSI0_INT_FIFO_ERR BIT(25)
# define DSI0_INT_CMDC_DONE_MASK VC4_MASK(24, 23)
# define DSI0_INT_CMDC_DONE_SHIFT 23
# define DSI0_INT_CMDC_DONE_NO_REPEAT 1
# define DSI0_INT_CMDC_DONE_REPEAT 3
# define DSI0_INT_PHY_DIR_RTF BIT(22)
# define DSI0_INT_PHY_D1_ULPS BIT(21)
# define DSI0_INT_PHY_D1_STOP BIT(20)
# define DSI0_INT_PHY_RXLPDT BIT(19)
# define DSI0_INT_PHY_RXTRIG BIT(18)
# define DSI0_INT_PHY_D0_ULPS BIT(17)
# define DSI0_INT_PHY_D0_LPDT BIT(16)
# define DSI0_INT_PHY_D0_FTR BIT(15)
# define DSI0_INT_PHY_D0_STOP BIT(14)
/* Signaled when the clock lane enters the given state. */
# define DSI0_INT_PHY_CLK_ULPS BIT(13)
# define DSI0_INT_PHY_CLK_HS BIT(12)
# define DSI0_INT_PHY_CLK_FTR BIT(11)
/* Signaled on timeouts */
# define DSI0_INT_PR_TO BIT(10)
# define DSI0_INT_TA_TO BIT(9)
# define DSI0_INT_LPRX_TO BIT(8)
# define DSI0_INT_HSTX_TO BIT(7)
/* Contention on a line when trying to drive the line low */
# define DSI0_INT_ERR_CONT_LP1 BIT(6)
# define DSI0_INT_ERR_CONT_LP0 BIT(5)
/* Control error: incorrect line state sequence on data lane 0. */
# define DSI0_INT_ERR_CONTROL BIT(4)
# define DSI0_INT_ERR_SYNC_ESC BIT(3)
# define DSI0_INT_RX2_PKT BIT(2)
# define DSI0_INT_RX1_PKT BIT(1)
# define DSI0_INT_CMD_PKT BIT(0)
#define DSI0_INTERRUPTS_ALWAYS_ENABLED (DSI0_INT_ERR_SYNC_ESC | \
DSI0_INT_ERR_CONTROL | \
DSI0_INT_ERR_CONT_LP0 | \
DSI0_INT_ERR_CONT_LP1 | \
DSI0_INT_HSTX_TO | \
DSI0_INT_LPRX_TO | \
DSI0_INT_TA_TO | \
DSI0_INT_PR_TO)
# define DSI1_INT_PHY_D3_ULPS BIT(30)
# define DSI1_INT_PHY_D3_STOP BIT(29)
# define DSI1_INT_PHY_D2_ULPS BIT(28)
@ -761,6 +803,9 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
if (iter->funcs->disable)
iter->funcs->disable(iter);
if (iter == dsi->bridge)
break;
}
vc4_dsi_ulps(dsi, true);
@ -805,11 +850,9 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
/* Find what divider gets us a faster clock than the requested
* pixel clock.
*/
for (divider = 1; divider < 8; divider++) {
if (parent_rate / divider < pll_clock) {
divider--;
for (divider = 1; divider < 255; divider++) {
if (parent_rate / (divider + 1) < pll_clock)
break;
}
}
/* Now that we've picked a PLL divider, calculate back to its
@ -894,6 +937,9 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
DSI_PORT_WRITE(PHY_AFEC0, afec0);
/* AFEC reset hold time */
mdelay(1);
DSI_PORT_WRITE(PHY_AFEC1,
VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE1) |
VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE0) |
@ -1060,12 +1106,9 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN);
/* Bring AFE out of reset. */
if (dsi->variant->port == 0) {
} else {
DSI_PORT_WRITE(PHY_AFEC0,
DSI_PORT_READ(PHY_AFEC0) &
~DSI1_PHY_AFEC0_RESET);
}
DSI_PORT_WRITE(PHY_AFEC0,
DSI_PORT_READ(PHY_AFEC0) &
~DSI_PORT_BIT(PHY_AFEC0_RESET));
vc4_dsi_ulps(dsi, false);
@ -1184,13 +1227,28 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
/* Enable the appropriate interrupt for the transfer completion. */
dsi->xfer_result = 0;
reinit_completion(&dsi->xfer_completion);
DSI_PORT_WRITE(INT_STAT, DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF);
if (msg->rx_len) {
DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED |
DSI1_INT_PHY_DIR_RTF));
if (dsi->variant->port == 0) {
DSI_PORT_WRITE(INT_STAT,
DSI0_INT_CMDC_DONE_MASK | DSI1_INT_PHY_DIR_RTF);
if (msg->rx_len) {
DSI_PORT_WRITE(INT_EN, (DSI0_INTERRUPTS_ALWAYS_ENABLED |
DSI0_INT_PHY_DIR_RTF));
} else {
DSI_PORT_WRITE(INT_EN,
(DSI0_INTERRUPTS_ALWAYS_ENABLED |
VC4_SET_FIELD(DSI0_INT_CMDC_DONE_NO_REPEAT,
DSI0_INT_CMDC_DONE)));
}
} else {
DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED |
DSI1_INT_TXPKT1_DONE));
DSI_PORT_WRITE(INT_STAT,
DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF);
if (msg->rx_len) {
DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED |
DSI1_INT_PHY_DIR_RTF));
} else {
DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED |
DSI1_INT_TXPKT1_DONE));
}
}
/* Send the packet. */
@ -1207,7 +1265,7 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
ret = dsi->xfer_result;
}
DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED);
DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED));
if (ret)
goto reset_fifo_and_return;
@ -1253,7 +1311,7 @@ reset_fifo_and_return:
DSI_PORT_BIT(CTRL_RESET_FIFOS));
DSI_PORT_WRITE(TXPKT1C, 0);
DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED);
DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED));
return ret;
}
@ -1390,26 +1448,28 @@ static irqreturn_t vc4_dsi_irq_handler(int irq, void *data)
DSI_PORT_WRITE(INT_STAT, stat);
dsi_handle_error(dsi, &ret, stat,
DSI1_INT_ERR_SYNC_ESC, "LPDT sync");
DSI_PORT_BIT(INT_ERR_SYNC_ESC), "LPDT sync");
dsi_handle_error(dsi, &ret, stat,
DSI1_INT_ERR_CONTROL, "data lane 0 sequence");
DSI_PORT_BIT(INT_ERR_CONTROL), "data lane 0 sequence");
dsi_handle_error(dsi, &ret, stat,
DSI1_INT_ERR_CONT_LP0, "LP0 contention");
DSI_PORT_BIT(INT_ERR_CONT_LP0), "LP0 contention");
dsi_handle_error(dsi, &ret, stat,
DSI1_INT_ERR_CONT_LP1, "LP1 contention");
DSI_PORT_BIT(INT_ERR_CONT_LP1), "LP1 contention");
dsi_handle_error(dsi, &ret, stat,
DSI1_INT_HSTX_TO, "HSTX timeout");
DSI_PORT_BIT(INT_HSTX_TO), "HSTX timeout");
dsi_handle_error(dsi, &ret, stat,
DSI1_INT_LPRX_TO, "LPRX timeout");
DSI_PORT_BIT(INT_LPRX_TO), "LPRX timeout");
dsi_handle_error(dsi, &ret, stat,
DSI1_INT_TA_TO, "turnaround timeout");
DSI_PORT_BIT(INT_TA_TO), "turnaround timeout");
dsi_handle_error(dsi, &ret, stat,
DSI1_INT_PR_TO, "peripheral reset timeout");
DSI_PORT_BIT(INT_PR_TO), "peripheral reset timeout");
if (stat & (DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF)) {
if (stat & ((dsi->variant->port ? DSI1_INT_TXPKT1_DONE :
DSI0_INT_CMDC_DONE_MASK) |
DSI_PORT_BIT(INT_PHY_DIR_RTF))) {
complete(&dsi->xfer_completion);
ret = IRQ_HANDLED;
} else if (stat & DSI1_INT_HSTX_TO) {
} else if (stat & DSI_PORT_BIT(INT_HSTX_TO)) {
complete(&dsi->xfer_completion);
dsi->xfer_result = -ETIMEDOUT;
ret = IRQ_HANDLED;
@ -1487,13 +1547,29 @@ vc4_dsi_init_phy_clocks(struct vc4_dsi *dsi)
dsi->clk_onecell);
}
static void vc4_dsi_dma_mem_release(void *ptr)
{
struct vc4_dsi *dsi = ptr;
struct device *dev = &dsi->pdev->dev;
dma_free_coherent(dev, 4, dsi->reg_dma_mem, dsi->reg_dma_paddr);
dsi->reg_dma_mem = NULL;
}
static void vc4_dsi_dma_chan_release(void *ptr)
{
struct vc4_dsi *dsi = ptr;
dma_release_channel(dsi->reg_dma_chan);
dsi->reg_dma_chan = NULL;
}
static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dsi *dsi = dev_get_drvdata(dev);
struct vc4_dsi_encoder *vc4_dsi_encoder;
dma_cap_mask_t dma_mask;
int ret;
dsi->variant = of_device_get_match_data(dev);
@ -1504,7 +1580,8 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
return -ENOMEM;
INIT_LIST_HEAD(&dsi->bridge_chain);
vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1;
vc4_dsi_encoder->base.type = dsi->variant->port ?
VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0;
vc4_dsi_encoder->dsi = dsi;
dsi->encoder = &vc4_dsi_encoder->base.base;
@ -1527,6 +1604,8 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
* so set up a channel for talking to it.
*/
if (dsi->variant->broken_axi_workaround) {
dma_cap_mask_t dma_mask;
dsi->reg_dma_mem = dma_alloc_coherent(dev, 4,
&dsi->reg_dma_paddr,
GFP_KERNEL);
@ -1535,8 +1614,13 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
return -ENOMEM;
}
ret = devm_add_action_or_reset(dev, vc4_dsi_dma_mem_release, dsi);
if (ret)
return ret;
dma_cap_zero(dma_mask);
dma_cap_set(DMA_MEMCPY, dma_mask);
dsi->reg_dma_chan = dma_request_chan_by_mask(&dma_mask);
if (IS_ERR(dsi->reg_dma_chan)) {
ret = PTR_ERR(dsi->reg_dma_chan);
@ -1546,6 +1630,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
return ret;
}
ret = devm_add_action_or_reset(dev, vc4_dsi_dma_chan_release, dsi);
if (ret)
return ret;
/* Get the physical address of the device's registers. The
* struct resource for the regs gives us the bus address
* instead.

View File

@ -79,6 +79,11 @@
#define VC5_HDMI_VERTB_VSPO_SHIFT 16
#define VC5_HDMI_VERTB_VSPO_MASK VC4_MASK(29, 16)
#define VC4_HDMI_MISC_CONTROL_PIXEL_REP_SHIFT 0
#define VC4_HDMI_MISC_CONTROL_PIXEL_REP_MASK VC4_MASK(3, 0)
#define VC5_HDMI_MISC_CONTROL_PIXEL_REP_SHIFT 0
#define VC5_HDMI_MISC_CONTROL_PIXEL_REP_MASK VC4_MASK(3, 0)
#define VC5_HDMI_SCRAMBLER_CTL_ENABLE BIT(0)
#define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_SHIFT 8
@ -145,6 +150,12 @@ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
drm_print_regset32(&p, &vc4_hdmi->hd_regset);
drm_print_regset32(&p, &vc4_hdmi->cec_regset);
drm_print_regset32(&p, &vc4_hdmi->csc_regset);
drm_print_regset32(&p, &vc4_hdmi->dvp_regset);
drm_print_regset32(&p, &vc4_hdmi->phy_regset);
drm_print_regset32(&p, &vc4_hdmi->ram_regset);
drm_print_regset32(&p, &vc4_hdmi->rm_regset);
return 0;
}
@ -418,6 +429,7 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
connector->stereo_allowed = 1;
if (vc4_hdmi->variant->supports_hdr)
drm_connector_attach_hdr_output_metadata_property(connector);
@ -455,9 +467,11 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
const struct vc4_hdmi_register *ram_packet_start =
&vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id;
u32 packet_reg_next = ram_packet_start->offset +
VC4_HDMI_PACKET_STRIDE * (packet_id + 1);
void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi,
ram_packet_start->reg);
uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {};
unsigned long flags;
ssize_t len, i;
int ret;
@ -493,6 +507,13 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
packet_reg += 4;
}
/*
* clear remainder of packet ram as it's included in the
* infoframe and triggers a checksum error on hdmi analyser
*/
for (; packet_reg < packet_reg_next; packet_reg += 4)
writel(0, base + packet_reg);
HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
@ -584,7 +605,9 @@ static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
union hdmi_infoframe frame;
memcpy(&frame.audio, audio, sizeof(*audio));
vc4_hdmi_write_infoframe(encoder, &frame);
if (vc4_hdmi->packet_ram_enabled)
vc4_hdmi_write_infoframe(encoder, &frame);
}
static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder)
@ -724,6 +747,8 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
mutex_lock(&vc4_hdmi->mutex);
vc4_hdmi->packet_ram_enabled = false;
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
@ -771,15 +796,6 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
mutex_unlock(&vc4_hdmi->mutex);
}
static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
mutex_lock(&vc4_hdmi->mutex);
vc4_hdmi->output_enabled = false;
mutex_unlock(&vc4_hdmi->mutex);
}
static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
struct drm_connector_state *state,
const struct drm_display_mode *mode)
@ -970,14 +986,15 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
VC4_HDMI_VERTA_VFP) |
VC4_SET_FIELD(mode->crtc_vdisplay, VC4_HDMI_VERTA_VAL));
u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end +
interlaced,
VC4_HDMI_VERTB_VBP));
u32 vertb_even = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
VC4_SET_FIELD(mode->crtc_vtotal -
mode->crtc_vsync_end -
interlaced,
mode->crtc_vsync_end,
VC4_HDMI_VERTB_VBP));
unsigned long flags;
u32 reg;
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
@ -1004,6 +1021,11 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
HDMI_WRITE(HDMI_VERTB0, vertb_even);
HDMI_WRITE(HDMI_VERTB1, vertb);
reg = HDMI_READ(HDMI_MISC_CONTROL);
reg &= ~VC4_HDMI_MISC_CONTROL_PIXEL_REP_MASK;
reg |= VC4_SET_FIELD(pixel_rep - 1, VC4_HDMI_MISC_CONTROL_PIXEL_REP);
HDMI_WRITE(HDMI_MISC_CONTROL, reg);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
}
@ -1022,13 +1044,13 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
VC5_HDMI_VERTA_VFP) |
VC4_SET_FIELD(mode->crtc_vdisplay, VC5_HDMI_VERTA_VAL));
u32 vertb = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
u32 vertb = (VC4_SET_FIELD(mode->htotal >> (2 - pixel_rep),
VC5_HDMI_VERTB_VSPO) |
VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
VC4_HDMI_VERTB_VBP));
u32 vertb_even = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
VC4_SET_FIELD(mode->crtc_vtotal -
mode->crtc_vsync_end -
interlaced,
mode->crtc_vsync_end - interlaced,
VC4_HDMI_VERTB_VBP));
unsigned long flags;
unsigned char gcp;
@ -1102,6 +1124,11 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
reg |= gcp_en ? VC5_HDMI_GCP_CONFIG_GCP_ENABLE : 0;
HDMI_WRITE(HDMI_GCP_CONFIG, reg);
reg = HDMI_READ(HDMI_MISC_CONTROL);
reg &= ~VC5_HDMI_MISC_CONTROL_PIXEL_REP_MASK;
reg |= VC4_SET_FIELD(pixel_rep - 1, VC5_HDMI_MISC_CONTROL_PIXEL_REP);
HDMI_WRITE(HDMI_MISC_CONTROL, reg);
HDMI_WRITE(HDMI_CLOCK_STOP, 0);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
@ -1330,14 +1357,12 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
HDMI_READ(HDMI_SCHEDULER_CONTROL) |
VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
VC4_HDMI_RAM_PACKET_ENABLE);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
vc4_hdmi->packet_ram_enabled = true;
vc4_hdmi_set_infoframes(encoder);
}
@ -1348,15 +1373,6 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
mutex_unlock(&vc4_hdmi->mutex);
}
static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
mutex_lock(&vc4_hdmi->mutex);
vc4_hdmi->output_enabled = true;
mutex_unlock(&vc4_hdmi->mutex);
}
static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
@ -1597,18 +1613,37 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_connector_state *old_conn_state =
drm_atomic_get_old_connector_state(conn_state->state, connector);
struct vc4_hdmi_connector_state *old_vc4_state =
conn_state_to_vc4_hdmi_conn_state(old_conn_state);
struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
unsigned long long tmds_char_rate = mode->clock * 1000;
unsigned long long tmds_bit_rate;
int ret;
if (vc4_hdmi->variant->unsupported_odd_h_timings &&
!(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
(mode->hsync_end % 2) || (mode->htotal % 2)))
return -EINVAL;
if (vc4_hdmi->variant->unsupported_odd_h_timings) {
if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
/* Only try to fixup DBLCLK modes to get 480i and 576i
* working.
* A generic solution for all modes with odd horizontal
* timing values seems impossible based on trying to
* solve it for 1366x768 monitors.
*/
if ((mode->hsync_start - mode->hdisplay) & 1)
mode->hsync_start--;
if ((mode->hsync_end - mode->hsync_start) & 1)
mode->hsync_end--;
}
/* Now check whether we still have odd values remaining */
if ((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
(mode->hsync_end % 2) || (mode->htotal % 2))
return -EINVAL;
}
/*
* The 1440p@60 pixel rate is in the same range than the first
@ -1628,6 +1663,11 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
if (ret)
return ret;
/* vc4_hdmi_encoder_compute_config may have changed output_bpc and/or output_format */
if (vc4_state->output_bpc != old_vc4_state->output_bpc ||
vc4_state->output_format != old_vc4_state->output_format)
crtc_state->mode_changed = true;
return 0;
}
@ -1650,8 +1690,6 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
.atomic_check = vc4_hdmi_encoder_atomic_check,
.atomic_mode_set = vc4_hdmi_encoder_atomic_mode_set,
.mode_valid = vc4_hdmi_encoder_mode_valid,
.disable = vc4_hdmi_encoder_disable,
.enable = vc4_hdmi_encoder_enable,
};
static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
@ -1748,19 +1786,15 @@ static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai)
static bool vc4_hdmi_audio_can_stream(struct vc4_hdmi *vc4_hdmi)
{
lockdep_assert_held(&vc4_hdmi->mutex);
struct drm_display_info *display = &vc4_hdmi->connector.display_info;
/*
* If the controller is disabled, prevent any ALSA output.
*/
if (!vc4_hdmi->output_enabled)
return false;
lockdep_assert_held(&vc4_hdmi->mutex);
/*
* If the encoder is currently in DVI mode, treat the codec DAI
* as missing.
*/
if (!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & VC4_HDMI_RAM_PACKET_ENABLE))
if (!display->is_hdmi)
return false;
return true;
@ -1941,10 +1975,10 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
/* Set the MAI threshold */
HDMI_WRITE(HDMI_MAI_THR,
VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) |
VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW));
VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_PANICHIGH) |
VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_PANICLOW) |
VC4_SET_FIELD(0x06, VC4_HD_MAI_THR_DREQHIGH) |
VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_DREQLOW));
HDMI_WRITE(HDMI_MAI_CONFIG,
VC4_HDMI_MAI_CONFIG_BIT_REVERSE |
@ -2035,12 +2069,12 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
struct device *dev = &vc4_hdmi->pdev->dev;
struct platform_device *codec_pdev;
const __be32 *addr;
int index;
int index, len;
int ret;
if (!of_find_property(dev->of_node, "dmas", NULL)) {
if (!of_find_property(dev->of_node, "dmas", &len) || !len) {
dev_warn(dev,
"'dmas' DT property is missing, no HDMI audio\n");
"'dmas' DT property is missing or empty, no HDMI audio\n");
return 0;
}
@ -2521,8 +2555,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
struct cec_connector_info conn_info;
struct platform_device *pdev = vc4_hdmi->pdev;
struct device *dev = &pdev->dev;
unsigned long flags;
u32 value;
int ret;
if (!of_find_property(dev->of_node, "interrupts", NULL)) {
@ -2541,15 +2573,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
value = HDMI_READ(HDMI_CEC_CNTRL_1);
/* Set the logical address to Unregistered */
value |= VC4_HDMI_CEC_ADDR_MASK;
HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
vc4_hdmi_cec_update_clk_div(vc4_hdmi);
if (vc4_hdmi->variant->external_irq_controller) {
ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-rx"),
vc4_cec_irq_handler_rx_bare,
@ -2565,10 +2588,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
if (ret)
goto err_remove_cec_rx_handler;
} else {
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
ret = request_threaded_irq(platform_get_irq(pdev, 0),
vc4_cec_irq_handler,
vc4_cec_irq_handler_thread, 0,
@ -2619,7 +2638,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
}
static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) {};
#endif
static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
@ -2704,6 +2722,7 @@ static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
struct platform_device *pdev = vc4_hdmi->pdev;
struct device *dev = &pdev->dev;
struct resource *res;
int ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi");
if (!res)
@ -2800,6 +2819,38 @@ static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
return PTR_ERR(vc4_hdmi->reset);
}
ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI);
if (ret)
return ret;
ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD);
if (ret)
return ret;
ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->cec_regset, VC5_CEC);
if (ret)
return ret;
ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->csc_regset, VC5_CSC);
if (ret)
return ret;
ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->dvp_regset, VC5_DVP);
if (ret)
return ret;
ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->phy_regset, VC5_PHY);
if (ret)
return ret;
ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->ram_regset, VC5_RAM);
if (ret)
return ret;
ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->rm_regset, VC5_RM);
if (ret)
return ret;
return 0;
}
@ -2815,12 +2866,34 @@ static int __maybe_unused vc4_hdmi_runtime_suspend(struct device *dev)
static int vc4_hdmi_runtime_resume(struct device *dev)
{
struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
unsigned long __maybe_unused flags;
u32 __maybe_unused value;
int ret;
ret = clk_prepare_enable(vc4_hdmi->hsm_clock);
if (ret)
return ret;
if (vc4_hdmi->variant->reset)
vc4_hdmi->variant->reset(vc4_hdmi);
#ifdef CONFIG_DRM_VC4_HDMI_CEC
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
value = HDMI_READ(HDMI_CEC_CNTRL_1);
/* Set the logical address to Unregistered */
value |= VC4_HDMI_CEC_ADDR_MASK;
HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
vc4_hdmi_cec_update_clk_div(vc4_hdmi);
if (!vc4_hdmi->variant->external_irq_controller) {
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
}
#endif
return 0;
}
@ -2910,9 +2983,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
if (vc4_hdmi->variant->reset)
vc4_hdmi->variant->reset(vc4_hdmi);
if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") ||
of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) &&
HDMI_READ(HDMI_VID_CTL) & VC4_HD_VID_CTL_ENABLE) {

View File

@ -179,6 +179,14 @@ struct vc4_hdmi {
struct debugfs_regset32 hdmi_regset;
struct debugfs_regset32 hd_regset;
/* VC5 only */
struct debugfs_regset32 cec_regset;
struct debugfs_regset32 csc_regset;
struct debugfs_regset32 dvp_regset;
struct debugfs_regset32 phy_regset;
struct debugfs_regset32 ram_regset;
struct debugfs_regset32 rm_regset;
/**
* @hw_lock: Spinlock protecting device register access.
*/
@ -205,10 +213,10 @@ struct vc4_hdmi {
struct drm_display_mode saved_adjusted_mode;
/**
* @output_enabled: Is the HDMI controller currently active?
* Protected by @mutex.
* @packet_ram_enabled: Is the HDMI controller packet RAM currently
* on? Protected by @mutex.
*/
bool output_enabled;
bool packet_ram_enabled;
/**
* @scdc_enabled: Is the HDMI controller currently running with

View File

@ -127,6 +127,17 @@ enum vc4_hdmi_field {
HDMI_VERTB0,
HDMI_VERTB1,
HDMI_VID_CTL,
HDMI_MISC_CONTROL,
HDMI_FORMAT_DET_1,
HDMI_FORMAT_DET_2,
HDMI_FORMAT_DET_3,
HDMI_FORMAT_DET_4,
HDMI_FORMAT_DET_5,
HDMI_FORMAT_DET_6,
HDMI_FORMAT_DET_7,
HDMI_FORMAT_DET_8,
HDMI_FORMAT_DET_9,
HDMI_FORMAT_DET_10,
};
struct vc4_hdmi_register {
@ -189,6 +200,7 @@ static const struct vc4_hdmi_register __maybe_unused vc4_hdmi_fields[] = {
VC4_HDMI_REG(HDMI_VERTB0, 0x00d0),
VC4_HDMI_REG(HDMI_VERTA1, 0x00d4),
VC4_HDMI_REG(HDMI_VERTB1, 0x00d8),
VC4_HDMI_REG(HDMI_MISC_CONTROL, 0x00e4),
VC4_HDMI_REG(HDMI_CEC_CNTRL_1, 0x00e8),
VC4_HDMI_REG(HDMI_CEC_CNTRL_2, 0x00ec),
VC4_HDMI_REG(HDMI_CEC_CNTRL_3, 0x00f0),
@ -237,8 +249,19 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi0_fields[] = {
VC4_HDMI_REG(HDMI_VERTB0, 0x0f0),
VC4_HDMI_REG(HDMI_VERTA1, 0x0f4),
VC4_HDMI_REG(HDMI_VERTB1, 0x0f8),
VC4_HDMI_REG(HDMI_MISC_CONTROL, 0x100),
VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c),
VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0),
VC4_HDMI_REG(HDMI_FORMAT_DET_1, 0x134),
VC4_HDMI_REG(HDMI_FORMAT_DET_2, 0x138),
VC4_HDMI_REG(HDMI_FORMAT_DET_3, 0x13c),
VC4_HDMI_REG(HDMI_FORMAT_DET_4, 0x140),
VC4_HDMI_REG(HDMI_FORMAT_DET_5, 0x144),
VC4_HDMI_REG(HDMI_FORMAT_DET_6, 0x148),
VC4_HDMI_REG(HDMI_FORMAT_DET_7, 0x14c),
VC4_HDMI_REG(HDMI_FORMAT_DET_8, 0x150),
VC4_HDMI_REG(HDMI_FORMAT_DET_9, 0x154),
VC4_HDMI_REG(HDMI_FORMAT_DET_10, 0x158),
VC4_HDMI_REG(HDMI_DEEP_COLOR_CONFIG_1, 0x170),
VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x178),
VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x17c),
@ -319,8 +342,19 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = {
VC4_HDMI_REG(HDMI_VERTB0, 0x0f0),
VC4_HDMI_REG(HDMI_VERTA1, 0x0f4),
VC4_HDMI_REG(HDMI_VERTB1, 0x0f8),
VC4_HDMI_REG(HDMI_MISC_CONTROL, 0x100),
VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c),
VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0),
VC4_HDMI_REG(HDMI_FORMAT_DET_1, 0x134),
VC4_HDMI_REG(HDMI_FORMAT_DET_2, 0x138),
VC4_HDMI_REG(HDMI_FORMAT_DET_3, 0x13c),
VC4_HDMI_REG(HDMI_FORMAT_DET_4, 0x140),
VC4_HDMI_REG(HDMI_FORMAT_DET_5, 0x144),
VC4_HDMI_REG(HDMI_FORMAT_DET_6, 0x148),
VC4_HDMI_REG(HDMI_FORMAT_DET_7, 0x14c),
VC4_HDMI_REG(HDMI_FORMAT_DET_8, 0x150),
VC4_HDMI_REG(HDMI_FORMAT_DET_9, 0x154),
VC4_HDMI_REG(HDMI_FORMAT_DET_10, 0x158),
VC4_HDMI_REG(HDMI_DEEP_COLOR_CONFIG_1, 0x170),
VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x178),
VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x17c),
@ -420,7 +454,7 @@ static inline u32 vc4_hdmi_read(struct vc4_hdmi *hdmi,
const struct vc4_hdmi_variant *variant = hdmi->variant;
void __iomem *base;
WARN_ON(!pm_runtime_active(&hdmi->pdev->dev));
WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev));
if (reg >= variant->num_registers) {
dev_warn(&hdmi->pdev->dev,
@ -450,7 +484,7 @@ static inline void vc4_hdmi_write(struct vc4_hdmi *hdmi,
lockdep_assert_held(&hdmi->hw_lock);
WARN_ON(!pm_runtime_active(&hdmi->pdev->dev));
WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev));
if (reg >= variant->num_registers) {
dev_warn(&hdmi->pdev->dev,

View File

@ -94,6 +94,46 @@ static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
return 0;
}
static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_hvs *hvs = vc4->hvs;
struct drm_printer p = drm_seq_file_printer(m);
unsigned int next_entry_start = 0;
unsigned int i, j;
u32 dlist_word, dispstat;
for (i = 0; i < SCALER_CHANNELS_COUNT; i++) {
dispstat = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(i)),
SCALER_DISPSTATX_MODE);
if (dispstat == SCALER_DISPSTATX_MODE_DISABLED ||
dispstat == SCALER_DISPSTATX_MODE_EOF) {
drm_printf(&p, "HVS chan %u disabled\n", i);
continue;
}
drm_printf(&p, "HVS chan %u:\n", i);
for (j = HVS_READ(SCALER_DISPLISTX(i)); j < 256; j++) {
dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
drm_printf(&p, "dlist: %02d: 0x%08x\n", j,
dlist_word);
if (!next_entry_start ||
next_entry_start == j) {
if (dlist_word & SCALER_CTL0_END)
break;
next_entry_start = j +
VC4_GET_FIELD(dlist_word,
SCALER_CTL0_SIZE);
}
}
}
return 0;
}
/* The filter kernel is composed of dwords each containing 3 9-bit
* signed integers packed next to each other.
*/
@ -734,6 +774,8 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset);
vc4_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun,
NULL);
vc4_debugfs_add_file(drm, "hvs_dlists", vc4_hvs_debugfs_dlist,
NULL);
return 0;
}

View File

@ -406,7 +406,7 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
* Do a temporary request on the core clock during the
* modeset.
*/
clk_set_min_rate(hvs->core_clk, core_rate);
WARN_ON(clk_set_min_rate(hvs->core_clk, core_rate));
}
drm_atomic_helper_commit_modeset_disables(dev, state);
@ -439,7 +439,7 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
* Request a clock rate based on the current HVS
* requirements.
*/
clk_set_min_rate(hvs->core_clk, new_hvs_state->core_clock_rate);
WARN_ON(clk_set_min_rate(hvs->core_clk, new_hvs_state->core_clock_rate));
drm_dbg(dev, "Core clock actual rate: %lu Hz\n",
clk_get_rate(hvs->core_clk));
@ -947,7 +947,9 @@ vc4_core_clock_atomic_check(struct drm_atomic_state *state)
continue;
num_outputs++;
cob_rate += hvs_new_state->fifo_state[i].fifo_load;
cob_rate = max_t(unsigned long,
hvs_new_state->fifo_state[i].fifo_load,
cob_rate);
}
pixel_rate = load_state->hvs_load;

View File

@ -312,16 +312,16 @@ static int vc4_plane_margins_adj(struct drm_plane_state *pstate)
adjhdisplay,
crtc_state->mode.hdisplay);
vc4_pstate->crtc_x += left;
if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - left)
vc4_pstate->crtc_x = crtc_state->mode.hdisplay - left;
if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - right)
vc4_pstate->crtc_x = crtc_state->mode.hdisplay - right;
adjvdisplay = crtc_state->mode.vdisplay - (top + bottom);
vc4_pstate->crtc_y = DIV_ROUND_CLOSEST(vc4_pstate->crtc_y *
adjvdisplay,
crtc_state->mode.vdisplay);
vc4_pstate->crtc_y += top;
if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - top)
vc4_pstate->crtc_y = crtc_state->mode.vdisplay - top;
if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - bottom)
vc4_pstate->crtc_y = crtc_state->mode.vdisplay - bottom;
vc4_pstate->crtc_w = DIV_ROUND_CLOSEST(vc4_pstate->crtc_w *
adjhdisplay,
@ -341,7 +341,6 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
struct drm_framebuffer *fb = state->fb;
struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
u32 subpixel_src_mask = (1 << 16) - 1;
int num_planes = fb->format->num_planes;
struct drm_crtc_state *crtc_state;
u32 h_subsample = fb->format->hsub;
@ -363,18 +362,15 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
for (i = 0; i < num_planes; i++)
vc4_state->offsets[i] = bo->paddr + fb->offsets[i];
/* We don't support subpixel source positioning for scaling. */
if ((state->src.x1 & subpixel_src_mask) ||
(state->src.x2 & subpixel_src_mask) ||
(state->src.y1 & subpixel_src_mask) ||
(state->src.y2 & subpixel_src_mask)) {
return -EINVAL;
}
vc4_state->src_x = state->src.x1 >> 16;
vc4_state->src_y = state->src.y1 >> 16;
vc4_state->src_w[0] = (state->src.x2 - state->src.x1) >> 16;
vc4_state->src_h[0] = (state->src.y2 - state->src.y1) >> 16;
/*
* We don't support subpixel source positioning for scaling,
* but fractional coordinates can be generated by clipping
* so just round for now
*/
vc4_state->src_x = DIV_ROUND_CLOSEST(state->src.x1, 1 << 16);
vc4_state->src_y = DIV_ROUND_CLOSEST(state->src.y1, 1 << 16);
vc4_state->src_w[0] = DIV_ROUND_CLOSEST(state->src.x2, 1 << 16) - vc4_state->src_x;
vc4_state->src_h[0] = DIV_ROUND_CLOSEST(state->src.y2, 1 << 16) - vc4_state->src_y;
vc4_state->crtc_x = state->dst.x1;
vc4_state->crtc_y = state->dst.y1;
@ -670,6 +666,48 @@ static const u32 colorspace_coeffs[2][DRM_COLOR_ENCODING_MAX][3] = {
}
};
static u32 vc4_hvs4_get_alpha_blend_mode(struct drm_plane_state *state)
{
if (!state->fb->format->has_alpha)
return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED,
SCALER_POS2_ALPHA_MODE);
switch (state->pixel_blend_mode) {
case DRM_MODE_BLEND_PIXEL_NONE:
return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED,
SCALER_POS2_ALPHA_MODE);
default:
case DRM_MODE_BLEND_PREMULTI:
return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_PIPELINE,
SCALER_POS2_ALPHA_MODE) |
SCALER_POS2_ALPHA_PREMULT;
case DRM_MODE_BLEND_COVERAGE:
return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_PIPELINE,
SCALER_POS2_ALPHA_MODE);
}
}
static u32 vc4_hvs5_get_alpha_blend_mode(struct drm_plane_state *state)
{
if (!state->fb->format->has_alpha)
return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
SCALER5_CTL2_ALPHA_MODE);
switch (state->pixel_blend_mode) {
case DRM_MODE_BLEND_PIXEL_NONE:
return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
SCALER5_CTL2_ALPHA_MODE);
default:
case DRM_MODE_BLEND_PREMULTI:
return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_PIPELINE,
SCALER5_CTL2_ALPHA_MODE) |
SCALER5_CTL2_ALPHA_PREMULT;
case DRM_MODE_BLEND_COVERAGE:
return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_PIPELINE,
SCALER5_CTL2_ALPHA_MODE);
}
}
/* Writes out a full display list for an active plane to the plane's
* private dlist state.
*/
@ -952,13 +990,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
/* Position Word 2: Source Image Size, Alpha */
vc4_state->pos2_offset = vc4_state->dlist_count;
vc4_dlist_write(vc4_state,
VC4_SET_FIELD(fb->format->has_alpha ?
SCALER_POS2_ALPHA_MODE_PIPELINE :
SCALER_POS2_ALPHA_MODE_FIXED,
SCALER_POS2_ALPHA_MODE) |
(mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) |
(fb->format->has_alpha ?
SCALER_POS2_ALPHA_PREMULT : 0) |
vc4_hvs4_get_alpha_blend_mode(state) |
VC4_SET_FIELD(vc4_state->src_w[0],
SCALER_POS2_WIDTH) |
VC4_SET_FIELD(vc4_state->src_h[0],
@ -1003,14 +1036,9 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
vc4_dlist_write(vc4_state,
VC4_SET_FIELD(state->alpha >> 4,
SCALER5_CTL2_ALPHA) |
(fb->format->has_alpha ?
SCALER5_CTL2_ALPHA_PREMULT : 0) |
vc4_hvs5_get_alpha_blend_mode(state) |
(mix_plane_alpha ?
SCALER5_CTL2_ALPHA_MIX : 0) |
VC4_SET_FIELD(fb->format->has_alpha ?
SCALER5_CTL2_ALPHA_MODE_PIPELINE :
SCALER5_CTL2_ALPHA_MODE_FIXED,
SCALER5_CTL2_ALPHA_MODE)
SCALER5_CTL2_ALPHA_MIX : 0)
);
/* Position Word 1: Scaled Image Dimensions. */
@ -1495,6 +1523,10 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
drm_plane_create_alpha_property(plane);
drm_plane_create_blend_mode_property(plane,
BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE));
drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_180 |

View File

@ -10,6 +10,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/aperture.h>
#include <linux/device.h>
#include <linux/eventfd.h>
#include <linux/file.h>
@ -1793,6 +1794,10 @@ static int vfio_pci_vga_init(struct vfio_pci_core_device *vdev)
if (!vfio_pci_is_vga(pdev))
return 0;
ret = aperture_remove_conflicting_pci_devices(pdev, vdev->vdev.ops->name);
if (ret)
return ret;
ret = vga_client_register(pdev, vfio_pci_set_decode);
if (ret)
return ret;

View File

@ -5,6 +5,12 @@
menu "Graphics support"
config APERTURE_HELPERS
bool
help
Support tracking and hand-over of aperture ownership. Required
by graphics drivers for firmware-provided framebuffers.
if HAS_IOMEM
config HAVE_FB_ATMEL

View File

@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_APERTURE_HELPERS) += aperture.o
obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o

351
drivers/video/aperture.c Normal file
View File

@ -0,0 +1,351 @@
// SPDX-License-Identifier: MIT
#include <linux/aperture.h>
#include <linux/device.h>
#include <linux/fb.h> /* for old fbdev helpers */
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/vgaarb.h>
/**
* DOC: overview
*
* A graphics device might be supported by different drivers, but only one
* driver can be active at any given time. Many systems load a generic
* graphics drivers, such as EFI-GOP or VESA, early during the boot process.
* During later boot stages, they replace the generic driver with a dedicated,
* hardware-specific driver. To take over the device the dedicated driver
* first has to remove the generic driver. Aperture functions manage
* ownership of framebuffer memory and hand-over between drivers.
*
* Graphics drivers should call aperture_remove_conflicting_devices()
* at the top of their probe function. The function removes any generic
* driver that is currently associated with the given framebuffer memory.
* An example for a graphics device on the platform bus is shown below.
*
* .. code-block:: c
*
* static int example_probe(struct platform_device *pdev)
* {
* struct resource *mem;
* resource_size_t base, size;
* int ret;
*
* mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
* if (!mem)
* return -ENODEV;
* base = mem->start;
* size = resource_size(mem);
*
* ret = aperture_remove_conflicting_devices(base, size, false, "example");
* if (ret)
* return ret;
*
* // Initialize the hardware
* ...
*
* return 0;
* }
*
* static const struct platform_driver example_driver = {
* .probe = example_probe,
* ...
* };
*
* The given example reads the platform device's I/O-memory range from the
* device instance. An active framebuffer will be located within this range.
* The call to aperture_remove_conflicting_devices() releases drivers that
* have previously claimed ownership of the range and are currently driving
* output on the framebuffer. If successful, the new driver can take over
* the device.
*
* While the given example uses a platform device, the aperture helpers work
* with every bus that has an addressable framebuffer. In the case of PCI,
* device drivers can also call aperture_remove_conflicting_pci_devices() and
* let the function detect the apertures automatically. Device drivers without
* knowledge of the framebuffer's location can call
* aperture_remove_all_conflicting_devices(), which removes all known devices.
*
* Drivers that are susceptible to being removed by other drivers, such as
* generic EFI or VESA drivers, have to register themselves as owners of their
* framebuffer apertures. Ownership of the framebuffer memory is achieved
* by calling devm_aperture_acquire_for_platform_device(). If successful, the
* driveris the owner of the framebuffer range. The function fails if the
* framebuffer is already owned by another driver. See below for an example.
*
* .. code-block:: c
*
* static int generic_probe(struct platform_device *pdev)
* {
* struct resource *mem;
* resource_size_t base, size;
*
* mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
* if (!mem)
* return -ENODEV;
* base = mem->start;
* size = resource_size(mem);
*
* ret = devm_aperture_acquire_for_platform_device(pdev, base, size);
* if (ret)
* return ret;
*
* // Initialize the hardware
* ...
*
* return 0;
* }
*
* static int generic_remove(struct platform_device *)
* {
* // Hot-unplug the device
* ...
*
* return 0;
* }
*
* static const struct platform_driver generic_driver = {
* .probe = generic_probe,
* .remove = generic_remove,
* ...
* };
*
* The similar to the previous example, the generic driver claims ownership
* of the framebuffer memory from its probe function. This will fail if the
* memory range, or parts of it, is already owned by another driver.
*
* If successful, the generic driver is now subject to forced removal by
* another driver. This only works for platform drivers that support hot
* unplugging. When a driver calls aperture_remove_conflicting_devices()
* et al for the registered framebuffer range, the aperture helpers call
* platform_device_unregister() and the generic driver unloads itself. The
* generic driver also has to provide a remove function to make this work.
* Once hot unplugged fro mhardware, it may not access the device's
* registers, framebuffer memory, ROM, etc afterwards.
*/
struct aperture_range {
struct device *dev;
resource_size_t base;
resource_size_t size;
struct list_head lh;
void (*detach)(struct device *dev);
};
static LIST_HEAD(apertures);
static DEFINE_MUTEX(apertures_lock);
static bool overlap(resource_size_t base1, resource_size_t end1,
resource_size_t base2, resource_size_t end2)
{
return (base1 < end2) && (end1 > base2);
}
static void devm_aperture_acquire_release(void *data)
{
struct aperture_range *ap = data;
bool detached = !ap->dev;
if (detached)
return;
mutex_lock(&apertures_lock);
list_del(&ap->lh);
mutex_unlock(&apertures_lock);
}
static int devm_aperture_acquire(struct device *dev,
resource_size_t base, resource_size_t size,
void (*detach)(struct device *))
{
size_t end = base + size;
struct list_head *pos;
struct aperture_range *ap;
mutex_lock(&apertures_lock);
list_for_each(pos, &apertures) {
ap = container_of(pos, struct aperture_range, lh);
if (overlap(base, end, ap->base, ap->base + ap->size)) {
mutex_unlock(&apertures_lock);
return -EBUSY;
}
}
ap = devm_kzalloc(dev, sizeof(*ap), GFP_KERNEL);
if (!ap) {
mutex_unlock(&apertures_lock);
return -ENOMEM;
}
ap->dev = dev;
ap->base = base;
ap->size = size;
ap->detach = detach;
INIT_LIST_HEAD(&ap->lh);
list_add(&ap->lh, &apertures);
mutex_unlock(&apertures_lock);
return devm_add_action_or_reset(dev, devm_aperture_acquire_release, ap);
}
static void aperture_detach_platform_device(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
/*
* Remove the device from the device hierarchy. This is the right thing
* to do for firmware-based DRM drivers, such as EFI, VESA or VGA. After
* the new driver takes over the hardware, the firmware device's state
* will be lost.
*
* For non-platform devices, a new callback would be required.
*
* If the aperture helpers ever need to handle native drivers, this call
* would only have to unplug the DRM device, so that the hardware device
* stays around after detachment.
*/
platform_device_unregister(pdev);
}
/**
* devm_aperture_acquire_for_platform_device - Acquires ownership of an aperture
* on behalf of a platform device.
* @pdev: the platform device to own the aperture
* @base: the aperture's byte offset in physical memory
* @size: the aperture size in bytes
*
* Installs the given device as the new owner of the aperture. The function
* expects the aperture to be provided by a platform device. If another
* driver takes over ownership of the aperture, aperture helpers will then
* unregister the platform device automatically. All acquired apertures are
* released automatically when the underlying device goes away.
*
* The function fails if the aperture, or parts of it, is currently
* owned by another device. To evict current owners, callers should use
* remove_conflicting_devices() et al. before calling this function.
*
* Returns:
* 0 on success, or a negative errno value otherwise.
*/
int devm_aperture_acquire_for_platform_device(struct platform_device *pdev,
resource_size_t base,
resource_size_t size)
{
return devm_aperture_acquire(&pdev->dev, base, size, aperture_detach_platform_device);
}
EXPORT_SYMBOL(devm_aperture_acquire_for_platform_device);
static void aperture_detach_devices(resource_size_t base, resource_size_t size)
{
resource_size_t end = base + size;
struct list_head *pos, *n;
mutex_lock(&apertures_lock);
list_for_each_safe(pos, n, &apertures) {
struct aperture_range *ap = container_of(pos, struct aperture_range, lh);
struct device *dev = ap->dev;
if (WARN_ON_ONCE(!dev))
continue;
if (!overlap(base, end, ap->base, ap->base + ap->size))
continue;
ap->dev = NULL; /* detach from device */
list_del(&ap->lh);
ap->detach(dev);
}
mutex_unlock(&apertures_lock);
}
/**
* aperture_remove_conflicting_devices - remove devices in the given range
* @base: the aperture's base address in physical memory
* @size: aperture size in bytes
* @primary: also kick vga16fb if present; only relevant for VGA devices
* @name: a descriptive name of the requesting driver
*
* This function removes devices that own apertures within @base and @size.
*
* Returns:
* 0 on success, or a negative errno code otherwise
*/
int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size,
bool primary, const char *name)
{
#if IS_REACHABLE(CONFIG_FB)
struct apertures_struct *a;
int ret;
a = alloc_apertures(1);
if (!a)
return -ENOMEM;
a->ranges[0].base = base;
a->ranges[0].size = size;
ret = remove_conflicting_framebuffers(a, name, primary);
kfree(a);
if (ret)
return ret;
#endif
aperture_detach_devices(base, size);
return 0;
}
EXPORT_SYMBOL(aperture_remove_conflicting_devices);
/**
* aperture_remove_conflicting_pci_devices - remove existing framebuffers for PCI devices
* @pdev: PCI device
* @name: a descriptive name of the requesting driver
*
* This function removes devices that own apertures within any of @pdev's
* memory bars. The function assumes that PCI device with shadowed ROM
* drives a primary display and therefore kicks out vga16fb as well.
*
* Returns:
* 0 on success, or a negative errno code otherwise
*/
int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name)
{
resource_size_t base, size;
int bar, ret;
/*
* WARNING: Apparently we must kick fbdev drivers before vgacon,
* otherwise the vga fbdev driver falls over.
*/
#if IS_REACHABLE(CONFIG_FB)
ret = remove_conflicting_pci_framebuffers(pdev, name);
if (ret)
return ret;
#endif
ret = vga_remove_vgacon(pdev);
if (ret)
return ret;
for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) {
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
continue;
base = pci_resource_start(pdev, bar);
size = pci_resource_len(pdev, bar);
aperture_detach_devices(base, size);
}
return 0;
}
EXPORT_SYMBOL(aperture_remove_conflicting_pci_devices);

View File

@ -10,6 +10,7 @@ config VGA_CONSOLE
depends on !4xx && !PPC_8xx && !SPARC && !M68K && !PARISC && !SUPERH && \
(!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) && \
!ARM64 && !ARC && !MICROBLAZE && !OPENRISC && !S390 && !UML
select APERTURE_HELPERS if (DRM || FB || VFIO_PCI_CORE)
default y
help
Saying Y here will allow you to use Linux in text mode through a

View File

@ -455,6 +455,7 @@ config FB_ATARI
config FB_OF
bool "Open Firmware frame buffer device support"
depends on (FB = y) && PPC && (!PPC_PSERIES || PCI)
select APERTURE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@ -527,6 +528,7 @@ config FB_IMSTT
config FB_VGA16
tristate "VGA 16-color graphics support"
depends on FB && (X86 || PPC)
select APERTURE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@ -551,7 +553,7 @@ config FB_STI
BIOS routines contained in a ROM chip in HP PA-RISC based machines.
Enabling this option will implement the linux framebuffer device
using calls to the STI BIOS routines for initialisation.
If you enable this option, you will get a planar framebuffer device
/dev/fb which will work on the most common HP graphic cards of the
NGLE family, including the artist chips (in the 7xx and Bxxx series),
@ -617,6 +619,7 @@ config FB_UVESA
config FB_VESA
bool "VESA VGA graphics support"
depends on (FB = y) && X86
select APERTURE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@ -630,6 +633,7 @@ config FB_VESA
config FB_EFI
bool "EFI-based Framebuffer Support"
depends on (FB = y) && !IA64 && EFI
select APERTURE_HELPERS
select DRM_PANEL_ORIENTATION_QUIRKS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
@ -2190,6 +2194,7 @@ config FB_SIMPLE
tristate "Simple framebuffer support"
depends on FB
depends on !DRM_SIMPLEDRM
select APERTURE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT

View File

@ -46,6 +46,8 @@ int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state,
int max_scale,
bool can_position,
bool can_update_disabled);
int drm_atomic_helper_check_crtc_state(struct drm_crtc_state *crtc_state,
bool can_disable_primary_plane);
int drm_atomic_helper_check_planes(struct drm_device *dev,
struct drm_atomic_state *state);
int drm_atomic_helper_check(struct drm_device *dev,

View File

@ -295,6 +295,23 @@ int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
u16 *brightness);
/**
* mipi_dsi_dcs_write_seq - transmit a DCS command with payload
* @dsi: DSI peripheral device
* @cmd: Command
* @seq: buffer containing data to be transmitted
*/
#define mipi_dsi_dcs_write_seq(dsi, cmd, seq...) do { \
static const u8 d[] = { cmd, seq }; \
struct device *dev = &dsi->dev; \
int ret; \
ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \
if (ret < 0) { \
dev_err_ratelimited(dev, "sending command %#02x failed: %d\n", cmd, ret); \
return ret; \
} \
} while (0)
/**
* struct mipi_dsi_driver - DSI driver
* @driver: device driver model driver

View File

@ -47,6 +47,22 @@ struct drm_rect {
int x1, y1, x2, y2;
};
/**
* DRM_RECT_INIT - initialize a rectangle from x/y/w/h
* @x: x coordinate
* @y: y coordinate
* @w: width
* @h: height
*
* RETURNS:
* A new rectangle of the specified size.
*/
#define DRM_RECT_INIT(x, y, w, h) ((struct drm_rect){ \
.x1 = (x), \
.y1 = (y), \
.x2 = (x) + (w), \
.y2 = (y) + (h) })
/**
* DRM_RECT_FMT - printf string for &struct drm_rect
*/

56
include/linux/aperture.h Normal file
View File

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: MIT */
#ifndef _LINUX_APERTURE_H_
#define _LINUX_APERTURE_H_
#include <linux/types.h>
struct pci_dev;
struct platform_device;
#if defined(CONFIG_APERTURE_HELPERS)
int devm_aperture_acquire_for_platform_device(struct platform_device *pdev,
resource_size_t base,
resource_size_t size);
int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size,
bool primary, const char *name);
int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name);
#else
static inline int devm_aperture_acquire_for_platform_device(struct platform_device *pdev,
resource_size_t base,
resource_size_t size)
{
return 0;
}
static inline int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size,
bool primary, const char *name)
{
return 0;
}
static inline int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name)
{
return 0;
}
#endif
/**
* aperture_remove_all_conflicting_devices - remove all existing framebuffers
* @primary: also kick vga16fb if present; only relevant for VGA devices
* @name: a descriptive name of the requesting driver
*
* This function removes all graphics device drivers. Use this function on systems
* that can have their framebuffer located anywhere in memory.
*
* Returns:
* 0 on success, or a negative errno code otherwise
*/
static inline int aperture_remove_all_conflicting_devices(bool primary, const char *name)
{
return aperture_remove_conflicting_devices(0, (resource_size_t)-1, primary, name);
}
#endif

View File

@ -6,6 +6,7 @@
#ifndef __IOSYS_MAP_H__
#define __IOSYS_MAP_H__
#include <linux/compiler_types.h>
#include <linux/io.h>
#include <linux/string.h>
@ -333,6 +334,36 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
memset(dst->vaddr + offset, value, len);
}
#ifdef CONFIG_64BIT
#define __iosys_map_rd_io_u64_case(val_, vaddr_iomem_) \
u64: val_ = readq(vaddr_iomem_)
#define __iosys_map_wr_io_u64_case(val_, vaddr_iomem_) \
u64: writeq(val_, vaddr_iomem_)
#else
#define __iosys_map_rd_io_u64_case(val_, vaddr_iomem_) \
u64: memcpy_fromio(&(val_), vaddr_iomem_, sizeof(u64))
#define __iosys_map_wr_io_u64_case(val_, vaddr_iomem_) \
u64: memcpy_toio(vaddr_iomem_, &(val_), sizeof(u64))
#endif
#define __iosys_map_rd_io(val__, vaddr_iomem__, type__) _Generic(val__, \
u8: val__ = readb(vaddr_iomem__), \
u16: val__ = readw(vaddr_iomem__), \
u32: val__ = readl(vaddr_iomem__), \
__iosys_map_rd_io_u64_case(val__, vaddr_iomem__))
#define __iosys_map_rd_sys(val__, vaddr__, type__) \
val__ = READ_ONCE(*(type__ *)(vaddr__))
#define __iosys_map_wr_io(val__, vaddr_iomem__, type__) _Generic(val__, \
u8: writeb(val__, vaddr_iomem__), \
u16: writew(val__, vaddr_iomem__), \
u32: writel(val__, vaddr_iomem__), \
__iosys_map_wr_io_u64_case(val__, vaddr_iomem__))
#define __iosys_map_wr_sys(val__, vaddr__, type__) \
WRITE_ONCE(*(type__ *)(vaddr__), val__)
/**
* iosys_map_rd - Read a C-type value from the iosys_map
*
@ -340,16 +371,21 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
* @offset__: The offset from which to read
* @type__: Type of the value being read
*
* Read a C type value from iosys_map, handling possible un-aligned accesses to
* the mapping.
* Read a C type value (u8, u16, u32 and u64) from iosys_map. For other types or
* if pointer may be unaligned (and problematic for the architecture supported),
* use iosys_map_memcpy_from().
*
* Returns:
* The value read from the mapping.
*/
#define iosys_map_rd(map__, offset__, type__) ({ \
type__ val; \
iosys_map_memcpy_from(&val, map__, offset__, sizeof(val)); \
val; \
#define iosys_map_rd(map__, offset__, type__) ({ \
type__ val; \
if ((map__)->is_iomem) { \
__iosys_map_rd_io(val, (map__)->vaddr_iomem + (offset__), type__);\
} else { \
__iosys_map_rd_sys(val, (map__)->vaddr + (offset__), type__); \
} \
val; \
})
/**
@ -360,12 +396,17 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
* @type__: Type of the value being written
* @val__: Value to write
*
* Write a C-type value to the iosys_map, handling possible un-aligned accesses
* to the mapping.
* Write a C type value (u8, u16, u32 and u64) to the iosys_map. For other types
* or if pointer may be unaligned (and problematic for the architecture
* supported), use iosys_map_memcpy_to()
*/
#define iosys_map_wr(map__, offset__, type__, val__) ({ \
type__ val = (val__); \
iosys_map_memcpy_to(map__, offset__, &val, sizeof(val)); \
#define iosys_map_wr(map__, offset__, type__, val__) ({ \
type__ val = (val__); \
if ((map__)->is_iomem) { \
__iosys_map_wr_io(val, (map__)->vaddr_iomem + (offset__), type__);\
} else { \
__iosys_map_wr_sys(val, (map__)->vaddr + (offset__), type__); \
} \
})
/**
@ -379,9 +420,10 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
*
* Read a value from iosys_map considering its layout is described by a C struct
* starting at @struct_offset__. The field offset and size is calculated and its
* value read handling possible un-aligned memory accesses. For example: suppose
* there is a @struct foo defined as below and the value ``foo.field2.inner2``
* needs to be read from the iosys_map:
* value read. If the field access would incur in un-aligned access, then either
* iosys_map_memcpy_from() needs to be used or the architecture must support it.
* For example: suppose there is a @struct foo defined as below and the value
* ``foo.field2.inner2`` needs to be read from the iosys_map:
*
* .. code-block:: c
*
@ -445,10 +487,12 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
* @field__: Member of the struct to read
* @val__: Value to write
*
* Write a value to the iosys_map considering its layout is described by a C struct
* starting at @struct_offset__. The field offset and size is calculated and the
* @val__ is written handling possible un-aligned memory accesses. Refer to
* iosys_map_rd_field() for expected usage and memory layout.
* Write a value to the iosys_map considering its layout is described by a C
* struct starting at @struct_offset__. The field offset and size is calculated
* and the @val__ is written. If the field access would incur in un-aligned
* access, then either iosys_map_memcpy_to() needs to be used or the
* architecture must support it. Refer to iosys_map_rd_field() for expected
* usage and memory layout.
*/
#define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({ \
struct_type__ *s; \

View File

@ -559,7 +559,7 @@ extern "C" {
*
* The main surface is Y-tiled and is at plane index 0 whereas CCS is linear
* and at index 1. The clear color is stored at index 2, and the pitch should
* be ignored. The clear color structure is 256 bits. The first 128 bits
* be 64 bytes aligned. The clear color structure is 256 bits. The first 128 bits
* represents Raw Clear Color Red, Green, Blue and Alpha color each represented
* by 32 bits. The raw clear color is consumed by the 3d engine and generates
* the converted clear color of size 64 bits. The first 32 bits store the Lower
@ -612,9 +612,9 @@ extern "C" {
* outside of the GEM object in a reserved memory area dedicated for the
* storage of the CCS data for all RC/RC_CC/MC compressible GEM objects. The
* main surface pitch is required to be a multiple of four Tile 4 widths. The
* clear color is stored at plane index 1 and the pitch should be ignored. The
* format of the 256 bits of clear color data matches the one used for the
* I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC modifier, see its description
* clear color is stored at plane index 1 and the pitch should be 64 bytes
* aligned. The format of the 256 bits of clear color data matches the one used
* for the I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC modifier, see its description
* for details.
*/
#define I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC fourcc_mod_code(INTEL, 12)