xref: /linux/drivers/media/platform/amd/isp4/isp4.c (revision 8c13415c8a4383447c21ec832b20b3b283f0e01a)
19a54c285SBin Du // SPDX-License-Identifier: GPL-2.0+
29a54c285SBin Du /*
39a54c285SBin Du  * Copyright (C) 2025 Advanced Micro Devices, Inc.
49a54c285SBin Du  */
59a54c285SBin Du 
64e5e7a7dSBin Du #include <linux/irq.h>
79a54c285SBin Du #include <linux/pm_runtime.h>
89a54c285SBin Du #include <linux/vmalloc.h>
99a54c285SBin Du #include <media/v4l2-ioctl.h>
109a54c285SBin Du 
119a54c285SBin Du #include "isp4.h"
12*ec4bec22SBin Du #include "isp4_debug.h"
134e5e7a7dSBin Du #include "isp4_hw_reg.h"
149a54c285SBin Du 
159a54c285SBin Du #define ISP4_DRV_NAME "amd_isp_capture"
164e5e7a7dSBin Du #define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \
174e5e7a7dSBin Du 	(ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK  | \
184e5e7a7dSBin Du 	 ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK)
199a54c285SBin Du 
209a54c285SBin Du static const struct {
219a54c285SBin Du 	const char *name;
229a54c285SBin Du 	u32 status_mask;
239a54c285SBin Du 	u32 en_mask;
249a54c285SBin Du 	u32 ack_mask;
259a54c285SBin Du 	u32 rb_int_num;
264e5e7a7dSBin Du } isp4_irq[ISP4SD_MAX_FW_RESP_STREAM_NUM] = {
279a54c285SBin Du 	/* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */
289a54c285SBin Du 	{
299a54c285SBin Du 		.name = "isp_irq_global",
304e5e7a7dSBin Du 		.status_mask =
314e5e7a7dSBin Du 		ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK,
324e5e7a7dSBin Du 		.en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK,
334e5e7a7dSBin Du 		.ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK,
349a54c285SBin Du 		.rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */
359a54c285SBin Du 	},
369a54c285SBin Du 	{
379a54c285SBin Du 		.name = "isp_irq_stream1",
384e5e7a7dSBin Du 		.status_mask =
394e5e7a7dSBin Du 		ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK,
404e5e7a7dSBin Du 		.en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK,
414e5e7a7dSBin Du 		.ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK,
429a54c285SBin Du 		.rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */
439a54c285SBin Du 	},
449a54c285SBin Du };
459a54c285SBin Du 
464e5e7a7dSBin Du void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable)
474e5e7a7dSBin Du {
484e5e7a7dSBin Du 	u32 intr_en;
494e5e7a7dSBin Du 
504e5e7a7dSBin Du 	/* Synchronize ISP_SYS_INT0_EN writes with the IRQ handler's writes */
514e5e7a7dSBin Du 	spin_lock_irq(&isp_subdev->irq_lock);
524e5e7a7dSBin Du 	intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN);
534e5e7a7dSBin Du 	if (enable)
544e5e7a7dSBin Du 		intr_en |= isp4_irq[index].en_mask;
554e5e7a7dSBin Du 	else
564e5e7a7dSBin Du 		intr_en &= ~isp4_irq[index].en_mask;
574e5e7a7dSBin Du 
584e5e7a7dSBin Du 	isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en);
594e5e7a7dSBin Du 	spin_unlock_irq(&isp_subdev->irq_lock);
604e5e7a7dSBin Du }
614e5e7a7dSBin Du 
624e5e7a7dSBin Du static void isp4_wake_up_resp_thread(struct isp4_subdev *isp_subdev, u32 index)
634e5e7a7dSBin Du {
644e5e7a7dSBin Du 	struct isp4sd_thread_handler *thread_ctx =
654e5e7a7dSBin Du 			&isp_subdev->fw_resp_thread[index];
664e5e7a7dSBin Du 
674e5e7a7dSBin Du 	thread_ctx->resp_ready = true;
684e5e7a7dSBin Du 	wake_up_interruptible(&thread_ctx->waitq);
694e5e7a7dSBin Du }
704e5e7a7dSBin Du 
719a54c285SBin Du static irqreturn_t isp4_irq_handler(int irq, void *arg)
729a54c285SBin Du {
734e5e7a7dSBin Du 	struct isp4_subdev *isp_subdev = arg;
744e5e7a7dSBin Du 	u32 intr_ack = 0, intr_en = 0, intr_status;
754e5e7a7dSBin Du 	int seen = 0;
764e5e7a7dSBin Du 
774e5e7a7dSBin Du 	/* Get the ISP_SYS interrupt status */
784e5e7a7dSBin Du 	intr_status = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_STATUS);
794e5e7a7dSBin Du 	intr_status &= ISP4_FW_RESP_RB_IRQ_STATUS_MASK;
804e5e7a7dSBin Du 
814e5e7a7dSBin Du 	/* Find which ISP_SYS interrupts fired */
824e5e7a7dSBin Du 	for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) {
834e5e7a7dSBin Du 		if (intr_status & isp4_irq[i].status_mask) {
844e5e7a7dSBin Du 			intr_ack |= isp4_irq[i].ack_mask;
854e5e7a7dSBin Du 			intr_en |= isp4_irq[i].en_mask;
864e5e7a7dSBin Du 			seen |= BIT(i);
874e5e7a7dSBin Du 		}
884e5e7a7dSBin Du 	}
894e5e7a7dSBin Du 
904e5e7a7dSBin Du 	/*
914e5e7a7dSBin Du 	 * Disable the ISP_SYS interrupts that fired. Must be done before waking
924e5e7a7dSBin Du 	 * the response threads, since they re-enable interrupts when finished.
934e5e7a7dSBin Du 	 * The lock synchronizes RMW of INT0_EN with isp4_enable_interrupt().
944e5e7a7dSBin Du 	 */
954e5e7a7dSBin Du 	spin_lock(&isp_subdev->irq_lock);
964e5e7a7dSBin Du 	intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN) & ~intr_en;
974e5e7a7dSBin Du 	isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en);
984e5e7a7dSBin Du 	spin_unlock(&isp_subdev->irq_lock);
994e5e7a7dSBin Du 
1004e5e7a7dSBin Du 	/*
1014e5e7a7dSBin Du 	 * Clear the ISP_SYS interrupts. This must be done after the interrupts
1024e5e7a7dSBin Du 	 * are disabled, so that ISP FW won't flag any new interrupts on these
1034e5e7a7dSBin Du 	 * streams, and thus we don't need to clear interrupts again before
1044e5e7a7dSBin Du 	 * re-enabling them in the response thread.
1054e5e7a7dSBin Du 	 */
1064e5e7a7dSBin Du 	isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack);
1074e5e7a7dSBin Du 
1084e5e7a7dSBin Du 	/*
1094e5e7a7dSBin Du 	 * The operation `(seen >> i) << i` is logically equivalent to
1104e5e7a7dSBin Du 	 * `seen &= ~BIT(i)`, with fewer instructions after compilation.
1114e5e7a7dSBin Du 	 */
1124e5e7a7dSBin Du 	for (int i; (i = ffs(seen)); seen = (seen >> i) << i)
1134e5e7a7dSBin Du 		isp4_wake_up_resp_thread(isp_subdev, i - 1);
1144e5e7a7dSBin Du 
1159a54c285SBin Du 	return IRQ_HANDLED;
1169a54c285SBin Du }
1179a54c285SBin Du 
1189a54c285SBin Du static int isp4_capture_probe(struct platform_device *pdev)
1199a54c285SBin Du {
1204e5e7a7dSBin Du 	int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM];
1219a54c285SBin Du 	struct device *dev = &pdev->dev;
1224e5e7a7dSBin Du 	struct isp4_subdev *isp_subdev;
1239a54c285SBin Du 	struct isp4_device *isp_dev;
1249a54c285SBin Du 	int ret;
1259a54c285SBin Du 
1269a54c285SBin Du 	isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL);
1279a54c285SBin Du 	if (!isp_dev)
1289a54c285SBin Du 		return -ENOMEM;
1299a54c285SBin Du 
1309a54c285SBin Du 	dev->init_name = ISP4_DRV_NAME;
1319a54c285SBin Du 
1324e5e7a7dSBin Du 	isp_subdev = &isp_dev->isp_subdev;
1334e5e7a7dSBin Du 	isp_subdev->mmio = devm_platform_ioremap_resource(pdev, 0);
1344e5e7a7dSBin Du 	if (IS_ERR(isp_subdev->mmio))
1354e5e7a7dSBin Du 		return dev_err_probe(dev, PTR_ERR(isp_subdev->mmio),
1364e5e7a7dSBin Du 				     "isp ioremap fail\n");
1374e5e7a7dSBin Du 
1389a54c285SBin Du 	for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) {
1399a54c285SBin Du 		irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num);
1409a54c285SBin Du 		if (irq[i] < 0)
1419a54c285SBin Du 			return dev_err_probe(dev, irq[i],
1429a54c285SBin Du 					     "fail to get irq %d\n",
1439a54c285SBin Du 					     isp4_irq[i].rb_int_num);
1449a54c285SBin Du 
1459a54c285SBin Du 		ret = devm_request_irq(dev, irq[i], isp4_irq_handler,
1464e5e7a7dSBin Du 				       IRQF_NO_AUTOEN, isp4_irq[i].name,
1474e5e7a7dSBin Du 				       isp_subdev);
1489a54c285SBin Du 		if (ret)
1499a54c285SBin Du 			return dev_err_probe(dev, ret, "fail to req irq %d\n",
1509a54c285SBin Du 					     irq[i]);
1519a54c285SBin Du 	}
1529a54c285SBin Du 
1539a54c285SBin Du 	isp_dev->v4l2_dev.mdev = &isp_dev->mdev;
1549a54c285SBin Du 
1559a54c285SBin Du 	strscpy(isp_dev->mdev.model, "amd_isp41_mdev",
1569a54c285SBin Du 		sizeof(isp_dev->mdev.model));
1579a54c285SBin Du 	isp_dev->mdev.dev = dev;
1589a54c285SBin Du 	media_device_init(&isp_dev->mdev);
1599a54c285SBin Du 
1609a54c285SBin Du 	snprintf(isp_dev->v4l2_dev.name, sizeof(isp_dev->v4l2_dev.name),
1619a54c285SBin Du 		 "AMD-V4L2-ROOT");
1629a54c285SBin Du 	ret = v4l2_device_register(dev, &isp_dev->v4l2_dev);
1639a54c285SBin Du 	if (ret) {
1649a54c285SBin Du 		dev_err_probe(dev, ret, "fail register v4l2 device\n");
1659a54c285SBin Du 		goto err_clean_media;
1669a54c285SBin Du 	}
1679a54c285SBin Du 
1689a54c285SBin Du 	pm_runtime_set_suspended(dev);
1699a54c285SBin Du 	pm_runtime_enable(dev);
1704e5e7a7dSBin Du 	spin_lock_init(&isp_subdev->irq_lock);
1714e5e7a7dSBin Du 	ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq);
1724e5e7a7dSBin Du 	if (ret) {
1734e5e7a7dSBin Du 		dev_err_probe(dev, ret, "fail init isp4 sub dev\n");
1744e5e7a7dSBin Du 		goto err_pm_disable;
1754e5e7a7dSBin Du 	}
1764e5e7a7dSBin Du 
1772ccf48afSBin Du 	ret = media_create_pad_link(&isp_dev->isp_subdev.sdev.entity,
1782ccf48afSBin Du 				    0,
1792ccf48afSBin Du 				    &isp_dev->isp_subdev.isp_vdev.vdev.entity,
1802ccf48afSBin Du 				    0,
1812ccf48afSBin Du 				    MEDIA_LNK_FL_ENABLED |
1822ccf48afSBin Du 				    MEDIA_LNK_FL_IMMUTABLE);
1832ccf48afSBin Du 	if (ret) {
1842ccf48afSBin Du 		dev_err_probe(dev, ret, "fail to create pad link\n");
1852ccf48afSBin Du 		goto err_isp4_deinit;
1862ccf48afSBin Du 	}
1872ccf48afSBin Du 
1889a54c285SBin Du 	ret = media_device_register(&isp_dev->mdev);
1899a54c285SBin Du 	if (ret) {
1909a54c285SBin Du 		dev_err_probe(dev, ret, "fail to register media device\n");
1919a54c285SBin Du 		goto err_isp4_deinit;
1929a54c285SBin Du 	}
1939a54c285SBin Du 
1949a54c285SBin Du 	platform_set_drvdata(pdev, isp_dev);
195*ec4bec22SBin Du 	isp_debugfs_create(isp_dev);
1969a54c285SBin Du 
1979a54c285SBin Du 	return 0;
1989a54c285SBin Du 
1999a54c285SBin Du err_isp4_deinit:
2004e5e7a7dSBin Du 	isp4sd_deinit(&isp_dev->isp_subdev);
2014e5e7a7dSBin Du err_pm_disable:
2029a54c285SBin Du 	pm_runtime_disable(dev);
2039a54c285SBin Du 	v4l2_device_unregister(&isp_dev->v4l2_dev);
2049a54c285SBin Du err_clean_media:
2059a54c285SBin Du 	media_device_cleanup(&isp_dev->mdev);
2069a54c285SBin Du 
2079a54c285SBin Du 	return ret;
2089a54c285SBin Du }
2099a54c285SBin Du 
2109a54c285SBin Du static void isp4_capture_remove(struct platform_device *pdev)
2119a54c285SBin Du {
2129a54c285SBin Du 	struct isp4_device *isp_dev = platform_get_drvdata(pdev);
2139a54c285SBin Du 	struct device *dev = &pdev->dev;
2149a54c285SBin Du 
215*ec4bec22SBin Du 	isp_debugfs_remove(isp_dev);
216*ec4bec22SBin Du 
2179a54c285SBin Du 	media_device_unregister(&isp_dev->mdev);
2184e5e7a7dSBin Du 	isp4sd_deinit(&isp_dev->isp_subdev);
2199a54c285SBin Du 	pm_runtime_disable(dev);
2209a54c285SBin Du 	v4l2_device_unregister(&isp_dev->v4l2_dev);
2219a54c285SBin Du 	media_device_cleanup(&isp_dev->mdev);
2229a54c285SBin Du }
2239a54c285SBin Du 
2249a54c285SBin Du static struct platform_driver isp4_capture_drv = {
2259a54c285SBin Du 	.probe = isp4_capture_probe,
2269a54c285SBin Du 	.remove = isp4_capture_remove,
2279a54c285SBin Du 	.driver = {
2289a54c285SBin Du 		.name = ISP4_DRV_NAME,
2299a54c285SBin Du 	}
2309a54c285SBin Du };
2319a54c285SBin Du 
2329a54c285SBin Du module_platform_driver(isp4_capture_drv);
2339a54c285SBin Du 
2349a54c285SBin Du MODULE_ALIAS("platform:" ISP4_DRV_NAME);
2359a54c285SBin Du MODULE_IMPORT_NS("DMA_BUF");
2369a54c285SBin Du 
2379a54c285SBin Du MODULE_DESCRIPTION("AMD ISP4 Driver");
2389a54c285SBin Du MODULE_AUTHOR("Bin Du <bin.du@amd.com>");
2399a54c285SBin Du MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
2409a54c285SBin Du MODULE_LICENSE("GPL");
241