Skip to content

Commit 7594874

Browse files
kunliu13alexdeucher
authored andcommitted
drm/amd/display: add CEC notifier to amdgpu driver
This patch adds the cec_notifier feature to amdgpu driver. The changes will allow amdgpu driver code to notify EDID and HPD changes to an eventual CEC adapter. Signed-off-by: Kun Liu <[email protected]> Reviewed-by: Mario Limonciello <[email protected]> Signed-off-by: Alex Deucher <[email protected]>
1 parent 85b7341 commit 7594874

File tree

5 files changed

+154
-1
lines changed

5 files changed

+154
-1
lines changed

drivers/gpu/drm/amd/display/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ config DRM_AMD_DC
88
bool "AMD DC - Enable new display engine"
99
default y
1010
depends on BROKEN || !CC_IS_CLANG || ARM64 || LOONGARCH || RISCV || SPARC64 || X86_64
11+
select CEC_CORE
12+
select CEC_NOTIFIER
1113
select SND_HDA_COMPONENT if SND_HDA_CORE
1214
# !CC_IS_CLANG: https://github.com/ClangBuiltLinux/linux/issues/1752
1315
select DRM_AMD_DC_FP if ARCH_HAS_KERNEL_FPU_SUPPORT && !(CC_IS_CLANG && (ARM64 || LOONGARCH || RISCV))

drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
#include <drm/drm_audio_component.h>
9898
#include <drm/drm_gem_atomic_helper.h>
9999

100+
#include <media/cec-notifier.h>
100101
#include <acpi/video.h>
101102

102103
#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h"
@@ -2751,6 +2752,48 @@ static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
27512752
mutex_unlock(&mgr->lock);
27522753
}
27532754

2755+
void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector)
2756+
{
2757+
struct cec_notifier *n = aconnector->notifier;
2758+
2759+
if (!n)
2760+
return;
2761+
2762+
cec_notifier_phys_addr_invalidate(n);
2763+
}
2764+
2765+
void hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector)
2766+
{
2767+
struct drm_connector *connector = &aconnector->base;
2768+
struct cec_notifier *n = aconnector->notifier;
2769+
2770+
if (!n)
2771+
return;
2772+
2773+
cec_notifier_set_phys_addr(n,
2774+
connector->display_info.source_physical_address);
2775+
}
2776+
2777+
static void s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend)
2778+
{
2779+
struct amdgpu_dm_connector *aconnector;
2780+
struct drm_connector *connector;
2781+
struct drm_connector_list_iter conn_iter;
2782+
2783+
drm_connector_list_iter_begin(ddev, &conn_iter);
2784+
drm_for_each_connector_iter(connector, &conn_iter) {
2785+
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
2786+
continue;
2787+
2788+
aconnector = to_amdgpu_dm_connector(connector);
2789+
if (suspend)
2790+
hdmi_cec_unset_edid(aconnector);
2791+
else
2792+
hdmi_cec_set_edid(aconnector);
2793+
}
2794+
drm_connector_list_iter_end(&conn_iter);
2795+
}
2796+
27542797
static void s3_handle_mst(struct drm_device *dev, bool suspend)
27552798
{
27562799
struct amdgpu_dm_connector *aconnector;
@@ -3022,6 +3065,8 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
30223065
if (IS_ERR(adev->dm.cached_state))
30233066
return PTR_ERR(adev->dm.cached_state);
30243067

3068+
s3_handle_hdmi_cec(adev_to_drm(adev), true);
3069+
30253070
s3_handle_mst(adev_to_drm(adev), true);
30263071

30273072
amdgpu_dm_irq_suspend(adev);
@@ -3294,6 +3339,8 @@ static int dm_resume(struct amdgpu_ip_block *ip_block)
32943339
*/
32953340
amdgpu_dm_irq_resume_early(adev);
32963341

3342+
s3_handle_hdmi_cec(ddev, false);
3343+
32973344
/* On resume we need to rewrite the MSTM control bits to enable MST*/
32983345
s3_handle_mst(ddev, false);
32993346

@@ -3603,6 +3650,7 @@ void amdgpu_dm_update_connector_after_detect(
36033650
dc_sink_retain(aconnector->dc_sink);
36043651
if (sink->dc_edid.length == 0) {
36053652
aconnector->drm_edid = NULL;
3653+
hdmi_cec_unset_edid(aconnector);
36063654
if (aconnector->dc_link->aux_mode) {
36073655
drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
36083656
}
@@ -3612,6 +3660,7 @@ void amdgpu_dm_update_connector_after_detect(
36123660
aconnector->drm_edid = drm_edid_alloc(edid, sink->dc_edid.length);
36133661
drm_edid_connector_update(connector, aconnector->drm_edid);
36143662

3663+
hdmi_cec_set_edid(aconnector);
36153664
if (aconnector->dc_link->aux_mode)
36163665
drm_dp_cec_attach(&aconnector->dm_dp_aux.aux,
36173666
connector->display_info.source_physical_address);
@@ -3628,6 +3677,7 @@ void amdgpu_dm_update_connector_after_detect(
36283677
amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid);
36293678
update_connector_ext_caps(aconnector);
36303679
} else {
3680+
hdmi_cec_unset_edid(aconnector);
36313681
drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
36323682
amdgpu_dm_update_freesync_caps(connector, NULL);
36333683
aconnector->num_modes = 0;
@@ -7044,6 +7094,7 @@ static void amdgpu_dm_connector_unregister(struct drm_connector *connector)
70447094
if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector))
70457095
sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
70467096

7097+
cec_notifier_conn_unregister(amdgpu_dm_connector->notifier);
70477098
drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux);
70487099
}
70497100

