FILE
stf-camss.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/starfive/camss/stf-camss.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * stf_camss.c 4 * 5 * Starfive Camera Subsystem driver 6 * 7 * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. 8 * 9 * Author: Jack Zhu <jack.zhu@starfivetech.com> 10 * Author: Changhuang Liang <changhuang.liang@starfivetech.com> 11 * 12 */ 13#include <linux/module.h> 14#include <linux/of.h> 15#include <linux/of_graph.h> 16#include <linux/platform_device.h> 17#include <linux/pm_runtime.h> 18#include <linux/videodev2.h> 19#include <media/v4l2-fwnode.h> 20#include <media/v4l2-mc.h> 21 22#include "stf-camss.h" 23 24static const char * const stfcamss_clocks[] = { 25 "wrapper_clk_c", 26 "ispcore_2x", 27 "isp_axi", 28}; 29 30static const char * const stfcamss_resets[] = { 31 "wrapper_p", 32 "wrapper_c", 33 "axiwr", 34 "isp_top_n", 35 "isp_top_axi", 36}; 37 38static const struct stf_isr_data stf_isrs[] = { 39 {"wr_irq", stf_wr_irq_handler}, 40 {"isp_irq", stf_isp_irq_handler}, 41 {"line_irq", stf_line_irq_handler}, 42}; 43 44static int stfcamss_get_mem_res(struct stfcamss *stfcamss) 45{ 46 struct platform_device *pdev = to_platform_device(stfcamss->dev); 47 48 stfcamss->syscon_base = 49 devm_platform_ioremap_resource_byname(pdev, "syscon"); 50 if (IS_ERR(stfcamss->syscon_base)) 51 return PTR_ERR(stfcamss->syscon_base); 52 53 stfcamss->isp_base = devm_platform_ioremap_resource_byname(pdev, "isp"); 54 if (IS_ERR(stfcamss->isp_base)) 55 return PTR_ERR(stfcamss->isp_base); 56 57 return 0; 58} 59 60/* 61 * stfcamss_of_parse_endpoint_node - Parse port endpoint node 62 * @dev: Device 63 * @node: Device node to be parsed 64 * @csd: Parsed data from port endpoint node 65 * 66 * Return 0 on success or a negative error code on failure 67 */ 68static int stfcamss_of_parse_endpoint_node(struct stfcamss *stfcamss, 69 struct device_node *node, 70 struct stfcamss_async_subdev *csd) 71{ 72 struct v4l2_fwnode_endpoint vep = { { 0 } }; 73 int ret; 74 75 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); 76 if (ret) { 77 dev_err(stfcamss->dev, "endpoint not defined at %pOF\n", node); 78 return ret; 79 } 80 81 csd->port = vep.base.port; 82 83 return 0; 84} 85 86/* 87 * stfcamss_of_parse_ports - Parse ports node 88 * @stfcamss: STFCAMSS device 89 * 90 * Return number of "port" nodes found in "ports" node 91 */ 92static int stfcamss_of_parse_ports(struct stfcamss *stfcamss) 93{ 94 struct device_node *node = NULL; 95 int ret, num_subdevs = 0; 96 97 for_each_endpoint_of_node(stfcamss->dev->of_node, node) { 98 struct stfcamss_async_subdev *csd; 99 100 if (!of_device_is_available(node)) 101 continue; 102 103 csd = v4l2_async_nf_add_fwnode_remote(&stfcamss->notifier, 104 of_fwnode_handle(node), 105 struct stfcamss_async_subdev); 106 if (IS_ERR(csd)) { 107 ret = PTR_ERR(csd); 108 dev_err(stfcamss->dev, "failed to add async notifier\n"); 109 goto err_cleanup; 110 } 111 112 ret = stfcamss_of_parse_endpoint_node(stfcamss, node, csd); 113 if (ret) 114 goto err_cleanup; 115 116 num_subdevs++; 117 } 118 119 return num_subdevs; 120 121err_cleanup: 122 of_node_put(node); 123 return ret; 124} 125 126static int stfcamss_register_devs(struct stfcamss *stfcamss) 127{ 128 struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV]; 129 struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; 130 int ret; 131 132 ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev); 133 if (ret < 0) { 134 dev_err(stfcamss->dev, 135 "failed to register stf isp%d entity: %d\n", 0, ret); 136 return ret; 137 } 138 139 ret = stf_capture_register(stfcamss, &stfcamss->v4l2_dev); 140 if (ret < 0) { 141 dev_err(stfcamss->dev, 142 "failed to register capture: %d\n", ret); 143 goto err_isp_unregister; 144 } 145 146 ret = media_create_pad_link(&isp_dev->subdev.entity, STF_ISP_PAD_SRC, 147 &cap_yuv->video.vdev.entity, 0, 0); 148 if (ret) 149 goto err_cap_unregister; 150 151 cap_yuv->video.source_subdev = &isp_dev->subdev; 152 153 return ret; 154 155err_cap_unregister: 156 stf_capture_unregister(stfcamss); 157err_isp_unregister: 158 stf_isp_unregister(&stfcamss->isp_dev); 159 160 return ret; 161} 162 163static void stfcamss_unregister_devs(struct stfcamss *stfcamss) 164{ 165 struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV]; 166 struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; 167 168 media_entity_remove_links(&isp_dev->subdev.entity); 169 media_entity_remove_links(&cap_yuv->video.vdev.entity); 170 171 stf_isp_unregister(&stfcamss->isp_dev); 172 stf_capture_unregister(stfcamss); 173} 174 175static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async, 176 struct v4l2_subdev *subdev, 177 struct v4l2_async_connection *asc) 178{ 179 struct stfcamss *stfcamss = 180 container_of(async, struct stfcamss, notifier); 181 struct stfcamss_async_subdev *csd = 182 container_of(asc, struct stfcamss_async_subdev, asd); 183 enum stf_port_num port = csd->port; 184 struct stf_isp_dev *isp_dev = &stfcamss->isp_dev; 185 struct stf_capture *cap_raw = &stfcamss->captures[STF_CAPTURE_RAW]; 186 struct media_pad *pad; 187 int ret; 188 189 if (port == STF_PORT_CSI2RX) { 190 pad = &isp_dev->pads[STF_ISP_PAD_SINK]; 191 } else { 192 dev_err(stfcamss->dev, "not support port %d\n", port); 193 return -EPERM; 194 } 195 196 ret = v4l2_create_fwnode_links_to_pad(subdev, pad, 0); 197 if (ret) 198 return ret; 199 200 ret = media_create_pad_link(&subdev->entity, 1, 201 &cap_raw->video.vdev.entity, 0, 0); 202 if (ret) 203 return ret; 204 205 isp_dev->source_subdev = subdev; 206 cap_raw->video.source_subdev = subdev; 207 208 return 0; 209} 210 211static int stfcamss_subdev_notifier_complete(struct v4l2_async_notifier *ntf) 212{ 213 struct stfcamss *stfcamss = 214 container_of(ntf, struct stfcamss, notifier); 215 216 return v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev); 217} 218 219static const struct v4l2_async_notifier_operations 220stfcamss_subdev_notifier_ops = { 221 .bound = stfcamss_subdev_notifier_bound, 222 .complete = stfcamss_subdev_notifier_complete, 223}; 224 225static void stfcamss_mc_init(struct platform_device *pdev, 226 struct stfcamss *stfcamss) 227{ 228 stfcamss->media_dev.dev = stfcamss->dev; 229 strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem", 230 sizeof(stfcamss->media_dev.model)); 231 media_device_init(&stfcamss->media_dev); 232 233 stfcamss->v4l2_dev.mdev = &stfcamss->media_dev; 234} 235 236/* 237 * stfcamss_probe - Probe STFCAMSS platform device 238 * @pdev: Pointer to STFCAMSS platform device 239 * 240 * Return 0 on success or a negative error code on failure 241 */ 242static int stfcamss_probe(struct platform_device *pdev) 243{ 244 struct stfcamss *stfcamss; 245 struct device *dev = &pdev->dev; 246 int ret, num_subdevs; 247 unsigned int i; 248 249 stfcamss = devm_kzalloc(dev, sizeof(*stfcamss), GFP_KERNEL); 250 if (!stfcamss) 251 return -ENOMEM; 252 253 stfcamss->dev = dev; 254 255 for (i = 0; i < ARRAY_SIZE(stf_isrs); ++i) { 256 int irq; 257 258 irq = platform_get_irq(pdev, i); 259 if (irq < 0) 260 return irq; 261 262 ret = devm_request_irq(stfcamss->dev, irq, stf_isrs[i].isr, 0, 263 stf_isrs[i].name, stfcamss); 264 if (ret) { 265 dev_err(dev, "request irq failed: %d\n", ret); 266 return ret; 267 } 268 } 269 270 stfcamss->nclks = ARRAY_SIZE(stfcamss->sys_clk); 271 for (i = 0; i < stfcamss->nclks; ++i) 272 stfcamss->sys_clk[i].id = stfcamss_clocks[i]; 273 ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk); 274 if (ret) { 275 dev_err(dev, "Failed to get clk controls\n"); 276 return ret; 277 } 278 279 stfcamss->nrsts = ARRAY_SIZE(stfcamss->sys_rst); 280 for (i = 0; i < stfcamss->nrsts; ++i) 281 stfcamss->sys_rst[i].id = stfcamss_resets[i]; 282 ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts, 283 stfcamss->sys_rst); 284 if (ret) { 285 dev_err(dev, "Failed to get reset controls\n"); 286 return ret; 287 } 288 289 ret = stfcamss_get_mem_res(stfcamss); 290 if (ret) { 291 dev_err(dev, "Could not map registers\n"); 292 return ret; 293 } 294 295 platform_set_drvdata(pdev, stfcamss); 296 297 v4l2_async_nf_init(&stfcamss->notifier, &stfcamss->v4l2_dev); 298 299 num_subdevs = stfcamss_of_parse_ports(stfcamss); 300 if (num_subdevs < 0) { 301 ret = -ENODEV; 302 dev_err(dev, "Failed to get sub devices: %d\n", ret); 303 goto err_cleanup_notifier; 304 } 305 306 ret = stf_isp_init(stfcamss); 307 if (ret < 0) { 308 dev_err(dev, "Failed to init isp: %d\n", ret); 309 goto err_cleanup_notifier; 310 } 311 312 stfcamss_mc_init(pdev, stfcamss); 313 314 ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev); 315 if (ret < 0) { 316 dev_err(dev, "Failed to register V4L2 device: %d\n", ret); 317 goto err_cleanup_media_device; 318 } 319 320 ret = media_device_register(&stfcamss->media_dev); 321 if (ret) { 322 dev_err(dev, "Failed to register media device: %d\n", ret); 323 goto err_unregister_device; 324 } 325 326 ret = stfcamss_register_devs(stfcamss); 327 if (ret < 0) { 328 dev_err(dev, "Failed to register subdevice: %d\n", ret); 329 goto err_unregister_media_dev; 330 } 331 332 pm_runtime_enable(dev); 333 334 stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops; 335 ret = v4l2_async_nf_register(&stfcamss->notifier); 336 if (ret) { 337 dev_err(dev, "Failed to register async subdev nodes: %d\n", 338 ret); 339 pm_runtime_disable(dev); 340 goto err_unregister_subdevs; 341 } 342 343 return 0; 344 345err_unregister_subdevs: 346 stfcamss_unregister_devs(stfcamss); 347err_unregister_media_dev: 348 media_device_unregister(&stfcamss->media_dev); 349err_unregister_device: 350 v4l2_device_unregister(&stfcamss->v4l2_dev); 351err_cleanup_media_device: 352 media_device_cleanup(&stfcamss->media_dev); 353err_cleanup_notifier: 354 v4l2_async_nf_cleanup(&stfcamss->notifier); 355 return ret; 356} 357 358/* 359 * stfcamss_remove - Remove STFCAMSS platform device 360 * @pdev: Pointer to STFCAMSS platform device 361 */ 362static void stfcamss_remove(struct platform_device *pdev) 363{ 364 struct stfcamss *stfcamss = platform_get_drvdata(pdev); 365 366 stfcamss_unregister_devs(stfcamss); 367 v4l2_device_unregister(&stfcamss->v4l2_dev); 368 media_device_cleanup(&stfcamss->media_dev); 369 v4l2_async_nf_cleanup(&stfcamss->notifier); 370 pm_runtime_disable(&pdev->dev); 371} 372 373static const struct of_device_id stfcamss_of_match[] = { 374 { .compatible = "starfive,jh7110-camss" }, 375 { /* sentinel */ }, 376}; 377 378MODULE_DEVICE_TABLE(of, stfcamss_of_match); 379 380static int __maybe_unused stfcamss_runtime_suspend(struct device *dev) 381{ 382 struct stfcamss *stfcamss = dev_get_drvdata(dev); 383 int ret; 384 385 ret = reset_control_bulk_assert(stfcamss->nrsts, stfcamss->sys_rst); 386 if (ret) { 387 dev_err(dev, "reset assert failed\n"); 388 return ret; 389 } 390 391 clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk); 392 393 return 0; 394} 395 396static int __maybe_unused stfcamss_runtime_resume(struct device *dev) 397{ 398 struct stfcamss *stfcamss = dev_get_drvdata(dev); 399 int ret; 400 401 ret = clk_bulk_prepare_enable(stfcamss->nclks, stfcamss->sys_clk); 402 if (ret) { 403 dev_err(dev, "clock prepare enable failed\n"); 404 return ret; 405 } 406 407 ret = reset_control_bulk_deassert(stfcamss->nrsts, stfcamss->sys_rst); 408 if (ret < 0) { 409 dev_err(dev, "cannot deassert resets\n"); 410 clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk); 411 return ret; 412 } 413 414 return 0; 415} 416 417static const struct dev_pm_ops stfcamss_pm_ops = { 418 SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend, 419 stfcamss_runtime_resume, 420 NULL) 421}; 422 423static struct platform_driver stfcamss_driver = { 424 .probe = stfcamss_probe, 425 .remove = stfcamss_remove, 426 .driver = { 427 .name = "starfive-camss", 428 .pm = &stfcamss_pm_ops, 429 .of_match_table = stfcamss_of_match, 430 }, 431}; 432 433module_platform_driver(stfcamss_driver); 434 435MODULE_AUTHOR("Jack Zhu <jack.zhu@starfivetech.com>"); 436MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>"); 437MODULE_DESCRIPTION("StarFive Camera Subsystem driver"); 438MODULE_LICENSE("GPL");
~/
Read-only file preview.