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