xref: /linux/drivers/platform/x86/amd/amd_isp4.c (revision b1c26e059536d8acbf9d508374f4b76537e20fb7)
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/string.h>
15 #include <linux/types.h>
16 #include <linux/units.h>
17 
18 #define AMDISP_OV05C10_I2C_ADDR		0x10
19 #define AMDISP_OV05C10_HID		"OMNI5C10"
20 #define AMDISP_OV05C10_REMOTE_EP_NAME	"ov05c10_isp_4_1_1"
21 #define AMD_ISP_PLAT_DRV_NAME		"amd-isp4"
22 
23 static const struct software_node isp4_mipi1_endpoint_node;
24 static const struct software_node ov05c10_endpoint_node;
25 
26 /*
27  * AMD ISP platform info definition to initialize sensor
28  * specific platform configuration to prepare the amdisp
29  * platform.
30  */
31 struct amdisp_platform_info {
32 	struct i2c_board_info board_info;
33 	const struct software_node **swnodes;
34 };
35 
36 /*
37  * AMD ISP platform definition to configure the device properties
38  * missing in the ACPI table.
39  */
40 struct amdisp_platform {
41 	const struct amdisp_platform_info *pinfo;
42 	struct i2c_board_info board_info;
43 	struct notifier_block i2c_nb;
44 	struct i2c_client *i2c_dev;
45 	struct mutex lock;	/* protects i2c client creation */
46 };
47 
48 /* Root AMD CAMERA SWNODE */
49 
50 /* Root amd camera node definition */
51 static const struct software_node amd_camera_node = {
52 	.name = "amd_camera",
53 };
54 
55 /* ISP4 SWNODE */
56 
57 /* ISP4 OV05C10 camera node definition */
58 static const struct software_node isp4_node = {
59 	.name = "isp4",
60 	.parent = &amd_camera_node,
61 };
62 
63 /*
64  * ISP4 Ports node definition. No properties defined for
65  * ports node.
66  */
67 static const struct software_node isp4_ports = {
68 	.name = "ports",
69 	.parent = &isp4_node,
70 };
71 
72 /*
73  * ISP4 Port node definition. No properties defined for
74  * port node.
75  */
76 static const struct software_node isp4_port_node = {
77 	.name = "port@0",
78 	.parent = &isp4_ports,
79 };
80 
81 /*
82  * ISP4 MIPI1 remote endpoint points to OV05C10 endpoint
83  * node.
84  */
85 static const struct software_node_ref_args isp4_refs[] = {
86 	SOFTWARE_NODE_REFERENCE(&ov05c10_endpoint_node),
87 };
88 
89 /* ISP4 MIPI1 endpoint node properties table */
90 static const struct property_entry isp4_mipi1_endpoint_props[] = {
91 	PROPERTY_ENTRY_REF_ARRAY("remote-endpoint", isp4_refs),
92 	{ }
93 };
94 
95 /* ISP4 MIPI1 endpoint node definition */
96 static const struct software_node isp4_mipi1_endpoint_node = {
97 	.name = "endpoint",
98 	.parent = &isp4_port_node,
99 	.properties = isp4_mipi1_endpoint_props,
100 };
101 
102 /* I2C1 SWNODE */
103 
104 /* I2C1 camera node property table */
105 static const struct property_entry i2c1_camera_props[] = {
106 	PROPERTY_ENTRY_U32("clock-frequency", 1 * HZ_PER_MHZ),
107 	{ }
108 };
109 
110 /* I2C1 camera node definition */
111 static const struct software_node i2c1_node = {
112 	.name = "i2c1",
113 	.parent = &amd_camera_node,
114 	.properties = i2c1_camera_props,
115 };
116 
117 /* I2C1 camera node property table */
118 static const struct property_entry ov05c10_camera_props[] = {
119 	PROPERTY_ENTRY_U32("clock-frequency", 24 * HZ_PER_MHZ),
120 	{ }
121 };
122 
123 /* OV05C10 camera node definition */
124 static const struct software_node ov05c10_camera_node = {
125 	.name = AMDISP_OV05C10_HID,
126 	.parent = &i2c1_node,
127 	.properties = ov05c10_camera_props,
128 };
129 
130 /*
131  * OV05C10 Ports node definition. No properties defined for
132  * ports node for OV05C10.
133  */
134 static const struct software_node ov05c10_ports = {
135 	.name = "ports",
136 	.parent = &ov05c10_camera_node,
137 };
138 
139 /*
140  * OV05C10 Port node definition.
141  */
142 static const struct software_node ov05c10_port_node = {
143 	.name = "port@0",
144 	.parent = &ov05c10_ports,
145 };
146 
147 /*
148  * OV05C10 remote endpoint points to ISP4 MIPI1 endpoint
149  * node.
150  */
151 static const struct software_node_ref_args ov05c10_refs[] = {
152 	SOFTWARE_NODE_REFERENCE(&isp4_mipi1_endpoint_node),
153 };
154 
155 /* OV05C10 supports one single link frequency */
156 static const u64 ov05c10_link_freqs[] = {
157 	900 * HZ_PER_MHZ,
158 };
159 
160 /* OV05C10 supports only 2-lane configuration */
161 static const u32 ov05c10_data_lanes[] = {
162 	1,
163 	2,
164 };
165 
166 /* OV05C10 endpoint node properties table */
167 static const struct property_entry ov05c10_endpoint_props[] = {
168 	PROPERTY_ENTRY_U32("bus-type", 4),
169 	PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes", ov05c10_data_lanes,
170 				     ARRAY_SIZE(ov05c10_data_lanes)),
171 	PROPERTY_ENTRY_U64_ARRAY_LEN("link-frequencies", ov05c10_link_freqs,
172 				     ARRAY_SIZE(ov05c10_link_freqs)),
173 	PROPERTY_ENTRY_REF_ARRAY("remote-endpoint", ov05c10_refs),
174 	{ }
175 };
176 
177 /* OV05C10 endpoint node definition */
178 static const struct software_node ov05c10_endpoint_node = {
179 	.name = "endpoint",
180 	.parent = &ov05c10_port_node,
181 	.properties = ov05c10_endpoint_props,
182 };
183 
184 /*
185  * AMD Camera swnode graph uses 10 nodes and also its relationship is
186  * fixed to align with the structure that v4l2 and i2c frameworks expects
187  * for successful parsing of fwnodes and its properties with standard names.
188  *
189  * It is only the node property_entries that will vary for each platform
190  * supporting different sensor modules.
191  *
192  * AMD ISP4 SWNODE GRAPH Structure
193  *
194  * amd_camera {
195  *  isp4 {
196  *	  ports {
197  *		  port@0 {
198  *			  isp4_mipi1_ep: endpoint {
199  *					  remote-endpoint = &OMNI5C10_ep;
200  *			  };
201  *		  };
202  *	  };
203  *  };
204  *
205  *  i2c1 {
206  *	  clock-frequency = 1 MHz;
207  *	  OMNI5C10 {
208  *		  clock-frequency = 24MHz;
209  *		  ports {
210  *			  port@0 {
211  *				  OMNI5C10_ep: endpoint {
212  *					  bus-type = 4;
213  *					  data-lanes = <1 2>;
214  *					  link-frequencies = 900MHz;
215  *					  remote-endpoint = &isp4_mipi1;
216  *				  };
217  *			  };
218  *		  };
219  *	  };
220  *	};
221  * };
222  *
223  */
224 static const struct software_node *amd_isp4_nodes[] = {
225 	&amd_camera_node,
226 	&isp4_node,
227 	&isp4_ports,
228 	&isp4_port_node,
229 	&isp4_mipi1_endpoint_node,
230 	&i2c1_node,
231 	&ov05c10_camera_node,
232 	&ov05c10_ports,
233 	&ov05c10_port_node,
234 	&ov05c10_endpoint_node,
235 	NULL
236 };
237 
238 /* OV05C10 specific AMD ISP platform configuration */
239 static const struct amdisp_platform_info ov05c10_platform_config = {
240 	.board_info = {
241 		.dev_name = "ov05c10",
242 		I2C_BOARD_INFO("ov05c10", AMDISP_OV05C10_I2C_ADDR),
243 	},
244 	.swnodes = amd_isp4_nodes,
245 };
246 
247 static const struct acpi_device_id amdisp_sensor_ids[] = {
248 	{ AMDISP_OV05C10_HID, (kernel_ulong_t)&ov05c10_platform_config },
249 	{ }
250 };
251 MODULE_DEVICE_TABLE(acpi, amdisp_sensor_ids);
252 
253 static inline bool is_isp_i2c_adapter(struct i2c_adapter *adap)
254 {
255 	return !strcmp(adap->owner->name, "i2c_designware_amdisp");
256 }
257 
258 static void instantiate_isp_i2c_client(struct amdisp_platform *isp4_platform,
259 				       struct i2c_adapter *adap)
260 {
261 	struct i2c_board_info *info = &isp4_platform->board_info;
262 	struct i2c_client *i2c_dev;
263 
264 	guard(mutex)(&isp4_platform->lock);
265 
266 	if (isp4_platform->i2c_dev)
267 		return;
268 
269 	i2c_dev = i2c_new_client_device(adap, info);
270 	if (IS_ERR(i2c_dev)) {
271 		dev_err(&adap->dev, "error %pe registering isp i2c_client\n", i2c_dev);
272 		return;
273 	}
274 	isp4_platform->i2c_dev = i2c_dev;
275 }
276 
277 static int isp_i2c_bus_notify(struct notifier_block *nb,
278 			      unsigned long action, void *data)
279 {
280 	struct amdisp_platform *isp4_platform =
281 		container_of(nb, struct amdisp_platform, i2c_nb);
282 	struct device *dev = data;
283 	struct i2c_client *client;
284 	struct i2c_adapter *adap;
285 
286 	switch (action) {
287 	case BUS_NOTIFY_ADD_DEVICE:
288 		adap = i2c_verify_adapter(dev);
289 		if (!adap)
290 			break;
291 		if (is_isp_i2c_adapter(adap))
292 			instantiate_isp_i2c_client(isp4_platform, adap);
293 		break;
294 	case BUS_NOTIFY_REMOVED_DEVICE:
295 		client = i2c_verify_client(dev);
296 		if (!client)
297 			break;
298 
299 		scoped_guard(mutex, &isp4_platform->lock) {
300 			if (isp4_platform->i2c_dev == client) {
301 				dev_dbg(&client->adapter->dev, "amdisp i2c_client removed\n");
302 				isp4_platform->i2c_dev = NULL;
303 			}
304 		}
305 		break;
306 	default:
307 		break;
308 	}
309 
310 	return NOTIFY_DONE;
311 }
312 
313 static struct amdisp_platform *prepare_amdisp_platform(struct device *dev,
314 						       const struct amdisp_platform_info *src)
315 {
316 	struct amdisp_platform *isp4_platform;
317 	int ret;
318 
319 	isp4_platform = devm_kzalloc(dev, sizeof(*isp4_platform), GFP_KERNEL);
320 	if (!isp4_platform)
321 		return ERR_PTR(-ENOMEM);
322 
323 	ret = devm_mutex_init(dev, &isp4_platform->lock);
324 	if (ret)
325 		return ERR_PTR(ret);
326 
327 	isp4_platform->board_info.dev_name = src->board_info.dev_name;
328 	strscpy(isp4_platform->board_info.type, src->board_info.type);
329 	isp4_platform->board_info.addr = src->board_info.addr;
330 	isp4_platform->pinfo = src;
331 
332 	ret = software_node_register_node_group(src->swnodes);
333 	if (ret)
334 		return ERR_PTR(ret);
335 
336 	/* initialize ov05c10_camera_node */
337 	isp4_platform->board_info.swnode = src->swnodes[6];
338 
339 	return isp4_platform;
340 }
341 
342 static int try_to_instantiate_i2c_client(struct device *dev, void *data)
343 {
344 	struct i2c_adapter *adap = i2c_verify_adapter(dev);
345 	struct amdisp_platform *isp4_platform = data;
346 
347 	if (!isp4_platform || !adap)
348 		return 0;
349 	if (!adap->owner)
350 		return 0;
351 
352 	if (is_isp_i2c_adapter(adap))
353 		instantiate_isp_i2c_client(isp4_platform, adap);
354 
355 	return 0;
356 }
357 
358 static int amd_isp_probe(struct platform_device *pdev)
359 {
360 	const struct amdisp_platform_info *pinfo;
361 	struct amdisp_platform *isp4_platform;
362 	struct acpi_device *adev;
363 	int ret;
364 
365 	pinfo = device_get_match_data(&pdev->dev);
366 	if (!pinfo)
367 		return dev_err_probe(&pdev->dev, -EINVAL,
368 				     "failed to get valid ACPI data\n");
369 
370 	isp4_platform = prepare_amdisp_platform(&pdev->dev, pinfo);
371 	if (IS_ERR(isp4_platform))
372 		return dev_err_probe(&pdev->dev, PTR_ERR(isp4_platform),
373 				     "failed to prepare AMD ISP platform fwnode\n");
374 
375 	isp4_platform->i2c_nb.notifier_call = isp_i2c_bus_notify;
376 	ret = bus_register_notifier(&i2c_bus_type, &isp4_platform->i2c_nb);
377 	if (ret)
378 		goto error_unregister_sw_node;
379 
380 	adev = ACPI_COMPANION(&pdev->dev);
381 	/* initialize root amd_camera_node */
382 	adev->driver_data = (void *)pinfo->swnodes[0];
383 
384 	/* check if adapter is already registered and create i2c client instance */
385 	i2c_for_each_dev(isp4_platform, try_to_instantiate_i2c_client);
386 
387 	platform_set_drvdata(pdev, isp4_platform);
388 	return 0;
389 
390 error_unregister_sw_node:
391 	software_node_unregister_node_group(isp4_platform->pinfo->swnodes);
392 	return ret;
393 }
394 
395 static void amd_isp_remove(struct platform_device *pdev)
396 {
397 	struct amdisp_platform *isp4_platform = platform_get_drvdata(pdev);
398 
399 	bus_unregister_notifier(&i2c_bus_type, &isp4_platform->i2c_nb);
400 	i2c_unregister_device(isp4_platform->i2c_dev);
401 	software_node_unregister_node_group(isp4_platform->pinfo->swnodes);
402 }
403 
404 static struct platform_driver amd_isp_platform_driver = {
405 	.driver	= {
406 		.name			= AMD_ISP_PLAT_DRV_NAME,
407 		.acpi_match_table	= amdisp_sensor_ids,
408 	},
409 	.probe	= amd_isp_probe,
410 	.remove	= amd_isp_remove,
411 };
412 
413 module_platform_driver(amd_isp_platform_driver);
414 
415 MODULE_AUTHOR("Benjamin Chan <benjamin.chan@amd.com>");
416 MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
417 MODULE_DESCRIPTION("AMD ISP4 Platform Driver");
418 MODULE_LICENSE("GPL");
419