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