1
0

amd_isp4.c 10 KB

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