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

Configure Feed

Select the types of activity you want to include in your feed.

at master 193 lines 5.5 kB view raw
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 16struct drm_connector_hdmi_cec_data { 17 struct cec_adapter *adapter; 18 const struct drm_connector_hdmi_cec_funcs *funcs; 19}; 20 21static 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 29static 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 37static 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 46static 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 52static 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 59static 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 67static 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 81static 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 86int 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_obj(*data); 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 151err_delete_adapter: 152 cec_delete_adapter(cec_adap); 153 154 connector->cec.data = NULL; 155 156 mutex_unlock(&connector->cec.mutex); 157 158err_free: 159 kfree(data); 160 161 return ret; 162} 163EXPORT_SYMBOL(drmm_connector_hdmi_cec_register); 164 165void 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} 172EXPORT_SYMBOL(drm_connector_hdmi_cec_received_msg); 173 174void 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} 181EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_attempt_done); 182 183void 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} 193EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_done);