Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/media/platform/nxp/imx8-isi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 14 kB image not shown  

Quelle  imx8-isi-core.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2019-2020 NXP
 */


#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>

#include <media/media-device.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mc.h>

#include "imx8-isi-core.h"

/* -----------------------------------------------------------------------------
 * V4L2 async subdevs
 */


struct mxc_isi_async_subdev {
 struct v4l2_async_connection asd;
 unsigned int port;
};

static inline struct mxc_isi_async_subdev *
asd_to_mxc_isi_async_subdev(struct v4l2_async_connection *asd)
{
 return container_of(asd, struct mxc_isi_async_subdev, asd);
};

static inline struct mxc_isi_dev *
notifier_to_mxc_isi_dev(struct v4l2_async_notifier *n)
{
 return container_of(n, struct mxc_isi_dev, notifier);
};

static int mxc_isi_async_notifier_bound(struct v4l2_async_notifier *notifier,
     struct v4l2_subdev *sd,
     struct v4l2_async_connection *asc)
{
 const unsigned int link_flags = MEDIA_LNK_FL_IMMUTABLE
          | MEDIA_LNK_FL_ENABLED;
 struct mxc_isi_dev *isi = notifier_to_mxc_isi_dev(notifier);
 struct mxc_isi_async_subdev *masd = asd_to_mxc_isi_async_subdev(asc);
 struct media_pad *pad = &isi->crossbar.pads[masd->port];
 struct device_link *link;

 dev_dbg(isi->dev, "Bound subdev %s to crossbar input %u\n", sd->name,
  masd->port);

 /*
 * Enforce suspend/resume ordering between the source (supplier) and
 * the ISI (consumer). The source will be suspended before and resume
 * after the ISI.
 */

 link = device_link_add(isi->dev, sd->dev, DL_FLAG_STATELESS);
 if (!link) {
  dev_err(isi->dev,
   "Failed to create device link to source %s\n", sd->name);
  return -EINVAL;
 }

 return v4l2_create_fwnode_links_to_pad(sd, pad, link_flags);
}

static int mxc_isi_async_notifier_complete(struct v4l2_async_notifier *notifier)
{
 struct mxc_isi_dev *isi = notifier_to_mxc_isi_dev(notifier);
 int ret;

 dev_dbg(isi->dev, "All subdevs bound\n");

 ret = v4l2_device_register_subdev_nodes(&isi->v4l2_dev);
 if (ret < 0) {
  dev_err(isi->dev,
   "Failed to register subdev nodes: %d\n", ret);
  return ret;
 }

 return media_device_register(&isi->media_dev);
}

static const struct v4l2_async_notifier_operations mxc_isi_async_notifier_ops = {
 .bound = mxc_isi_async_notifier_bound,
 .complete = mxc_isi_async_notifier_complete,
};

static int mxc_isi_pipe_register(struct mxc_isi_pipe *pipe)
{
 int ret;

 ret = v4l2_device_register_subdev(&pipe->isi->v4l2_dev, &pipe->sd);
 if (ret < 0)
  return ret;

 return mxc_isi_video_register(pipe, &pipe->isi->v4l2_dev);
}

static void mxc_isi_pipe_unregister(struct mxc_isi_pipe *pipe)
{
 mxc_isi_video_unregister(pipe);
}

static int mxc_isi_v4l2_init(struct mxc_isi_dev *isi)
{
 struct fwnode_handle *node = dev_fwnode(isi->dev);
 struct media_device *media_dev = &isi->media_dev;
 struct v4l2_device *v4l2_dev = &isi->v4l2_dev;
 unsigned int i;
 int ret;

 /* Initialize the media device. */
 strscpy(media_dev->model, "FSL Capture Media Device",
  sizeof(media_dev->model));
 media_dev->dev = isi->dev;

 media_device_init(media_dev);

 /* Initialize and register the V4L2 device. */
 v4l2_dev->mdev = media_dev;
 strscpy(v4l2_dev->name, "mx8-img-md"sizeof(v4l2_dev->name));

 ret = v4l2_device_register(isi->dev, v4l2_dev);
 if (ret < 0) {
  dev_err(isi->dev,
   "Failed to register V4L2 device: %d\n", ret);
  goto err_media;
 }

 /* Register the crossbar switch subdev. */
 ret = mxc_isi_crossbar_register(&isi->crossbar);
 if (ret < 0) {
  dev_err(isi->dev, "Failed to register crossbar: %d\n", ret);
  goto err_v4l2;
 }

 /* Register the pipeline subdevs and link them to the crossbar switch. */
 for (i = 0; i < isi->pdata->num_channels; ++i) {
  struct mxc_isi_pipe *pipe = &isi->pipes[i];

  ret = mxc_isi_pipe_register(pipe);
  if (ret < 0) {
   dev_err(isi->dev, "Failed to register pipe%u: %d\n", i,
    ret);
   goto err_v4l2;
  }

  ret = media_create_pad_link(&isi->crossbar.sd.entity,
         isi->crossbar.num_sinks + i,
         &pipe->sd.entity,
         MXC_ISI_PIPE_PAD_SINK,
         MEDIA_LNK_FL_IMMUTABLE |
         MEDIA_LNK_FL_ENABLED);
  if (ret < 0)
   goto err_v4l2;
 }

 /* Register the M2M device. */
 ret = mxc_isi_m2m_register(isi, v4l2_dev);
 if (ret < 0) {
  dev_err(isi->dev, "Failed to register M2M device: %d\n", ret);
  goto err_v4l2;
 }

 /* Initialize, fill and register the async notifier. */
 v4l2_async_nf_init(&isi->notifier, v4l2_dev);
 isi->notifier.ops = &mxc_isi_async_notifier_ops;

 for (i = 0; i < isi->pdata->num_ports; ++i) {
  struct mxc_isi_async_subdev *masd;
  struct fwnode_handle *ep;

  ep = fwnode_graph_get_endpoint_by_id(node, i, 0,
           FWNODE_GRAPH_ENDPOINT_NEXT);

  if (!ep)
   continue;

  masd = v4l2_async_nf_add_fwnode_remote(&isi->notifier, ep,
             struct mxc_isi_async_subdev);
  fwnode_handle_put(ep);

  if (IS_ERR(masd)) {
   ret = PTR_ERR(masd);
   goto err_m2m;
  }

  masd->port = i;
 }

 ret = v4l2_async_nf_register(&isi->notifier);
 if (ret < 0) {
  dev_err(isi->dev,
   "Failed to register async notifier: %d\n", ret);
  goto err_m2m;
 }

 return 0;

err_m2m:
 mxc_isi_m2m_unregister(isi);
 v4l2_async_nf_cleanup(&isi->notifier);
err_v4l2:
 v4l2_device_unregister(v4l2_dev);
err_media:
 media_device_cleanup(media_dev);
 return ret;
}

static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev *isi)
{
 unsigned int i;

 v4l2_async_nf_unregister(&isi->notifier);
 v4l2_async_nf_cleanup(&isi->notifier);

 v4l2_device_unregister(&isi->v4l2_dev);
 media_device_unregister(&isi->media_dev);

 mxc_isi_m2m_unregister(isi);

 for (i = 0; i < isi->pdata->num_channels; ++i)
  mxc_isi_pipe_unregister(&isi->pipes[i]);

 mxc_isi_crossbar_unregister(&isi->crossbar);

 media_device_cleanup(&isi->media_dev);
}

/* -----------------------------------------------------------------------------
 * Device information
 */


/* Panic will assert when the buffers are 50% full */

/* For i.MX8MN ISI IER version */
static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v1 = {
 .oflw_y_buf_en = { .mask = BIT(19) },
 .oflw_u_buf_en = { .mask = BIT(21) },
 .oflw_v_buf_en = { .mask = BIT(23) },

 .panic_y_buf_en = { .mask = BIT(20) },
 .panic_u_buf_en = { .mask = BIT(22) },
 .panic_v_buf_en = { .mask = BIT(24) },
};

/* For i.MX8QXP C0 and i.MX8MP ISI IER version */
static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v2 = {
 .oflw_y_buf_en = { .mask = BIT(18) },
 .oflw_u_buf_en = { .mask = BIT(20) },
 .oflw_v_buf_en = { .mask = BIT(22) },

 .panic_y_buf_en = { .mask = BIT(19) },
 .panic_u_buf_en = { .mask = BIT(21) },
 .panic_v_buf_en = { .mask = BIT(23) },
};

/* For i.MX8QM ISI IER version */
static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_qm = {
 .oflw_y_buf_en = { .mask = BIT(16) },
 .oflw_u_buf_en = { .mask = BIT(19) },
 .oflw_v_buf_en = { .mask = BIT(22) },

 .excs_oflw_y_buf_en = { .mask = BIT(17) },
 .excs_oflw_u_buf_en = { .mask = BIT(20) },
 .excs_oflw_v_buf_en = { .mask = BIT(23) },

 .panic_y_buf_en = { .mask = BIT(18) },
 .panic_u_buf_en = { .mask = BIT(21) },
 .panic_v_buf_en = { .mask = BIT(24) },
};

/* Panic will assert when the buffers are 50% full */
static const struct mxc_isi_set_thd mxc_imx8_isi_thd_v1 = {
 .panic_set_thd_y = { .mask = 0x0000f, .offset = 0,  .threshold = 0x7 },
 .panic_set_thd_u = { .mask = 0x00f00, .offset = 8,  .threshold = 0x7 },
 .panic_set_thd_v = { .mask = 0xf0000, .offset = 16, .threshold = 0x7 },
};

static const struct mxc_isi_plat_data mxc_imx8mn_data = {
 .model   = MXC_ISI_IMX8MN,
 .num_ports  = 1,
 .num_channels  = 1,
 .reg_offset  = 0,
 .ier_reg  = &mxc_imx8_isi_ier_v1,
 .set_thd  = &mxc_imx8_isi_thd_v1,
 .buf_active_reverse = false,
 .gasket_ops  = &mxc_imx8_gasket_ops,
 .has_36bit_dma  = false,
};

static const struct mxc_isi_plat_data mxc_imx8mp_data = {
 .model   = MXC_ISI_IMX8MP,
 .num_ports  = 2,
 .num_channels  = 2,
 .reg_offset  = 0x2000,
 .ier_reg  = &mxc_imx8_isi_ier_v2,
 .set_thd  = &mxc_imx8_isi_thd_v1,
 .buf_active_reverse = true,
 .gasket_ops  = &mxc_imx8_gasket_ops,
 .has_36bit_dma  = true,
};

static const struct mxc_isi_plat_data mxc_imx8ulp_data = {
 .model   = MXC_ISI_IMX8ULP,
 .num_ports  = 1,
 .num_channels  = 1,
 .reg_offset  = 0x0,
 .ier_reg  = &mxc_imx8_isi_ier_v2,
 .set_thd  = &mxc_imx8_isi_thd_v1,
 .buf_active_reverse = true,
 .has_36bit_dma  = false,
};

static const struct mxc_isi_plat_data mxc_imx93_data = {
 .model   = MXC_ISI_IMX93,
 .num_ports  = 1,
 .num_channels  = 1,
 .reg_offset  = 0,
 .ier_reg  = &mxc_imx8_isi_ier_v2,
 .set_thd  = &mxc_imx8_isi_thd_v1,
 .buf_active_reverse = true,
 .gasket_ops  = &mxc_imx93_gasket_ops,
 .has_36bit_dma  = false,
};

static const struct mxc_isi_plat_data mxc_imx8qm_data = {
 .model   = MXC_ISI_IMX8QM,
 .num_ports  = 5,
 .num_channels  = 8,
 .reg_offset  = 0x10000,
 .ier_reg  = &mxc_imx8_isi_ier_qm,
 .set_thd  = &mxc_imx8_isi_thd_v1,
 .buf_active_reverse = true,
 .has_36bit_dma  = false,
};

static const struct mxc_isi_plat_data mxc_imx8qxp_data = {
 .model   = MXC_ISI_IMX8QXP,
 .num_ports  = 5,
 .num_channels  = 6,
 .reg_offset  = 0x10000,
 .ier_reg  = &mxc_imx8_isi_ier_v2,
 .set_thd  = &mxc_imx8_isi_thd_v1,
 .buf_active_reverse = true,
 .has_36bit_dma  = false,
};

/* -----------------------------------------------------------------------------
 * Power management
 */


static int mxc_isi_pm_suspend(struct device *dev)
{
 struct mxc_isi_dev *isi = dev_get_drvdata(dev);
 unsigned int i;

 for (i = 0; i < isi->pdata->num_channels; ++i) {
  struct mxc_isi_pipe *pipe = &isi->pipes[i];

  mxc_isi_video_suspend(pipe);
 }

 return pm_runtime_force_suspend(dev);
}

static int mxc_isi_pm_resume(struct device *dev)
{
 struct mxc_isi_dev *isi = dev_get_drvdata(dev);
 unsigned int i;
 int err = 0;
 int ret;

 ret = pm_runtime_force_resume(dev);
 if (ret < 0)
  return ret;

 for (i = 0; i < isi->pdata->num_channels; ++i) {
  struct mxc_isi_pipe *pipe = &isi->pipes[i];

  ret = mxc_isi_video_resume(pipe);
  if (ret) {
   dev_err(dev, "Failed to resume pipeline %u (%d)\n", i,
    ret);
   /*
 * Record the last error as it's as meaningful as any,
 * and continue resuming the other pipelines.
 */

   err = ret;
  }
 }

 return err;
}

