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:
commit
f929217499
@ -20,6 +20,7 @@ properties:
|
||||
- fsl,imx23-lcdif
|
||||
- fsl,imx28-lcdif
|
||||
- fsl,imx6sx-lcdif
|
||||
- fsl,imx8mp-lcdif
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx6sl-lcdif
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -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
|
||||
|
@ -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,.*":
|
||||
|
13
Documentation/driver-api/aperture.rst
Normal file
13
Documentation/driver-api/aperture.rst
Normal 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:
|
@ -27,6 +27,7 @@ available subsections can be seen below.
|
||||
component
|
||||
message-based
|
||||
infiniband
|
||||
aperture
|
||||
frame-buffer
|
||||
regulator
|
||||
reset
|
||||
|
@ -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
|
||||
===================
|
||||
|
||||
|
13
MAINTAINERS
13
MAINTAINERS
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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/
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
340
drivers/gpu/drm/mxsfb/lcdif_drv.c
Normal file
340
drivers/gpu/drm/mxsfb/lcdif_drv.c
Normal 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");
|
44
drivers/gpu/drm/mxsfb/lcdif_drv.h
Normal file
44
drivers/gpu/drm/mxsfb/lcdif_drv.h
Normal 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__ */
|
484
drivers/gpu/drm/mxsfb/lcdif_kms.c
Normal file
484
drivers/gpu/drm/mxsfb/lcdif_kms.c
Normal 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);
|
||||
}
|
257
drivers/gpu/drm/mxsfb/lcdif_regs.h
Normal file
257
drivers/gpu/drm/mxsfb/lcdif_regs.h
Normal 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__ */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
285
drivers/gpu/drm/panel/panel-ebbg-ft8719.c
Normal file
285
drivers/gpu/drm/panel/panel-ebbg-ft8719.c
Normal 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");
|
@ -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"),
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 = &ire_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 = &ire_am800480r3tmqwa1h,
|
||||
}, {
|
||||
.compatible = "ampire,am800600p5tmqw-tb8h",
|
||||
.data = &ire_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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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]"
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
3
drivers/gpu/drm/tests/.kunitconfig
Normal file
3
drivers/gpu/drm/tests/.kunitconfig
Normal file
@ -0,0 +1,3 @@
|
||||
CONFIG_KUNIT=y
|
||||
CONFIG_DRM=y
|
||||
CONFIG_DRM_KUNIT_TEST=y
|
3
drivers/gpu/drm/tests/Makefile
Normal file
3
drivers/gpu/drm/tests/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o
|
161
drivers/gpu/drm/tests/drm_format_helper_test.c
Normal file
161
drivers/gpu/drm/tests/drm_format_helper_test.c
Normal 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,
|
||||
¶ms->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, ¶ms->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>");
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 |
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
351
drivers/video/aperture.c
Normal 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);
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
56
include/linux/aperture.h
Normal 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
|
@ -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; \
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user