@@ -8280,6 +8331,27 @@ create_i2c(struct ddc_service *ddc_service,
82808331
return i2c;
82818332
}
82828333

8334+
int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector)
8335+
{
8336+
struct cec_connector_info conn_info;
8337+
struct drm_device *ddev = aconnector->base.dev;
8338+
struct device *hdmi_dev = ddev->dev;
8339+
8340+
if (amdgpu_dc_debug_mask & DC_DISABLE_HDMI_CEC) {
8341+
drm_info(ddev, "HDMI-CEC feature masked\n");
8342+
return -EINVAL;
8343+
}
8344+
8345+
cec_fill_conn_info_from_drm(&conn_info, &aconnector->base);
8346+
aconnector->notifier =
8347+
cec_notifier_conn_register(hdmi_dev, NULL, &conn_info);
8348+
if (!aconnector->notifier) {
8349+
drm_err(ddev, "Failed to create cec notifier\n");
8350+
return -ENOMEM;
8351+
}
8352+
8353+
return 0;
8354+
}
82838355

82848356
/*
82858357
* Note: this function assumes that dc_link_detect() was called for the
@@ -8343,6 +8415,10 @@ static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
83438415
drm_connector_attach_encoder(
83448416
&aconnector->base, &aencoder->base);
83458417

8418+
if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
8419+
connector_type == DRM_MODE_CONNECTOR_HDMIB)
8420+
amdgpu_dm_initialize_hdmi_connector(aconnector);
8421+
83468422
if (connector_type == DRM_MODE_CONNECTOR_DisplayPort
83478423
|| connector_type == DRM_MODE_CONNECTOR_eDP)
83488424
amdgpu_dm_initialize_dp_connector(dm, aconnector, link->link_index);

drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,8 @@ struct amdgpu_dm_connector {
671671
uint32_t connector_id;
672672
int bl_idx;
673673

674+
struct cec_notifier *notifier;
675+
674676
/* we need to mind the EDID between detect
675677
and get modes due to analog/digital/tvencoder */
676678
const struct drm_edid *drm_edid;
@@ -1010,4 +1012,8 @@ void dm_free_gpu_mem(struct amdgpu_device *adev,
10101012

10111013
bool amdgpu_dm_is_headless(struct amdgpu_device *adev);
10121014

1015+
void hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector);
1016+
void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector);
1017+
int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector);
1018+
10131019
#endif /* __AMDGPU_DM_H__ */

drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <linux/string_helpers.h>
2727
#include <linux/uaccess.h>
28+
#include <media/cec-notifier.h>
2829

2930
#include "dc.h"
3031
#include "amdgpu.h"
@@ -2848,6 +2849,67 @@ static int is_dpia_link_show(struct seq_file *m, void *data)
28482849
return 0;
28492850
}
28502851

