File Index
Files and directories
Sign in
FILE

drm_hdmi_cec_helper.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/display/drm_hdmi_cec_helper.c C
  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(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
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);
~/ Read-only file preview.