diff options
61 files changed, 1588 insertions, 714 deletions
diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 22487ac1b011..520da44a04a6 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -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 ================= diff --git a/MAINTAINERS b/MAINTAINERS index b8a5569606d2..e405c257f62b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index 1dcc79b35225..20a76d81d532 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -23,6 +23,9 @@ * Authors: AMD * */ + +#include <drm/drm_colorop.h> + #include "amdgpu.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" diff --git a/drivers/gpu/drm/ast/ast_cursor.c b/drivers/gpu/drm/ast/ast_cursor.c index 30b62d3f0151..3fa8e75db9c0 100644 --- a/drivers/gpu/drm/ast/ast_cursor.c +++ b/drivers/gpu/drm/ast/ast_cursor.c @@ -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; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index cd08990a10f9..57c6fbc3232b 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -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); diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index a250afd8d662..39385deafc68 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -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 diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index c7dc03182e59..909c21cc3acd 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -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 diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c index 8517b1c953d4..74eda8b54023 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c @@ -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); diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c index e5943506981d..433c080197a2 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c @@ -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); diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c index 111310acab2c..2c40ca86e319 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c @@ -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; - - ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1); - if (IS_ERR(ep)) { - ret = PTR_ERR(ep); - return ERR_PTR(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); - 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) diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c index 9f7a8cf0ab44..ab4572eb8395 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/bridge/inno-hdmi.c @@ -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; -}; - -struct inno_hdmi_connector_state { - struct drm_connector_state base; - unsigned int colorimetry; + const struct inno_hdmi_plat_data *plat_data; }; -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; - - 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); - } + const struct inno_hdmi_plat_ops *plat_ops = hdmi->plat_data->ops; + u32 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, hdmi->ddc); - drm_edid_connector_update(connector, drm_edid); - ret = drm_edid_connector_add_modes(connector); - drm_edid_free(drm_edid); - return ret; -} - -static enum drm_mode_status -inno_hdmi_connector_mode_valid(struct drm_connector *connector, - const struct drm_display_mode *mode) -{ - struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector); - - return inno_hdmi_display_mode_valid(hdmi, mode); -} - -static void -inno_hdmi_connector_destroy_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - struct inno_hdmi_connector_state *inno_conn_state = - to_inno_hdmi_conn_state(state); + drm_edid = drm_edid_read_ddc(connector, bridge->ddc); + if (!drm_edid) + dev_dbg(hdmi->dev, "failed to get edid\n"); - __drm_atomic_helper_connector_destroy_state(&inno_conn_state->base); - kfree(inno_conn_state); + return drm_edid; } -static void inno_hdmi_connector_reset(struct drm_connector *connector) +static void inno_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - struct inno_hdmi_connector_state *inno_conn_state; + struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); - 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; + inno_hdmi_setup(hdmi, state); } -static struct drm_connector_state * -inno_hdmi_connector_duplicate_state(struct drm_connector *connector) +static void inno_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - 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); + struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge); - return &inno_conn_state->base; + inno_hdmi_standby(hdmi); } -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 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 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 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; - - hdmi->dev = dev; + if (!plat_data->phy_configs || !plat_data->default_phy_config) { + dev_err(dev, "Missing platform PHY ops\n"); + return ERR_PTR(-ENODEV); + } - variant = of_device_get_match_data(hdmi->dev); - if (!variant) - return -EINVAL; + hdmi = devm_drm_bridge_alloc(dev, struct inno_hdmi, bridge, &inno_hdmi_bridge_funcs); + if (IS_ERR(hdmi)) + return ERR_CAST(hdmi); - hdmi->variant = variant; + hdmi->dev = dev; + 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"); + if (IS_ERR(hdmi->pclk)) { + dev_err_probe(dev, PTR_ERR(hdmi->pclk), "Unable to get HDMI pclk\n"); + return ERR_CAST(hdmi->pclk); + } 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->refclk)) { + dev_err_probe(dev, PTR_ERR(hdmi->refclk), "Unable to get HDMI refclk\n"); + return ERR_CAST(hdmi->refclk); } - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - 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; - - return 0; -err_cleanup_hdmi: - hdmi->connector.funcs->destroy(&hdmi->connector); - hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); - return ret; -} - -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); -} + if (ret) + return ERR_PTR(ret); -static const struct component_ops inno_hdmi_ops = { - .bind = inno_hdmi_bind, - .unbind = inno_hdmi_unbind, -}; + 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); -static int inno_hdmi_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &inno_hdmi_ops); -} + ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) + return ERR_PTR(ret); -static void inno_hdmi_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &inno_hdmi_ops); + return hdmi; } - -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"); diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 0185f61e6e59..9246e9c15a6e 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -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; } diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 38fb8776c0f4..11aab07d88df 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -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); diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 2cd1847ba776..873b32cfb508 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -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; } diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c index 4d3005273b27..2bd5189c967e 100644 --- a/drivers/gpu/drm/clients/drm_log.c +++ b/drivers/gpu/drm/clients/drm_log.c @@ -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; } diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index a2d30cf9e06d..57a0cceabd34 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -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); } } diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 6d3ea8056b60..52738b80ddbe 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -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) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 10adac9397cf..5840e9cc6f66 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -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> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index db40c26d1cb3..6dcf8f6d3ecf 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -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) { @@ -1480,29 +1482,66 @@ 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); - - list_for_each_entry(bridge, &bridge_list, list) { - if (bridge->of_node == np) { - mutex_unlock(&bridge_lock); - return bridge; - } - } + /* + * 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); - mutex_unlock(&bridge_lock); - return NULL; + return bridge; } EXPORT_SYMBOL(of_drm_find_bridge); #endif diff --git a/drivers/gpu/drm/drm_draw.c b/drivers/gpu/drm/drm_draw.c index 5b956229c82f..5043baf901b8 100644 --- a/drivers/gpu/drm/drm_draw.c +++ b/drivers/gpu/drm/drm_draw.c @@ -16,6 +16,35 @@ #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 * @format: output format diff --git a/drivers/gpu/drm/drm_draw_internal.h b/drivers/gpu/drm/drm_draw_internal.h index 20cb404e23ea..261967145635 100644 --- a/drivers/gpu/drm/drm_draw_internal.h +++ b/drivers/gpu/drm/drm_draw_internal.h @@ -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, diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index fbd1164174b0..de73c8ab8cca 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -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"); diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index f42be7f1d8c2..eb7ef17b9c71 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -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 diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 46e5ffad69a6..903101e8751a 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -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 diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c index ad5e6f7b23f9..03b2576af67f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c @@ -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) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.h b/drivers/gpu/drm/etnaviv/etnaviv_buffer.h new file mode 100644 index 000000000000..4331ca8aac47 --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.h @@ -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__ */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 54ceae87b401..bb1b84eecec8 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -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); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index b3eb1662e90c..20dad16fd554 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -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, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_flop_reset.c b/drivers/gpu/drm/etnaviv/etnaviv_flop_reset.c new file mode 100644 index 000000000000..58d957ee861d --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_flop_reset.c @@ -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); +} diff --git a/drivers/gpu/drm/etnaviv/etnaviv_flop_reset.h b/drivers/gpu/drm/etnaviv/etnaviv_flop_reset.h new file mode 100644 index 000000000000..e6dee1db788f --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_flop_reset.h @@ -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 diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index ca0be293f5fe..94326dddaf6f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -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"); diff --git a/drivers/gpu/drm/etnaviv/state_3d.xml.h b/drivers/gpu/drm/etnaviv/state_3d.xml.h index ebbd4fcf3096..a654c05414ec 100644 --- a/drivers/gpu/drm/etnaviv/state_3d.xml.h +++ b/drivers/gpu/drm/etnaviv/state_3d.xml.h @@ -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 diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 6ff53cd58052..eb2e3f1e83c9 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -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> diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c index dc374bfc5951..41071d6e05e5 100644 --- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -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"); diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c index 6c6624f9ba24..eba246791c68 100644 --- a/drivers/gpu/drm/meson/meson_encoder_dsi.c +++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c @@ -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"); diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c index 8205ee56a691..1abb0572bb5f 100644 --- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -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; diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c index 3bf0d6e4fc30..4abd0c09b442 100644 --- a/drivers/gpu/drm/meson/meson_venc.c +++ b/drivers/gpu/drm/meson/meson_venc.c @@ -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; diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 9bd9cd5c1e03..c0569229a1d1 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -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; diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h index d8c9b905f8bf..9eb3431dd93a 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.h +++ b/drivers/gpu/drm/msm/dp/dp_drm.h @@ -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_ */ diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 3cd612af2449..29b2dfb90b5f 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -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); diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 85dd3f4cb8e1..679f4af5246d 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -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"), diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index b95120682a72..2f58a9b0773a 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -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); diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 473a8bebd61e..b888fff05efe 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -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); } diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 0f83e778d89a..ca272dbae14d 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -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. */ diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h index f4a475aa34c0..9a8692de8ade 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.h +++ b/drivers/gpu/drm/panthor/panthor_sched.h @@ -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); diff --git a/drivers/gpu/drm/pl111/pl111_nomadik.c b/drivers/gpu/drm/pl111/pl111_nomadik.c index f3218d59c5f1..0e5153deb1ac 100644 --- a/drivers/gpu/drm/pl111/pl111_nomadik.c +++ b/drivers/gpu/drm/pl111/pl111_nomadik.c @@ -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 diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index b7b025814e72..6f068ce1021d 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -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 diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 097f062399c7..948b0f906d3d 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -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 diff --git a/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c new file mode 100644 index 000000000000..97c20500f790 --- /dev/null +++ b/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c @@ -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, + }, +}; diff --git a/drivers/gpu/drm/tests/drm_gem_shmem_test.c b/drivers/gpu/drm/tests/drm_gem_shmem_test.c index 68f2c3162354..4b459f21acfd 100644 --- a/drivers/gpu/drm/tests/drm_gem_shmem_test.c +++ b/drivers/gpu/drm/tests/drm_gem_shmem_test.c @@ -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), {} diff --git a/drivers/gpu/drm/tests/drm_panic_test.c b/drivers/gpu/drm/tests/drm_panic_test.c new file mode 100644 index 000000000000..d60150877df8 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_panic_test.c @@ -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); diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c index 7cf0f0ea1bfe..505888497482 100644 --- a/drivers/gpu/drm/tiny/arcpgu.c +++ b/drivers/gpu/drm/tiny/arcpgu.c @@ -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; diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index 8faa9382846f..a11ca276061a 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -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); diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 3cf3f26e0d8e..cd85de4ffd03 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -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> diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index dd1402f43773..434c295f44ba 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -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> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 59b4b5e126ba..e987d260e346 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -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) diff --git a/include/drm/bridge/inno_hdmi.h b/include/drm/bridge/inno_hdmi.h new file mode 100644 index 000000000000..8b39655212e2 --- /dev/null +++ b/include/drm/bridge/inno_hdmi.h @@ -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__ */ diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 74ce26fa8838..178f8f62c80f 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -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); @@ -809,36 +816,6 @@ drm_atomic_get_new_plane_state(const struct drm_atomic_state *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 * @connector: connector to grab diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index d2683846cc61..3e6cbfa9dc44 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -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; diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h index 6b6478f5ca24..5ccdae21b94a 100644 --- a/include/drm/drm_gem_shmem_helper.h +++ b/include/drm/drm_gem_shmem_helper.h @@ -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__ */ diff --git a/include/linux/console.h b/include/linux/console.h index fc9f5c5c1b04..e22f5c993e24 100644 --- a/include/linux/console.h +++ b/include/linux/console.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; |
