Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/bridge/aux-hpd-bridge.c
26494 views
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
3
* Copyright (C) 2023 Linaro Ltd.
4
*
5
* Author: Dmitry Baryshkov <[email protected]>
6
*/
7
#include <linux/auxiliary_bus.h>
8
#include <linux/export.h>
9
#include <linux/module.h>
10
#include <linux/of.h>
11
12
#include <drm/drm_bridge.h>
13
#include <drm/bridge/aux-bridge.h>
14
15
static DEFINE_IDA(drm_aux_hpd_bridge_ida);
16
17
struct drm_aux_hpd_bridge_data {
18
struct drm_bridge bridge;
19
struct device *dev;
20
};
21
22
static void drm_aux_hpd_bridge_release(struct device *dev)
23
{
24
struct auxiliary_device *adev = to_auxiliary_dev(dev);
25
26
ida_free(&drm_aux_hpd_bridge_ida, adev->id);
27
28
of_node_put(adev->dev.platform_data);
29
of_node_put(adev->dev.of_node);
30
31
kfree(adev);
32
}
33
34
static void drm_aux_hpd_bridge_free_adev(void *_adev)
35
{
36
auxiliary_device_uninit(_adev);
37
}
38
39
/**
40
* devm_drm_dp_hpd_bridge_alloc - allocate a HPD DisplayPort bridge
41
* @parent: device instance providing this bridge
42
* @np: device node pointer corresponding to this bridge instance
43
*
44
* Creates a simple DRM bridge with the type set to
45
* DRM_MODE_CONNECTOR_DisplayPort, which terminates the bridge chain and is
46
* able to send the HPD events.
47
*
48
* Return: bridge auxiliary device pointer or an error pointer
49
*/
50
struct auxiliary_device *devm_drm_dp_hpd_bridge_alloc(struct device *parent, struct device_node *np)
51
{
52
struct auxiliary_device *adev;
53
int ret;
54
55
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
56
if (!adev)
57
return ERR_PTR(-ENOMEM);
58
59
ret = ida_alloc(&drm_aux_hpd_bridge_ida, GFP_KERNEL);
60
if (ret < 0) {
61
kfree(adev);
62
return ERR_PTR(ret);
63
}
64
65
adev->id = ret;
66
adev->name = "dp_hpd_bridge";
67
adev->dev.parent = parent;
68
adev->dev.release = drm_aux_hpd_bridge_release;
69
adev->dev.platform_data = of_node_get(np);
70
71
device_set_of_node_from_dev(&adev->dev, parent);
72
73
ret = auxiliary_device_init(adev);
74
if (ret) {
75
of_node_put(adev->dev.platform_data);
76
of_node_put(adev->dev.of_node);
77
ida_free(&drm_aux_hpd_bridge_ida, adev->id);
78
kfree(adev);
79
return ERR_PTR(ret);
80
}
81
82
ret = devm_add_action_or_reset(parent, drm_aux_hpd_bridge_free_adev, adev);
83
if (ret)
84
return ERR_PTR(ret);
85
86
return adev;
87
}
88
EXPORT_SYMBOL_GPL(devm_drm_dp_hpd_bridge_alloc);
89
90
static void drm_aux_hpd_bridge_del_adev(void *_adev)
91
{
92
auxiliary_device_delete(_adev);
93
}
94
95
/**
96
* devm_drm_dp_hpd_bridge_add - register a HDP DisplayPort bridge
97
* @dev: struct device to tie registration lifetime to
98
* @adev: bridge auxiliary device to be registered
99
*
100
* Returns: zero on success or a negative errno
101
*/
102
int devm_drm_dp_hpd_bridge_add(struct device *dev, struct auxiliary_device *adev)
103
{
104
int ret;
105
106
ret = auxiliary_device_add(adev);
107
if (ret)
108
return ret;
109
110
return devm_add_action_or_reset(dev, drm_aux_hpd_bridge_del_adev, adev);
111
}
112
EXPORT_SYMBOL_GPL(devm_drm_dp_hpd_bridge_add);
113
114
/**
115
* drm_dp_hpd_bridge_register - allocate and register a HDP DisplayPort bridge
116
* @parent: device instance providing this bridge
117
* @np: device node pointer corresponding to this bridge instance
118
*
119
* Return: device instance that will handle created bridge or an error pointer
120
*/
121
struct device *drm_dp_hpd_bridge_register(struct device *parent, struct device_node *np)
122
{
123
struct auxiliary_device *adev;
124
int ret;
125
126
adev = devm_drm_dp_hpd_bridge_alloc(parent, np);
127
if (IS_ERR(adev))
128
return ERR_CAST(adev);
129
130
ret = devm_drm_dp_hpd_bridge_add(parent, adev);
131
if (ret)
132
return ERR_PTR(ret);
133
134
return &adev->dev;
135
}
136
EXPORT_SYMBOL_GPL(drm_dp_hpd_bridge_register);
137
138
/**
139
* drm_aux_hpd_bridge_notify - notify hot plug detection events
140
* @dev: device created for the HPD bridge
141
* @status: output connection status
142
*
143
* A wrapper around drm_bridge_hpd_notify() that is used to report hot plug
144
* detection events for bridges created via drm_dp_hpd_bridge_register().
145
*
146
* This function shall be called in a context that can sleep.
147
*/
148
void drm_aux_hpd_bridge_notify(struct device *dev, enum drm_connector_status status)
149
{
150
struct auxiliary_device *adev = to_auxiliary_dev(dev);
151
struct drm_aux_hpd_bridge_data *data = auxiliary_get_drvdata(adev);
152
153
if (!data)
154
return;
155
156
drm_bridge_hpd_notify(&data->bridge, status);
157
}
158
EXPORT_SYMBOL_GPL(drm_aux_hpd_bridge_notify);
159
160
static int drm_aux_hpd_bridge_attach(struct drm_bridge *bridge,
161
struct drm_encoder *encoder,
162
enum drm_bridge_attach_flags flags)
163
{
164
return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
165
}
166
167
static const struct drm_bridge_funcs drm_aux_hpd_bridge_funcs = {
168
.attach = drm_aux_hpd_bridge_attach,
169
};
170
171
static int drm_aux_hpd_bridge_probe(struct auxiliary_device *auxdev,
172
const struct auxiliary_device_id *id)
173
{
174
struct drm_aux_hpd_bridge_data *data;
175
176
data = devm_drm_bridge_alloc(&auxdev->dev,
177
struct drm_aux_hpd_bridge_data, bridge,
178
&drm_aux_hpd_bridge_funcs);
179
if (IS_ERR(data))
180
return PTR_ERR(data);
181
182
data->dev = &auxdev->dev;
183
data->bridge.of_node = dev_get_platdata(data->dev);
184
data->bridge.ops = DRM_BRIDGE_OP_HPD;
185
data->bridge.type = id->driver_data;
186
187
/* passthrough data, allow everything */
188
data->bridge.interlace_allowed = true;
189
data->bridge.ycbcr_420_allowed = true;
190
191
auxiliary_set_drvdata(auxdev, data);
192
193
return devm_drm_bridge_add(data->dev, &data->bridge);
194
}
195
196
static const struct auxiliary_device_id drm_aux_hpd_bridge_table[] = {
197
{ .name = KBUILD_MODNAME ".dp_hpd_bridge", .driver_data = DRM_MODE_CONNECTOR_DisplayPort, },
198
{},
199
};
200
MODULE_DEVICE_TABLE(auxiliary, drm_aux_hpd_bridge_table);
201
202
static struct auxiliary_driver drm_aux_hpd_bridge_drv = {
203
.name = "aux_hpd_bridge",
204
.id_table = drm_aux_hpd_bridge_table,
205
.probe = drm_aux_hpd_bridge_probe,
206
};
207
module_auxiliary_driver(drm_aux_hpd_bridge_drv);
208
209
MODULE_AUTHOR("Dmitry Baryshkov <[email protected]>");
210
MODULE_DESCRIPTION("DRM HPD bridge");
211
MODULE_LICENSE("GPL");
212
213