1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright (c) 2024 Linaro Ltd 4 */ 5 6 #include <drm/drm_bridge.h> 7 #include <drm/drm_connector.h> 8 #include <drm/drm_managed.h> 9 #include <drm/display/drm_hdmi_cec_helper.h> 10 11 #include <linux/export.h> 12 #include <linux/mutex.h> 13 14 #include <media/cec.h> 15 16 struct drm_connector_hdmi_cec_data { 17 struct cec_adapter *adapter; 18 const struct drm_connector_hdmi_cec_funcs *funcs; 19 }; 20 21 static int drm_connector_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) 22 { 23 struct drm_connector *connector = cec_get_drvdata(adap); 24 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 25 26 return data->funcs->enable(connector, enable); 27 } 28 29 static int drm_connector_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) 30 { 31 struct drm_connector *connector = cec_get_drvdata(adap); 32 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 33 34 return data->funcs->log_addr(connector, logical_addr); 35 } 36 37 static int drm_connector_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, 38 u32 signal_free_time, struct cec_msg *msg) 39 { 40 struct drm_connector *connector = cec_get_drvdata(adap); 41 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 42 43 return data->funcs->transmit(connector, attempts, signal_free_time, msg); 44 } 45 46 static const struct cec_adap_ops drm_connector_hdmi_cec_adap_ops = { 47 .adap_enable = drm_connector_hdmi_cec_adap_enable, 48 .adap_log_addr = drm_connector_hdmi_cec_adap_log_addr, 49 .adap_transmit = drm_connector_hdmi_cec_adap_transmit, 50 }; 51 52 static void drm_connector_hdmi_cec_adapter_phys_addr_invalidate(struct drm_connector *connector) 53 { 54 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 55 56 cec_phys_addr_invalidate(data->adapter); 57 } 58 59 static void drm_connector_hdmi_cec_adapter_phys_addr_set(struct drm_connector *connector, 60 u16 addr) 61 { 62 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 63 64 cec_s_phys_addr(data->adapter, addr, false); 65 } 66 67 static void drm_connector_hdmi_cec_adapter_unregister(struct drm_device *dev, void *res) 68 { 69 struct drm_connector *connector = res; 70 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 71 72 cec_unregister_adapter(data->adapter); 73 74 if (data->funcs->uninit) 75 data->funcs->uninit(connector); 76 77 kfree(data); 78 connector->cec.data = NULL; 79 } 80 81 static struct drm_connector_cec_funcs drm_connector_hdmi_cec_adapter_funcs = { 82 .phys_addr_invalidate = drm_connector_hdmi_cec_adapter_phys_addr_invalidate, 83 .phys_addr_set = drm_connector_hdmi_cec_adapter_phys_addr_set, 84 }; 85 86 int drmm_connector_hdmi_cec_register(struct drm_connector *connector, 87 const struct drm_connector_hdmi_cec_funcs *funcs, 88 const char *name, 89 u8 available_las, 90 struct device *dev) 91 { 92 struct drm_connector_hdmi_cec_data *data; 93 struct cec_connector_info conn_info; 94 struct cec_adapter *cec_adap; 95 int ret; 96 97 if (!funcs->init || !funcs->enable || !funcs->log_addr || !funcs->transmit) 98 return -EINVAL; 99 100 data = kzalloc(sizeof(*data), GFP_KERNEL); 101 if (!data) 102 return -ENOMEM; 103 104 data->funcs = funcs; 105 106 cec_adap = cec_allocate_adapter(&drm_connector_hdmi_cec_adap_ops, connector, name, 107 CEC_CAP_DEFAULTS | CEC_CAP_CONNECTOR_INFO, 108 available_las ? : CEC_MAX_LOG_ADDRS); 109 ret = PTR_ERR_OR_ZERO(cec_adap); 110 if (ret < 0) 111 goto err_free; 112 113 cec_fill_conn_info_from_drm(&conn_info, connector); 114 cec_s_conn_info(cec_adap, &conn_info); 115 116 data->adapter = cec_adap; 117 118 mutex_lock(&connector->cec.mutex); 119 120 connector->cec.data = data; 121 connector->cec.funcs = &drm_connector_hdmi_cec_adapter_funcs; 122 123 ret = funcs->init(connector); 124 if (ret < 0) 125 goto err_delete_adapter; 126 127 /* 128 * NOTE: the CEC adapter will be unregistered by drmm cleanup from 129 * drm_managed_release(), which is called from drm_dev_release() 130 * during device unbind. 131 * 132 * However, the CEC framework cleans up the CEC adapter only when the 133 * last user has closed its file descriptor, so we don't need to handle 134 * it in DRM. 135 * 136 * Before that CEC framework makes sure that even if the userspace 137 * still holds CEC device open, all calls will be shortcut via 138 * cec_is_registered(), making sure that there is no access to the 139 * freed memory. 140 */ 141 ret = cec_register_adapter(cec_adap, dev); 142 if (ret < 0) 143 goto err_delete_adapter; 144 145 mutex_unlock(&connector->cec.mutex); 146 147 return drmm_add_action_or_reset(connector->dev, 148 drm_connector_hdmi_cec_adapter_unregister, 149 connector); 150 151 err_delete_adapter: 152 cec_delete_adapter(cec_adap); 153 154 connector->cec.data = NULL; 155 156 mutex_unlock(&connector->cec.mutex); 157 158 err_free: 159 kfree(data); 160 161 return ret; 162 } 163 EXPORT_SYMBOL(drmm_connector_hdmi_cec_register); 164 165 void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector, 166 struct cec_msg *msg) 167 { 168 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 169 170 cec_received_msg(data->adapter, msg); 171 } 172 EXPORT_SYMBOL(drm_connector_hdmi_cec_received_msg); 173 174 void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *connector, 175 u8 status) 176 { 177 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 178 179 cec_transmit_attempt_done(data->adapter, status); 180 } 181 EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_attempt_done); 182 183 void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector, 184 u8 status, 185 u8 arb_lost_cnt, u8 nack_cnt, 186 u8 low_drive_cnt, u8 error_cnt) 187 { 188 struct drm_connector_hdmi_cec_data *data = connector->cec.data; 189 190 cec_transmit_done(data->adapter, status, 191 arb_lost_cnt, nack_cnt, low_drive_cnt, error_cnt); 192 } 193 EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_done); 194