mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:04:51 +01:00
drm-misc-next for 6.20:
UAPI Changes:
Cross-subsystem Changes:
Core Changes:
- draw: Add API to check if a format conversion can be done
- panic: Rename draw_panic_static_* to draw_panic_screen_*, Add kunit
tests
- shmem: Improve tests
Driver Changes:
- ast: Big endian fixes
- etnaviv: Add PPU flop reset support
- panfrost: Add GPU_PM_RT support for RZ/G3E SoC
- panthor: multiple fixes around VM termination, huge page support
- pl111: Fix build regression
- v3d: Fix DMA segment size
- bridge:
- Add connector argument to .hpd_notify
- Plenty of patches to convert existing drivers to refcounting
- Convert Rockchip's inno hdmi support to a proper bridge
- lontium-lt9611uxc: Switch to HDMI audio helpers
- panel:
- New panel: BOE NV140WUM-T08
-----BEGIN PGP SIGNATURE-----
iJUEABMJAB0WIQTkHFbLp4ejekA/qfgnX84Zoj2+dgUCaV9qAAAKCRAnX84Zoj2+
duxGAX4vPiNmTlX8Sh0cTPW0eOUtHCBc2kf1TdWPmczCWeF9PVuMICCWYfhgWaJf
d7yTLaQBfiniO/x1+uUx5izUVrdVbyp6i9ZooijwBOn2cvxZuW2UT0/1I6040L1G
T+vcJsmJoA==
=l26/
-----END PGP SIGNATURE-----
Merge tag 'drm-misc-next-2026-01-08' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for 6.20:
UAPI Changes:
Cross-subsystem Changes:
Core Changes:
- draw: Add API to check if a format conversion can be done
- panic: Rename draw_panic_static_* to draw_panic_screen_*, Add kunit
tests
- shmem: Improve tests
Driver Changes:
- ast: Big endian fixes
- etnaviv: Add PPU flop reset support
- panfrost: Add GPU_PM_RT support for RZ/G3E SoC
- panthor: multiple fixes around VM termination, huge page support
- pl111: Fix build regression
- v3d: Fix DMA segment size
- bridge:
- Add connector argument to .hpd_notify
- Plenty of patches to convert existing drivers to refcounting
- Convert Rockchip's inno hdmi support to a proper bridge
- lontium-lt9611uxc: Switch to HDMI audio helpers
- panel:
- New panel: BOE NV140WUM-T08
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <mripard@redhat.com>
Link: https://patch.msgid.link/20260108-literate-nyala-of-courtesy-de501a@houat
This commit is contained in:
commit
a87fef0880
61 changed files with 1590 additions and 716 deletions
|
|
@ -506,6 +506,22 @@ Contact: Maxime Ripard <mripard@kernel.org>,
|
|||
|
||||
Level: Intermediate
|
||||
|
||||
Convert users of of_drm_find_bridge() to of_drm_find_and_get_bridge()
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Taking a struct drm_bridge pointer requires getting a reference and putting
|
||||
it after disposing of the pointer. Most functions returning a struct
|
||||
drm_bridge pointer already call drm_bridge_get() to increment the refcount
|
||||
and their users have been updated to call drm_bridge_put() when
|
||||
appropriate. of_drm_find_bridge() does not get a reference and it has been
|
||||
deprecated in favor of of_drm_find_and_get_bridge() which does, but some
|
||||
users still need to be converted.
|
||||
|
||||
Contact: Maxime Ripard <mripard@kernel.org>,
|
||||
Luca Ceresoli <luca.ceresoli@bootlin.com>
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Core refactorings
|
||||
=================
|
||||
|
||||
|
|
|
|||
|
|
@ -8749,6 +8749,7 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
|||
F: drivers/gpu/drm/drm_draw.c
|
||||
F: drivers/gpu/drm/drm_draw_internal.h
|
||||
F: drivers/gpu/drm/drm_panic*.c
|
||||
F: drivers/gpu/drm/tests/drm_panic_test.c
|
||||
F: include/drm/drm_panic*
|
||||
|
||||
DRM PANIC QR CODE
|
||||
|
|
@ -12457,6 +12458,14 @@ M: Samuel Holland <samuel@sholland.org>
|
|||
S: Maintained
|
||||
F: drivers/power/supply/ip5xxx_power.c
|
||||
|
||||
INNOSILICON HDMI BRIDGE DRIVER
|
||||
M: Andy Yan <andy.yan@rock-chips.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/gpu/drm/bridge/inno-hdmi.c
|
||||
F: include/drm/bridge/inno_hdmi.h
|
||||
|
||||
INOTIFY
|
||||
M: Jan Kara <jack@suse.cz>
|
||||
R: Amir Goldstein <amir73il@gmail.com>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@
|
|||
* Authors: AMD
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drm_colorop.h>
|
||||
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_mode.h"
|
||||
#include "amdgpu_dm.h"
|
||||
|
|
|
|||
|
|
@ -93,12 +93,17 @@ static void ast_set_cursor_image(struct ast_device *ast, const u8 *src,
|
|||
unsigned int width, unsigned int height)
|
||||
{
|
||||
u8 __iomem *dst = ast_plane_vaddr(&ast->cursor_plane.base);
|
||||
u32 csum;
|
||||
|
||||
csum = ast_cursor_calculate_checksum(src, width, height);
|
||||
u32 csum = ast_cursor_calculate_checksum(src, width, height);
|
||||
|
||||
/* write pixel data */
|
||||
#if defined(__BIG_ENDIAN)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < AST_HWC_SIZE; i += 2)
|
||||
writew(swab16(*(const __u16 *)&src[i]), &dst[i]);
|
||||
#else
|
||||
memcpy_toio(dst, src, AST_HWC_SIZE);
|
||||
#endif
|
||||
|
||||
/* write checksum + signature */
|
||||
dst += AST_HWC_SIZE;
|
||||
|
|
|
|||
|
|
@ -526,12 +526,18 @@ static int ast_primary_plane_helper_atomic_check(struct drm_plane *plane,
|
|||
|
||||
static void ast_handle_damage(struct ast_plane *ast_plane, struct iosys_map *src,
|
||||
struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip)
|
||||
const struct drm_rect *clip,
|
||||
struct drm_format_conv_state *fmtcnv_state)
|
||||
{
|
||||
struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(ast_plane_vaddr(ast_plane));
|
||||
|
||||
iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip));
|
||||
|
||||
#if defined(__BIG_ENDIAN)
|
||||
drm_fb_swab(&dst, fb->pitches, src, fb, clip, !src[0].is_iomem, fmtcnv_state);
|
||||
#else
|
||||
drm_fb_memcpy(&dst, fb->pitches, src, fb, clip);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
|
||||
|
|
@ -561,7 +567,8 @@ static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
|
|||
if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
|
||||
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
|
||||
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
||||
ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage);
|
||||
ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage,
|
||||
&shadow_plane_state->fmtcnv_state);
|
||||
}
|
||||
|
||||
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
|
||||
|
|
|
|||
|
|
@ -100,6 +100,13 @@ config DRM_I2C_NXP_TDA998X
|
|||
help
|
||||
Support for NXP Semiconductors TDA998X HDMI encoders.
|
||||
|
||||
config DRM_INNO_HDMI
|
||||
tristate
|
||||
select DRM_BRIDGE_CONNECTOR
|
||||
select DRM_DISPLAY_HDMI_HELPER
|
||||
select DRM_DISPLAY_HELPER
|
||||
select DRM_KMS_HELPER
|
||||
|
||||
config DRM_ITE_IT6263
|
||||
tristate "ITE IT6263 LVDS/HDMI bridge"
|
||||
depends on OF
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ obj-$(CONFIG_DRM_FSL_LDB) += fsl-ldb.o
|
|||
tda998x-y := tda998x_drv.o
|
||||
obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
|
||||
|
||||
obj-$(CONFIG_DRM_INNO_HDMI) += inno-hdmi.o
|
||||
obj-$(CONFIG_DRM_ITE_IT6263) += ite-it6263.o
|
||||
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
|
||||
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ enum imx8qxp_pc_pix_data_format {
|
|||
|
||||
struct imx8qxp_pc_channel {
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
struct imx8qxp_pc *pc;
|
||||
unsigned int stream_id;
|
||||
};
|
||||
|
|
@ -120,7 +119,7 @@ static int imx8qxp_pc_bridge_attach(struct drm_bridge *bridge,
|
|||
}
|
||||
|
||||
return drm_bridge_attach(encoder,
|
||||
ch->next_bridge, bridge,
|
||||
ch->bridge.next_bridge, bridge,
|
||||
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
}
|
||||
|
||||
|
|
@ -326,8 +325,8 @@ static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
|
|||
goto free_child;
|
||||
}
|
||||
|
||||
ch->next_bridge = of_drm_find_bridge(remote);
|
||||
if (!ch->next_bridge) {
|
||||
ch->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
|
||||
if (!ch->bridge.next_bridge) {
|
||||
of_node_put(remote);
|
||||
ret = -EPROBE_DEFER;
|
||||
DRM_DEV_DEBUG_DRIVER(dev,
|
||||
|
|
@ -349,7 +348,7 @@ static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
|
|||
free_child:
|
||||
of_node_put(child);
|
||||
|
||||
if (i == 1 && pc->ch[0]->next_bridge)
|
||||
if (i == 1 && pc->ch[0]->bridge.next_bridge)
|
||||
drm_bridge_remove(&pc->ch[0]->bridge);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
|
|
|
|||
|
|
@ -374,13 +374,8 @@ static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
|
||||
pl->next_bridge = imx8qxp_pixel_link_find_next_bridge(pl);
|
||||
if (IS_ERR(pl->next_bridge)) {
|
||||
ret = PTR_ERR(pl->next_bridge);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(pl->next_bridge))
|
||||
return PTR_ERR(pl->next_bridge);
|
||||
|
||||
platform_set_drvdata(pdev, pl);
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
struct imx8qxp_pxl2dpi {
|
||||
struct regmap *regmap;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
struct drm_bridge *companion;
|
||||
struct device *dev;
|
||||
struct imx_sc_ipc *ipc_handle;
|
||||
|
|
@ -60,10 +59,17 @@ static int imx8qxp_pxl2dpi_bridge_attach(struct drm_bridge *bridge,
|
|||
}
|
||||
|
||||
return drm_bridge_attach(encoder,
|
||||
p2d->next_bridge, bridge,
|
||||
p2d->bridge.next_bridge, bridge,
|
||||
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
}
|
||||
|
||||
static void imx8qxp_pxl2dpi_bridge_destroy(struct drm_bridge *bridge)
|
||||
{
|
||||
struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
|
||||
|
||||
drm_bridge_put(p2d->companion);
|
||||
}
|
||||
|
||||
static int
|
||||
imx8qxp_pxl2dpi_bridge_atomic_check(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
|
|
@ -206,6 +212,7 @@ static const struct drm_bridge_funcs imx8qxp_pxl2dpi_bridge_funcs = {
|
|||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
.atomic_reset = drm_atomic_helper_bridge_reset,
|
||||
.attach = imx8qxp_pxl2dpi_bridge_attach,
|
||||
.destroy = imx8qxp_pxl2dpi_bridge_destroy,
|
||||
.atomic_check = imx8qxp_pxl2dpi_bridge_atomic_check,
|
||||
.mode_set = imx8qxp_pxl2dpi_bridge_mode_set,
|
||||
.atomic_disable = imx8qxp_pxl2dpi_bridge_atomic_disable,
|
||||
|
|
@ -255,40 +262,27 @@ out:
|
|||
return ep;
|
||||
}
|
||||
|
||||
static struct drm_bridge *
|
||||
imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d)
|
||||
static int imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d)
|
||||
{
|
||||
struct device_node *ep, *remote;
|
||||
struct drm_bridge *next_bridge;
|
||||
int ret;
|
||||
struct device_node *ep __free(device_node) =
|
||||
imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1);
|
||||
if (IS_ERR(ep))
|
||||
return PTR_ERR(ep);
|
||||
|
||||
ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1);
|
||||
if (IS_ERR(ep)) {
|
||||
ret = PTR_ERR(ep);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
struct device_node *remote __free(device_node) = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote || !of_device_is_available(remote)) {
|
||||
DRM_DEV_ERROR(p2d->dev, "no available remote\n");
|
||||
next_bridge = ERR_PTR(-ENODEV);
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
} else if (!of_device_is_available(remote->parent)) {
|
||||
DRM_DEV_ERROR(p2d->dev, "remote parent is not available\n");
|
||||
next_bridge = ERR_PTR(-ENODEV);
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
next_bridge = of_drm_find_bridge(remote);
|
||||
if (!next_bridge) {
|
||||
next_bridge = ERR_PTR(-EPROBE_DEFER);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
of_node_put(remote);
|
||||
of_node_put(ep);
|
||||
p2d->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
|
||||
if (!p2d->bridge.next_bridge)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return next_bridge;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8qxp_pxl2dpi_set_pixel_link_sel(struct imx8qxp_pxl2dpi *p2d)
|
||||
|
|
@ -347,7 +341,7 @@ static int imx8qxp_pxl2dpi_parse_dt_companion(struct imx8qxp_pxl2dpi *p2d)
|
|||
goto out;
|
||||
}
|
||||
|
||||
p2d->companion = of_drm_find_bridge(companion);
|
||||
p2d->companion = of_drm_find_and_get_bridge(companion);
|
||||
if (!p2d->companion) {
|
||||
ret = -EPROBE_DEFER;
|
||||
DRM_DEV_DEBUG_DRIVER(p2d->dev,
|
||||
|
|
@ -364,8 +358,8 @@ static int imx8qxp_pxl2dpi_parse_dt_companion(struct imx8qxp_pxl2dpi *p2d)
|
|||
* the next bridges are connected to. If they are marked as expecting
|
||||
* even pixels and odd pixels than we need to use the companion PXL2DPI.
|
||||
*/
|
||||
port1 = of_graph_get_port_by_id(p2d->next_bridge->of_node, 1);
|
||||
port2 = of_graph_get_port_by_id(companion_p2d->next_bridge->of_node, 1);
|
||||
port1 = of_graph_get_port_by_id(p2d->bridge.next_bridge->of_node, 1);
|
||||
port2 = of_graph_get_port_by_id(companion_p2d->bridge.next_bridge->of_node, 1);
|
||||
dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
|
||||
of_node_put(port1);
|
||||
of_node_put(port2);
|
||||
|
|
@ -421,14 +415,9 @@ static int imx8qxp_pxl2dpi_bridge_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
p2d->next_bridge = imx8qxp_pxl2dpi_find_next_bridge(p2d);
|
||||
if (IS_ERR(p2d->next_bridge)) {
|
||||
ret = PTR_ERR(p2d->next_bridge);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
|
||||
ret);
|
||||
ret = imx8qxp_pxl2dpi_find_next_bridge(p2d);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = imx8qxp_pxl2dpi_set_pixel_link_sel(p2d);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@
|
|||
* Copyright (C) Rockchip Electronics Co., Ltd.
|
||||
* Zheng Yang <zhengyang@rock-chips.com>
|
||||
* Yakir Yang <ykk@rock-chips.com>
|
||||
* Andy Yan <andyshrk@163.com>
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <linux/hw_bitfield.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -18,6 +19,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <drm/bridge/inno_hdmi.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
|
@ -29,8 +31,6 @@
|
|||
#include <drm/display/drm_hdmi_helper.h>
|
||||
#include <drm/display/drm_hdmi_state_helper.h>
|
||||
|
||||
#include "rockchip_drm_drv.h"
|
||||
|
||||
#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U
|
||||
|
||||
#define DDC_SEGMENT_ADDR 0x30
|
||||
|
|
@ -384,27 +384,6 @@ enum {
|
|||
#define HDMI_CEC_BUSFREETIME_H 0xdd
|
||||
#define HDMI_CEC_LOGICADDR 0xde
|
||||
|
||||
#define RK3036_GRF_SOC_CON2 0x148
|
||||
#define RK3036_HDMI_PHSYNC BIT(4)
|
||||
#define RK3036_HDMI_PVSYNC BIT(5)
|
||||
|
||||
enum inno_hdmi_dev_type {
|
||||
RK3036_HDMI,
|
||||
RK3128_HDMI,
|
||||
};
|
||||
|
||||
struct inno_hdmi_phy_config {
|
||||
unsigned long pixelclock;
|
||||
u8 pre_emphasis;
|
||||
u8 voltage_level_control;
|
||||
};
|
||||
|
||||
struct inno_hdmi_variant {
|
||||
enum inno_hdmi_dev_type dev_type;
|
||||
struct inno_hdmi_phy_config *phy_configs;
|
||||
struct inno_hdmi_phy_config *default_phy_config;
|
||||
};
|
||||
|
||||
struct inno_hdmi_i2c {
|
||||
struct i2c_adapter adap;
|
||||
|
||||
|
|
@ -417,41 +396,17 @@ struct inno_hdmi_i2c {
|
|||
|
||||
struct inno_hdmi {
|
||||
struct device *dev;
|
||||
|
||||
struct drm_bridge bridge;
|
||||
struct clk *pclk;
|
||||
struct clk *refclk;
|
||||
void __iomem *regs;
|
||||
struct regmap *grf;
|
||||
|
||||
struct drm_connector connector;
|
||||
struct rockchip_encoder encoder;
|
||||
|
||||
struct inno_hdmi_i2c *i2c;
|
||||
struct i2c_adapter *ddc;
|
||||
|
||||
const struct inno_hdmi_variant *variant;
|
||||
const struct inno_hdmi_plat_data *plat_data;
|
||||
};
|
||||
|
||||
struct inno_hdmi_connector_state {
|
||||
struct drm_connector_state base;
|
||||
unsigned int colorimetry;
|
||||
};
|
||||
|
||||
static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||||
|
||||
return container_of(rkencoder, struct inno_hdmi, encoder);
|
||||
}
|
||||
|
||||
static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct inno_hdmi, connector);
|
||||
}
|
||||
|
||||
#define to_inno_hdmi_conn_state(conn_state) \
|
||||
container_of_const(conn_state, struct inno_hdmi_connector_state, base)
|
||||
|
||||
enum {
|
||||
CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
|
||||
CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
|
||||
|
|
@ -494,23 +449,15 @@ static const char coeff_csc[][24] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = {
|
||||
{ 74250000, 0x3f, 0xbb },
|
||||
{ 165000000, 0x6f, 0xbb },
|
||||
{ ~0UL, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
static struct inno_hdmi_phy_config rk3128_hdmi_phy_configs[] = {
|
||||
{ 74250000, 0x3f, 0xaa },
|
||||
{ 165000000, 0x5f, 0xaa },
|
||||
{ ~0UL, 0x00, 0x00 }
|
||||
};
|
||||
static struct inno_hdmi *bridge_to_inno_hdmi(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct inno_hdmi, bridge);
|
||||
}
|
||||
|
||||
static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi,
|
||||
unsigned long pixelclk)
|
||||
{
|
||||
const struct inno_hdmi_phy_config *phy_configs =
|
||||
hdmi->variant->phy_configs;
|
||||
const struct inno_hdmi_phy_config *phy_configs = hdmi->plat_data->phy_configs;
|
||||
int i;
|
||||
|
||||
for (i = 0; phy_configs[i].pixelclock != ~0UL; i++) {
|
||||
|
|
@ -582,12 +529,12 @@ static void inno_hdmi_power_up(struct inno_hdmi *hdmi,
|
|||
int ret = inno_hdmi_find_phy_config(hdmi, mpixelclock);
|
||||
|
||||
if (ret < 0) {
|
||||
phy_config = hdmi->variant->default_phy_config;
|
||||
phy_config = hdmi->plat_data->default_phy_config;
|
||||
DRM_DEV_ERROR(hdmi->dev,
|
||||
"Using default phy configuration for TMDS rate %lu",
|
||||
mpixelclock);
|
||||
} else {
|
||||
phy_config = &hdmi->variant->phy_configs[ret];
|
||||
phy_config = &hdmi->plat_data->phy_configs[ret];
|
||||
}
|
||||
|
||||
inno_hdmi_sys_power(hdmi, false);
|
||||
|
|
@ -637,14 +584,13 @@ static void inno_hdmi_init_hw(struct inno_hdmi *hdmi)
|
|||
hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
|
||||
}
|
||||
|
||||
static int inno_hdmi_disable_frame(struct drm_connector *connector,
|
||||
enum hdmi_infoframe_type type)
|
||||
static int inno_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge,
|
||||
enum hdmi_infoframe_type type)
|
||||
{
|
||||
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
|
||||
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
|
||||
|
||||
if (type != HDMI_INFOFRAME_TYPE_AVI) {
|
||||
drm_err(connector->dev,
|
||||
"Unsupported infoframe type: %u\n", type);
|
||||
drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -653,20 +599,19 @@ static int inno_hdmi_disable_frame(struct drm_connector *connector,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int inno_hdmi_upload_frame(struct drm_connector *connector,
|
||||
enum hdmi_infoframe_type type,
|
||||
const u8 *buffer, size_t len)
|
||||
static int inno_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
|
||||
enum hdmi_infoframe_type type,
|
||||
const u8 *buffer, size_t len)
|
||||
{
|
||||
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
|
||||
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
|
||||
ssize_t i;
|
||||
|
||||
if (type != HDMI_INFOFRAME_TYPE_AVI) {
|
||||
drm_err(connector->dev,
|
||||
"Unsupported infoframe type: %u\n", type);
|
||||
drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inno_hdmi_disable_frame(connector, type);
|
||||
inno_hdmi_bridge_clear_infoframe(bridge, type);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, buffer[i]);
|
||||
|
|
@ -674,23 +619,26 @@ static int inno_hdmi_upload_frame(struct drm_connector *connector,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_hdmi_funcs inno_hdmi_hdmi_connector_funcs = {
|
||||
.clear_infoframe = inno_hdmi_disable_frame,
|
||||
.write_infoframe = inno_hdmi_upload_frame,
|
||||
};
|
||||
|
||||
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
|
||||
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi,
|
||||
struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_connector *connector = &hdmi->connector;
|
||||
struct drm_connector_state *conn_state = connector->state;
|
||||
struct inno_hdmi_connector_state *inno_conn_state =
|
||||
to_inno_hdmi_conn_state(conn_state);
|
||||
int c0_c2_change = 0;
|
||||
int csc_enable = 0;
|
||||
int csc_mode = 0;
|
||||
int auto_csc = 0;
|
||||
int value;
|
||||
int i;
|
||||
int colorimetry;
|
||||
u8 vic = drm_match_cea_mode(mode);
|
||||
|
||||
if (vic == 6 || vic == 7 || vic == 21 || vic == 22 ||
|
||||
vic == 2 || vic == 3 || vic == 17 || vic == 18)
|
||||
colorimetry = HDMI_COLORIMETRY_ITU_601;
|
||||
else
|
||||
colorimetry = HDMI_COLORIMETRY_ITU_709;
|
||||
|
||||
|
||||
/* Input video mode is SDR RGB24bit, data enable signal from external */
|
||||
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
|
||||
|
|
@ -720,7 +668,7 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
|
|||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) {
|
||||
if (colorimetry == HDMI_COLORIMETRY_ITU_601) {
|
||||
if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) {
|
||||
csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
|
||||
auto_csc = AUTO_CSC_DISABLE;
|
||||
|
|
@ -738,8 +686,7 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
|
|||
}
|
||||
|
||||
for (i = 0; i < 24; i++)
|
||||
hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
|
||||
coeff_csc[csc_mode][i]);
|
||||
hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i, coeff_csc[csc_mode][i]);
|
||||
|
||||
value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
|
||||
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
|
||||
|
|
@ -753,15 +700,11 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
|
|||
static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
int value, psync;
|
||||
const struct inno_hdmi_plat_ops *plat_ops = hdmi->plat_data->ops;
|
||||
u32 value;
|
||||
|
||||
if (hdmi->variant->dev_type == RK3036_HDMI) {
|
||||
psync = mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0;
|
||||
value = FIELD_PREP_WM16(RK3036_HDMI_PHSYNC, psync);
|
||||
psync = mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0;
|
||||
value |= FIELD_PREP_WM16(RK3036_HDMI_PVSYNC, psync);
|
||||
regmap_write(hdmi->grf, RK3036_GRF_SOC_CON2, value);
|
||||
}
|
||||
if (plat_ops && plat_ops->enable)
|
||||
plat_ops->enable(hdmi->dev, mode);
|
||||
|
||||
/* Set detail external video timing polarity and interlace mode */
|
||||
value = v_EXTERANL_VIDEO(1);
|
||||
|
|
@ -810,14 +753,16 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int inno_hdmi_setup(struct inno_hdmi *hdmi,
|
||||
struct drm_atomic_state *state)
|
||||
static int inno_hdmi_setup(struct inno_hdmi *hdmi, struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector *connector = &hdmi->connector;
|
||||
struct drm_display_info *display = &connector->display_info;
|
||||
struct drm_bridge *bridge = &hdmi->bridge;
|
||||
struct drm_connector *connector;
|
||||
struct drm_display_info *info;
|
||||
struct drm_connector_state *new_conn_state;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
|
||||
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
|
||||
|
||||
new_conn_state = drm_atomic_get_new_connector_state(state, connector);
|
||||
if (WARN_ON(!new_conn_state))
|
||||
return -EINVAL;
|
||||
|
|
@ -826,17 +771,18 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
|
|||
if (WARN_ON(!new_crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
info = &connector->display_info;
|
||||
|
||||
/* Mute video and audio output */
|
||||
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
|
||||
v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
|
||||
|
||||
/* Set HDMI Mode */
|
||||
hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
|
||||
v_HDMI_DVI(display->is_hdmi));
|
||||
hdmi_writeb(hdmi, HDMI_HDCP_CTRL, v_HDMI_DVI(info->is_hdmi));
|
||||
|
||||
inno_hdmi_config_video_timing(hdmi, &new_crtc_state->adjusted_mode);
|
||||
|
||||
inno_hdmi_config_video_csc(hdmi);
|
||||
inno_hdmi_config_video_csc(hdmi, connector, &new_crtc_state->adjusted_mode);
|
||||
|
||||
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
|
||||
|
||||
|
|
@ -857,9 +803,11 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static enum drm_mode_status inno_hdmi_display_mode_valid(struct inno_hdmi *hdmi,
|
||||
const struct drm_display_mode *mode)
|
||||
static enum drm_mode_status inno_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_info *info,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
|
||||
unsigned long mpixelclk, max_tolerance;
|
||||
long rounded_refclk;
|
||||
|
||||
|
|
@ -889,189 +837,57 @@ static enum drm_mode_status inno_hdmi_display_mode_valid(struct inno_hdmi *hdmi,
|
|||
return MODE_OK;
|
||||
}
|
||||
|
||||
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
|
||||
|
||||
inno_hdmi_setup(hdmi, state);
|
||||
}
|
||||
|
||||
static void inno_hdmi_encoder_disable(struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
|
||||
|
||||
inno_hdmi_standby(hdmi);
|
||||
}
|
||||
|
||||
static int
|
||||
inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
|
||||
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
|
||||
u8 vic = drm_match_cea_mode(mode);
|
||||
struct inno_hdmi_connector_state *inno_conn_state =
|
||||
to_inno_hdmi_conn_state(conn_state);
|
||||
|
||||
s->output_mode = ROCKCHIP_OUT_MODE_P888;
|
||||
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
|
||||
if (vic == 6 || vic == 7 ||
|
||||
vic == 21 || vic == 22 ||
|
||||
vic == 2 || vic == 3 ||
|
||||
vic == 17 || vic == 18)
|
||||
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_601;
|
||||
else
|
||||
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
|
||||
.atomic_check = inno_hdmi_encoder_atomic_check,
|
||||
.atomic_enable = inno_hdmi_encoder_enable,
|
||||
.atomic_disable = inno_hdmi_encoder_disable,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
inno_hdmi_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
|
||||
{
|
||||
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
|
||||
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
|
||||
|
||||
return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
|
||||
connector_status_connected : connector_status_disconnected;
|
||||
}
|
||||
|
||||
static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
static const struct drm_edid *
|
||||
inno_hdmi_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector)
|
||||
{
|
||||
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
|
||||
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
|
||||
const struct drm_edid *drm_edid;
|
||||
int ret = 0;
|
||||
|
||||
if (!hdmi->ddc)
|
||||
return 0;
|
||||
drm_edid = drm_edid_read_ddc(connector, bridge->ddc);
|
||||
if (!drm_edid)
|
||||
dev_dbg(hdmi->dev, "failed to get edid\n");
|
||||
|
||||
drm_edid = drm_edid_read_ddc(connector, hdmi->ddc);
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
ret = drm_edid_connector_add_modes(connector);
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
return ret;
|
||||
return drm_edid;
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
inno_hdmi_connector_mode_valid(struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode)
|
||||
static void inno_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
|
||||
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
|
||||
|
||||
return inno_hdmi_display_mode_valid(hdmi, mode);
|
||||
inno_hdmi_setup(hdmi, state);
|
||||
}
|
||||
|
||||
static void
|
||||
inno_hdmi_connector_destroy_state(struct drm_connector *connector,
|
||||
struct drm_connector_state *state)
|
||||
static void inno_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct inno_hdmi_connector_state *inno_conn_state =
|
||||
to_inno_hdmi_conn_state(state);
|
||||
struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
|
||||
|
||||
__drm_atomic_helper_connector_destroy_state(&inno_conn_state->base);
|
||||
kfree(inno_conn_state);
|
||||
inno_hdmi_standby(hdmi);
|
||||
}
|
||||
|
||||
static void inno_hdmi_connector_reset(struct drm_connector *connector)
|
||||
{
|
||||
struct inno_hdmi_connector_state *inno_conn_state;
|
||||
|
||||
if (connector->state) {
|
||||
inno_hdmi_connector_destroy_state(connector, connector->state);
|
||||
connector->state = NULL;
|
||||
}
|
||||
|
||||
inno_conn_state = kzalloc(sizeof(*inno_conn_state), GFP_KERNEL);
|
||||
if (!inno_conn_state)
|
||||
return;
|
||||
|
||||
__drm_atomic_helper_connector_reset(connector, &inno_conn_state->base);
|
||||
__drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
|
||||
|
||||
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
|
||||
}
|
||||
|
||||
static struct drm_connector_state *
|
||||
inno_hdmi_connector_duplicate_state(struct drm_connector *connector)
|
||||
{
|
||||
struct inno_hdmi_connector_state *inno_conn_state;
|
||||
|
||||
if (WARN_ON(!connector->state))
|
||||
return NULL;
|
||||
|
||||
inno_conn_state = kmemdup(to_inno_hdmi_conn_state(connector->state),
|
||||
sizeof(*inno_conn_state), GFP_KERNEL);
|
||||
|
||||
if (!inno_conn_state)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_connector_duplicate_state(connector,
|
||||
&inno_conn_state->base);
|
||||
|
||||
return &inno_conn_state->base;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = inno_hdmi_connector_detect,
|
||||
.reset = inno_hdmi_connector_reset,
|
||||
.atomic_duplicate_state = inno_hdmi_connector_duplicate_state,
|
||||
.atomic_destroy_state = inno_hdmi_connector_destroy_state,
|
||||
static const struct drm_bridge_funcs inno_hdmi_bridge_funcs = {
|
||||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
.atomic_reset = drm_atomic_helper_bridge_reset,
|
||||
.atomic_enable = inno_hdmi_bridge_atomic_enable,
|
||||
.atomic_disable = inno_hdmi_bridge_atomic_disable,
|
||||
.detect = inno_hdmi_bridge_detect,
|
||||
.edid_read = inno_hdmi_bridge_edid_read,
|
||||
.hdmi_clear_infoframe = inno_hdmi_bridge_clear_infoframe,
|
||||
.hdmi_write_infoframe = inno_hdmi_bridge_write_infoframe,
|
||||
.mode_valid = inno_hdmi_bridge_mode_valid,
|
||||
};
|
||||
|
||||
static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
|
||||
.atomic_check = drm_atomic_helper_connector_hdmi_check,
|
||||
.get_modes = inno_hdmi_connector_get_modes,
|
||||
.mode_valid = inno_hdmi_connector_mode_valid,
|
||||
};
|
||||
|
||||
static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
|
||||
{
|
||||
struct drm_encoder *encoder = &hdmi->encoder.encoder;
|
||||
struct device *dev = hdmi->dev;
|
||||
|
||||
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
|
||||
|
||||
/*
|
||||
* If we failed to find the CRTC(s) which this encoder is
|
||||
* supposed to be connected to, it's because the CRTC has
|
||||
* not been registered yet. Defer probing, and hope that
|
||||
* the required CRTC is added later.
|
||||
*/
|
||||
if (encoder->possible_crtcs == 0)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
|
||||
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
|
||||
|
||||
hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
drm_connector_helper_add(&hdmi->connector,
|
||||
&inno_hdmi_connector_helper_funcs);
|
||||
drmm_connector_hdmi_init(drm, &hdmi->connector,
|
||||
"Rockchip", "Inno HDMI",
|
||||
&inno_hdmi_connector_funcs,
|
||||
&inno_hdmi_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA,
|
||||
hdmi->ddc,
|
||||
BIT(HDMI_COLORSPACE_RGB),
|
||||
8);
|
||||
|
||||
drm_connector_attach_encoder(&hdmi->connector, encoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
|
||||
{
|
||||
struct inno_hdmi_i2c *i2c = hdmi->i2c;
|
||||
|
|
@ -1111,7 +927,7 @@ static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
|
|||
{
|
||||
struct inno_hdmi *hdmi = dev_id;
|
||||
|
||||
drm_helper_hpd_irq_event(hdmi->connector.dev);
|
||||
drm_helper_hpd_irq_event(hdmi->bridge.dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -1243,128 +1059,80 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
|
|||
return adap;
|
||||
}
|
||||
|
||||
static int inno_hdmi_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
struct inno_hdmi *inno_hdmi_bind(struct device *dev,
|
||||
struct drm_encoder *encoder,
|
||||
const struct inno_hdmi_plat_data *plat_data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *drm = data;
|
||||
struct inno_hdmi *hdmi;
|
||||
const struct inno_hdmi_variant *variant;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
|
||||
if (!hdmi)
|
||||
return -ENOMEM;
|
||||
if (!plat_data->phy_configs || !plat_data->default_phy_config) {
|
||||
dev_err(dev, "Missing platform PHY ops\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
hdmi = devm_drm_bridge_alloc(dev, struct inno_hdmi, bridge, &inno_hdmi_bridge_funcs);
|
||||
if (IS_ERR(hdmi))
|
||||
return ERR_CAST(hdmi);
|
||||
|
||||
hdmi->dev = dev;
|
||||
|
||||
variant = of_device_get_match_data(hdmi->dev);
|
||||
if (!variant)
|
||||
return -EINVAL;
|
||||
|
||||
hdmi->variant = variant;
|
||||
hdmi->plat_data = plat_data;
|
||||
|
||||
hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(hdmi->regs))
|
||||
return PTR_ERR(hdmi->regs);
|
||||
return ERR_CAST(hdmi->regs);
|
||||
|
||||
hdmi->pclk = devm_clk_get_enabled(hdmi->dev, "pclk");
|
||||
if (IS_ERR(hdmi->pclk))
|
||||
return dev_err_probe(dev, PTR_ERR(hdmi->pclk), "Unable to get HDMI pclk\n");
|
||||
|
||||
hdmi->refclk = devm_clk_get_optional_enabled(hdmi->dev, "ref");
|
||||
if (IS_ERR(hdmi->refclk))
|
||||
return dev_err_probe(dev, PTR_ERR(hdmi->refclk), "Unable to get HDMI refclk\n");
|
||||
|
||||
if (hdmi->variant->dev_type == RK3036_HDMI) {
|
||||
hdmi->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
|
||||
if (IS_ERR(hdmi->grf))
|
||||
return dev_err_probe(dev,
|
||||
PTR_ERR(hdmi->grf), "Unable to get rockchip,grf\n");
|
||||
if (IS_ERR(hdmi->pclk)) {
|
||||
dev_err_probe(dev, PTR_ERR(hdmi->pclk), "Unable to get HDMI pclk\n");
|
||||
return ERR_CAST(hdmi->pclk);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
hdmi->refclk = devm_clk_get_optional_enabled(hdmi->dev, "ref");
|
||||
if (IS_ERR(hdmi->refclk)) {
|
||||
dev_err_probe(dev, PTR_ERR(hdmi->refclk), "Unable to get HDMI refclk\n");
|
||||
return ERR_CAST(hdmi->refclk);
|
||||
}
|
||||
|
||||
inno_hdmi_init_hw(hdmi);
|
||||
|
||||
hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
|
||||
if (IS_ERR(hdmi->ddc))
|
||||
return PTR_ERR(hdmi->ddc);
|
||||
|
||||
ret = inno_hdmi_register(drm, hdmi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(dev, hdmi);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return ERR_PTR(irq);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
|
||||
inno_hdmi_irq, IRQF_SHARED,
|
||||
dev_name(dev), hdmi);
|
||||
if (ret < 0)
|
||||
goto err_cleanup_hdmi;
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return 0;
|
||||
err_cleanup_hdmi:
|
||||
hdmi->connector.funcs->destroy(&hdmi->connector);
|
||||
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
|
||||
return ret;
|
||||
hdmi->bridge.driver_private = hdmi;
|
||||
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
|
||||
DRM_BRIDGE_OP_EDID |
|
||||
DRM_BRIDGE_OP_HDMI |
|
||||
DRM_BRIDGE_OP_HPD;
|
||||
hdmi->bridge.of_node = pdev->dev.of_node;
|
||||
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
hdmi->bridge.vendor = "Inno";
|
||||
hdmi->bridge.product = "Inno HDMI";
|
||||
|
||||
hdmi->bridge.ddc = inno_hdmi_i2c_adapter(hdmi);
|
||||
if (IS_ERR(hdmi->bridge.ddc))
|
||||
return ERR_CAST(hdmi->bridge.ddc);
|
||||
|
||||
ret = devm_drm_bridge_add(dev, &hdmi->bridge);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return hdmi;
|
||||
}
|
||||
|
||||
static void inno_hdmi_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct inno_hdmi *hdmi = dev_get_drvdata(dev);
|
||||
|
||||
hdmi->connector.funcs->destroy(&hdmi->connector);
|
||||
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
|
||||
}
|
||||
|
||||
static const struct component_ops inno_hdmi_ops = {
|
||||
.bind = inno_hdmi_bind,
|
||||
.unbind = inno_hdmi_unbind,
|
||||
};
|
||||
|
||||
static int inno_hdmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &inno_hdmi_ops);
|
||||
}
|
||||
|
||||
static void inno_hdmi_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &inno_hdmi_ops);
|
||||
}
|
||||
|
||||
static const struct inno_hdmi_variant rk3036_inno_hdmi_variant = {
|
||||
.dev_type = RK3036_HDMI,
|
||||
.phy_configs = rk3036_hdmi_phy_configs,
|
||||
.default_phy_config = &rk3036_hdmi_phy_configs[1],
|
||||
};
|
||||
|
||||
static const struct inno_hdmi_variant rk3128_inno_hdmi_variant = {
|
||||
.dev_type = RK3128_HDMI,
|
||||
.phy_configs = rk3128_hdmi_phy_configs,
|
||||
.default_phy_config = &rk3128_hdmi_phy_configs[1],
|
||||
};
|
||||
|
||||
static const struct of_device_id inno_hdmi_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3036-inno-hdmi",
|
||||
.data = &rk3036_inno_hdmi_variant,
|
||||
},
|
||||
{ .compatible = "rockchip,rk3128-inno-hdmi",
|
||||
.data = &rk3128_inno_hdmi_variant,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
|
||||
|
||||
struct platform_driver inno_hdmi_driver = {
|
||||
.probe = inno_hdmi_probe,
|
||||
.remove = inno_hdmi_remove,
|
||||
.driver = {
|
||||
.name = "innohdmi-rockchip",
|
||||
.of_match_table = inno_hdmi_dt_ids,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(inno_hdmi_bind);
|
||||
MODULE_AUTHOR("Andy Yan <andyshrk@163.com>");
|
||||
MODULE_DESCRIPTION("INNOSILICON HDMI transmitter library");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -298,7 +298,6 @@ struct it66121_chip_info {
|
|||
struct it66121_ctx {
|
||||
struct regmap *regmap;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
struct drm_connector *connector;
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio_reset;
|
||||
|
|
@ -596,7 +595,7 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,
|
|||
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
|
||||
return -EINVAL;
|
||||
|
||||
ret = drm_bridge_attach(encoder, ctx->next_bridge, bridge, flags);
|
||||
ret = drm_bridge_attach(encoder, ctx->bridge.next_bridge, bridge, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1543,9 +1542,9 @@ static int it66121_probe(struct i2c_client *client)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->next_bridge = of_drm_find_bridge(ep);
|
||||
ctx->bridge.next_bridge = of_drm_find_and_get_bridge(ep);
|
||||
of_node_put(ep);
|
||||
if (!ctx->next_bridge) {
|
||||
if (!ctx->bridge.next_bridge) {
|
||||
dev_dbg(ctx->dev, "Next bridge not found, deferring probe\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <sound/hdmi-codec.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
|
@ -27,6 +25,8 @@
|
|||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include <drm/display/drm_hdmi_audio_helper.h>
|
||||
|
||||
#define EDID_BLOCK_SIZE 128
|
||||
#define EDID_NUM_BLOCKS 2
|
||||
|
||||
|
|
@ -48,7 +48,6 @@ struct lt9611uxc {
|
|||
struct device_node *dsi1_node;
|
||||
struct mipi_dsi_device *dsi0;
|
||||
struct mipi_dsi_device *dsi1;
|
||||
struct platform_device *audio_pdev;
|
||||
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
|
@ -429,12 +428,52 @@ static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *brid
|
|||
return drm_edid_read_custom(connector, lt9611uxc_get_edid_block, lt9611uxc);
|
||||
}
|
||||
|
||||
static void lt9611uxc_bridge_hpd_notify(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector,
|
||||
enum drm_connector_status status)
|
||||
{
|
||||
const struct drm_edid *drm_edid;
|
||||
|
||||
if (status == connector_status_disconnected) {
|
||||
drm_connector_hdmi_audio_plugged_notify(connector, false);
|
||||
drm_edid_connector_update(connector, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
drm_edid = lt9611uxc_bridge_edid_read(bridge, connector);
|
||||
drm_edid_connector_update(connector, drm_edid);
|
||||
drm_edid_free(drm_edid);
|
||||
|
||||
if (status == connector_status_connected)
|
||||
drm_connector_hdmi_audio_plugged_notify(connector, true);
|
||||
}
|
||||
|
||||
static int lt9611uxc_hdmi_audio_prepare(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
/*
|
||||
* LT9611UXC will automatically detect rate and sample size, so no need
|
||||
* to setup anything here.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lt9611uxc_hdmi_audio_shutdown(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = {
|
||||
.attach = lt9611uxc_bridge_attach,
|
||||
.mode_valid = lt9611uxc_bridge_mode_valid,
|
||||
.mode_set = lt9611uxc_bridge_mode_set,
|
||||
.detect = lt9611uxc_bridge_detect,
|
||||
.edid_read = lt9611uxc_bridge_edid_read,
|
||||
.hpd_notify = lt9611uxc_bridge_hpd_notify,
|
||||
.hdmi_audio_prepare = lt9611uxc_hdmi_audio_prepare,
|
||||
.hdmi_audio_shutdown = lt9611uxc_hdmi_audio_shutdown,
|
||||
};
|
||||
|
||||
static int lt9611uxc_parse_dt(struct device *dev,
|
||||
|
|
@ -508,73 +547,6 @@ static int lt9611uxc_read_version(struct lt9611uxc *lt9611uxc)
|
|||
return ret < 0 ? ret : rev;
|
||||
}
|
||||
|
||||
static int lt9611uxc_hdmi_hw_params(struct device *dev, void *data,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
/*
|
||||
* LT9611UXC will automatically detect rate and sample size, so no need
|
||||
* to setup anything here.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lt9611uxc_audio_shutdown(struct device *dev, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint,
|
||||
void *data)
|
||||
{
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
||||
ret = of_graph_parse_endpoint(endpoint, &of_ep);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* HDMI sound should be located as reg = <2>
|
||||
* Then, it is sound port 0
|
||||
*/
|
||||
if (of_ep.port == 2)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct hdmi_codec_ops lt9611uxc_codec_ops = {
|
||||
.hw_params = lt9611uxc_hdmi_hw_params,
|
||||
.audio_shutdown = lt9611uxc_audio_shutdown,
|
||||
.get_dai_id = lt9611uxc_hdmi_i2s_get_dai_id,
|
||||
};
|
||||
|
||||
static int lt9611uxc_audio_init(struct device *dev, struct lt9611uxc *lt9611uxc)
|
||||
{
|
||||
struct hdmi_codec_pdata codec_data = {
|
||||
.ops = <9611uxc_codec_ops,
|
||||
.max_i2s_channels = 2,
|
||||
.i2s = 1,
|
||||
.data = lt9611uxc,
|
||||
};
|
||||
|
||||
lt9611uxc->audio_pdev =
|
||||
platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
|
||||
PLATFORM_DEVID_AUTO,
|
||||
&codec_data, sizeof(codec_data));
|
||||
|
||||
return PTR_ERR_OR_ZERO(lt9611uxc->audio_pdev);
|
||||
}
|
||||
|
||||
static void lt9611uxc_audio_exit(struct lt9611uxc *lt9611uxc)
|
||||
{
|
||||
if (lt9611uxc->audio_pdev) {
|
||||
platform_device_unregister(lt9611uxc->audio_pdev);
|
||||
lt9611uxc->audio_pdev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define LT9611UXC_FW_PAGE_SIZE 32
|
||||
static void lt9611uxc_firmware_write_page(struct lt9611uxc *lt9611uxc, u16 addr, const u8 *buf)
|
||||
{
|
||||
|
|
@ -858,11 +830,17 @@ retry:
|
|||
i2c_set_clientdata(client, lt9611uxc);
|
||||
|
||||
lt9611uxc->bridge.of_node = client->dev.of_node;
|
||||
lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
|
||||
lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT |
|
||||
DRM_BRIDGE_OP_EDID |
|
||||
DRM_BRIDGE_OP_HDMI_AUDIO;
|
||||
if (lt9611uxc->hpd_supported)
|
||||
lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_HPD;
|
||||
lt9611uxc->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
|
||||
lt9611uxc->bridge.hdmi_audio_dev = dev;
|
||||
lt9611uxc->bridge.hdmi_audio_max_i2s_playback_channels = 2;
|
||||
lt9611uxc->bridge.hdmi_audio_dai_port = 2;
|
||||
|
||||
drm_bridge_add(<9611uxc->bridge);
|
||||
|
||||
/* Attach primary DSI */
|
||||
|
|
@ -881,10 +859,6 @@ retry:
|
|||
}
|
||||
}
|
||||
|
||||
ret = lt9611uxc_audio_init(dev, lt9611uxc);
|
||||
if (ret)
|
||||
goto err_remove_bridge;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_bridge:
|
||||
|
|
@ -908,7 +882,6 @@ static void lt9611uxc_remove(struct i2c_client *client)
|
|||
|
||||
free_irq(client->irq, lt9611uxc);
|
||||
cancel_work_sync(<9611uxc->work);
|
||||
lt9611uxc_audio_exit(lt9611uxc);
|
||||
drm_bridge_remove(<9611uxc->bridge);
|
||||
|
||||
mutex_destroy(<9611uxc->ocm_lock);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ struct simple_bridge {
|
|||
|
||||
const struct simple_bridge_info *info;
|
||||
|
||||
struct drm_bridge *next_bridge;
|
||||
struct regulator *vdd;
|
||||
struct gpio_desc *enable;
|
||||
};
|
||||
|
|
@ -54,8 +53,8 @@ static int simple_bridge_get_modes(struct drm_connector *connector)
|
|||
const struct drm_edid *drm_edid;
|
||||
int ret;
|
||||
|
||||
if (sbridge->next_bridge->ops & DRM_BRIDGE_OP_EDID) {
|
||||
drm_edid = drm_bridge_edid_read(sbridge->next_bridge, connector);
|
||||
if (sbridge->bridge.next_bridge->ops & DRM_BRIDGE_OP_EDID) {
|
||||
drm_edid = drm_bridge_edid_read(sbridge->bridge.next_bridge, connector);
|
||||
if (!drm_edid)
|
||||
DRM_INFO("EDID read failed. Fallback to standard modes\n");
|
||||
} else {
|
||||
|
|
@ -90,7 +89,7 @@ simple_bridge_connector_detect(struct drm_connector *connector, bool force)
|
|||
{
|
||||
struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
|
||||
|
||||
return drm_bridge_detect(sbridge->next_bridge, connector);
|
||||
return drm_bridge_detect(sbridge->bridge.next_bridge, connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs simple_bridge_con_funcs = {
|
||||
|
|
@ -109,7 +108,7 @@ static int simple_bridge_attach(struct drm_bridge *bridge,
|
|||
struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
|
||||
int ret;
|
||||
|
||||
ret = drm_bridge_attach(encoder, sbridge->next_bridge, bridge,
|
||||
ret = drm_bridge_attach(encoder, sbridge->bridge.next_bridge, bridge,
|
||||
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -122,7 +121,7 @@ static int simple_bridge_attach(struct drm_bridge *bridge,
|
|||
ret = drm_connector_init_with_ddc(bridge->dev, &sbridge->connector,
|
||||
&simple_bridge_con_funcs,
|
||||
sbridge->info->connector_type,
|
||||
sbridge->next_bridge->ddc);
|
||||
sbridge->bridge.next_bridge->ddc);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to initialize connector\n");
|
||||
return ret;
|
||||
|
|
@ -180,10 +179,10 @@ static int simple_bridge_probe(struct platform_device *pdev)
|
|||
if (!remote)
|
||||
return -EINVAL;
|
||||
|
||||
sbridge->next_bridge = of_drm_find_bridge(remote);
|
||||
sbridge->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
|
||||
of_node_put(remote);
|
||||
|
||||
if (!sbridge->next_bridge) {
|
||||
if (!sbridge->bridge.next_bridge) {
|
||||
dev_dbg(&pdev->dev, "Next bridge not found, deferring probe\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ static u32 drm_log_find_usable_format(struct drm_plane *plane)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < plane->format_count; i++)
|
||||
if (drm_draw_color_from_xrgb8888(0xffffff, plane->format_types[i]) != 0)
|
||||
if (drm_draw_can_convert_from_xrgb8888(plane->format_types[i]))
|
||||
return plane->format_types[i];
|
||||
return DRM_FORMAT_INVALID;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
|
|||
/* Notify all bridges in the pipeline of hotplug events. */
|
||||
drm_for_each_bridge_in_chain_scoped(bridge_connector->encoder, bridge) {
|
||||
if (bridge->funcs->hpd_notify)
|
||||
bridge->funcs->hpd_notify(bridge, status);
|
||||
bridge->funcs->hpd_notify(bridge, connector, status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -641,6 +641,38 @@ drm_atomic_get_colorop_state(struct drm_atomic_state *state,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_colorop_state);
|
||||
|
||||
/**
|
||||
* drm_atomic_get_old_colorop_state - get colorop state, if it exists
|
||||
* @state: global atomic state object
|
||||
* @colorop: colorop to grab
|
||||
*
|
||||
* This function returns the old colorop state for the given colorop, or
|
||||
* NULL if the colorop is not part of the global atomic state.
|
||||
*/
|
||||
struct drm_colorop_state *
|
||||
drm_atomic_get_old_colorop_state(struct drm_atomic_state *state,
|
||||
struct drm_colorop *colorop)
|
||||
{
|
||||
return state->colorops[drm_colorop_index(colorop)].old_state;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_old_colorop_state);
|
||||
|
||||
/**
|
||||
* drm_atomic_get_new_colorop_state - get colorop state, if it exists
|
||||
* @state: global atomic state object
|
||||
* @colorop: colorop to grab
|
||||
*
|
||||
* This function returns the new colorop state for the given colorop, or
|
||||
* NULL if the colorop is not part of the global atomic state.
|
||||
*/
|
||||
struct drm_colorop_state *
|
||||
drm_atomic_get_new_colorop_state(struct drm_atomic_state *state,
|
||||
struct drm_colorop *colorop)
|
||||
{
|
||||
return state->colorops[drm_colorop_index(colorop)].new_state;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_new_colorop_state);
|
||||
|
||||
static bool
|
||||
plane_switching_crtc(const struct drm_plane_state *old_plane_state,
|
||||
const struct drm_plane_state *new_plane_state)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <drm/drm_atomic_uapi.h>
|
||||
#include <drm/drm_blend.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_colorop.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_drv.h>
|
||||
|
|
|
|||
|
|
@ -275,6 +275,8 @@ static void __drm_bridge_free(struct kref *kref)
|
|||
if (bridge->funcs->destroy)
|
||||
bridge->funcs->destroy(bridge);
|
||||
|
||||
drm_bridge_put(bridge->next_bridge);
|
||||
|
||||
kfree(bridge->container);
|
||||
}
|
||||
|
||||
|
|
@ -361,7 +363,7 @@ EXPORT_SYMBOL(__devm_drm_bridge_alloc);
|
|||
* @bridge: bridge control structure
|
||||
*
|
||||
* Add the given bridge to the global list of bridges, where they can be
|
||||
* found by users via of_drm_find_bridge().
|
||||
* found by users via of_drm_find_and_get_bridge().
|
||||
*
|
||||
* The bridge to be added must have been allocated by
|
||||
* devm_drm_bridge_alloc().
|
||||
|
|
@ -422,9 +424,9 @@ EXPORT_SYMBOL(devm_drm_bridge_add);
|
|||
* @bridge: bridge control structure
|
||||
*
|
||||
* Remove the given bridge from the global list of registered bridges, so
|
||||
* it won't be found by users via of_drm_find_bridge(), and add it to the
|
||||
* lingering bridge list, to keep track of it until its allocated memory is
|
||||
* eventually freed.
|
||||
* it won't be found by users via of_drm_find_and_get_bridge(), and add it
|
||||
* to the lingering bridge list, to keep track of it until its allocated
|
||||
* memory is eventually freed.
|
||||
*/
|
||||
void drm_bridge_remove(struct drm_bridge *bridge)
|
||||
{
|
||||
|
|
@ -1479,30 +1481,67 @@ void drm_bridge_hpd_notify(struct drm_bridge *bridge,
|
|||
EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/**
|
||||
* of_drm_find_and_get_bridge - find the bridge corresponding to the device
|
||||
* node in the global bridge list
|
||||
* @np: device node
|
||||
*
|
||||
* The refcount of the returned bridge is incremented. Use drm_bridge_put()
|
||||
* when done with it.
|
||||
*
|
||||
* RETURNS:
|
||||
* drm_bridge control struct on success, NULL on failure
|
||||
*/
|
||||
struct drm_bridge *of_drm_find_and_get_bridge(struct device_node *np)
|
||||
{
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
scoped_guard(mutex, &bridge_lock) {
|
||||
list_for_each_entry(bridge, &bridge_list, list)
|
||||
if (bridge->of_node == np)
|
||||
return drm_bridge_get(bridge);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(of_drm_find_and_get_bridge);
|
||||
|
||||
/**
|
||||
* of_drm_find_bridge - find the bridge corresponding to the device node in
|
||||
* the global bridge list
|
||||
*
|
||||
* @np: device node
|
||||
*
|
||||
* This function is deprecated. Convert to of_drm_find_and_get_bridge()
|
||||
* instead for proper refcounting.
|
||||
*
|
||||
* The bridge returned by this function is not refcounted. This is
|
||||
* dangerous because the bridge might be deallocated even before the caller
|
||||
* has a chance to use it. To use this function you have to do one of:
|
||||
* - get a reference with drm_bridge_get() as soon as possible to
|
||||
* minimize the race window, and then drm_bridge_put() when no longer
|
||||
* using the pointer
|
||||
* - not call drm_bridge_get() or drm_bridge_put() at all, which used to
|
||||
* be the correct practice before dynamic bridge lifetime was introduced
|
||||
* - again, convert to of_drm_find_and_get_bridge(), which is the only safe
|
||||
* thing to do
|
||||
*
|
||||
* RETURNS:
|
||||
* drm_bridge control struct on success, NULL on failure
|
||||
*/
|
||||
struct drm_bridge *of_drm_find_bridge(struct device_node *np)
|
||||
{
|
||||
struct drm_bridge *bridge;
|
||||
struct drm_bridge *bridge = of_drm_find_and_get_bridge(np);
|
||||
|
||||
mutex_lock(&bridge_lock);
|
||||
/*
|
||||
* We need to emulate the original semantics of
|
||||
* of_drm_find_bridge(), which was not getting any bridge
|
||||
* reference. Being now based on of_drm_find_and_get_bridge() which
|
||||
* gets a reference, put it before returning.
|
||||
*/
|
||||
drm_bridge_put(bridge);
|
||||
|
||||
list_for_each_entry(bridge, &bridge_list, list) {
|
||||
if (bridge->of_node == np) {
|
||||
mutex_unlock(&bridge_lock);
|
||||
return bridge;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&bridge_lock);
|
||||
return NULL;
|
||||
return bridge;
|
||||
}
|
||||
EXPORT_SYMBOL(of_drm_find_bridge);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -15,6 +15,35 @@
|
|||
#include "drm_draw_internal.h"
|
||||
#include "drm_format_internal.h"
|
||||
|
||||
/**
|
||||
* drm_draw_can_convert_from_xrgb8888 - check if xrgb8888 can be converted to the desired format
|
||||
* @format: format
|
||||
*
|
||||
* Returns:
|
||||
* True if XRGB8888 can be converted to the specified format, false otherwise.
|
||||
*/
|
||||
bool drm_draw_can_convert_from_xrgb8888(u32 format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
case DRM_FORMAT_RGBA5551:
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_XRGB2101010:
|
||||
case DRM_FORMAT_ARGB2101010:
|
||||
case DRM_FORMAT_ABGR2101010:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_draw_can_convert_from_xrgb8888);
|
||||
|
||||
/**
|
||||
* drm_draw_color_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format
|
||||
* @color: input color, in xrgb8888 format
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ static inline const u8 *drm_draw_get_char_bitmap(const struct font_desc *font,
|
|||
return font->data + (c * font->height) * font_pitch;
|
||||
}
|
||||
|
||||
bool drm_draw_can_convert_from_xrgb8888(u32 format);
|
||||
|
||||
u32 drm_draw_color_from_xrgb8888(u32 color, u32 format);
|
||||
|
||||
void drm_draw_blit16(struct iosys_map *dmap, unsigned int dpitch,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
#include <asm/set_memory.h>
|
||||
#endif
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include <drm/drm.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_drv.h>
|
||||
|
|
@ -901,6 +903,67 @@ fail_detach:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_prime_import_no_map);
|
||||
|
||||
/*
|
||||
* Kunit helpers
|
||||
*/
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem, struct iosys_map *map)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
int ret;
|
||||
|
||||
ret = dma_resv_lock_interruptible(obj->resv, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = drm_gem_shmem_vmap_locked(shmem, map);
|
||||
dma_resv_unlock(obj->resv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(drm_gem_shmem_vmap);
|
||||
|
||||
void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem, struct iosys_map *map)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
|
||||
dma_resv_lock_interruptible(obj->resv, NULL);
|
||||
drm_gem_shmem_vunmap_locked(shmem, map);
|
||||
dma_resv_unlock(obj->resv);
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(drm_gem_shmem_vunmap);
|
||||
|
||||
int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
int ret;
|
||||
|
||||
ret = dma_resv_lock_interruptible(obj->resv, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = drm_gem_shmem_madvise_locked(shmem, madv);
|
||||
dma_resv_unlock(obj->resv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(drm_gem_shmem_madvise);
|
||||
|
||||
int drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
int ret;
|
||||
|
||||
ret = dma_resv_lock_interruptible(obj->resv, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
drm_gem_shmem_purge_locked(shmem);
|
||||
dma_resv_unlock(obj->resv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(drm_gem_shmem_purge);
|
||||
#endif
|
||||
|
||||
MODULE_DESCRIPTION("DRM SHMEM memory-management helpers");
|
||||
MODULE_IMPORT_NS("DMA_BUF");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
|||
|
|
@ -476,7 +476,7 @@ static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *
|
|||
fg_color);
|
||||
}
|
||||
|
||||
static void draw_panic_static_user(struct drm_scanout_buffer *sb)
|
||||
static void draw_panic_screen_user(struct drm_scanout_buffer *sb)
|
||||
{
|
||||
u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,
|
||||
sb->format->format);
|
||||
|
|
@ -545,7 +545,7 @@ static int draw_line_with_wrap(struct drm_scanout_buffer *sb, const struct font_
|
|||
* Draw the kmsg buffer to the screen, starting from the youngest message at the bottom,
|
||||
* and going up until reaching the top of the screen.
|
||||
*/
|
||||
static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
|
||||
static void draw_panic_screen_kmsg(struct drm_scanout_buffer *sb)
|
||||
{
|
||||
u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,
|
||||
sb->format->format);
|
||||
|
|
@ -733,7 +733,7 @@ static int drm_panic_get_qr_code(u8 **qr_image)
|
|||
/*
|
||||
* Draw the panic message at the center of the screen, with a QR Code
|
||||
*/
|
||||
static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
|
||||
static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
|
||||
{
|
||||
u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,
|
||||
sb->format->format);
|
||||
|
|
@ -801,10 +801,10 @@ static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
|
||||
static void draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
|
||||
{
|
||||
if (_draw_panic_static_qr_code(sb))
|
||||
draw_panic_static_user(sb);
|
||||
if (_draw_panic_screen_qr_code(sb))
|
||||
draw_panic_screen_user(sb);
|
||||
}
|
||||
#else
|
||||
static void drm_panic_qr_init(void) {};
|
||||
|
|
@ -872,25 +872,25 @@ static bool drm_panic_is_format_supported(const struct drm_format_info *format)
|
|||
{
|
||||
if (format->num_planes != 1)
|
||||
return false;
|
||||
return drm_draw_color_from_xrgb8888(0xffffff, format->format) != 0;
|
||||
return drm_draw_can_convert_from_xrgb8888(format->format);
|
||||
}
|
||||
|
||||
static void draw_panic_dispatch(struct drm_scanout_buffer *sb)
|
||||
{
|
||||
switch (drm_panic_type) {
|
||||
case DRM_PANIC_TYPE_KMSG:
|
||||
draw_panic_static_kmsg(sb);
|
||||
draw_panic_screen_kmsg(sb);
|
||||
break;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
|
||||
case DRM_PANIC_TYPE_QR:
|
||||
draw_panic_static_qr_code(sb);
|
||||
draw_panic_screen_qr_code(sb);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case DRM_PANIC_TYPE_USER:
|
||||
default:
|
||||
draw_panic_static_user(sb);
|
||||
draw_panic_screen_user(sb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1084,3 +1084,7 @@ void drm_panic_exit(void)
|
|||
{
|
||||
drm_panic_qr_exit();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_KUNIT_TEST
|
||||
#include "tests/drm_panic_test.c"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ etnaviv-y := \
|
|||
etnaviv_iommu.o \
|
||||
etnaviv_mmu.o \
|
||||
etnaviv_perfmon.o \
|
||||
etnaviv_flop_reset.o \
|
||||
etnaviv_sched.o
|
||||
|
||||
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "etnaviv_gpu.h"
|
||||
#include "etnaviv_gem.h"
|
||||
#include "etnaviv_mmu.h"
|
||||
#include "etnaviv_buffer.h"
|
||||
|
||||
#include "common.xml.h"
|
||||
#include "state.xml.h"
|
||||
|
|
@ -18,75 +19,7 @@
|
|||
#include "state_3d.xml.h"
|
||||
#include "cmdstream.xml.h"
|
||||
|
||||
/*
|
||||
* Command Buffer helper:
|
||||
*/
|
||||
|
||||
|
||||
static inline void OUT(struct etnaviv_cmdbuf *buffer, u32 data)
|
||||
{
|
||||
u32 *vaddr = (u32 *)buffer->vaddr;
|
||||
|
||||
BUG_ON(buffer->user_size >= buffer->size);
|
||||
|
||||
vaddr[buffer->user_size / 4] = data;
|
||||
buffer->user_size += 4;
|
||||
}
|
||||
|
||||
static inline void CMD_LOAD_STATE(struct etnaviv_cmdbuf *buffer,
|
||||
u32 reg, u32 value)
|
||||
{
|
||||
u32 index = reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR;
|
||||
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
/* write a register via cmd stream */
|
||||
OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE |
|
||||
VIV_FE_LOAD_STATE_HEADER_COUNT(1) |
|
||||
VIV_FE_LOAD_STATE_HEADER_OFFSET(index));
|
||||
OUT(buffer, value);
|
||||
}
|
||||
|
||||
static inline void CMD_END(struct etnaviv_cmdbuf *buffer)
|
||||
{
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
OUT(buffer, VIV_FE_END_HEADER_OP_END);
|
||||
}
|
||||
|
||||
static inline void CMD_WAIT(struct etnaviv_cmdbuf *buffer,
|
||||
unsigned int waitcycles)
|
||||
{
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
OUT(buffer, VIV_FE_WAIT_HEADER_OP_WAIT | waitcycles);
|
||||
}
|
||||
|
||||
static inline void CMD_LINK(struct etnaviv_cmdbuf *buffer,
|
||||
u16 prefetch, u32 address)
|
||||
{
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
OUT(buffer, VIV_FE_LINK_HEADER_OP_LINK |
|
||||
VIV_FE_LINK_HEADER_PREFETCH(prefetch));
|
||||
OUT(buffer, address);
|
||||
}
|
||||
|
||||
static inline void CMD_STALL(struct etnaviv_cmdbuf *buffer,
|
||||
u32 from, u32 to)
|
||||
{
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
OUT(buffer, VIV_FE_STALL_HEADER_OP_STALL);
|
||||
OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to));
|
||||
}
|
||||
|
||||
static inline void CMD_SEM(struct etnaviv_cmdbuf *buffer, u32 from, u32 to)
|
||||
{
|
||||
CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN,
|
||||
VIVS_GL_SEMAPHORE_TOKEN_FROM(from) |
|
||||
VIVS_GL_SEMAPHORE_TOKEN_TO(to));
|
||||
}
|
||||
#include "etnaviv_flop_reset.h"
|
||||
|
||||
static void etnaviv_cmd_select_pipe(struct etnaviv_gpu *gpu,
|
||||
struct etnaviv_cmdbuf *buffer, u8 pipe)
|
||||
|
|
@ -170,6 +103,10 @@ u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
|
|||
/* initialize buffer */
|
||||
buffer->user_size = 0;
|
||||
|
||||
/* Queue in PPU flop reset */
|
||||
if (etnaviv_flop_reset_ppu_require(&gpu->identity))
|
||||
etnaviv_flop_reset_ppu_run(gpu);
|
||||
|
||||
CMD_WAIT(buffer, gpu->fe_waitcycles);
|
||||
CMD_LINK(buffer, 2,
|
||||
etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping)
|
||||
|
|
|
|||
99
drivers/gpu/drm/etnaviv/etnaviv_buffer.h
Normal file
99
drivers/gpu/drm/etnaviv/etnaviv_buffer.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2014-2025 Etnaviv Project
|
||||
*/
|
||||
|
||||
#ifndef __ETNAVIV_BUFFER_H__
|
||||
#define __ETNAVIV_BUFFER_H__
|
||||
|
||||
#include "etnaviv_cmdbuf.h"
|
||||
#include "etnaviv_gpu.h"
|
||||
#include "etnaviv_gem.h"
|
||||
#include "etnaviv_mmu.h"
|
||||
|
||||
#include "common.xml.h"
|
||||
#include "linux/printk.h"
|
||||
#include "state.xml.h"
|
||||
#include "state_blt.xml.h"
|
||||
#include "state_hi.xml.h"
|
||||
#include "state_3d.xml.h"
|
||||
#include "cmdstream.xml.h"
|
||||
|
||||
static inline void OUT(struct etnaviv_cmdbuf *buffer, u32 data)
|
||||
{
|
||||
u32 *vaddr = (u32 *)buffer->vaddr;
|
||||
|
||||
BUG_ON(buffer->user_size >= buffer->size);
|
||||
|
||||
vaddr[buffer->user_size / 4] = data;
|
||||
buffer->user_size += 4;
|
||||
}
|
||||
|
||||
static inline void CMD_LOAD_STATE(struct etnaviv_cmdbuf *buffer, u32 reg,
|
||||
u32 value)
|
||||
{
|
||||
u32 index = reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR;
|
||||
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
/* write a register via cmd stream */
|
||||
OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE |
|
||||
VIV_FE_LOAD_STATE_HEADER_COUNT(1) |
|
||||
VIV_FE_LOAD_STATE_HEADER_OFFSET(index));
|
||||
OUT(buffer, value);
|
||||
}
|
||||
|
||||
static inline void CMD_LOAD_STATES_START(struct etnaviv_cmdbuf *buffer, u32 reg,
|
||||
u32 nvalues)
|
||||
{
|
||||
u32 index = reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR;
|
||||
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
/* write a register via cmd stream */
|
||||
OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE |
|
||||
VIV_FE_LOAD_STATE_HEADER_OFFSET(index) |
|
||||
VIV_FE_LOAD_STATE_HEADER_COUNT(nvalues));
|
||||
}
|
||||
|
||||
static inline void CMD_END(struct etnaviv_cmdbuf *buffer)
|
||||
{
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
OUT(buffer, VIV_FE_END_HEADER_OP_END);
|
||||
}
|
||||
|
||||
static inline void CMD_WAIT(struct etnaviv_cmdbuf *buffer,
|
||||
unsigned int waitcycles)
|
||||
{
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
OUT(buffer, VIV_FE_WAIT_HEADER_OP_WAIT | waitcycles);
|
||||
}
|
||||
|
||||
static inline void CMD_LINK(struct etnaviv_cmdbuf *buffer, u16 prefetch,
|
||||
u32 address)
|
||||
{
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
OUT(buffer,
|
||||
VIV_FE_LINK_HEADER_OP_LINK | VIV_FE_LINK_HEADER_PREFETCH(prefetch));
|
||||
OUT(buffer, address);
|
||||
}
|
||||
|
||||
static inline void CMD_STALL(struct etnaviv_cmdbuf *buffer, u32 from, u32 to)
|
||||
{
|
||||
buffer->user_size = ALIGN(buffer->user_size, 8);
|
||||
|
||||
OUT(buffer, VIV_FE_STALL_HEADER_OP_STALL);
|
||||
OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to));
|
||||
}
|
||||
|
||||
static inline void CMD_SEM(struct etnaviv_cmdbuf *buffer, u32 from, u32 to)
|
||||
{
|
||||
CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN,
|
||||
VIVS_GL_SEMAPHORE_TOKEN_FROM(from) |
|
||||
VIVS_GL_SEMAPHORE_TOKEN_TO(to));
|
||||
}
|
||||
|
||||
#endif /* __ETNAVIV_BUFFER_H__ */
|
||||
|
|
@ -601,6 +601,9 @@ static void etnaviv_unbind(struct device *dev)
|
|||
|
||||
component_unbind_all(dev, drm);
|
||||
|
||||
etnaviv_cmdbuf_free(priv->flop_reset_data_ppu);
|
||||
kfree(priv->flop_reset_data_ppu);
|
||||
|
||||
etnaviv_cmdbuf_suballoc_destroy(priv->cmdbuf_suballoc);
|
||||
|
||||
xa_destroy(&priv->active_contexts);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ struct etnaviv_drm_private {
|
|||
/* list of GEM objects: */
|
||||
struct mutex gem_lock;
|
||||
struct list_head gem_list;
|
||||
|
||||
/* ppu flop reset data */
|
||||
struct etnaviv_cmdbuf *flop_reset_data_ppu;
|
||||
};
|
||||
|
||||
int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
|
|
|
|||
224
drivers/gpu/drm/etnaviv/etnaviv_flop_reset.c
Normal file
224
drivers/gpu/drm/etnaviv/etnaviv_flop_reset.c
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2025 Etnaviv Project
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "etnaviv_buffer.h"
|
||||
#include "etnaviv_cmdbuf.h"
|
||||
#include "etnaviv_gpu.h"
|
||||
#include "state_3d.xml.h"
|
||||
|
||||
#include "etnaviv_flop_reset.h"
|
||||
|
||||
static int etnaviv_force_flop_reset;
|
||||
module_param_named(force_flop_reset, etnaviv_force_flop_reset, int, 0);
|
||||
|
||||
#define PPU_IMAGE_STRIDE 64
|
||||
#define PPU_IMAGE_XSIZE 64
|
||||
#define PPU_IMAGE_YSIZE 6
|
||||
|
||||
#define PPU_FLOP_RESET_INSTR_DWORD_COUNT 16
|
||||
|
||||
static void etnaviv_emit_flop_reset_state_ppu(struct etnaviv_cmdbuf *cmdbuf,
|
||||
u32 buffer_base, u32 input_offset,
|
||||
u32 output_offset,
|
||||
u32 shader_offset,
|
||||
u32 shader_size,
|
||||
u32 shader_register_count)
|
||||
{
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_GL_API_MODE, VIVS_GL_API_MODE_OPENCL);
|
||||
CMD_SEM(cmdbuf, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
|
||||
CMD_STALL(cmdbuf, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
|
||||
|
||||
CMD_LOAD_STATES_START(cmdbuf, VIVS_SH_HALTI5_UNIFORMS(0), 4);
|
||||
|
||||
OUT(cmdbuf, buffer_base + input_offset);
|
||||
OUT(cmdbuf, PPU_IMAGE_STRIDE);
|
||||
OUT(cmdbuf, PPU_IMAGE_XSIZE | (PPU_IMAGE_YSIZE << 16));
|
||||
OUT(cmdbuf, 0x444051f0);
|
||||
OUT(cmdbuf, 0xffffffff);
|
||||
|
||||
CMD_LOAD_STATES_START(cmdbuf, VIVS_SH_HALTI5_UNIFORMS(4), 4);
|
||||
OUT(cmdbuf, buffer_base + output_offset);
|
||||
OUT(cmdbuf, PPU_IMAGE_STRIDE);
|
||||
OUT(cmdbuf, PPU_IMAGE_XSIZE | (PPU_IMAGE_YSIZE << 16));
|
||||
OUT(cmdbuf, 0x444051f0);
|
||||
OUT(cmdbuf, 0xffffffff);
|
||||
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_CL_CONFIG,
|
||||
VIVS_CL_CONFIG_DIMENSIONS(2) |
|
||||
VIVS_CL_CONFIG_VALUE_ORDER(3));
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_VS_ICACHE_INVALIDATE, 0x1f);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_VARYING_NUM_COMPONENTS(0), 0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_TEMP_REGISTER_CONTROL,
|
||||
shader_register_count);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_SAMPLER_BASE, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_UNIFORM_BASE, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_NEWRANGE_LOW, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_NEWRANGE_HIGH, shader_size / 16);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_INST_ADDR, buffer_base + shader_offset);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_SH_CONFIG, VIVS_SH_CONFIG_RTNE_ROUNDING);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_VS_ICACHE_CONTROL,
|
||||
VIVS_VS_ICACHE_CONTROL_ENABLE);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_ICACHE_COUNT, shader_size / 16 - 1);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_INPUT_COUNT, 0x1f01);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_VS_HALTI5_UNK008A0, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PA_VS_OUTPUT_COUNT, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_GL_VARYING_TOTAL_COMPONENTS, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_CONTROL_EXT, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_VS_OUTPUT_COUNT, 0x1);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_GL_HALTI5_SH_SPECIALS, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_PS_ICACHE_PREFETCH, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_CL_UNK00924, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_CL_THREAD_ALLOCATION, 0x1);
|
||||
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_CL_GLOBAL_WORK_OFFSET_X, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_CL_GLOBAL_WORK_OFFSET_Y, 0x0);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_CL_GLOBAL_WORK_OFFSET_Z, 0x0);
|
||||
|
||||
CMD_LOAD_STATES_START(cmdbuf, VIVS_CL_WORKGROUP_COUNT_X, 9);
|
||||
OUT(cmdbuf, 0xf);
|
||||
OUT(cmdbuf, 0x5);
|
||||
OUT(cmdbuf, 0xffffffff);
|
||||
OUT(cmdbuf, 0x0);
|
||||
OUT(cmdbuf, 0x0);
|
||||
OUT(cmdbuf, 0x3ff);
|
||||
OUT(cmdbuf, 0x0);
|
||||
OUT(cmdbuf, 0x4);
|
||||
OUT(cmdbuf, 0x1);
|
||||
OUT(cmdbuf, 0x0);
|
||||
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_CL_KICKER, 0xbadabeeb);
|
||||
CMD_LOAD_STATE(cmdbuf, VIVS_GL_FLUSH_CACHE,
|
||||
VIVS_GL_FLUSH_CACHE_SHADER_L1 |
|
||||
VIVS_GL_FLUSH_CACHE_UNK10 |
|
||||
VIVS_GL_FLUSH_CACHE_UNK11);
|
||||
}
|
||||
|
||||
static void etnaviv_flop_reset_ppu_fill_input(u32 *buffer, u32 size)
|
||||
{
|
||||
memset32(buffer, 0x01010101, size / 4);
|
||||
}
|
||||
|
||||
static void etnaviv_flop_reset_ppu_set_shader(u8 *dest)
|
||||
{
|
||||
static const u32 inst[PPU_FLOP_RESET_INSTR_DWORD_COUNT] = {
|
||||
/* img_load.u8 r1, c0, r0.xy */
|
||||
0x78011779,
|
||||
0x39000804,
|
||||
0x00A90050,
|
||||
0x00000000,
|
||||
/* img_load.u8 r2, c0, r0.xy */
|
||||
0x78021779,
|
||||
0x39000804,
|
||||
0x00A90050,
|
||||
0x00000000,
|
||||
/* dp2x8 r1, r1, r2, c3_512 */
|
||||
0xB8017145,
|
||||
0x390018FC,
|
||||
0x01C90140,
|
||||
0x40390028,
|
||||
/* img_store.u8 r1, c2, r0.xy, r1 */
|
||||
0x380007BA,
|
||||
0x39001804,
|
||||
0x00A90050,
|
||||
0x00390018,
|
||||
};
|
||||
memcpy(dest, inst, sizeof(inst));
|
||||
}
|
||||
|
||||
static const struct etnaviv_flop_reset_entry {
|
||||
u16 chip_model;
|
||||
u16 revision;
|
||||
u32 flags;
|
||||
} etnaviv_flop_reset_db[] = {
|
||||
{
|
||||
.chip_model = 0x8000,
|
||||
.revision = 0x6205,
|
||||
},
|
||||
};
|
||||
|
||||
bool etnaviv_flop_reset_ppu_require(const struct etnaviv_chip_identity *chip_id)
|
||||
{
|
||||
const struct etnaviv_flop_reset_entry *e = etnaviv_flop_reset_db;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(etnaviv_flop_reset_db); ++i, ++e) {
|
||||
if (chip_id->model == e->chip_model &&
|
||||
chip_id->revision == e->revision)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (etnaviv_force_flop_reset) {
|
||||
if (!(chip_id->features & chipFeatures_PIPE_3D)) {
|
||||
pr_warn("Etnaviv: model: 0x%04x, revision: 0x%04x does not support PIPE_3D\n",
|
||||
chip_id->model, chip_id->revision);
|
||||
pr_warn("Request to force PPU flop reset ignored.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
pr_info("Force PPU flop reset for model: 0x%04x, revision: 0x%04x\n",
|
||||
chip_id->model, chip_id->revision);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const u32 image_data_size = PPU_IMAGE_STRIDE * PPU_IMAGE_YSIZE;
|
||||
static const u32 output_offset = ALIGN(image_data_size, 64);
|
||||
static const u32 shader_offset = ALIGN(output_offset + image_data_size, 64);
|
||||
static const u32 shader_size = PPU_FLOP_RESET_INSTR_DWORD_COUNT * sizeof(u32);
|
||||
static const u32 shader_register_count = 3;
|
||||
static const u32 buffer_size = shader_offset + shader_size;
|
||||
|
||||
int etnaviv_flop_reset_ppu_init(struct etnaviv_drm_private *priv)
|
||||
{
|
||||
/* Get some space from the ring buffer to put the payload
|
||||
* (input and output image, and shader), we keep this buffer
|
||||
* for the whole life time the driver is bound
|
||||
*/
|
||||
priv->flop_reset_data_ppu =
|
||||
kzalloc(sizeof(*priv->flop_reset_data_ppu), GFP_KERNEL);
|
||||
|
||||
if (!priv->flop_reset_data_ppu)
|
||||
return -ENOMEM;
|
||||
|
||||
int ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc,
|
||||
priv->flop_reset_data_ppu, buffer_size);
|
||||
if (ret) {
|
||||
kfree(priv->flop_reset_data_ppu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *buffer_base = priv->flop_reset_data_ppu->vaddr;
|
||||
u32 *input_data = (u32 *)buffer_base;
|
||||
u8 *shader_data = (u8 *)buffer_base + shader_offset;
|
||||
|
||||
etnaviv_flop_reset_ppu_fill_input(input_data, image_data_size);
|
||||
etnaviv_flop_reset_ppu_set_shader(shader_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void etnaviv_flop_reset_ppu_run(struct etnaviv_gpu *gpu)
|
||||
{
|
||||
struct etnaviv_drm_private *priv = gpu->drm->dev_private;
|
||||
|
||||
if (!priv->flop_reset_data_ppu) {
|
||||
dev_err(gpu->dev,
|
||||
"Oops: Flop reset data was not initialized, skipping\n");
|
||||
return;
|
||||
}
|
||||
|
||||
u32 buffer_base = etnaviv_cmdbuf_get_va(priv->flop_reset_data_ppu,
|
||||
&gpu->mmu_context->cmdbuf_mapping);
|
||||
|
||||
etnaviv_emit_flop_reset_state_ppu(&gpu->buffer, buffer_base, 0,
|
||||
output_offset, shader_offset,
|
||||
shader_size, shader_register_count);
|
||||
}
|
||||
21
drivers/gpu/drm/etnaviv/etnaviv_flop_reset.h
Normal file
21
drivers/gpu/drm/etnaviv/etnaviv_flop_reset.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2025 Etnaviv Project
|
||||
*/
|
||||
|
||||
#ifndef _ETNAVIV_FLOP_RESET_H_
|
||||
#define _ETNAVIV_FLOP_RESET_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct etnaviv_chip_identity;
|
||||
struct etnaviv_drm_private;
|
||||
struct etnaviv_gpu;
|
||||
|
||||
bool etnaviv_flop_reset_ppu_require(const struct etnaviv_chip_identity *chip_id);
|
||||
|
||||
int etnaviv_flop_reset_ppu_init(struct etnaviv_drm_private *priv);
|
||||
|
||||
void etnaviv_flop_reset_ppu_run(struct etnaviv_gpu *gpu);
|
||||
|
||||
#endif
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "etnaviv_cmdbuf.h"
|
||||
#include "etnaviv_dump.h"
|
||||
#include "etnaviv_flop_reset.h"
|
||||
#include "etnaviv_gpu.h"
|
||||
#include "etnaviv_gem.h"
|
||||
#include "etnaviv_mmu.h"
|
||||
|
|
@ -839,6 +840,16 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (etnaviv_flop_reset_ppu_require(&gpu->identity) &&
|
||||
!priv->flop_reset_data_ppu) {
|
||||
ret = etnaviv_flop_reset_ppu_init(priv);
|
||||
if (ret) {
|
||||
dev_err(gpu->dev,
|
||||
"Unable to initialize PPU flop reset data\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu->identity.nn_core_count > 0)
|
||||
dev_warn(gpu->dev, "etnaviv has been instantiated on a NPU, "
|
||||
"for which the UAPI is still experimental\n");
|
||||
|
|
|
|||
|
|
@ -4,6 +4,101 @@
|
|||
|
||||
/* This is a cut-down version of the state_3d.xml.h file */
|
||||
|
||||
#define VIVS_CL_CONFIG 0x00000900
|
||||
#define VIVS_CL_CONFIG_DIMENSIONS__MASK 0x00000003
|
||||
#define VIVS_CL_CONFIG_DIMENSIONS__SHIFT 0
|
||||
#define VIVS_CL_CONFIG_DIMENSIONS(x) (((x) << VIVS_CL_CONFIG_DIMENSIONS__SHIFT) & VIVS_CL_CONFIG_DIMENSIONS__MASK)
|
||||
#define VIVS_CL_CONFIG_TRAVERSE_ORDER__MASK 0x00000070
|
||||
#define VIVS_CL_CONFIG_TRAVERSE_ORDER__SHIFT 4
|
||||
#define VIVS_CL_CONFIG_TRAVERSE_ORDER(x) (((x) << VIVS_CL_CONFIG_TRAVERSE_ORDER__SHIFT) & VIVS_CL_CONFIG_TRAVERSE_ORDER__MASK)
|
||||
#define VIVS_CL_CONFIG_ENABLE_SWATH_X 0x00000100
|
||||
#define VIVS_CL_CONFIG_ENABLE_SWATH_Y 0x00000200
|
||||
#define VIVS_CL_CONFIG_ENABLE_SWATH_Z 0x00000400
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_X__MASK 0x0000f000
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_X__SHIFT 12
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_X(x) (((x) << VIVS_CL_CONFIG_SWATH_SIZE_X__SHIFT) & VIVS_CL_CONFIG_SWATH_SIZE_X__MASK)
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_Y__MASK 0x000f0000
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_Y__SHIFT 16
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_Y(x) (((x) << VIVS_CL_CONFIG_SWATH_SIZE_Y__SHIFT) & VIVS_CL_CONFIG_SWATH_SIZE_Y__MASK)
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_Z__MASK 0x00f00000
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_Z__SHIFT 20
|
||||
#define VIVS_CL_CONFIG_SWATH_SIZE_Z(x) (((x) << VIVS_CL_CONFIG_SWATH_SIZE_Z__SHIFT) & VIVS_CL_CONFIG_SWATH_SIZE_Z__MASK)
|
||||
|
||||
#define VIVS_CL_CONFIG_DIMENSIONS__MASK 0x00000003
|
||||
#define VIVS_CL_CONFIG_DIMENSIONS__SHIFT 0
|
||||
#define VIVS_CL_CONFIG_DIMENSIONS(x) (((x) << VIVS_CL_CONFIG_DIMENSIONS__SHIFT) & VIVS_CL_CONFIG_DIMENSIONS__MASK)
|
||||
|
||||
#define VIVS_CL_CONFIG_VALUE_ORDER__MASK 0x07000000
|
||||
#define VIVS_CL_CONFIG_VALUE_ORDER__SHIFT 24
|
||||
#define VIVS_CL_CONFIG_VALUE_ORDER(x) (((x) << VIVS_CL_CONFIG_VALUE_ORDER__SHIFT) & VIVS_CL_CONFIG_VALUE_ORDER__MASK)
|
||||
|
||||
#define VIVS_CL_GLOBAL_WORK_OFFSET_X 0x0000092c
|
||||
#define VIVS_CL_GLOBAL_WORK_OFFSET_Y 0x00000934
|
||||
#define VIVS_CL_GLOBAL_WORK_OFFSET_Z 0x0000093c
|
||||
|
||||
#define VIVS_CL_KICKER 0x00000920
|
||||
#define VIVS_CL_THREAD_ALLOCATION 0x0000091c
|
||||
#define VIVS_CL_UNK00924 0x00000924
|
||||
|
||||
#define VIVS_CL_WORKGROUP_COUNT_X 0x00000940
|
||||
#define VIVS_CL_WORKGROUP_COUNT_Y 0x00000944
|
||||
#define VIVS_CL_WORKGROUP_COUNT_Z 0x00000948
|
||||
#define VIVS_CL_WORKGROUP_SIZE_X 0x0000094c
|
||||
#define VIVS_CL_WORKGROUP_SIZE_Y 0x00000950
|
||||
#define VIVS_CL_WORKGROUP_SIZE_Z 0x00000954
|
||||
|
||||
#define VIVS_CL_GLOBAL_SCALE_X 0x00000958
|
||||
#define VIVS_CL_GLOBAL_SCALE_Y 0x0000095c
|
||||
#define VIVS_CL_GLOBAL_SCALE_Z 0x00000960
|
||||
|
||||
#define VIVS_PA_VS_OUTPUT_COUNT 0x00000aa8
|
||||
#define VIVS_PS_CONTROL_EXT 0x00001030
|
||||
#define VIVS_PS_ICACHE_COUNT 0x00001094
|
||||
#define VIVS_PS_ICACHE_PREFETCH 0x00001048
|
||||
|
||||
#define VIVS_PS_INPUT_COUNT 0x00001008
|
||||
#define VIVS_PS_INPUT_COUNT_COUNT__MASK 0x0000001f
|
||||
#define VIVS_PS_INPUT_COUNT_COUNT__SHIFT 0
|
||||
#define VIVS_PS_INPUT_COUNT_COUNT(x) (((x) << VIVS_PS_INPUT_COUNT_COUNT__SHIFT) & VIVS_PS_INPUT_COUNT_COUNT__MASK)
|
||||
|
||||
#define VIVS_PS_NEWRANGE_LOW 0x0000087c
|
||||
#define VIVS_PS_NEWRANGE_HIGH 0x00001090
|
||||
#define VIVS_PS_SAMPLER_BASE 0x00001058
|
||||
|
||||
#define VIVS_PS_UNIFORM_BASE 0x00001024
|
||||
#define VIVS_PS_INST_ADDR 0x00001028
|
||||
|
||||
#define VIVS_PS_TEMP_REGISTER_CONTROL 0x0000100c
|
||||
#define VIVS_PS_TEMP_REGISTER_CONTROL_NUM_TEMPS__MASK 0x0000003f
|
||||
#define VIVS_PS_TEMP_REGISTER_CONTROL_NUM_TEMPS__SHIFT 0
|
||||
#define VIVS_PS_TEMP_REGISTER_CONTROL_NUM_TEMPS(x) (((x) << VIVS_PS_TEMP_REGISTER_CONTROL_NUM_TEMPS__SHIFT) & VIVS_PS_TEMP_REGISTER_CONTROL_NUM_TEMPS__MASK)
|
||||
|
||||
#define VIVS_PS_VARYING_NUM_COMPONENTS(i0) (0x00001080 + 0x4*(i0))
|
||||
#define VIVS_PS_VARYING_NUM_COMPONENTS__ESIZE 0x00000004
|
||||
#define VIVS_PS_VARYING_NUM_COMPONENTS__LEN 0x00000004
|
||||
|
||||
#define VIVS_SH_CONFIG 0x00015600
|
||||
#define VIVS_SH_CONFIG_RTNE_ROUNDING 0x00000002
|
||||
|
||||
#define VIVS_SH_HALTI5_UNIFORMS(i0) (0x00036000 + 0x4*(i0))
|
||||
#define VIVS_SH_HALTI5_UNIFORMS__ESIZE 0x00000004
|
||||
#define VIVS_SH_HALTI5_UNIFORMS__LEN 0x00000800
|
||||
|
||||
#define VIVS_VS_HALTI5_UNK008A0 0x000008a0
|
||||
#define VIVS_VS_HALTI5_UNK008A0_A__MASK 0x0000003f
|
||||
#define VIVS_VS_HALTI5_UNK008A0_A__SHIFT 0
|
||||
#define VIVS_VS_HALTI5_UNK008A0_A(x) (((x) << VIVS_VS_HALTI5_UNK008A0_A__SHIFT) & VIVS_VS_HALTI5_UNK008A0_A__MASK)
|
||||
|
||||
#define VIVS_VS_ICACHE_CONTROL 0x00000868
|
||||
#define VIVS_VS_ICACHE_CONTROL_ENABLE 0x00000001
|
||||
|
||||
#define VIVS_VS_ICACHE_INVALIDATE 0x000008b0
|
||||
|
||||
#define VIVS_VS_OUTPUT_COUNT 0x00000804
|
||||
#define VIVS_VS_OUTPUT_COUNT_COUNT__MASK 0x000000ff
|
||||
#define VIVS_VS_OUTPUT_COUNT_COUNT__SHIFT 0
|
||||
#define VIVS_VS_OUTPUT_COUNT_COUNT(x) (((x) << VIVS_VS_OUTPUT_COUNT_COUNT__SHIFT) & VIVS_VS_OUTPUT_COUNT_COUNT__MASK)
|
||||
|
||||
#define VIVS_TS_FLUSH_CACHE 0x00001650
|
||||
#define VIVS_TS_FLUSH_CACHE_FLUSH 0x00000001
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <drm/display/drm_dp_tunnel.h>
|
||||
#include <drm/display/drm_dsc.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_colorop.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
struct meson_encoder_cvbs {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
|
||||
|
|
@ -89,7 +88,7 @@ static int meson_encoder_cvbs_attach(struct drm_bridge *bridge,
|
|||
struct meson_encoder_cvbs *meson_encoder_cvbs =
|
||||
bridge_to_meson_encoder_cvbs(bridge);
|
||||
|
||||
return drm_bridge_attach(encoder, meson_encoder_cvbs->next_bridge,
|
||||
return drm_bridge_attach(encoder, meson_encoder_cvbs->bridge.next_bridge,
|
||||
&meson_encoder_cvbs->bridge, flags);
|
||||
}
|
||||
|
||||
|
|
@ -241,9 +240,9 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote);
|
||||
meson_encoder_cvbs->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
|
||||
of_node_put(remote);
|
||||
if (!meson_encoder_cvbs->next_bridge)
|
||||
if (!meson_encoder_cvbs->bridge.next_bridge)
|
||||
return dev_err_probe(priv->dev, -EPROBE_DEFER,
|
||||
"Failed to find CVBS Connector bridge\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
struct meson_encoder_dsi {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
|
||||
|
|
@ -38,7 +37,7 @@ static int meson_encoder_dsi_attach(struct drm_bridge *bridge,
|
|||
{
|
||||
struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge);
|
||||
|
||||
return drm_bridge_attach(encoder, encoder_dsi->next_bridge,
|
||||
return drm_bridge_attach(encoder, encoder_dsi->bridge.next_bridge,
|
||||
&encoder_dsi->bridge, flags);
|
||||
}
|
||||
|
||||
|
|
@ -120,8 +119,8 @@ int meson_encoder_dsi_probe(struct meson_drm *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
meson_encoder_dsi->next_bridge = of_drm_find_bridge(remote);
|
||||
if (!meson_encoder_dsi->next_bridge)
|
||||
meson_encoder_dsi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
|
||||
if (!meson_encoder_dsi->bridge.next_bridge)
|
||||
return dev_err_probe(priv->dev, -EPROBE_DEFER,
|
||||
"Failed to find DSI transceiver bridge\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
struct meson_encoder_hdmi {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
struct drm_connector *connector;
|
||||
struct meson_drm *priv;
|
||||
unsigned long output_bus_fmt;
|
||||
|
|
@ -54,7 +53,7 @@ static int meson_encoder_hdmi_attach(struct drm_bridge *bridge,
|
|||
{
|
||||
struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
|
||||
|
||||
return drm_bridge_attach(encoder, encoder_hdmi->next_bridge,
|
||||
return drm_bridge_attach(encoder, encoder_hdmi->bridge.next_bridge,
|
||||
&encoder_hdmi->bridge, flags);
|
||||
}
|
||||
|
||||
|
|
@ -323,6 +322,7 @@ static int meson_encoder_hdmi_atomic_check(struct drm_bridge *bridge,
|
|||
}
|
||||
|
||||
static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector,
|
||||
enum drm_connector_status status)
|
||||
{
|
||||
struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
|
||||
|
|
@ -334,7 +334,7 @@ static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge,
|
|||
const struct drm_edid *drm_edid;
|
||||
const struct edid *edid;
|
||||
|
||||
drm_edid = drm_bridge_edid_read(encoder_hdmi->next_bridge,
|
||||
drm_edid = drm_bridge_edid_read(encoder_hdmi->bridge.next_bridge,
|
||||
encoder_hdmi->connector);
|
||||
if (!drm_edid)
|
||||
return;
|
||||
|
|
@ -390,8 +390,8 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote);
|
||||
if (!meson_encoder_hdmi->next_bridge) {
|
||||
meson_encoder_hdmi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
|
||||
if (!meson_encoder_hdmi->bridge.next_bridge) {
|
||||
ret = dev_err_probe(priv->dev, -EPROBE_DEFER,
|
||||
"Failed to find HDMI transceiver bridge\n");
|
||||
goto err_put_node;
|
||||
|
|
|
|||
|
|
@ -868,10 +868,10 @@ meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode)
|
|||
DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))
|
||||
return MODE_BAD;
|
||||
|
||||
if (mode->hdisplay < 400 || mode->hdisplay > 1920)
|
||||
if (mode->hdisplay < 400 || mode->hdisplay > 3840)
|
||||
return MODE_BAD_HVALUE;
|
||||
|
||||
if (mode->vdisplay < 480 || mode->vdisplay > 1920)
|
||||
if (mode->vdisplay < 480 || mode->vdisplay > 2160)
|
||||
return MODE_BAD_VVALUE;
|
||||
|
||||
return MODE_OK;
|
||||
|
|
|
|||
|
|
@ -1783,7 +1783,8 @@ void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge)
|
|||
}
|
||||
|
||||
void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
|
||||
enum drm_connector_status status)
|
||||
struct drm_connector *connector,
|
||||
enum drm_connector_status status)
|
||||
{
|
||||
struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(bridge);
|
||||
struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge,
|
|||
void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge);
|
||||
void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge);
|
||||
void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
|
||||
enum drm_connector_status status);
|
||||
struct drm_connector *connector,
|
||||
enum drm_connector_status status);
|
||||
|
||||
#endif /* _DP_DRM_H_ */
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ static void hdmi4_bridge_disable(struct drm_bridge *bridge,
|
|||
}
|
||||
|
||||
static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector,
|
||||
enum drm_connector_status status)
|
||||
{
|
||||
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
|
||||
|
|
|
|||
|
|
@ -1730,6 +1730,12 @@ static const struct panel_delay delay_200_500_p2e100 = {
|
|||
.prepare_to_enable = 100,
|
||||
};
|
||||
|
||||
static const struct panel_delay delay_200_500_p2e200 = {
|
||||
.hpd_absent = 200,
|
||||
.unprepare = 500,
|
||||
.prepare_to_enable = 200,
|
||||
};
|
||||
|
||||
static const struct panel_delay delay_200_500_e50 = {
|
||||
.hpd_absent = 200,
|
||||
.unprepare = 500,
|
||||
|
|
@ -1977,6 +1983,7 @@ static const struct edp_panel_entry edp_panels[] = {
|
|||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b56, &delay_200_500_e80, "NT140FHM-N47"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b66, &delay_200_500_e80, "NE140WUM-N6G"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c20, &delay_200_500_e80, "NT140FHM-N47"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c26, &delay_200_500_p2e200, "NV140WUM-T08"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c93, &delay_200_500_e200, "Unknown"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cb6, &delay_200_500_e200, "NT116WHM-N44"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cf2, &delay_200_500_e200, "NV156FHM-N4S"),
|
||||
|
|
|
|||
|
|
@ -1077,7 +1077,7 @@ static const struct panfrost_compatible default_data = {
|
|||
.pm_domain_names = NULL,
|
||||
};
|
||||
|
||||
static const struct panfrost_compatible allwinner_h616_data = {
|
||||
static const struct panfrost_compatible default_pm_rt_data = {
|
||||
.num_supplies = ARRAY_SIZE(default_supplies) - 1,
|
||||
.supply_names = default_supplies,
|
||||
.num_pm_domains = 1,
|
||||
|
|
@ -1157,6 +1157,7 @@ static const struct of_device_id dt_match[] = {
|
|||
.data = &amlogic_data, },
|
||||
{ .compatible = "amlogic,meson-g12a-mali",
|
||||
.data = &amlogic_data, },
|
||||
{ .compatible = "renesas,r9a09g047-mali", .data = &default_pm_rt_data },
|
||||
{ .compatible = "arm,mali-t604", .data = &default_data, },
|
||||
{ .compatible = "arm,mali-t624", .data = &default_data, },
|
||||
{ .compatible = "arm,mali-t628", .data = &default_data, },
|
||||
|
|
@ -1174,7 +1175,7 @@ static const struct of_device_id dt_match[] = {
|
|||
{ .compatible = "mediatek,mt8188-mali", .data = &mediatek_mt8188_data },
|
||||
{ .compatible = "mediatek,mt8192-mali", .data = &mediatek_mt8192_data },
|
||||
{ .compatible = "mediatek,mt8370-mali", .data = &mediatek_mt8370_data },
|
||||
{ .compatible = "allwinner,sun50i-h616-mali", .data = &allwinner_h616_data },
|
||||
{ .compatible = "allwinner,sun50i-h616-mali", .data = &default_pm_rt_data },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dt_match);
|
||||
|
|
|
|||
|
|
@ -533,12 +533,12 @@ static int as_send_cmd_and_wait(struct panthor_device *ptdev, u32 as_nr, u32 cmd
|
|||
return status;
|
||||
}
|
||||
|
||||
static u64 pack_region_range(struct panthor_device *ptdev, u64 region_start, u64 size)
|
||||
static u64 pack_region_range(struct panthor_device *ptdev, u64 *region_start, u64 *size)
|
||||
{
|
||||
u8 region_width;
|
||||
u64 region_end = region_start + size;
|
||||
u64 region_end = *region_start + *size;
|
||||
|
||||
if (drm_WARN_ON_ONCE(&ptdev->base, !size))
|
||||
if (drm_WARN_ON_ONCE(&ptdev->base, !*size))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
|
@ -549,16 +549,17 @@ static u64 pack_region_range(struct panthor_device *ptdev, u64 region_start, u64
|
|||
* change, the desired region starts with this bit (and subsequent bits)
|
||||
* zeroed and ends with the bit (and subsequent bits) set to one.
|
||||
*/
|
||||
region_width = max(fls64(region_start ^ (region_end - 1)),
|
||||
region_width = max(fls64(*region_start ^ (region_end - 1)),
|
||||
const_ilog2(AS_LOCK_REGION_MIN_SIZE)) - 1;
|
||||
|
||||
/*
|
||||
* Mask off the low bits of region_start (which would be ignored by
|
||||
* the hardware anyway)
|
||||
*/
|
||||
region_start &= GENMASK_ULL(63, region_width);
|
||||
*region_start &= GENMASK_ULL(63, region_width);
|
||||
*size = 1ull << (region_width + 1);
|
||||
|
||||
return region_width | region_start;
|
||||
return region_width | *region_start;
|
||||
}
|
||||
|
||||
static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
|
||||
|
|
@ -1504,6 +1505,10 @@ static void panthor_vm_destroy(struct panthor_vm *vm)
|
|||
|
||||
vm->destroyed = true;
|
||||
|
||||
/* Tell scheduler to stop all GPU work related to this VM */
|
||||
if (refcount_read(&vm->as.active_cnt) > 0)
|
||||
panthor_sched_prepare_for_vm_destruction(vm->ptdev);
|
||||
|
||||
mutex_lock(&vm->heaps.lock);
|
||||
panthor_heap_pool_destroy(vm->heaps.pool);
|
||||
vm->heaps.pool = NULL;
|
||||
|
|
@ -1637,12 +1642,19 @@ static int panthor_vm_lock_region(struct panthor_vm *vm, u64 start, u64 size)
|
|||
struct panthor_device *ptdev = vm->ptdev;
|
||||
int ret = 0;
|
||||
|
||||
/* sm_step_remap() can call panthor_vm_lock_region() to account for
|
||||
* the wider unmap needed when doing a partial huge page unamp. We
|
||||
* need to ignore the lock if it's already part of the locked region.
|
||||
*/
|
||||
if (start >= vm->locked_region.start &&
|
||||
start + size <= vm->locked_region.start + vm->locked_region.size)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&ptdev->mmu->as.slots_lock);
|
||||
drm_WARN_ON(&ptdev->base, vm->locked_region.start || vm->locked_region.size);
|
||||
if (vm->as.id >= 0 && size) {
|
||||
/* Lock the region that needs to be updated */
|
||||
gpu_write64(ptdev, AS_LOCKADDR(vm->as.id),
|
||||
pack_region_range(ptdev, start, size));
|
||||
pack_region_range(ptdev, &start, &size));
|
||||
|
||||
/* If the lock succeeded, update the locked_region info. */
|
||||
ret = as_send_cmd_and_wait(ptdev, vm->as.id, AS_COMMAND_LOCK);
|
||||
|
|
@ -2102,6 +2114,48 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
iova_mapped_as_huge_page(struct drm_gpuva_op_map *op, u64 addr)
|
||||
{
|
||||
const struct page *pg;
|
||||
pgoff_t bo_offset;
|
||||
|
||||
bo_offset = addr - op->va.addr + op->gem.offset;
|
||||
pg = to_panthor_bo(op->gem.obj)->base.pages[bo_offset >> PAGE_SHIFT];
|
||||
|
||||
return folio_size(page_folio(pg)) >= SZ_2M;
|
||||
}
|
||||
|
||||
static void
|
||||
unmap_hugepage_align(const struct drm_gpuva_op_remap *op,
|
||||
u64 *unmap_start, u64 *unmap_range)
|
||||
{
|
||||
u64 aligned_unmap_start, aligned_unmap_end, unmap_end;
|
||||
|
||||
unmap_end = *unmap_start + *unmap_range;
|
||||
aligned_unmap_start = ALIGN_DOWN(*unmap_start, SZ_2M);
|
||||
aligned_unmap_end = ALIGN(unmap_end, SZ_2M);
|
||||
|
||||
/* If we're dealing with a huge page, make sure the unmap region is
|
||||
* aligned on the start of the page.
|
||||
*/
|
||||
if (op->prev && aligned_unmap_start < *unmap_start &&
|
||||
op->prev->va.addr <= aligned_unmap_start &&
|
||||
iova_mapped_as_huge_page(op->prev, *unmap_start)) {
|
||||
*unmap_range += *unmap_start - aligned_unmap_start;
|
||||
*unmap_start = aligned_unmap_start;
|
||||
}
|
||||
|
||||
/* If we're dealing with a huge page, make sure the unmap region is
|
||||
* aligned on the end of the page.
|
||||
*/
|
||||
if (op->next && aligned_unmap_end > unmap_end &&
|
||||
op->next->va.addr + op->next->va.range >= aligned_unmap_end &&
|
||||
iova_mapped_as_huge_page(op->next, unmap_end - 1)) {
|
||||
*unmap_range += aligned_unmap_end - unmap_end;
|
||||
}
|
||||
}
|
||||
|
||||
static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op,
|
||||
void *priv)
|
||||
{
|
||||
|
|
@ -2110,16 +2164,50 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op,
|
|||
struct panthor_vm_op_ctx *op_ctx = vm->op_ctx;
|
||||
struct panthor_vma *prev_vma = NULL, *next_vma = NULL;
|
||||
u64 unmap_start, unmap_range;
|
||||
int ret;
|
||||
|
||||
drm_gpuva_op_remap_to_unmap_range(&op->remap, &unmap_start, &unmap_range);
|
||||
|
||||
/*
|
||||
* ARM IOMMU page table management code disallows partial unmaps of huge pages,
|
||||
* so when a partial unmap is requested, we must first unmap the entire huge
|
||||
* page and then remap the difference between the huge page minus the requested
|
||||
* unmap region. Calculating the right start address and range for the expanded
|
||||
* unmap operation is the responsibility of the following function.
|
||||
*/
|
||||
unmap_hugepage_align(&op->remap, &unmap_start, &unmap_range);
|
||||
|
||||
/* If the range changed, we might have to lock a wider region to guarantee
|
||||
* atomicity. panthor_vm_lock_region() bails out early if the new region
|
||||
* is already part of the locked region, so no need to do this check here.
|
||||
*/
|
||||
panthor_vm_lock_region(vm, unmap_start, unmap_range);
|
||||
panthor_vm_unmap_pages(vm, unmap_start, unmap_range);
|
||||
|
||||
if (op->remap.prev) {
|
||||
struct panthor_gem_object *bo = to_panthor_bo(op->remap.prev->gem.obj);
|
||||
u64 offset = op->remap.prev->gem.offset + unmap_start - op->remap.prev->va.addr;
|
||||
u64 size = op->remap.prev->va.addr + op->remap.prev->va.range - unmap_start;
|
||||
|
||||
ret = panthor_vm_map_pages(vm, unmap_start, flags_to_prot(unmap_vma->flags),
|
||||
bo->base.sgt, offset, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
prev_vma = panthor_vm_op_ctx_get_vma(op_ctx);
|
||||
panthor_vma_init(prev_vma, unmap_vma->flags);
|
||||
}
|
||||
|
||||
if (op->remap.next) {
|
||||
struct panthor_gem_object *bo = to_panthor_bo(op->remap.next->gem.obj);
|
||||
u64 addr = op->remap.next->va.addr;
|
||||
u64 size = unmap_start + unmap_range - op->remap.next->va.addr;
|
||||
|
||||
ret = panthor_vm_map_pages(vm, addr, flags_to_prot(unmap_vma->flags),
|
||||
bo->base.sgt, op->remap.next->gem.offset, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
next_vma = panthor_vm_op_ctx_get_vma(op_ctx);
|
||||
panthor_vma_init(next_vma, unmap_vma->flags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2786,6 +2786,20 @@ void panthor_sched_report_mmu_fault(struct panthor_device *ptdev)
|
|||
sched_queue_delayed_work(ptdev->scheduler, tick, 0);
|
||||
}
|
||||
|
||||
void panthor_sched_prepare_for_vm_destruction(struct panthor_device *ptdev)
|
||||
{
|
||||
/* FW can write out internal state, like the heap context, during CSG
|
||||
* suspend. It is therefore important that the scheduler has fully
|
||||
* evicted any pending and related groups before VM destruction can
|
||||
* safely continue. Failure to do so can lead to GPU page faults.
|
||||
* A controlled termination of a Panthor instance involves destroying
|
||||
* the group(s) before the VM. This means any relevant group eviction
|
||||
* has already been initiated by this point, and we just need to
|
||||
* ensure that any pending tick_work() has been completed.
|
||||
*/
|
||||
flush_work(&ptdev->scheduler->tick_work.work);
|
||||
}
|
||||
|
||||
void panthor_sched_resume(struct panthor_device *ptdev)
|
||||
{
|
||||
/* Force a tick to re-evaluate after a resume. */
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ void panthor_sched_suspend(struct panthor_device *ptdev);
|
|||
void panthor_sched_resume(struct panthor_device *ptdev);
|
||||
|
||||
void panthor_sched_report_mmu_fault(struct panthor_device *ptdev);
|
||||
void panthor_sched_prepare_for_vm_destruction(struct panthor_device *ptdev);
|
||||
void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events);
|
||||
|
||||
void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include "pl111_nomadik.h"
|
||||
|
||||
#define PMU_CTRL_OFFSET 0x0000
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ config DRM_ROCKCHIP
|
|||
select DRM_DW_HDMI_QP if ROCKCHIP_DW_HDMI_QP
|
||||
select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
|
||||
select DRM_DW_MIPI_DSI2 if ROCKCHIP_DW_MIPI_DSI2
|
||||
select DRM_INNO_HDMI if ROCKCHIP_INNO_HDMI
|
||||
select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI
|
||||
select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI
|
||||
select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI_QP) += dw_hdmi_qp-rockchip.o
|
|||
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI2) += dw-mipi-dsi2-rockchip.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_DW_DP) += dw_dp-rockchip.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi-rockchip.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_RK3066_HDMI) += rk3066_hdmi.o
|
||||
|
|
|
|||
189
drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c
Normal file
189
drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) Rockchip Electronics Co., Ltd.
|
||||
* Zheng Yang <zhengyang@rock-chips.com>
|
||||
* Andy Yan <andy.yan@rock-chips.com>
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/hw_bitfield.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <drm/bridge/inno_hdmi.h>
|
||||
#include <drm/drm_bridge_connector.h>
|
||||
#include <drm/drm_of.h>
|
||||
|
||||
#include "rockchip_drm_drv.h"
|
||||
|
||||
#define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16)
|
||||
|
||||
#define RK3036_GRF_SOC_CON2 0x148
|
||||
#define RK3036_HDMI_PHSYNC BIT(4)
|
||||
#define RK3036_HDMI_PVSYNC BIT(5)
|
||||
|
||||
enum inno_hdmi_dev_type {
|
||||
RK3036_HDMI,
|
||||
RK3128_HDMI,
|
||||
};
|
||||
|
||||
struct inno_hdmi_connector_state {
|
||||
struct drm_connector_state base;
|
||||
unsigned int colorimetry;
|
||||
};
|
||||
|
||||
struct rockchip_inno_hdmi {
|
||||
struct inno_hdmi *base;
|
||||
struct device *dev;
|
||||
struct regmap *grf;
|
||||
struct rockchip_encoder encoder;
|
||||
};
|
||||
|
||||
static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = {
|
||||
{ 74250000, 0x3f, 0xbb },
|
||||
{ 165000000, 0x6f, 0xbb },
|
||||
{ ~0UL, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
static struct inno_hdmi_phy_config rk3128_hdmi_phy_configs[] = {
|
||||
{ 74250000, 0x3f, 0xaa },
|
||||
{ 165000000, 0x5f, 0xaa },
|
||||
{ ~0UL, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
static void inno_hdmi_rk3036_enable(struct device *dev, struct drm_display_mode *mode)
|
||||
{
|
||||
struct rockchip_inno_hdmi *hdmi = dev_get_drvdata(dev);
|
||||
int value, psync;
|
||||
|
||||
psync = mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0;
|
||||
value = FIELD_PREP_WM16(RK3036_HDMI_PHSYNC, psync);
|
||||
psync = mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0;
|
||||
value |= FIELD_PREP_WM16(RK3036_HDMI_PVSYNC, psync);
|
||||
regmap_write(hdmi->grf, RK3036_GRF_SOC_CON2, value);
|
||||
}
|
||||
|
||||
static int inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
|
||||
|
||||
s->output_mode = ROCKCHIP_OUT_MODE_P888;
|
||||
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs inno_hdmi_rockchip_encoder_helper_funcs = {
|
||||
.atomic_check = inno_hdmi_encoder_atomic_check,
|
||||
};
|
||||
|
||||
static int inno_hdmi_rockchip_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct drm_device *drm = data;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct rockchip_inno_hdmi *hdmi;
|
||||
const struct inno_hdmi_plat_data *plat_data;
|
||||
int ret;
|
||||
|
||||
hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
|
||||
if (!hdmi)
|
||||
return -ENOMEM;
|
||||
|
||||
hdmi->dev = dev;
|
||||
|
||||
plat_data = of_device_get_match_data(hdmi->dev);
|
||||
if (!plat_data)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "rockchip,rk3036-inno-hdmi")) {
|
||||
hdmi->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
|
||||
if (IS_ERR(hdmi->grf))
|
||||
return dev_err_probe(dev,
|
||||
PTR_ERR(hdmi->grf), "Unable to get rockchip,grf\n");
|
||||
}
|
||||
|
||||
encoder = &hdmi->encoder.encoder;
|
||||
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
|
||||
|
||||
/*
|
||||
* If we failed to find the CRTC(s) which this encoder is
|
||||
* supposed to be connected to, it's because the CRTC has
|
||||
* not been registered yet. Defer probing, and hope that
|
||||
* the required CRTC is added later.
|
||||
*/
|
||||
if (encoder->possible_crtcs == 0)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_encoder_helper_add(encoder, &inno_hdmi_rockchip_encoder_helper_funcs);
|
||||
|
||||
dev_set_drvdata(dev, hdmi);
|
||||
|
||||
hdmi->base = inno_hdmi_bind(dev, encoder, plat_data);
|
||||
|
||||
connector = drm_bridge_connector_init(drm, encoder);
|
||||
if (IS_ERR(connector)) {
|
||||
ret = PTR_ERR(connector);
|
||||
dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return drm_connector_attach_encoder(connector, encoder);
|
||||
}
|
||||
|
||||
static const struct component_ops inno_hdmi_rockchip_ops = {
|
||||
.bind = inno_hdmi_rockchip_bind,
|
||||
};
|
||||
|
||||
static int inno_hdmi_rockchip_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &inno_hdmi_rockchip_ops);
|
||||
}
|
||||
|
||||
static void inno_hdmi_rockchip_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &inno_hdmi_rockchip_ops);
|
||||
}
|
||||
|
||||
static const struct inno_hdmi_plat_ops rk3036_inno_hdmi_plat_ops = {
|
||||
.enable = inno_hdmi_rk3036_enable,
|
||||
};
|
||||
|
||||
static const struct inno_hdmi_plat_data rk3036_inno_hdmi_plat_data = {
|
||||
.ops = &rk3036_inno_hdmi_plat_ops,
|
||||
.phy_configs = rk3036_hdmi_phy_configs,
|
||||
.default_phy_config = &rk3036_hdmi_phy_configs[1],
|
||||
};
|
||||
|
||||
static const struct inno_hdmi_plat_data rk3128_inno_hdmi_plat_data = {
|
||||
.phy_configs = rk3128_hdmi_phy_configs,
|
||||
.default_phy_config = &rk3128_hdmi_phy_configs[1],
|
||||
};
|
||||
|
||||
static const struct of_device_id inno_hdmi_rockchip_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3036-inno-hdmi",
|
||||
.data = &rk3036_inno_hdmi_plat_data,
|
||||
},
|
||||
{ .compatible = "rockchip,rk3128-inno-hdmi",
|
||||
.data = &rk3128_inno_hdmi_plat_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, inno_hdmi_rockchip_dt_ids);
|
||||
|
||||
struct platform_driver inno_hdmi_driver = {
|
||||
.probe = inno_hdmi_rockchip_probe,
|
||||
.remove = inno_hdmi_rockchip_remove,
|
||||
.driver = {
|
||||
.name = "innohdmi-rockchip",
|
||||
.of_match_table = inno_hdmi_rockchip_dt_ids,
|
||||
},
|
||||
};
|
||||
|
|
@ -19,6 +19,8 @@
|
|||
#include <drm/drm_gem_shmem_helper.h>
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
|
||||
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
|
||||
|
||||
#define TEST_SIZE SZ_1M
|
||||
#define TEST_BYTE 0xae
|
||||
|
||||
|
|
@ -34,6 +36,9 @@ KUNIT_DEFINE_ACTION_WRAPPER(sg_free_table_wrapper, sg_free_table,
|
|||
KUNIT_DEFINE_ACTION_WRAPPER(drm_gem_shmem_free_wrapper, drm_gem_shmem_free,
|
||||
struct drm_gem_shmem_object *);
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(drm_gem_shmem_unpin_wrapper, drm_gem_shmem_unpin,
|
||||
struct drm_gem_shmem_object *);
|
||||
|
||||
/*
|
||||
* Test creating a shmem GEM object backed by shmem buffer. The test
|
||||
* case succeeds if the GEM object is successfully allocated with the
|
||||
|
|
@ -173,7 +178,7 @@ static void drm_gem_shmem_test_vmap(struct kunit *test)
|
|||
ret = kunit_add_action_or_reset(test, drm_gem_shmem_free_wrapper, shmem);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = drm_gem_shmem_vmap_locked(shmem, &map);
|
||||
ret = drm_gem_shmem_vmap(shmem, &map);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
KUNIT_ASSERT_NOT_NULL(test, shmem->vaddr);
|
||||
KUNIT_ASSERT_FALSE(test, iosys_map_is_null(&map));
|
||||
|
|
@ -183,7 +188,7 @@ static void drm_gem_shmem_test_vmap(struct kunit *test)
|
|||
for (i = 0; i < TEST_SIZE; i++)
|
||||
KUNIT_EXPECT_EQ(test, iosys_map_rd(&map, i, u8), TEST_BYTE);
|
||||
|
||||
drm_gem_shmem_vunmap_locked(shmem, &map);
|
||||
drm_gem_shmem_vunmap(shmem, &map);
|
||||
KUNIT_EXPECT_NULL(test, shmem->vaddr);
|
||||
KUNIT_EXPECT_EQ(test, refcount_read(&shmem->vmap_use_count), 0);
|
||||
}
|
||||
|
|
@ -194,7 +199,7 @@ static void drm_gem_shmem_test_vmap(struct kunit *test)
|
|||
* scatter/gather table large enough to accommodate the backing memory
|
||||
* is successfully exported.
|
||||
*/
|
||||
static void drm_gem_shmem_test_get_pages_sgt(struct kunit *test)
|
||||
static void drm_gem_shmem_test_get_sg_table(struct kunit *test)
|
||||
{
|
||||
struct drm_device *drm_dev = test->priv;
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
|
|
@ -212,6 +217,9 @@ static void drm_gem_shmem_test_get_pages_sgt(struct kunit *test)
|
|||
ret = drm_gem_shmem_pin(shmem);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = kunit_add_action_or_reset(test, drm_gem_shmem_unpin_wrapper, shmem);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
sgt = drm_gem_shmem_get_sg_table(shmem);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sgt);
|
||||
KUNIT_EXPECT_NULL(test, shmem->sgt);
|
||||
|
|
@ -236,7 +244,7 @@ static void drm_gem_shmem_test_get_pages_sgt(struct kunit *test)
|
|||
* backing pages are pinned and a scatter/gather table large enough to
|
||||
* accommodate the backing memory is successfully exported.
|
||||
*/
|
||||
static void drm_gem_shmem_test_get_sg_table(struct kunit *test)
|
||||
static void drm_gem_shmem_test_get_pages_sgt(struct kunit *test)
|
||||
{
|
||||
struct drm_device *drm_dev = test->priv;
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
|
|
@ -284,17 +292,17 @@ static void drm_gem_shmem_test_madvise(struct kunit *test)
|
|||
ret = kunit_add_action_or_reset(test, drm_gem_shmem_free_wrapper, shmem);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = drm_gem_shmem_madvise_locked(shmem, 1);
|
||||
ret = drm_gem_shmem_madvise(shmem, 1);
|
||||
KUNIT_EXPECT_TRUE(test, ret);
|
||||
KUNIT_ASSERT_EQ(test, shmem->madv, 1);
|
||||
|
||||
/* Set madv to a negative value */
|
||||
ret = drm_gem_shmem_madvise_locked(shmem, -1);
|
||||
ret = drm_gem_shmem_madvise(shmem, -1);
|
||||
KUNIT_EXPECT_FALSE(test, ret);
|
||||
KUNIT_ASSERT_EQ(test, shmem->madv, -1);
|
||||
|
||||
/* Check that madv cannot be set back to a positive value */
|
||||
ret = drm_gem_shmem_madvise_locked(shmem, 0);
|
||||
ret = drm_gem_shmem_madvise(shmem, 0);
|
||||
KUNIT_EXPECT_FALSE(test, ret);
|
||||
KUNIT_ASSERT_EQ(test, shmem->madv, -1);
|
||||
}
|
||||
|
|
@ -322,7 +330,7 @@ static void drm_gem_shmem_test_purge(struct kunit *test)
|
|||
ret = drm_gem_shmem_is_purgeable(shmem);
|
||||
KUNIT_EXPECT_FALSE(test, ret);
|
||||
|
||||
ret = drm_gem_shmem_madvise_locked(shmem, 1);
|
||||
ret = drm_gem_shmem_madvise(shmem, 1);
|
||||
KUNIT_EXPECT_TRUE(test, ret);
|
||||
|
||||
/* The scatter/gather table will be freed by drm_gem_shmem_free */
|
||||
|
|
@ -332,7 +340,9 @@ static void drm_gem_shmem_test_purge(struct kunit *test)
|
|||
ret = drm_gem_shmem_is_purgeable(shmem);
|
||||
KUNIT_EXPECT_TRUE(test, ret);
|
||||
|
||||
drm_gem_shmem_purge_locked(shmem);
|
||||
ret = drm_gem_shmem_purge(shmem);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
KUNIT_EXPECT_NULL(test, shmem->pages);
|
||||
KUNIT_EXPECT_NULL(test, shmem->sgt);
|
||||
KUNIT_EXPECT_EQ(test, shmem->madv, -1);
|
||||
|
|
@ -366,8 +376,8 @@ static struct kunit_case drm_gem_shmem_test_cases[] = {
|
|||
KUNIT_CASE(drm_gem_shmem_test_obj_create_private),
|
||||
KUNIT_CASE(drm_gem_shmem_test_pin_pages),
|
||||
KUNIT_CASE(drm_gem_shmem_test_vmap),
|
||||
KUNIT_CASE(drm_gem_shmem_test_get_pages_sgt),
|
||||
KUNIT_CASE(drm_gem_shmem_test_get_sg_table),
|
||||
KUNIT_CASE(drm_gem_shmem_test_get_pages_sgt),
|
||||
KUNIT_CASE(drm_gem_shmem_test_madvise),
|
||||
KUNIT_CASE(drm_gem_shmem_test_purge),
|
||||
{}
|
||||
|
|
|
|||
221
drivers/gpu/drm/tests/drm_panic_test.c
Normal file
221
drivers/gpu/drm/tests/drm_panic_test.c
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 or MIT
|
||||
/*
|
||||
* Copyright (c) 2025 Red Hat.
|
||||
* Author: Jocelyn Falempe <jfalempe@redhat.com>
|
||||
*
|
||||
* KUNIT tests for drm panic
|
||||
*/
|
||||
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_panic.h>
|
||||
|
||||
#include <kunit/test.h>
|
||||
|
||||
#include <linux/units.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/* Check the framebuffer color only if the panic colors are the default */
|
||||
#if (CONFIG_DRM_PANIC_BACKGROUND_COLOR == 0 && \
|
||||
CONFIG_DRM_PANIC_FOREGROUND_COLOR == 0xffffff)
|
||||
|
||||
static void drm_panic_check_color_byte(struct kunit *test, u8 b)
|
||||
{
|
||||
KUNIT_EXPECT_TRUE(test, (b == 0 || b == 0xff));
|
||||
}
|
||||
#else
|
||||
static void drm_panic_check_color_byte(struct kunit *test, u8 b) {}
|
||||
#endif
|
||||
|
||||
struct drm_test_mode {
|
||||
const int width;
|
||||
const int height;
|
||||
const u32 format;
|
||||
void (*draw_screen)(struct drm_scanout_buffer *sb);
|
||||
const char *fname;
|
||||
};
|
||||
|
||||
/*
|
||||
* Run all tests for the 3 panic screens: user, kmsg and qr_code
|
||||
*/
|
||||
#define DRM_TEST_MODE_LIST(func) \
|
||||
DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_XRGB8888, func) \
|
||||
DRM_PANIC_TEST_MODE(300, 200, DRM_FORMAT_XRGB8888, func) \
|
||||
DRM_PANIC_TEST_MODE(1920, 1080, DRM_FORMAT_XRGB8888, func) \
|
||||
DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_RGB565, func) \
|
||||
DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_RGB888, func) \
|
||||
|
||||
#define DRM_PANIC_TEST_MODE(w, h, f, name) { \
|
||||
.width = w, \
|
||||
.height = h, \
|
||||
.format = f, \
|
||||
.draw_screen = draw_panic_screen_##name, \
|
||||
.fname = #name, \
|
||||
}, \
|
||||
|
||||
static const struct drm_test_mode drm_test_modes_cases[] = {
|
||||
DRM_TEST_MODE_LIST(user)
|
||||
DRM_TEST_MODE_LIST(kmsg)
|
||||
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
|
||||
DRM_TEST_MODE_LIST(qr_code)
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef DRM_PANIC_TEST_MODE
|
||||
|
||||
static int drm_test_panic_init(struct kunit *test)
|
||||
{
|
||||
struct drm_scanout_buffer *priv;
|
||||
|
||||
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, priv);
|
||||
|
||||
test->priv = priv;
|
||||
|
||||
drm_panic_set_description("Kunit testing");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test drawing the panic screen, using a memory mapped framebuffer
|
||||
* Set the whole buffer to 0xa5, and then check that all pixels have been
|
||||
* written.
|
||||
*/
|
||||
static void drm_test_panic_screen_user_map(struct kunit *test)
|
||||
{
|
||||
struct drm_scanout_buffer *sb = test->priv;
|
||||
const struct drm_test_mode *params = test->param_value;
|
||||
char *fb;
|
||||
int fb_size;
|
||||
int i;
|
||||
|
||||
sb->format = drm_format_info(params->format);
|
||||
fb_size = params->width * params->height * sb->format->cpp[0];
|
||||
|
||||
fb = vmalloc(fb_size);
|
||||
KUNIT_ASSERT_NOT_NULL(test, fb);
|
||||
|
||||
memset(fb, 0xa5, fb_size);
|
||||
|
||||
iosys_map_set_vaddr(&sb->map[0], fb);
|
||||
sb->width = params->width;
|
||||
sb->height = params->height;
|
||||
sb->pitch[0] = params->width * sb->format->cpp[0];
|
||||
|
||||
params->draw_screen(sb);
|
||||
|
||||
for (i = 0; i < fb_size; i++)
|
||||
drm_panic_check_color_byte(test, fb[i]);
|
||||
|
||||
vfree(fb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test drawing the panic screen, using a list of pages framebuffer
|
||||
* Set the whole buffer to 0xa5, and then check that all pixels have been
|
||||
* written.
|
||||
*/
|
||||
static void drm_test_panic_screen_user_page(struct kunit *test)
|
||||
{
|
||||
struct drm_scanout_buffer *sb = test->priv;
|
||||
const struct drm_test_mode *params = test->param_value;
|
||||
int fb_size, p, i, npages;
|
||||
struct page **pages;
|
||||
u8 *vaddr;
|
||||
|
||||
sb->format = drm_format_info(params->format);
|
||||
fb_size = params->width * params->height * sb->format->cpp[0];
|
||||
npages = DIV_ROUND_UP(fb_size, PAGE_SIZE);
|
||||
|
||||
pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, pages);
|
||||
|
||||
for (p = 0; p < npages; p++) {
|
||||
pages[p] = alloc_page(GFP_KERNEL);
|
||||
if (!pages[p]) {
|
||||
npages = p - 1;
|
||||
KUNIT_FAIL(test, "Can't allocate page\n");
|
||||
goto free_pages;
|
||||
}
|
||||
vaddr = kmap_local_page(pages[p]);
|
||||
memset(vaddr, 0xa5, PAGE_SIZE);
|
||||
kunmap_local(vaddr);
|
||||
}
|
||||
sb->pages = pages;
|
||||
sb->width = params->width;
|
||||
sb->height = params->height;
|
||||
sb->pitch[0] = params->width * sb->format->cpp[0];
|
||||
|
||||
params->draw_screen(sb);
|
||||
|
||||
for (p = 0; p < npages; p++) {
|
||||
int bytes_in_page = (p == npages - 1) ? fb_size - p * PAGE_SIZE : PAGE_SIZE;
|
||||
|
||||
vaddr = kmap_local_page(pages[p]);
|
||||
for (i = 0; i < bytes_in_page; i++)
|
||||
drm_panic_check_color_byte(test, vaddr[i]);
|
||||
|
||||
kunmap_local(vaddr);
|
||||
}
|
||||
|
||||
free_pages:
|
||||
for (p = 0; p < npages; p++)
|
||||
__free_page(pages[p]);
|
||||
kfree(pages);
|
||||
}
|
||||
|
||||
static void drm_test_panic_set_pixel(struct drm_scanout_buffer *sb,
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
u32 color)
|
||||
{
|
||||
struct kunit *test = (struct kunit *)sb->private;
|
||||
|
||||
KUNIT_ASSERT_TRUE(test, x < sb->width && y < sb->height);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test drawing the panic screen, using the set_pixel callback
|
||||
* Check that all calls to set_pixel() are within the framebuffer
|
||||
*/
|
||||
static void drm_test_panic_screen_user_set_pixel(struct kunit *test)
|
||||
{
|
||||
struct drm_scanout_buffer *sb = test->priv;
|
||||
const struct drm_test_mode *params = test->param_value;
|
||||
|
||||
sb->format = drm_format_info(params->format);
|
||||
sb->set_pixel = drm_test_panic_set_pixel;
|
||||
sb->width = params->width;
|
||||
sb->height = params->height;
|
||||
sb->private = test;
|
||||
|
||||
params->draw_screen(sb);
|
||||
}
|
||||
|
||||
static void drm_test_panic_desc(const struct drm_test_mode *t, char *desc)
|
||||
{
|
||||
sprintf(desc, "Panic screen %s, mode: %d x %d \t%p4cc",
|
||||
t->fname, t->width, t->height, &t->format);
|
||||
}
|
||||
|
||||
KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_map, drm_test_modes_cases, drm_test_panic_desc);
|
||||
KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_page, drm_test_modes_cases, drm_test_panic_desc);
|
||||
KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_set_pixel, drm_test_modes_cases, drm_test_panic_desc);
|
||||
|
||||
static struct kunit_case drm_panic_screen_user_test[] = {
|
||||
KUNIT_CASE_PARAM(drm_test_panic_screen_user_map,
|
||||
drm_test_panic_screen_user_map_gen_params),
|
||||
KUNIT_CASE_PARAM(drm_test_panic_screen_user_page,
|
||||
drm_test_panic_screen_user_page_gen_params),
|
||||
KUNIT_CASE_PARAM(drm_test_panic_screen_user_set_pixel,
|
||||
drm_test_panic_screen_user_set_pixel_gen_params),
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct kunit_suite drm_panic_suite = {
|
||||
.name = "drm_panic",
|
||||
.init = drm_test_panic_init,
|
||||
.test_cases = drm_panic_screen_user_test,
|
||||
};
|
||||
|
||||
kunit_test_suite(drm_panic_suite);
|
||||
|
|
@ -308,10 +308,9 @@ static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
|
|||
return ret;
|
||||
|
||||
if (encoder_node) {
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
/* Locate drm bridge from the hdmi encoder DT node */
|
||||
bridge = of_drm_find_bridge(encoder_node);
|
||||
struct drm_bridge *bridge __free(drm_bridge_put) =
|
||||
of_drm_find_and_get_bridge(encoder_node);
|
||||
if (!bridge)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
|
|
|
|||
|
|
@ -378,6 +378,8 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
dma_set_max_seg_size(&pdev->dev, UINT_MAX);
|
||||
|
||||
v3d->va_width = 30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_VA_WIDTH);
|
||||
|
||||
ident1 = V3D_READ(V3D_HUB_IDENT1);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_blend.h>
|
||||
#include <drm/drm_colorop.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_fixed.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_colorop.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fbdev_shmem.h>
|
||||
#include <drm/drm_file.h>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
*
|
||||
* - Arno Griffioen <arno@usn.nl>
|
||||
* - David Carter <carter@cs.bris.ac.uk>
|
||||
*
|
||||
*
|
||||
* The abstract console driver provides a generic interface for a text
|
||||
* console. It supports VGA text mode, frame buffer based graphical consoles
|
||||
* and special graphics processors that are only accessible through some
|
||||
|
|
@ -187,19 +187,12 @@ static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);
|
|||
* fg_console is the current virtual console,
|
||||
* last_console is the last used one,
|
||||
* want_console is the console we want to switch to,
|
||||
* saved_* variants are for save/restore around kernel debugger enter/leave
|
||||
*/
|
||||
int fg_console;
|
||||
EXPORT_SYMBOL(fg_console);
|
||||
int last_console;
|
||||
int want_console = -1;
|
||||
|
||||
static int saved_fg_console;
|
||||
static int saved_last_console;
|
||||
static int saved_want_console;
|
||||
static int saved_vc_mode;
|
||||
static int saved_console_blanked;
|
||||
|
||||
/*
|
||||
* For each existing display, we have a pointer to console currently visible
|
||||
* on that display, allowing consoles other than fg_console to be refreshed
|
||||
|
|
@ -4287,15 +4280,6 @@ EXPORT_SYMBOL(con_is_visible);
|
|||
*/
|
||||
void con_debug_enter(struct vc_data *vc)
|
||||
{
|
||||
saved_fg_console = fg_console;
|
||||
saved_last_console = last_console;
|
||||
saved_want_console = want_console;
|
||||
saved_vc_mode = vc->vc_mode;
|
||||
saved_console_blanked = console_blanked;
|
||||
vc->vc_mode = KD_TEXT;
|
||||
console_blanked = 0;
|
||||
if (vc->vc_sw->con_debug_enter)
|
||||
vc->vc_sw->con_debug_enter(vc);
|
||||
#ifdef CONFIG_KGDB_KDB
|
||||
/* Set the initial LINES variable if it is not already set */
|
||||
if (vc->vc_rows < 999) {
|
||||
|
|
@ -4335,19 +4319,7 @@ EXPORT_SYMBOL_GPL(con_debug_enter);
|
|||
* was invoked.
|
||||
*/
|
||||
void con_debug_leave(void)
|
||||
{
|
||||
struct vc_data *vc;
|
||||
|
||||
fg_console = saved_fg_console;
|
||||
last_console = saved_last_console;
|
||||
want_console = saved_want_console;
|
||||
console_blanked = saved_console_blanked;
|
||||
vc_cons[fg_console].d->vc_mode = saved_vc_mode;
|
||||
|
||||
vc = vc_cons[fg_console].d;
|
||||
if (vc->vc_sw->con_debug_leave)
|
||||
vc->vc_sw->con_debug_leave(vc);
|
||||
}
|
||||
{ }
|
||||
EXPORT_SYMBOL_GPL(con_debug_leave);
|
||||
|
||||
static int do_register_con_driver(const struct consw *csw, int first, int last)
|
||||
|
|
|
|||
33
include/drm/bridge/inno_hdmi.h
Normal file
33
include/drm/bridge/inno_hdmi.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2025 Rockchip Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __INNO_HDMI__
|
||||
#define __INNO_HDMI__
|
||||
|
||||
struct device;
|
||||
struct drm_encoder;
|
||||
struct drm_display_mode;
|
||||
struct inno_hdmi;
|
||||
|
||||
struct inno_hdmi_plat_ops {
|
||||
void (*enable)(struct device *pdev, struct drm_display_mode *mode);
|
||||
};
|
||||
|
||||
struct inno_hdmi_phy_config {
|
||||
unsigned long pixelclock;
|
||||
u8 pre_emphasis;
|
||||
u8 voltage_level_control;
|
||||
};
|
||||
|
||||
struct inno_hdmi_plat_data {
|
||||
const struct inno_hdmi_plat_ops *ops;
|
||||
struct inno_hdmi_phy_config *phy_configs;
|
||||
struct inno_hdmi_phy_config *default_phy_config;
|
||||
};
|
||||
|
||||
struct inno_hdmi *inno_hdmi_bind(struct device *pdev,
|
||||
struct drm_encoder *encoder,
|
||||
const struct inno_hdmi_plat_data *plat_data);
|
||||
#endif /* __INNO_HDMI__ */
|
||||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_util.h>
|
||||
#include <drm/drm_colorop.h>
|
||||
|
||||
/**
|
||||
* struct drm_crtc_commit - track modeset commits on a CRTC
|
||||
|
|
@ -712,6 +711,14 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
|
|||
struct drm_colorop_state *
|
||||
drm_atomic_get_colorop_state(struct drm_atomic_state *state,
|
||||
struct drm_colorop *colorop);
|
||||
|
||||
struct drm_colorop_state *
|
||||
drm_atomic_get_old_colorop_state(struct drm_atomic_state *state,
|
||||
struct drm_colorop *colorop);
|
||||
struct drm_colorop_state *
|
||||
drm_atomic_get_new_colorop_state(struct drm_atomic_state *state,
|
||||
struct drm_colorop *colorop);
|
||||
|
||||
struct drm_connector_state * __must_check
|
||||
drm_atomic_get_connector_state(struct drm_atomic_state *state,
|
||||
struct drm_connector *connector);
|
||||
|
|
@ -808,36 +815,6 @@ drm_atomic_get_new_plane_state(const struct drm_atomic_state *state,
|
|||
return state->planes[drm_plane_index(plane)].new_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_get_old_colorop_state - get colorop state, if it exists
|
||||
* @state: global atomic state object
|
||||
* @colorop: colorop to grab
|
||||
*
|
||||
* This function returns the old colorop state for the given colorop, or
|
||||
* NULL if the colorop is not part of the global atomic state.
|
||||
*/
|
||||
static inline struct drm_colorop_state *
|
||||
drm_atomic_get_old_colorop_state(struct drm_atomic_state *state,
|
||||
struct drm_colorop *colorop)
|
||||
{
|
||||
return state->colorops[drm_colorop_index(colorop)].old_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_get_new_colorop_state - get colorop state, if it exists
|
||||
* @state: global atomic state object
|
||||
* @colorop: colorop to grab
|
||||
*
|
||||
* This function returns the new colorop state for the given colorop, or
|
||||
* NULL if the colorop is not part of the global atomic state.
|
||||
*/
|
||||
static inline struct drm_colorop_state *
|
||||
drm_atomic_get_new_colorop_state(struct drm_atomic_state *state,
|
||||
struct drm_colorop *colorop)
|
||||
{
|
||||
return state->colorops[drm_colorop_index(colorop)].new_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_get_old_connector_state - get connector state, if it exists
|
||||
* @state: global atomic state object
|
||||
|
|
|
|||
|
|
@ -731,6 +731,7 @@ struct drm_bridge_funcs {
|
|||
* controllers for HDMI bridges.
|
||||
*/
|
||||
void (*hpd_notify)(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector,
|
||||
enum drm_connector_status status);
|
||||
|
||||
/**
|
||||
|
|
@ -1278,6 +1279,17 @@ struct drm_bridge {
|
|||
* @hpd_cb.
|
||||
*/
|
||||
void *hpd_data;
|
||||
|
||||
/**
|
||||
* @next_bridge: Pointer to the following bridge, automatically put
|
||||
* when this bridge is freed (i.e. at destroy time). This is for
|
||||
* drivers needing to store a pointer to the next bridge in the
|
||||
* chain, and ensures any code still holding a reference to this
|
||||
* bridge after its removal cannot use-after-free the next
|
||||
* bridge. Any other bridge pointers stored by the driver must be
|
||||
* put in the .destroy callback by driver code.
|
||||
*/
|
||||
struct drm_bridge *next_bridge;
|
||||
};
|
||||
|
||||
static inline struct drm_bridge *
|
||||
|
|
@ -1325,8 +1337,13 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
|
|||
enum drm_bridge_attach_flags flags);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
struct drm_bridge *of_drm_find_and_get_bridge(struct device_node *np);
|
||||
struct drm_bridge *of_drm_find_bridge(struct device_node *np);
|
||||
#else
|
||||
static inline struct drm_bridge *of_drm_find_and_get_bridge(struct device_node *np)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline struct drm_bridge *of_drm_find_bridge(struct device_node *np)
|
||||
{
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -300,4 +300,15 @@ struct drm_gem_object *drm_gem_shmem_prime_import_no_map(struct drm_device *dev,
|
|||
.gem_prime_import = drm_gem_shmem_prime_import_no_map, \
|
||||
.dumb_create = drm_gem_shmem_dumb_create
|
||||
|
||||
/*
|
||||
* Kunit helpers
|
||||
*/
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem, struct iosys_map *map);
|
||||
void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem, struct iosys_map *map);
|
||||
int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv);
|
||||
int drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem);
|
||||
#endif
|
||||
|
||||
#endif /* __DRM_GEM_SHMEM_HELPER_H__ */
|
||||
|
|
|
|||
|
|
@ -79,12 +79,6 @@ enum vc_intensity;
|
|||
* characters. (optional)
|
||||
* @con_invert_region: invert a region of length @count on @vc starting at @p.
|
||||
* (optional)
|
||||
* @con_debug_enter: prepare the console for the debugger. This includes, but
|
||||
* is not limited to, unblanking the console, loading an
|
||||
* appropriate palette, and allowing debugger generated output.
|
||||
* (optional)
|
||||
* @con_debug_leave: restore the console to its pre-debug state as closely as
|
||||
* possible. (optional)
|
||||
*/
|
||||
struct consw {
|
||||
struct module *owner;
|
||||
|
|
@ -123,8 +117,6 @@ struct consw {
|
|||
enum vc_intensity intensity,
|
||||
bool blink, bool underline, bool reverse, bool italic);
|
||||
void (*con_invert_region)(struct vc_data *vc, u16 *p, int count);
|
||||
void (*con_debug_enter)(struct vc_data *vc);
|
||||
void (*con_debug_leave)(struct vc_data *vc);
|
||||
};
|
||||
|
||||
extern const struct consw *conswitchp;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue