summaryrefslogtreecommitdiff
path: root/drivers/media/platform/imx8/mxc-isi-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/imx8/mxc-isi-core.c')
-rw-r--r--drivers/media/platform/imx8/mxc-isi-core.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/drivers/media/platform/imx8/mxc-isi-core.c b/drivers/media/platform/imx8/mxc-isi-core.c
new file mode 100644
index 000000000000..b72eadd093f0
--- /dev/null
+++ b/drivers/media/platform/imx8/mxc-isi-core.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017-2018 NXP
+ */
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "mxc-media-dev.h"
+
+static irqreturn_t mxc_isi_irq_handler(int irq, void *priv)
+{
+ struct mxc_isi_dev *mxc_isi = priv;
+ struct device *dev = &mxc_isi->pdev->dev;
+ u32 status;
+
+ spin_lock(&mxc_isi->slock);
+
+ status = mxc_isi_get_irq_status(mxc_isi);
+ mxc_isi_clean_irq_status(mxc_isi, status);
+
+ if (status & CHNL_STS_FRM_STRD_MASK)
+ mxc_isi_frame_write_done(mxc_isi);
+
+ if (status & (CHNL_STS_AXI_WR_ERR_Y_MASK |
+ CHNL_STS_AXI_WR_ERR_U_MASK |
+ CHNL_STS_AXI_WR_ERR_V_MASK))
+ dev_dbg(dev, "%s, IRQ AXI Error stat=0x%X\n", __func__, status);
+ if (status & (CHNL_STS_OFLW_PANIC_Y_BUF_MASK |
+ CHNL_STS_OFLW_PANIC_U_BUF_MASK |
+ CHNL_STS_OFLW_PANIC_V_BUF_MASK))
+ dev_dbg(dev, "%s, IRQ Panic OFLW Error stat=0x%X\n", __func__, status);
+ if (status & (CHNL_STS_OFLW_Y_BUF_MASK |
+ CHNL_STS_OFLW_U_BUF_MASK |
+ CHNL_STS_OFLW_V_BUF_MASK))
+ dev_dbg(dev, "%s, IRQ OFLW Error stat=0x%X\n", __func__, status);
+ if (status & (CHNL_STS_EXCS_OFLW_Y_BUF_MASK |
+ CHNL_STS_EXCS_OFLW_U_BUF_MASK |
+ CHNL_STS_EXCS_OFLW_V_BUF_MASK))
+ dev_dbg(dev, "%s, IRQ EXCS OFLW Error stat=0x%X\n", __func__, status);
+
+ spin_unlock(&mxc_isi->slock);
+ return IRQ_HANDLED;
+}
+
+/**
+ * mxc_isi_adjust_mplane_format - adjust bytesperline or sizeimage
+ */
+void mxc_isi_adjust_mplane_format(struct mxc_isi_fmt *fmt, u32 width, u32 height,
+ struct v4l2_pix_format_mplane *pix)
+{
+ u32 bytesperline = 0;
+ int i;
+
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ pix->field = V4L2_FIELD_NONE;
+ pix->num_planes = fmt->memplanes;
+ pix->pixelformat = fmt->fourcc;
+ pix->height = height;
+ pix->width = width;
+
+ for (i = 0; i < pix->num_planes; ++i) {
+ struct v4l2_plane_pix_format *plane_fmt = &pix->plane_fmt[i];
+ u32 bpl = plane_fmt->bytesperline;
+
+ if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))
+ bpl = pix->width; /* Planar */
+
+ if (fmt->colplanes == 1 && /* Packed */
+ (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
+ bpl = (pix->width * fmt->depth[0]) / 8;
+
+ if (i == 0)
+ bytesperline = bpl;
+ else if (i == 1 && fmt->memplanes == 3)
+ bytesperline /= 2;
+
+ plane_fmt->bytesperline = bytesperline;
+ plane_fmt->sizeimage = max((pix->width * pix->height *
+ fmt->depth[i]) / 8, plane_fmt->sizeimage);
+ }
+}
+
+static int mxc_isi_parse_dt(struct mxc_isi_dev *mxc_isi)
+{
+ struct device *dev = &mxc_isi->pdev->dev;
+ struct device_node *node = dev->of_node;
+ int ret = 0;
+
+ mxc_isi->id = of_alias_get_id(node, "isi");
+
+ ret = of_property_read_u32_array(node, "interface",
+ mxc_isi->interface, 3);
+ if (ret < 0)
+ return ret;
+
+ mxc_isi->parallel_csi = of_property_read_bool(node, "parallel_csi");
+
+ dev_dbg(dev, "%s, isi_%d,interface(%d, %d, %d)\n", __func__, mxc_isi->id,
+ mxc_isi->interface[0], mxc_isi->interface[1], mxc_isi->interface[2]);
+
+ mxc_isi->chain_buf = of_property_read_bool(node, "fsl,chain_buf");
+ return 0;
+}
+
+static int mxc_isi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxc_isi_dev *mxc_isi;
+ struct resource *res;
+ int ret = 0;
+
+ mxc_isi = devm_kzalloc(dev, sizeof(*mxc_isi), GFP_KERNEL);
+ if (!mxc_isi)
+ return -ENOMEM;
+
+ mxc_isi->pdev = pdev;
+
+ ret = mxc_isi_parse_dt(mxc_isi);
+ if (ret < 0)
+ return ret;
+
+ if (mxc_isi->id >= MXC_ISI_MAX_DEVS || mxc_isi->id < 0) {
+ dev_err(dev, "Invalid driver data or device id (%d)\n",
+ mxc_isi->id);
+ return -EINVAL;
+ }
+
+ init_waitqueue_head(&mxc_isi->irq_queue);
+ spin_lock_init(&mxc_isi->slock);
+ mutex_init(&mxc_isi->lock);
+
+ mxc_isi->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(mxc_isi->clk)) {
+ dev_err(dev, "failed to get isi clk\n");
+ return PTR_ERR(mxc_isi->clk);
+ }
+ ret = clk_prepare(mxc_isi->clk);
+ if (ret < 0) {
+ dev_err(dev, "%s, prepare clk error\n", __func__);
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mxc_isi->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mxc_isi->regs)) {
+ dev_err(dev, "Failed to get ISI register map\n");
+ return PTR_ERR(mxc_isi->regs);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "Failed to get IRQ resource\n");
+ return -ENXIO;
+ }
+
+ ret = devm_request_irq(dev, res->start, mxc_isi_irq_handler,
+ 0, dev_name(dev), mxc_isi);
+ if (ret < 0) {
+ dev_err(dev, "failed to install irq (%d)\n", ret);
+ return -EINVAL;
+ }
+
+ ret = mxc_isi_initialize_capture_subdev(mxc_isi);
+ if (ret < 0) {
+ dev_err(dev, "failed to init cap subdev (%d)\n", ret);
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, mxc_isi);
+
+ ret = clk_enable(mxc_isi->clk);
+ if (ret < 0) {
+ dev_err(dev, "%s, enable clk error\n", __func__);
+ goto err_sclk;
+ }
+
+ mxc_isi_channel_set_chain_buf(mxc_isi);
+ clk_disable_unprepare(mxc_isi->clk);
+
+ pm_runtime_enable(dev);
+
+ dev_dbg(dev, "mxc_isi.%d registered successfully\n", mxc_isi->id);
+
+ return 0;
+
+err_sclk:
+ mxc_isi_unregister_capture_subdev(mxc_isi);
+ return ret;
+}
+
+static int mxc_isi_remove(struct platform_device *pdev)
+{
+ struct mxc_isi_dev *mxc_isi = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ mxc_isi_unregister_capture_subdev(mxc_isi);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mxc_isi_pm_suspend(struct device *dev)
+{
+ return pm_runtime_force_suspend(dev);
+}
+
+static int mxc_isi_pm_resume(struct device *dev)
+{
+ return pm_runtime_force_resume(dev);
+}
+#endif
+
+static int mxc_isi_runtime_suspend(struct device *dev)
+{
+ struct mxc_isi_dev *mxc_isi = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(mxc_isi->clk);
+ return 0;
+}
+
+static int mxc_isi_runtime_resume(struct device *dev)
+{
+ struct mxc_isi_dev *mxc_isi = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(mxc_isi->clk);
+ if (ret)
+ dev_err(dev, "%s clk enable fail\n", __func__);
+
+ return (ret) ? ret : 0;
+}
+
+static const struct dev_pm_ops mxc_isi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mxc_isi_pm_suspend, mxc_isi_pm_resume)
+ SET_RUNTIME_PM_OPS(mxc_isi_runtime_suspend, mxc_isi_runtime_resume, NULL)
+};
+
+static const struct of_device_id mxc_isi_of_match[] = {
+ {.compatible = "fsl,imx8-isi",},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mxc_isi_of_match);
+
+static struct platform_driver mxc_isi_driver = {
+ .probe = mxc_isi_probe,
+ .remove = mxc_isi_remove,
+ .driver = {
+ .of_match_table = mxc_isi_of_match,
+ .name = MXC_ISI_DRIVER_NAME,
+ .pm = &mxc_isi_pm_ops,
+ }
+};
+
+module_platform_driver(mxc_isi_driver);