1 // SPDX-License-Identifier: (GPL-2.0-only OR MIT) 2 /* 3 * Copyright (C) 2024 Amlogic, Inc. All rights reserved 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/device.h> 8 #include <linux/module.h> 9 #include <linux/mutex.h> 10 #include <linux/platform_device.h> 11 #include <linux/pm_runtime.h> 12 13 #include <media/v4l2-common.h> 14 #include <media/v4l2-device.h> 15 #include <media/v4l2-fwnode.h> 16 #include <media/v4l2-mc.h> 17 18 #include "c3-isp-common.h" 19 #include "c3-isp-regs.h" 20 21 u32 c3_isp_read(struct c3_isp_device *isp, u32 reg) 22 { 23 return readl(isp->base + reg); 24 } 25 26 void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val) 27 { 28 writel(val, isp->base + reg); 29 } 30 31 void c3_isp_update_bits(struct c3_isp_device *isp, u32 reg, u32 mask, u32 val) 32 { 33 u32 orig, tmp; 34 35 orig = c3_isp_read(isp, reg); 36 37 tmp = orig & ~mask; 38 tmp |= val & mask; 39 40 if (tmp != orig) 41 c3_isp_write(isp, reg, tmp); 42 } 43 44 /* PM runtime suspend */ 45 static int c3_isp_runtime_suspend(struct device *dev) 46 { 47 struct c3_isp_device *isp = dev_get_drvdata(dev); 48 49 clk_bulk_disable_unprepare(isp->info->clock_num, isp->clks); 50 51 return 0; 52 } 53 54 /* PM runtime resume */ 55 static int c3_isp_runtime_resume(struct device *dev) 56 { 57 struct c3_isp_device *isp = dev_get_drvdata(dev); 58 59 return clk_bulk_prepare_enable(isp->info->clock_num, isp->clks); 60 } 61 62 static const struct dev_pm_ops c3_isp_pm_ops = { 63 SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 64 pm_runtime_force_resume) 65 RUNTIME_PM_OPS(c3_isp_runtime_suspend, 66 c3_isp_runtime_resume, NULL) 67 }; 68 69 /* IRQ handling */ 70 static irqreturn_t c3_isp_irq_handler(int irq, void *dev) 71 { 72 struct c3_isp_device *isp = dev; 73 u32 status; 74 75 /* Get irq status and clear irq status */ 76 status = c3_isp_read(isp, ISP_TOP_RO_IRQ_STAT); 77 c3_isp_write(isp, ISP_TOP_IRQ_CLR, status); 78 79 if (status & ISP_TOP_RO_IRQ_STAT_FRM_END_MASK) { 80 c3_isp_stats_isr(isp); 81 c3_isp_params_isr(isp); 82 c3_isp_captures_isr(isp); 83 isp->frm_sequence++; 84 } 85 86 if (status & ISP_TOP_RO_IRQ_STAT_FRM_RST_MASK) 87 c3_isp_core_queue_sof(isp); 88 89 return IRQ_HANDLED; 90 } 91 92 /* Subdev notifier register */ 93 static int c3_isp_notify_bound(struct v4l2_async_notifier *notifier, 94 struct v4l2_subdev *sd, 95 struct v4l2_async_connection *asc) 96 { 97 struct c3_isp_device *isp = 98 container_of(notifier, struct c3_isp_device, notifier); 99 struct media_pad *sink = 100 &isp->core.sd.entity.pads[C3_ISP_CORE_PAD_SINK_VIDEO]; 101 102 return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED | 103 MEDIA_LNK_FL_IMMUTABLE); 104 } 105 106 static int c3_isp_notify_complete(struct v4l2_async_notifier *notifier) 107 { 108 struct c3_isp_device *isp = 109 container_of(notifier, struct c3_isp_device, notifier); 110 111 return v4l2_device_register_subdev_nodes(&isp->v4l2_dev); 112 } 113 114 static const struct v4l2_async_notifier_operations c3_isp_notify_ops = { 115 .bound = c3_isp_notify_bound, 116 .complete = c3_isp_notify_complete, 117 }; 118 119 static int c3_isp_async_nf_register(struct c3_isp_device *isp) 120 { 121 struct v4l2_async_connection *asc; 122 struct fwnode_handle *ep; 123 int ret; 124 125 v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev); 126 127 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), 0, 0, 128 FWNODE_GRAPH_ENDPOINT_NEXT); 129 if (!ep) 130 return -ENOTCONN; 131 132 asc = v4l2_async_nf_add_fwnode_remote(&isp->notifier, ep, 133 struct v4l2_async_connection); 134 fwnode_handle_put(ep); 135 136 if (IS_ERR(asc)) 137 return PTR_ERR(asc); 138 139 isp->notifier.ops = &c3_isp_notify_ops; 140 ret = v4l2_async_nf_register(&isp->notifier); 141 if (ret) 142 v4l2_async_nf_cleanup(&isp->notifier); 143 144 return ret; 145 } 146 147 static void c3_isp_async_nf_unregister(struct c3_isp_device *isp) 148 { 149 v4l2_async_nf_unregister(&isp->notifier); 150 v4l2_async_nf_cleanup(&isp->notifier); 151 } 152 153 static int c3_isp_media_register(struct c3_isp_device *isp) 154 { 155 struct media_device *media_dev = &isp->media_dev; 156 struct v4l2_device *v4l2_dev = &isp->v4l2_dev; 157 int ret; 158 159 /* Initialize media device */ 160 strscpy(media_dev->model, C3_ISP_DRIVER_NAME, sizeof(media_dev->model)); 161 media_dev->dev = isp->dev; 162 163 media_device_init(media_dev); 164 165 /* Initialize v4l2 device */ 166 v4l2_dev->mdev = media_dev; 167 strscpy(v4l2_dev->name, C3_ISP_DRIVER_NAME, sizeof(v4l2_dev->name)); 168 169 ret = v4l2_device_register(isp->dev, v4l2_dev); 170 if (ret) 171 goto err_media_dev_cleanup; 172 173 ret = media_device_register(&isp->media_dev); 174 if (ret) { 175 dev_err(isp->dev, "Failed to register media device: %d\n", ret); 176 goto err_unreg_v4l2_dev; 177 } 178 179 return 0; 180 181 err_unreg_v4l2_dev: 182 v4l2_device_unregister(&isp->v4l2_dev); 183 err_media_dev_cleanup: 184 media_device_cleanup(media_dev); 185 return ret; 186 } 187 188 static void c3_isp_media_unregister(struct c3_isp_device *isp) 189 { 190 media_device_unregister(&isp->media_dev); 191 v4l2_device_unregister(&isp->v4l2_dev); 192 media_device_cleanup(&isp->media_dev); 193 } 194 195 static void c3_isp_remove_links(struct c3_isp_device *isp) 196 { 197 unsigned int i; 198 199 media_entity_remove_links(&isp->core.sd.entity); 200 201 for (i = 0; i < C3_ISP_NUM_RSZ; i++) 202 media_entity_remove_links(&isp->resizers[i].sd.entity); 203 204 for (i = 0; i < C3_ISP_NUM_CAP_DEVS; i++) 205 media_entity_remove_links(&isp->caps[i].vdev.entity); 206 } 207 208 static int c3_isp_create_links(struct c3_isp_device *isp) 209 { 210 unsigned int i; 211 int ret; 212 213 for (i = 0; i < C3_ISP_NUM_RSZ; i++) { 214 ret = media_create_pad_link(&isp->resizers[i].sd.entity, 215 C3_ISP_RSZ_PAD_SOURCE, 216 &isp->caps[i].vdev.entity, 0, 217 MEDIA_LNK_FL_ENABLED | 218 MEDIA_LNK_FL_IMMUTABLE); 219 if (ret) { 220 dev_err(isp->dev, 221 "Failed to link rsz %u and cap %u\n", i, i); 222 goto err_remove_links; 223 } 224 225 ret = media_create_pad_link(&isp->core.sd.entity, 226 C3_ISP_CORE_PAD_SOURCE_VIDEO_0 + i, 227 &isp->resizers[i].sd.entity, 228 C3_ISP_RSZ_PAD_SINK, 229 MEDIA_LNK_FL_ENABLED); 230 if (ret) { 231 dev_err(isp->dev, 232 "Failed to link core and rsz %u\n", i); 233 goto err_remove_links; 234 } 235 } 236 237 ret = media_create_pad_link(&isp->core.sd.entity, 238 C3_ISP_CORE_PAD_SOURCE_STATS, 239 &isp->stats.vdev.entity, 240 0, MEDIA_LNK_FL_ENABLED); 241 if (ret) { 242 dev_err(isp->dev, "Failed to link core and stats\n"); 243 goto err_remove_links; 244 } 245 246 ret = media_create_pad_link(&isp->params.vdev.entity, 0, 247 &isp->core.sd.entity, 248 C3_ISP_CORE_PAD_SINK_PARAMS, 249 MEDIA_LNK_FL_ENABLED); 250 if (ret) { 251 dev_err(isp->dev, "Failed to link params and core\n"); 252 goto err_remove_links; 253 } 254 255 return 0; 256 257 err_remove_links: 258 c3_isp_remove_links(isp); 259 return ret; 260 } 261 262 static int c3_isp_videos_register(struct c3_isp_device *isp) 263 { 264 int ret; 265 266 ret = c3_isp_captures_register(isp); 267 if (ret) 268 return ret; 269 270 ret = c3_isp_stats_register(isp); 271 if (ret) 272 goto err_captures_unregister; 273 274 ret = c3_isp_params_register(isp); 275 if (ret) 276 goto err_stats_unregister; 277 278 ret = c3_isp_create_links(isp); 279 if (ret) 280 goto err_params_unregister; 281 282 return 0; 283 284 err_params_unregister: 285 c3_isp_params_unregister(isp); 286 err_stats_unregister: 287 c3_isp_stats_unregister(isp); 288 err_captures_unregister: 289 c3_isp_captures_unregister(isp); 290 return ret; 291 } 292 293 static void c3_isp_videos_unregister(struct c3_isp_device *isp) 294 { 295 c3_isp_remove_links(isp); 296 c3_isp_params_unregister(isp); 297 c3_isp_stats_unregister(isp); 298 c3_isp_captures_unregister(isp); 299 } 300 301 static int c3_isp_get_clocks(struct c3_isp_device *isp) 302 { 303 const struct c3_isp_info *info = isp->info; 304 305 for (unsigned int i = 0; i < info->clock_num; i++) 306 isp->clks[i].id = info->clocks[i]; 307 308 return devm_clk_bulk_get(isp->dev, info->clock_num, isp->clks); 309 } 310 311 static int c3_isp_probe(struct platform_device *pdev) 312 { 313 struct device *dev = &pdev->dev; 314 struct c3_isp_device *isp; 315 int irq; 316 int ret; 317 318 isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); 319 if (!isp) 320 return -ENOMEM; 321 322 isp->info = of_device_get_match_data(dev); 323 isp->dev = dev; 324 325 isp->base = devm_platform_ioremap_resource_byname(pdev, "isp"); 326 if (IS_ERR(isp->base)) 327 return dev_err_probe(dev, PTR_ERR(isp->base), 328 "Failed to ioremap resource\n"); 329 330 irq = platform_get_irq(pdev, 0); 331 if (irq < 0) 332 return irq; 333 334 ret = c3_isp_get_clocks(isp); 335 if (ret) 336 return dev_err_probe(dev, ret, "Failed to get clocks\n"); 337 338 platform_set_drvdata(pdev, isp); 339 340 pm_runtime_enable(dev); 341 342 ret = c3_isp_media_register(isp); 343 if (ret) 344 goto err_runtime_disable; 345 346 ret = c3_isp_core_register(isp); 347 if (ret) 348 goto err_v4l2_unregister; 349 350 ret = c3_isp_resizers_register(isp); 351 if (ret) 352 goto err_core_unregister; 353 354 ret = c3_isp_async_nf_register(isp); 355 if (ret) 356 goto err_resizers_unregister; 357 358 ret = devm_request_irq(dev, irq, 359 c3_isp_irq_handler, IRQF_SHARED, 360 dev_driver_string(dev), isp); 361 if (ret) 362 goto err_nf_unregister; 363 364 ret = c3_isp_videos_register(isp); 365 if (ret) 366 goto err_nf_unregister; 367 368 return 0; 369 370 err_nf_unregister: 371 c3_isp_async_nf_unregister(isp); 372 err_resizers_unregister: 373 c3_isp_resizers_unregister(isp); 374 err_core_unregister: 375 c3_isp_core_unregister(isp); 376 err_v4l2_unregister: 377 c3_isp_media_unregister(isp); 378 err_runtime_disable: 379 pm_runtime_disable(dev); 380 return ret; 381 }; 382 383 static void c3_isp_remove(struct platform_device *pdev) 384 { 385 struct c3_isp_device *isp = platform_get_drvdata(pdev); 386 387 c3_isp_videos_unregister(isp); 388 c3_isp_async_nf_unregister(isp); 389 c3_isp_core_unregister(isp); 390 c3_isp_resizers_unregister(isp); 391 c3_isp_media_unregister(isp); 392 pm_runtime_disable(isp->dev); 393 }; 394 395 static const struct c3_isp_info isp_info = { 396 .clocks = {"vapb", "isp0"}, 397 .clock_num = 2 398 }; 399 400 static const struct of_device_id c3_isp_of_match[] = { 401 { .compatible = "amlogic,c3-isp", 402 .data = &isp_info }, 403 { }, 404 }; 405 MODULE_DEVICE_TABLE(of, c3_isp_of_match); 406 407 static struct platform_driver c3_isp_driver = { 408 .probe = c3_isp_probe, 409 .remove = c3_isp_remove, 410 .driver = { 411 .name = "c3-isp", 412 .of_match_table = c3_isp_of_match, 413 .pm = pm_ptr(&c3_isp_pm_ops), 414 }, 415 }; 416 417 module_platform_driver(c3_isp_driver); 418 419 MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>"); 420 MODULE_DESCRIPTION("Amlogic C3 ISP pipeline"); 421 MODULE_LICENSE("GPL"); 422