Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/display: bridge_connector: get/put the stored bridges

drm_bridge_connector_init() takes eight pointers to various bridges, some
of which can be identical, and stores them in pointers inside struct
drm_bridge_connector. Get a reference to each of the taken bridges and put
it on cleanup.

Achieve this by adding a drmm cleanup callback whic puts all the non-NULL
bridges. Using drmm ensures the cleanup happens on drm_device teardown,
whichever is the return value of this function.

Four of these pointers (edid, hpd, detect and modes) can be written
multiple times (up to once per loop iterations), in order to eventually
store the last matching bridge. So when one of those pointers is
overwritten, we need to put the reference that we got during the previous
assignment. Add a drm_bridge_put() before writing them to handle this.

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Tested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> # db410c
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Tested-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://patch.msgid.link/20251017-drm-bridge-alloc-getput-bridge-connector-fix-hdmi_cec-v2-2-667abf6d47c0@bootlin.com
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

+39 -13
+39 -13
drivers/gpu/drm/display/drm_bridge_connector.c
··· 618 618 * Bridge Connector Initialisation 619 619 */ 620 620 621 + static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data) 622 + { 623 + struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data; 624 + 625 + drm_bridge_put(bridge_connector->bridge_edid); 626 + drm_bridge_put(bridge_connector->bridge_hpd); 627 + drm_bridge_put(bridge_connector->bridge_detect); 628 + drm_bridge_put(bridge_connector->bridge_modes); 629 + drm_bridge_put(bridge_connector->bridge_hdmi); 630 + drm_bridge_put(bridge_connector->bridge_hdmi_audio); 631 + drm_bridge_put(bridge_connector->bridge_dp_audio); 632 + drm_bridge_put(bridge_connector->bridge_hdmi_cec); 633 + } 634 + 621 635 /** 622 636 * drm_bridge_connector_init - Initialise a connector for a chain of bridges 623 637 * @drm: the DRM device ··· 663 649 if (!bridge_connector) 664 650 return ERR_PTR(-ENOMEM); 665 651 652 + ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector); 653 + if (ret) 654 + return ERR_PTR(ret); 655 + 666 656 bridge_connector->encoder = encoder; 667 657 668 658 /* ··· 690 672 if (!bridge->ycbcr_420_allowed) 691 673 connector->ycbcr_420_allowed = false; 692 674 693 - if (bridge->ops & DRM_BRIDGE_OP_EDID) 694 - bridge_connector->bridge_edid = bridge; 695 - if (bridge->ops & DRM_BRIDGE_OP_HPD) 696 - bridge_connector->bridge_hpd = bridge; 697 - if (bridge->ops & DRM_BRIDGE_OP_DETECT) 698 - bridge_connector->bridge_detect = bridge; 699 - if (bridge->ops & DRM_BRIDGE_OP_MODES) 700 - bridge_connector->bridge_modes = bridge; 675 + if (bridge->ops & DRM_BRIDGE_OP_EDID) { 676 + drm_bridge_put(bridge_connector->bridge_edid); 677 + bridge_connector->bridge_edid = drm_bridge_get(bridge); 678 + } 679 + if (bridge->ops & DRM_BRIDGE_OP_HPD) { 680 + drm_bridge_put(bridge_connector->bridge_hpd); 681 + bridge_connector->bridge_hpd = drm_bridge_get(bridge); 682 + } 683 + if (bridge->ops & DRM_BRIDGE_OP_DETECT) { 684 + drm_bridge_put(bridge_connector->bridge_detect); 685 + bridge_connector->bridge_detect = drm_bridge_get(bridge); 686 + } 687 + if (bridge->ops & DRM_BRIDGE_OP_MODES) { 688 + drm_bridge_put(bridge_connector->bridge_modes); 689 + bridge_connector->bridge_modes = drm_bridge_get(bridge); 690 + } 701 691 if (bridge->ops & DRM_BRIDGE_OP_HDMI) { 702 692 if (bridge_connector->bridge_hdmi) 703 693 return ERR_PTR(-EBUSY); ··· 713 687 !bridge->funcs->hdmi_clear_infoframe) 714 688 return ERR_PTR(-EINVAL); 715 689 716 - bridge_connector->bridge_hdmi = bridge; 690 + bridge_connector->bridge_hdmi = drm_bridge_get(bridge); 717 691 718 692 if (bridge->supported_formats) 719 693 supported_formats = bridge->supported_formats; ··· 736 710 !bridge->funcs->hdmi_audio_shutdown) 737 711 return ERR_PTR(-EINVAL); 738 712 739 - bridge_connector->bridge_hdmi_audio = bridge; 713 + bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge); 740 714 } 741 715 742 716 if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) { ··· 754 728 !bridge->funcs->dp_audio_shutdown) 755 729 return ERR_PTR(-EINVAL); 756 730 757 - bridge_connector->bridge_dp_audio = bridge; 731 + bridge_connector->bridge_dp_audio = drm_bridge_get(bridge); 758 732 } 759 733 760 734 if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { 761 735 if (bridge_connector->bridge_hdmi_cec) 762 736 return ERR_PTR(-EBUSY); 763 737 764 - bridge_connector->bridge_hdmi_cec = bridge; 738 + bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge); 765 739 } 766 740 767 741 if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { 768 742 if (bridge_connector->bridge_hdmi_cec) 769 743 return ERR_PTR(-EBUSY); 770 744 771 - bridge_connector->bridge_hdmi_cec = bridge; 745 + bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge); 772 746 773 747 if (!bridge->funcs->hdmi_cec_enable || 774 748 !bridge->funcs->hdmi_cec_log_addr ||