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