static int mxc_isi_runtime_suspend(struct device *dev)
{
 struct mxc_isi_dev *isi = dev_get_drvdata(dev);

 clk_bulk_disable_unprepare(isi->num_clks, isi->clks);

 return 0;
}

static int mxc_isi_runtime_resume(struct device *dev)
{
 struct mxc_isi_dev *isi = dev_get_drvdata(dev);
 int ret;

 ret = clk_bulk_prepare_enable(isi->num_clks, isi->clks);
 if (ret) {
  dev_err(dev, "Failed to enable clocks (%d)\n", ret);
  return ret;
 }

 return 0;
}

static const struct dev_pm_ops mxc_isi_pm_ops = {
 SYSTEM_SLEEP_PM_OPS(mxc_isi_pm_suspend, mxc_isi_pm_resume)
 RUNTIME_PM_OPS(mxc_isi_runtime_suspend, mxc_isi_runtime_resume, NULL)
};

/* -----------------------------------------------------------------------------
 * Probe, remove & driver
 */


static int mxc_isi_probe(struct platform_device *pdev)
{
 struct device *dev = &pdev->dev;
 struct mxc_isi_dev *isi;
 unsigned int dma_size;
 unsigned int i;
 int ret = 0;

 isi = devm_kzalloc(dev, sizeof(*isi), GFP_KERNEL);
 if (!isi)
  return -ENOMEM;

 isi->dev = dev;
 platform_set_drvdata(pdev, isi);

 isi->pdata = of_device_get_match_data(dev);

 isi->pipes = kcalloc(isi->pdata->num_channels, sizeof(isi->pipes[0]),
        GFP_KERNEL);
 if (!isi->pipes)
  return -ENOMEM;

 isi->num_clks = devm_clk_bulk_get_all(dev, &isi->clks);
 if (isi->num_clks < 0)
  return dev_err_probe(dev, isi->num_clks, "Failed to get clocks\n");

 isi->regs = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(isi->regs))
  return dev_err_probe(dev, PTR_ERR(isi->regs),
         "Failed to get ISI register map\n");

 if (isi->pdata->gasket_ops) {
  isi->gasket = syscon_regmap_lookup_by_phandle(dev->of_node,
             "fsl,blk-ctrl");
  if (IS_ERR(isi->gasket))
   return dev_err_probe(dev, PTR_ERR(isi->gasket),
          "failed to get gasket\n");
 }

 dma_size = isi->pdata->has_36bit_dma ? 36 : 32;
 dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));

 pm_runtime_enable(dev);

 ret = mxc_isi_crossbar_init(isi);
 if (ret) {
  dev_err(dev, "Failed to initialize crossbar: %d\n", ret);
  goto err_pm;
 }

 for (i = 0; i < isi->pdata->num_channels; ++i) {
  ret = mxc_isi_pipe_init(isi, i);
  if (ret < 0) {
   dev_err(dev, "Failed to initialize pipe%u: %d\n", i,
    ret);
   goto err_xbar;
  }
 }

 ret = mxc_isi_v4l2_init(isi);
 if (ret < 0) {
  dev_err(dev, "Failed to initialize V4L2: %d\n", ret);
  goto err_xbar;
 }

 mxc_isi_debug_init(isi);

 return 0;

err_xbar:
 mxc_isi_crossbar_cleanup(&isi->crossbar);
err_pm:
 pm_runtime_disable(isi->dev);
 return ret;
}

static void mxc_isi_remove(struct platform_device *pdev)
{
 struct mxc_isi_dev *isi = platform_get_drvdata(pdev);
 unsigned int i;

 mxc_isi_debug_cleanup(isi);

 for (i = 0; i < isi->pdata->num_channels; ++i) {
  struct mxc_isi_pipe *pipe = &isi->pipes[i];

  mxc_isi_pipe_cleanup(pipe);
 }

 mxc_isi_crossbar_cleanup(&isi->crossbar);
 mxc_isi_v4l2_cleanup(isi);

 pm_runtime_disable(isi->dev);
}

static const struct of_device_id mxc_isi_of_match[] = {
 { .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data },
 { .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data },
 { .compatible = "fsl,imx8qm-isi", .data = &mxc_imx8qm_data },
 { .compatible = "fsl,imx8qxp-isi", .data = &mxc_imx8qxp_data },
 { .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data },
 { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data },
 { /* 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  = pm_ptr(&mxc_isi_pm_ops),
 }
};
module_platform_driver(mxc_isi_driver);

MODULE_ALIAS("ISI");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("IMX8 Image Sensing Interface driver");
MODULE_LICENSE("GPL");

Messung V0.5
C=96 H=87 G=91

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.