2852+
/**
2853+
* hdmi_cec_state_show - Read out the HDMI-CEC feature status
2854+
* @m: sequence file.
2855+
* @data: unused.
2856+
*
2857+
* Return 0 on success
2858+
*/
2859+
static int hdmi_cec_state_show(struct seq_file *m, void *data)
2860+
{
2861+
struct drm_connector *connector = m->private;
2862+
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
2863+
2864+
seq_printf(m, "%s:%d\n", connector->name, connector->base.id);
2865+
seq_printf(m, "HDMI-CEC status: %d\n", aconnector->notifier ? 1 : 0);
2866+
2867+
return 0;
2868+
}
2869+
2870+
/**
2871+
* hdmi_cec_state_write - Enable/Disable HDMI-CEC feature from driver side
2872+
* @f: file structure.
2873+
* @buf: userspace buffer. set to '1' to enable; '0' to disable cec feature.
2874+
* @size: size of buffer from userpsace.
2875+
* @pos: unused.
2876+
*
2877+
* Return size on success, error code on failure
2878+
*/
2879+
static ssize_t hdmi_cec_state_write(struct file *f, const char __user *buf,
2880+
size_t size, loff_t *pos)
2881+
{
2882+
int ret;
2883+
bool enable;
2884+
struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2885+
struct drm_device *ddev = aconnector->base.dev;
2886+
2887+
if (size == 0)
2888+
return -EINVAL;
2889+
2890+
ret = kstrtobool_from_user(buf, size, &enable);
2891+
if (ret) {
2892+
drm_dbg_driver(ddev, "invalid user data !\n");
2893+
return ret;
2894+
}
2895+
2896+
if (enable) {
2897+
if (aconnector->notifier)
2898+
return -EINVAL;
2899+
ret = amdgpu_dm_initialize_hdmi_connector(aconnector);
2900+
if (ret)
2901+
return ret;
2902+
hdmi_cec_set_edid(aconnector);
2903+
} else {
2904+
if (!aconnector->notifier)
2905+
return -EINVAL;
2906+
cec_notifier_conn_unregister(aconnector->notifier);
2907+
aconnector->notifier = NULL;
2908+
}
2909+
2910+
return size;
2911+
}
2912+
28512913
DEFINE_SHOW_ATTRIBUTE(dp_dsc_fec_support);
28522914
DEFINE_SHOW_ATTRIBUTE(dmub_fw_state);
28532915
DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer);
@@ -2860,6 +2922,7 @@ DEFINE_SHOW_ATTRIBUTE(psr_capability);
28602922
DEFINE_SHOW_ATTRIBUTE(dp_is_mst_connector);
28612923
DEFINE_SHOW_ATTRIBUTE(dp_mst_progress_status);
28622924
DEFINE_SHOW_ATTRIBUTE(is_dpia_link);
2925+
DEFINE_SHOW_STORE_ATTRIBUTE(hdmi_cec_state);
28632926

28642927
static const struct file_operations dp_dsc_clock_en_debugfs_fops = {
28652928
.owner = THIS_MODULE,
@@ -2995,7 +3058,8 @@ static const struct {
29953058
char *name;
29963059
const struct file_operations *fops;
29973060
} hdmi_debugfs_entries[] = {
2998-
{"hdcp_sink_capability", &hdcp_sink_capability_fops}
3061+
{"hdcp_sink_capability", &hdcp_sink_capability_fops},
3062+
{"hdmi_cec_state", &hdmi_cec_state_fops}
29993063
};
30003064

30013065
/*

drivers/gpu/drm/amd/include/amd_shared.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@ enum DC_DEBUG_MASK {
344344
* eDP display from ACPI _DDC method.
345345
*/
346346
DC_DISABLE_ACPI_EDID = 0x8000,
347+
348+
/*
349+
* @DC_DISABLE_HDMI_CEC: If set, disable HDMI-CEC feature in amdgpu driver.
350+
*/
351+
DC_DISABLE_HDMI_CEC = 0x10000,
347352
};
348353

349354
enum amd_dpm_forced_level;

0 commit comments

Comments
 (0)