xref: /linux/drivers/platform/x86/amd/amd_isp4.c (revision 25489a4f556414445d342951615178368ee45cde)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * AMD ISP platform driver for sensor i2-client instantiation
4  *
5  * Copyright 2025 Advanced Micro Devices, Inc.
6  */
7 
8 #include <linux/err.h>
9 #include <linux/i2c.h>
10 #include <linux/module.h>
11 #include <linux/mutex.h>
12 #include <linux/platform_device.h>
13 #include <linux/property.h>
14 #include <linux/soc/amd/isp4_misc.h>
15 #include <linux/string.h>
16 #include <linux/types.h>
17 #include <linux/units.h>
18 
19 #define AMDISP_OV05C10_I2C_ADDR		0x10
20 #define AMDISP_OV05C10_HID		"OMNI5C10"
21 #define AMDISP_OV05C10_REMOTE_EP_NAME	"ov05c10_isp_4_1_1"
22 #define AMD_ISP_PLAT_DRV_NAME		"amd-isp4"
23 
24 /*
25  * AMD ISP platform info definition to initialize sensor
26  * specific platform configuration to prepare the amdisp
27  * platform.
28  */
29 struct amdisp_platform_info {
30 	struct i2c_board_info board_info;
31 	const struct software_node **swnodes;
32 };
33 
34 /*
35  * AMD ISP platform definition to configure the device properties
36  * missing in the ACPI table.
37  */
38 struct amdisp_platform {
39 	const struct amdisp_platform_info *pinfo;
40 	struct i2c_board_info board_info;
41 	struct notifier_block i2c_nb;
42 	struct i2c_client *i2c_dev;
43 	struct mutex lock;	/* protects i2c client creation */
44 };
45 
46 /* Top-level OV05C10 camera node property table */
47 static const struct property_entry ov05c10_camera_props[] = {
48 	PROPERTY_ENTRY_U32("clock-frequency", 24 * HZ_PER_MHZ),
49 	{ }
50 };
51 
52 /* Root AMD ISP OV05C10 camera node definition */
53 static const struct software_node camera_node = {
54 	.name = AMDISP_OV05C10_HID,
55 	.properties = ov05c10_camera_props,
56 };
57 
58 /*
59  * AMD ISP OV05C10 Ports node definition. No properties defined for
60  * ports node for OV05C10.
61  */
62 static const struct software_node ports = {
63 	.name = "ports",
64 	.parent = &camera_node,
65 };
66 
67 /*
68  * AMD ISP OV05C10 Port node definition. No properties defined for
69  * port node for OV05C10.
70  */
71 static const struct software_node port_node = {
72 	.name = "port@",
73 	.parent = &ports,
74 };
75 
76 /*
77  * Remote endpoint AMD ISP node definition. No properties defined for
78  * remote endpoint node for OV05C10.
79  */
80 static const struct software_node remote_ep_isp_node = {
81 	.name = AMDISP_OV05C10_REMOTE_EP_NAME,
82 };
83 
84 /*
85  * Remote endpoint reference for isp node included in the
86  * OV05C10 endpoint.
87  */
88 static const struct software_node_ref_args ov05c10_refs[] = {
89 	SOFTWARE_NODE_REFERENCE(&remote_ep_isp_node),
90 };
91 
92 /* OV05C10 supports one single link frequency */
93 static const u64 ov05c10_link_freqs[] = {
94 	925 * HZ_PER_MHZ,
95 };
96 
97 /* OV05C10 supports only 2-lane configuration */
98 static const u32 ov05c10_data_lanes[] = {
99 	1,
100 	2,
101 };
102 
103 /* OV05C10 endpoint node properties table */
104 static const struct property_entry ov05c10_endpoint_props[] = {
105 	PROPERTY_ENTRY_U32("bus-type", 4),
106 	PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes", ov05c10_data_lanes,
107 				     ARRAY_SIZE(ov05c10_data_lanes)),
108 	PROPERTY_ENTRY_U64_ARRAY_LEN("link-frequencies", ov05c10_link_freqs,
109 				     ARRAY_SIZE(ov05c10_link_freqs)),
110 	PROPERTY_ENTRY_REF_ARRAY("remote-endpoint", ov05c10_refs),
111 	{ }
112 };
113 
114 /* AMD ISP endpoint node definition */
115 static const struct software_node endpoint_node = {
116 	.name = "endpoint",
117 	.parent = &port_node,
118 	.properties = ov05c10_endpoint_props,
119 };
120 
121 /*
122  * AMD ISP swnode graph uses 5 nodes and also its relationship is
123  * fixed to align with the structure that v4l2 expects for successful
124  * endpoint fwnode parsing.
125  *
126  * It is only the node property_entries that will vary for each platform
127  * supporting different sensor modules.
128  */
129 static const struct software_node *ov05c10_nodes[] = {
130 	&camera_node,
131 	&ports,
132 	&port_node,
133 	&endpoint_node,
134 	&remote_ep_isp_node,
135 	NULL
136 };
137 
138 /* OV05C10 specific AMD ISP platform configuration */
139 static const struct amdisp_platform_info ov05c10_platform_config = {
140 	.board_info = {
141 		.dev_name = "ov05c10",
142 		I2C_BOARD_INFO("ov05c10", AMDISP_OV05C10_I2C_ADDR),
143 	},
144 	.swnodes = ov05c10_nodes,
145 };
146 
147 static const struct acpi_device_id amdisp_sensor_ids[] = {
148 	{ AMDISP_OV05C10_HID, (kernel_ulong_t)&ov05c10_platform_config },
149 	{ }
150 };
151 MODULE_DEVICE_TABLE(acpi, amdisp_sensor_ids);
152 
153 static inline bool is_isp_i2c_adapter(struct i2c_adapter *adap)
154 {
155 	return !strcmp(adap->name, AMDISP_I2C_ADAP_NAME);
156 }
157 
158 static void instantiate_isp_i2c_client(struct amdisp_platform *isp4_platform,
159 				       struct i2c_adapter *adap)
160 {
161 	struct i2c_board_info *info = &isp4_platform->board_info;
162 	struct i2c_client *i2c_dev;
163 
164 	guard(mutex)(&isp4_platform->lock);
165 
166 	if (isp4_platform->i2c_dev)
167 		return;
168 
169 	i2c_dev = i2c_new_client_device(adap, info);
170 	if (IS_ERR(i2c_dev)) {
171 		dev_err(&adap->dev, "error %pe registering isp i2c_client\n", i2c_dev);
172 		return;
173 	}
174 	isp4_platform->i2c_dev = i2c_dev;
175 }
176 
177 static int isp_i2c_bus_notify(struct notifier_block *nb,
178 			      unsigned long action, void *data)
179 {
180 	struct amdisp_platform *isp4_platform =
181 		container_of(nb, struct amdisp_platform, i2c_nb);
182 	struct device *dev = data;
183 	struct i2c_client *client;
184 	struct i2c_adapter *adap;
185 
186 	switch (action) {
187 	case BUS_NOTIFY_ADD_DEVICE:
188 		adap = i2c_verify_adapter(dev);
189 		if (!adap)
190 			break;
191 		if (is_isp_i2c_adapter(adap))
192 			instantiate_isp_i2c_client(isp4_platform, adap);
193 		break;
194 	case BUS_NOTIFY_REMOVED_DEVICE:
195 		client = i2c_verify_client(dev);
196 		if (!client)
197 			break;
198 
199 		scoped_guard(mutex, &isp4_platform->lock) {
200 			if (isp4_platform->i2c_dev == client) {
201 				dev_dbg(&client->adapter->dev, "amdisp i2c_client removed\n");
202 				isp4_platform->i2c_dev = NULL;
203 			}
204 		}
205 		break;
206 	default:
207 		break;
208 	}
209 
210 	return NOTIFY_DONE;
211 }
212 
213 static struct amdisp_platform *prepare_amdisp_platform(struct device *dev,
214 						       const struct amdisp_platform_info *src)
215 {
216 	struct amdisp_platform *isp4_platform;
217 	int ret;
218 
219 	isp4_platform = devm_kzalloc(dev, sizeof(*isp4_platform), GFP_KERNEL);
220 	if (!isp4_platform)
221 		return ERR_PTR(-ENOMEM);
222 
223 	ret = devm_mutex_init(dev, &isp4_platform->lock);
224 	if (ret)
225 		return ERR_PTR(ret);
226 
227 	isp4_platform->board_info.dev_name = src->board_info.dev_name;
228 	strscpy(isp4_platform->board_info.type, src->board_info.type);
229 	isp4_platform->board_info.addr = src->board_info.addr;
230 	isp4_platform->pinfo = src;
231 
232 	ret = software_node_register_node_group(src->swnodes);
233 	if (ret)
234 		return ERR_PTR(ret);
235 
236 	isp4_platform->board_info.swnode = src->swnodes[0];
237 
238 	return isp4_platform;
239 }
240 
241 static int try_to_instantiate_i2c_client(struct device *dev, void *data)
242 {
243 	struct i2c_adapter *adap = i2c_verify_adapter(dev);
244 	struct amdisp_platform *isp4_platform = data;
245 
246 	if (!isp4_platform || !adap)
247 		return 0;
248 	if (!adap->owner)
249 		return 0;
250 
251 	if (is_isp_i2c_adapter(adap))
252 		instantiate_isp_i2c_client(isp4_platform, adap);
253 
254 	return 0;
255 }
256 
257 static int amd_isp_probe(struct platform_device *pdev)
258 {
259 	const struct amdisp_platform_info *pinfo;
260 	struct amdisp_platform *isp4_platform;
261 	int ret;
262 
263 	pinfo = device_get_match_data(&pdev->dev);
264 	if (!pinfo)
265 		return dev_err_probe(&pdev->dev, -EINVAL,
266 				     "failed to get valid ACPI data\n");
267 
268 	isp4_platform = prepare_amdisp_platform(&pdev->dev, pinfo);
269 	if (IS_ERR(isp4_platform))
270 		return dev_err_probe(&pdev->dev, PTR_ERR(isp4_platform),
271 				     "failed to prepare AMD ISP platform fwnode\n");
272 
273 	isp4_platform->i2c_nb.notifier_call = isp_i2c_bus_notify;
274 	ret = bus_register_notifier(&i2c_bus_type, &isp4_platform->i2c_nb);
275 	if (ret)
276 		goto error_unregister_sw_node;
277 
278 	/* check if adapter is already registered and create i2c client instance */
279 	i2c_for_each_dev(isp4_platform, try_to_instantiate_i2c_client);
280 
281 	platform_set_drvdata(pdev, isp4_platform);
282 	return 0;
283 
284 error_unregister_sw_node:
285 	software_node_unregister_node_group(isp4_platform->pinfo->swnodes);
286 	return ret;
287 }
288 
289 static void amd_isp_remove(struct platform_device *pdev)
290 {
291 	struct amdisp_platform *isp4_platform = platform_get_drvdata(pdev);
292 
293 	bus_unregister_notifier(&i2c_bus_type, &isp4_platform->i2c_nb);
294 	i2c_unregister_device(isp4_platform->i2c_dev);
295 	software_node_unregister_node_group(isp4_platform->pinfo->swnodes);
296 }
297 
298 static struct platform_driver amd_isp_platform_driver = {
299 	.driver	= {
300 		.name			= AMD_ISP_PLAT_DRV_NAME,
301 		.acpi_match_table	= amdisp_sensor_ids,
302 	},
303 	.probe	= amd_isp_probe,
304 	.remove	= amd_isp_remove,
305 };
306 
307 module_platform_driver(amd_isp_platform_driver);
308 
309 MODULE_AUTHOR("Benjamin Chan <benjamin.chan@amd.com>");
310 MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
311 MODULE_DESCRIPTION("AMD ISP4 Platform Driver");
312 MODULE_LICENSE("GPL");
313