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

drm/vc4: hdmi: switch to generic CEC helpers

Switch VC4 driver to using CEC helpers code, simplifying hotplug and
registration / cleanup. The existing vc4_hdmi_cec_release() is kept for
now.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Tested-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Link: https://lore.kernel.org/r/20250705-drm-hdmi-connector-cec-v7-1-d14fa0c31b74@oss.qualcomm.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

+55 -84
+1
drivers/gpu/drm/vc4/Kconfig
··· 35 35 bool "Broadcom VC4 HDMI CEC Support" 36 36 depends on DRM_VC4 37 37 select CEC_CORE 38 + select DRM_DISPLAY_HDMI_CEC_HELPER 38 39 help 39 40 Choose this option if you have a Broadcom VC4 GPU 40 41 and want to use CEC.
+54 -83
drivers/gpu/drm/vc4/vc4_hdmi.c
··· 32 32 */ 33 33 34 34 #include <drm/display/drm_hdmi_audio_helper.h> 35 + #include <drm/display/drm_hdmi_cec_helper.h> 35 36 #include <drm/display/drm_hdmi_helper.h> 36 37 #include <drm/display/drm_hdmi_state_helper.h> 37 38 #include <drm/display/drm_scdc_helper.h> ··· 375 374 */ 376 375 377 376 drm_atomic_helper_connector_hdmi_hotplug(connector, status); 378 - 379 - if (status == connector_status_disconnected) { 380 - cec_phys_addr_invalidate(vc4_hdmi->cec_adap); 381 - return; 382 - } 383 - 384 - cec_s_phys_addr(vc4_hdmi->cec_adap, 385 - connector->display_info.source_physical_address, false); 386 377 387 378 if (status != connector_status_connected) 388 379 return; ··· 2377 2384 struct vc4_hdmi *vc4_hdmi = priv; 2378 2385 2379 2386 if (vc4_hdmi->cec_rx_msg.len) 2380 - cec_received_msg(vc4_hdmi->cec_adap, 2381 - &vc4_hdmi->cec_rx_msg); 2387 + drm_connector_hdmi_cec_received_msg(&vc4_hdmi->connector, 2388 + &vc4_hdmi->cec_rx_msg); 2382 2389 2383 2390 return IRQ_HANDLED; 2384 2391 } ··· 2388 2395 struct vc4_hdmi *vc4_hdmi = priv; 2389 2396 2390 2397 if (vc4_hdmi->cec_tx_ok) { 2391 - cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK, 2392 - 0, 0, 0, 0); 2398 + drm_connector_hdmi_cec_transmit_done(&vc4_hdmi->connector, 2399 + CEC_TX_STATUS_OK, 2400 + 0, 0, 0, 0); 2393 2401 } else { 2394 2402 /* 2395 2403 * This CEC implementation makes 1 retry, so if we 2396 2404 * get a NACK, then that means it made 2 attempts. 2397 2405 */ 2398 - cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK, 2399 - 0, 2, 0, 0); 2406 + drm_connector_hdmi_cec_transmit_done(&vc4_hdmi->connector, 2407 + CEC_TX_STATUS_NACK, 2408 + 0, 2, 0, 0); 2400 2409 } 2401 2410 return IRQ_HANDLED; 2402 2411 } ··· 2555 2560 return ret; 2556 2561 } 2557 2562 2558 - static int vc4_hdmi_cec_enable(struct cec_adapter *adap) 2563 + static int vc4_hdmi_cec_enable(struct drm_connector *connector) 2559 2564 { 2560 - struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); 2565 + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); 2561 2566 struct drm_device *drm = vc4_hdmi->connector.dev; 2562 2567 /* clock period in microseconds */ 2563 2568 const u32 usecs = 1000000 / CEC_CLOCK_FREQ; ··· 2622 2627 return 0; 2623 2628 } 2624 2629 2625 - static int vc4_hdmi_cec_disable(struct cec_adapter *adap) 2630 + static int vc4_hdmi_cec_disable(struct drm_connector *connector) 2626 2631 { 2627 - struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); 2632 + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); 2628 2633 struct drm_device *drm = vc4_hdmi->connector.dev; 2629 2634 unsigned long flags; 2630 2635 int idx; ··· 2658 2663 return 0; 2659 2664 } 2660 2665 2661 - static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) 2666 + static int vc4_hdmi_cec_adap_enable(struct drm_connector *connector, bool enable) 2662 2667 { 2663 2668 if (enable) 2664 - return vc4_hdmi_cec_enable(adap); 2669 + return vc4_hdmi_cec_enable(connector); 2665 2670 else 2666 - return vc4_hdmi_cec_disable(adap); 2671 + return vc4_hdmi_cec_disable(connector); 2667 2672 } 2668 2673 2669 - static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) 2674 + static int vc4_hdmi_cec_adap_log_addr(struct drm_connector *connector, u8 log_addr) 2670 2675 { 2671 - struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); 2676 + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); 2672 2677 struct drm_device *drm = vc4_hdmi->connector.dev; 2673 2678 unsigned long flags; 2674 2679 int idx; ··· 2694 2699 return 0; 2695 2700 } 2696 2701 2697 - static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, 2702 + static int vc4_hdmi_cec_adap_transmit(struct drm_connector *connector, u8 attempts, 2698 2703 u32 signal_free_time, struct cec_msg *msg) 2699 2704 { 2700 - struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); 2705 + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); 2701 2706 struct drm_device *dev = vc4_hdmi->connector.dev; 2702 2707 unsigned long flags; 2703 2708 u32 val; ··· 2740 2745 return 0; 2741 2746 } 2742 2747 2743 - static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = { 2744 - .adap_enable = vc4_hdmi_cec_adap_enable, 2745 - .adap_log_addr = vc4_hdmi_cec_adap_log_addr, 2746 - .adap_transmit = vc4_hdmi_cec_adap_transmit, 2747 - }; 2748 - 2749 - static void vc4_hdmi_cec_release(void *ptr) 2748 + static int vc4_hdmi_cec_init(struct drm_connector *connector) 2750 2749 { 2751 - struct vc4_hdmi *vc4_hdmi = ptr; 2752 - 2753 - cec_unregister_adapter(vc4_hdmi->cec_adap); 2754 - vc4_hdmi->cec_adap = NULL; 2755 - } 2756 - 2757 - static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) 2758 - { 2759 - struct cec_connector_info conn_info; 2750 + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); 2760 2751 struct platform_device *pdev = vc4_hdmi->pdev; 2761 2752 struct device *dev = &pdev->dev; 2762 2753 int ret; 2763 - 2764 - if (!of_property_present(dev->of_node, "interrupts")) { 2765 - dev_warn(dev, "'interrupts' DT property is missing, no CEC\n"); 2766 - return 0; 2767 - } 2768 - 2769 - vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, 2770 - vc4_hdmi, 2771 - vc4_hdmi->variant->card_name, 2772 - CEC_CAP_DEFAULTS | 2773 - CEC_CAP_CONNECTOR_INFO, 1); 2774 - ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap); 2775 - if (ret < 0) 2776 - return ret; 2777 - 2778 - cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector); 2779 - cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); 2780 2754 2781 2755 if (vc4_hdmi->variant->external_irq_controller) { 2782 2756 ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-rx"), ··· 2753 2789 vc4_cec_irq_handler_rx_thread, 0, 2754 2790 "vc4 hdmi cec rx", vc4_hdmi); 2755 2791 if (ret) 2756 - goto err_delete_cec_adap; 2792 + return ret; 2757 2793 2758 2794 ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-tx"), 2759 2795 vc4_cec_irq_handler_tx_bare, 2760 2796 vc4_cec_irq_handler_tx_thread, 0, 2761 2797 "vc4 hdmi cec tx", vc4_hdmi); 2762 2798 if (ret) 2763 - goto err_delete_cec_adap; 2799 + return ret; 2764 2800 } else { 2765 2801 ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), 2766 2802 vc4_cec_irq_handler, 2767 2803 vc4_cec_irq_handler_thread, 0, 2768 2804 "vc4 hdmi cec", vc4_hdmi); 2769 2805 if (ret) 2770 - goto err_delete_cec_adap; 2806 + return ret; 2771 2807 } 2772 2808 2773 - ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev); 2774 - if (ret < 0) 2775 - goto err_delete_cec_adap; 2809 + return 0; 2810 + } 2811 + 2812 + static const struct drm_connector_hdmi_cec_funcs vc4_hdmi_cec_funcs = { 2813 + .init = vc4_hdmi_cec_init, 2814 + .enable = vc4_hdmi_cec_adap_enable, 2815 + .log_addr = vc4_hdmi_cec_adap_log_addr, 2816 + .transmit = vc4_hdmi_cec_adap_transmit, 2817 + }; 2818 + 2819 + static int vc4_hdmi_cec_register(struct vc4_hdmi *vc4_hdmi) 2820 + { 2821 + struct platform_device *pdev = vc4_hdmi->pdev; 2822 + struct device *dev = &pdev->dev; 2823 + 2824 + if (!of_property_present(dev->of_node, "interrupts")) { 2825 + dev_warn(dev, "'interrupts' DT property is missing, no CEC\n"); 2826 + return 0; 2827 + } 2776 2828 2777 2829 /* 2778 - * NOTE: Strictly speaking, we should probably use a DRM-managed 2779 - * registration there to avoid removing the CEC adapter by the 2780 - * time the DRM driver doesn't have any user anymore. 2830 + * NOTE: the CEC adapter will be unregistered by drmm cleanup from 2831 + * drm_managed_release(), which is called from drm_dev_release() 2832 + * during device unbind. 2781 2833 * 2782 2834 * However, the CEC framework already cleans up the CEC adapter 2783 2835 * only when the last user has closed its file descriptor, so we 2784 2836 * don't need to handle it in DRM. 2785 - * 2786 - * By the time the device-managed hook is executed, we will give 2787 - * up our reference to the CEC adapter and therefore don't 2788 - * really care when it's actually freed. 2789 2837 * 2790 2838 * There's still a problematic sequence: if we unregister our 2791 2839 * CEC adapter, but the userspace keeps a handle on the CEC ··· 2809 2833 * the CEC framework already handles this too, by calling 2810 2834 * cec_is_registered() in cec_ioctl() and cec_poll(). 2811 2835 */ 2812 - ret = devm_add_action_or_reset(dev, vc4_hdmi_cec_release, vc4_hdmi); 2813 - if (ret) 2814 - return ret; 2815 - 2816 - return 0; 2817 - 2818 - err_delete_cec_adap: 2819 - cec_delete_adapter(vc4_hdmi->cec_adap); 2820 - 2821 - return ret; 2836 + return drmm_connector_hdmi_cec_register(&vc4_hdmi->connector, 2837 + &vc4_hdmi_cec_funcs, 2838 + vc4_hdmi->variant->card_name, 2839 + 1, 2840 + &pdev->dev); 2822 2841 } 2823 2842 #else 2824 - static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) 2843 + static int vc4_hdmi_cec_register(struct vc4_hdmi *vc4_hdmi) 2825 2844 { 2826 2845 return 0; 2827 2846 } ··· 3221 3250 if (ret) 3222 3251 goto err_put_runtime_pm; 3223 3252 3224 - ret = vc4_hdmi_cec_init(vc4_hdmi); 3253 + ret = vc4_hdmi_cec_register(vc4_hdmi); 3225 3254 if (ret) 3226 3255 goto err_put_runtime_pm; 3227 3256
-1
drivers/gpu/drm/vc4/vc4_hdmi.h
··· 147 147 */ 148 148 bool disable_wifi_frequencies; 149 149 150 - struct cec_adapter *cec_adap; 151 150 struct cec_msg cec_rx_msg; 152 151 bool cec_tx_ok; 153 152 bool cec_irq_was_rx;