FILE
c3-isp-dev.c
File preview with syntax highlighting.
linux-6.19.14/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c
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 21u32 c3_isp_read(struct c3_isp_device *isp, u32 reg) 22{ 23 return readl(isp->base + reg); 24} 25 26void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val) 27{ 28 writel(val, isp->base + reg); 29} 30 31void 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 */ 45static 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 */ 55static 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 62static 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 */ 70static 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 */ 93static 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 106static 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 114static const struct v4l2_async_notifier_operations c3_isp_notify_ops = { 115 .bound = c3_isp_notify_bound, 116 .complete = c3_isp_notify_complete, 117}; 118 119static 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 147static 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 153static 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 181err_unreg_v4l2_dev: 182 v4l2_device_unregister(&isp->v4l2_dev); 183err_media_dev_cleanup: 184 media_device_cleanup(media_dev); 185 return ret; 186} 187 188static 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 195static 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 208static 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 257err_remove_links: 258 c3_isp_remove_links(isp); 259 return ret; 260} 261 262static 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 284err_params_unregister: 285 c3_isp_params_unregister(isp); 286err_stats_unregister: 287 c3_isp_stats_unregister(isp); 288err_captures_unregister: 289 c3_isp_captures_unregister(isp); 290 return ret; 291} 292 293static 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 301static 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 311static 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 370err_nf_unregister: 371 c3_isp_async_nf_unregister(isp); 372err_resizers_unregister: 373 c3_isp_resizers_unregister(isp); 374err_core_unregister: 375 c3_isp_core_unregister(isp); 376err_v4l2_unregister: 377 c3_isp_media_unregister(isp); 378err_runtime_disable: 379 pm_runtime_disable(dev); 380 return ret; 381}; 382 383static 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 395static const struct c3_isp_info isp_info = { 396 .clocks = {"vapb", "isp0"}, 397 .clock_num = 2 398}; 399 400static const struct of_device_id c3_isp_of_match[] = { 401 { .compatible = "amlogic,c3-isp", 402 .data = &isp_info }, 403 { }, 404}; 405MODULE_DEVICE_TABLE(of, c3_isp_of_match); 406 407static 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 417module_platform_driver(c3_isp_driver); 418 419MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>"); 420MODULE_DESCRIPTION("Amlogic C3 ISP pipeline"); 421MODULE_LICENSE("GPL");
~/
Read-only file preview.