Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/display/drm_hdmi_cec_helper.c
26494 views
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
195