xref: /linux/drivers/gpu/drm/bridge/aux-bridge.c (revision 0f657938e4345a77be871d906f3e0de3c58a7a49)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2023 Linaro Ltd.
4  *
5  * Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
6  */
7 #include <linux/auxiliary_bus.h>
8 #include <linux/module.h>
9 
10 #include <drm/drm_bridge.h>
11 #include <drm/bridge/aux-bridge.h>
12 
13 static DEFINE_IDA(drm_aux_bridge_ida);
14 
15 static void drm_aux_bridge_release(struct device *dev)
16 {
17 	struct auxiliary_device *adev = to_auxiliary_dev(dev);
18 
19 	ida_free(&drm_aux_bridge_ida, adev->id);
20 
21 	kfree(adev);
22 }
23 
24 static void drm_aux_bridge_unregister_adev(void *_adev)
25 {
26 	struct auxiliary_device *adev = _adev;
27 
28 	auxiliary_device_delete(adev);
29 	auxiliary_device_uninit(adev);
30 }
31 
32 /**
33  * drm_aux_bridge_register - Create a simple bridge device to link the chain
34  * @parent: device instance providing this bridge
35  *
36  * Creates a simple DRM bridge that doesn't implement any drm_bridge
37  * operations. Such bridges merely fill a place in the bridge chain linking
38  * surrounding DRM bridges.
39  *
40  * Return: zero on success, negative error code on failure
41  */
42 int drm_aux_bridge_register(struct device *parent)
43 {
44 	struct auxiliary_device *adev;
45 	int ret;
46 
47 	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
48 	if (!adev)
49 		return -ENOMEM;
50 
51 	ret = ida_alloc(&drm_aux_bridge_ida, GFP_KERNEL);
52 	if (ret < 0) {
53 		kfree(adev);
54 		return ret;
55 	}
56 
57 	adev->id = ret;
58 	adev->name = "aux_bridge";
59 	adev->dev.parent = parent;
60 	adev->dev.of_node = parent->of_node;
61 	adev->dev.release = drm_aux_bridge_release;
62 
63 	ret = auxiliary_device_init(adev);
64 	if (ret) {
65 		ida_free(&drm_aux_bridge_ida, adev->id);
66 		kfree(adev);
67 		return ret;
68 	}
69 
70 	ret = auxiliary_device_add(adev);
71 	if (ret) {
72 		auxiliary_device_uninit(adev);
73 		return ret;
74 	}
75 
76 	return devm_add_action_or_reset(parent, drm_aux_bridge_unregister_adev, adev);
77 }
78 EXPORT_SYMBOL_GPL(drm_aux_bridge_register);
79 
80 struct drm_aux_bridge_data {
81 	struct drm_bridge bridge;
82 	struct drm_bridge *next_bridge;
83 	struct device *dev;
84 };
85 
86 static int drm_aux_bridge_attach(struct drm_bridge *bridge,
87 				 enum drm_bridge_attach_flags flags)
88 {
89 	struct drm_aux_bridge_data *data;
90 
91 	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
92 		return -EINVAL;
93 
94 	data = container_of(bridge, struct drm_aux_bridge_data, bridge);
95 
96 	return drm_bridge_attach(bridge->encoder, data->next_bridge, bridge,
97 				 DRM_BRIDGE_ATTACH_NO_CONNECTOR);
98 }
99 
100 static const struct drm_bridge_funcs drm_aux_bridge_funcs = {
101 	.attach	= drm_aux_bridge_attach,
102 };
103 
104 static int drm_aux_bridge_probe(struct auxiliary_device *auxdev,
105 				const struct auxiliary_device_id *id)
106 {
107 	struct drm_aux_bridge_data *data;
108 
109 	data = devm_kzalloc(&auxdev->dev, sizeof(*data), GFP_KERNEL);
110 	if (!data)
111 		return -ENOMEM;
112 
113 	data->dev = &auxdev->dev;
114 	data->next_bridge = devm_drm_of_get_bridge(&auxdev->dev, auxdev->dev.of_node, 0, 0);
115 	if (IS_ERR(data->next_bridge))
116 		return dev_err_probe(&auxdev->dev, PTR_ERR(data->next_bridge),
117 				     "failed to acquire drm_bridge\n");
118 
119 	data->bridge.funcs = &drm_aux_bridge_funcs;
120 	data->bridge.of_node = data->dev->of_node;
121 
122 	return devm_drm_bridge_add(data->dev, &data->bridge);
123 }
124 
125 static const struct auxiliary_device_id drm_aux_bridge_table[] = {
126 	{ .name = KBUILD_MODNAME ".aux_bridge" },
127 	{},
128 };
129 MODULE_DEVICE_TABLE(auxiliary, drm_aux_bridge_table);
130 
131 static struct auxiliary_driver drm_aux_bridge_drv = {
132 	.name = "aux_bridge",
133 	.id_table = drm_aux_bridge_table,
134 	.probe = drm_aux_bridge_probe,
135 };
136 module_auxiliary_driver(drm_aux_bridge_drv);
137 
138 MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
139 MODULE_DESCRIPTION("DRM transparent bridge");
140 MODULE_LICENSE("GPL");
141