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