Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  imx8qxp-pixel-link.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+

/*
 * Copyright 2020,2022 NXP
 */


#include <linux/firmware/imx/svc/misc.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>

#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_print.h>

#include <dt-bindings/firmware/imx/rsrc.h>

#define DRIVER_NAME  "imx8qxp-display-pixel-link"
#define PL_MAX_MST_ADDR  3
#define PL_MAX_NEXT_BRIDGES 2

struct imx8qxp_pixel_link {
 struct drm_bridge bridge;
 struct drm_bridge *next_bridge;
 struct device *dev;
 struct imx_sc_ipc *ipc_handle;
 u8 stream_id;
 u8 dc_id;
 u32 sink_rsc;
 u32 mst_addr;
 u8 mst_addr_ctrl;
 u8 mst_en_ctrl;
 u8 mst_vld_ctrl;
 u8 sync_ctrl;
};

static void imx8qxp_pixel_link_enable_mst_en(struct imx8qxp_pixel_link *pl)
{
 int ret;

 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
          pl->mst_en_ctrl, true);
 if (ret)
  DRM_DEV_ERROR(pl->dev,
         "failed to enable DC%u stream%u pixel link mst_en: %d\n",
         pl->dc_id, pl->stream_id, ret);
}

static void imx8qxp_pixel_link_enable_mst_vld(struct imx8qxp_pixel_link *pl)
{
 int ret;

 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
          pl->mst_vld_ctrl, true);
 if (ret)
  DRM_DEV_ERROR(pl->dev,
         "failed to enable DC%u stream%u pixel link mst_vld: %d\n",
         pl->dc_id, pl->stream_id, ret);
}

static void imx8qxp_pixel_link_enable_sync(struct imx8qxp_pixel_link *pl)
{
 int ret;

 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
          pl->sync_ctrl, true);
 if (ret)
  DRM_DEV_ERROR(pl->dev,
         "failed to enable DC%u stream%u pixel link sync: %d\n",
         pl->dc_id, pl->stream_id, ret);
}

static int imx8qxp_pixel_link_disable_mst_en(struct imx8qxp_pixel_link *pl)
{
 int ret;

 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
          pl->mst_en_ctrl, false);
 if (ret)
  DRM_DEV_ERROR(pl->dev,
         "failed to disable DC%u stream%u pixel link mst_en: %d\n",
         pl->dc_id, pl->stream_id, ret);

 return ret;
}

static int imx8qxp_pixel_link_disable_mst_vld(struct imx8qxp_pixel_link *pl)
{
 int ret;

 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
          pl->mst_vld_ctrl, false);
 if (ret)
  DRM_DEV_ERROR(pl->dev,
         "failed to disable DC%u stream%u pixel link mst_vld: %d\n",
         pl->dc_id, pl->stream_id, ret);

 return ret;
}

static int imx8qxp_pixel_link_disable_sync(struct imx8qxp_pixel_link *pl)
{
 int ret;

 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
          pl->sync_ctrl, false);
 if (ret)
  DRM_DEV_ERROR(pl->dev,
         "failed to disable DC%u stream%u pixel link sync: %d\n",
         pl->dc_id, pl->stream_id, ret);

 return ret;
}

static void imx8qxp_pixel_link_set_mst_addr(struct imx8qxp_pixel_link *pl)
{
 int ret;

 ret = imx_sc_misc_set_control(pl->ipc_handle,
          pl->sink_rsc, pl->mst_addr_ctrl,
          pl->mst_addr);
 if (ret)
  DRM_DEV_ERROR(pl->dev,
         "failed to set DC%u stream%u pixel link mst addr(%u): %d\n",
         pl->dc_id, pl->stream_id, pl->mst_addr, ret);
}

static int imx8qxp_pixel_link_bridge_attach(struct drm_bridge *bridge,
         struct drm_encoder *encoder,
         enum drm_bridge_attach_flags flags)
{
 struct imx8qxp_pixel_link *pl = bridge->driver_private;

 if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
  DRM_DEV_ERROR(pl->dev,
         "do not support creating a drm_connector\n");
  return -EINVAL;
 }

 return drm_bridge_attach(encoder,
     pl->next_bridge, bridge,
     DRM_BRIDGE_ATTACH_NO_CONNECTOR);
}

static void
imx8qxp_pixel_link_bridge_mode_set(struct drm_bridge *bridge,
       const struct drm_display_mode *mode,
       const struct drm_display_mode *adjusted_mode)
{
 struct imx8qxp_pixel_link *pl = bridge->driver_private;

 imx8qxp_pixel_link_set_mst_addr(pl);
}

static void imx8qxp_pixel_link_bridge_atomic_enable(struct drm_bridge *bridge,
          struct drm_atomic_state *state)
{
 struct imx8qxp_pixel_link *pl = bridge->driver_private;

 imx8qxp_pixel_link_enable_mst_en(pl);
 imx8qxp_pixel_link_enable_mst_vld(pl);
 imx8qxp_pixel_link_enable_sync(pl);
}

static void imx8qxp_pixel_link_bridge_atomic_disable(struct drm_bridge *bridge,
           struct drm_atomic_state *state)
{
 struct imx8qxp_pixel_link *pl = bridge->driver_private;

 imx8qxp_pixel_link_disable_mst_en(pl);
 imx8qxp_pixel_link_disable_mst_vld(pl);
 imx8qxp_pixel_link_disable_sync(pl);
}

static const u32 imx8qxp_pixel_link_bus_output_fmts[] = {
 MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
 MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
};

static bool imx8qxp_pixel_link_bus_output_fmt_supported(u32 fmt)
{
 int i;

 for (i = 0; i < ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts); i++) {
  if (imx8qxp_pixel_link_bus_output_fmts[i] == fmt)
   return true;
 }

 return false;
}

static u32 *
imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
          struct drm_bridge_state *bridge_state,
          struct drm_crtc_state *crtc_state,
          struct drm_connector_state *conn_state,
          u32 output_fmt,
          unsigned int *num_input_fmts)
{
 u32 *input_fmts;

 if (!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))
  return NULL;

 *num_input_fmts = 1;

 input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
 if (!input_fmts)
  return NULL;

 input_fmts[0] = output_fmt;

 return input_fmts;
}

static u32 *
imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
           struct drm_bridge_state *bridge_state,
           struct drm_crtc_state *crtc_state,
           struct drm_connector_state *conn_state,
           unsigned int *num_output_fmts)
{
 *num_output_fmts = ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);
 return kmemdup(imx8qxp_pixel_link_bus_output_fmts,
   sizeof(imx8qxp_pixel_link_bus_output_fmts), GFP_KERNEL);
}

static const struct drm_bridge_funcs imx8qxp_pixel_link_bridge_funcs = {
 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
 .atomic_reset  = drm_atomic_helper_bridge_reset,
 .attach   = imx8qxp_pixel_link_bridge_attach,
 .mode_set  = imx8qxp_pixel_link_bridge_mode_set,
 .atomic_enable  = imx8qxp_pixel_link_bridge_atomic_enable,
 .atomic_disable  = imx8qxp_pixel_link_bridge_atomic_disable,
 .atomic_get_input_bus_fmts =
   imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts,
 .atomic_get_output_bus_fmts =
   imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts,
};

static int imx8qxp_pixel_link_disable_all_controls(struct imx8qxp_pixel_link *pl)
{
 int ret;

 ret = imx8qxp_pixel_link_disable_mst_en(pl);
 if (ret)
  return ret;

 ret = imx8qxp_pixel_link_disable_mst_vld(pl);
 if (ret)
  return ret;

 return imx8qxp_pixel_link_disable_sync(pl);
}

static struct drm_bridge *
imx8qxp_pixel_link_find_next_bridge(struct imx8qxp_pixel_link *pl)
{
 struct device_node *np = pl->dev->of_node;
 struct device_node *port, *remote;
 struct drm_bridge *next_bridge[PL_MAX_NEXT_BRIDGES];
 u32 port_id;
 bool found_port = false;
 int reg, ep_cnt = 0;
 /* select the first next bridge by default */
 int bridge_sel = 0;

 for (port_id = 1; port_id <= PL_MAX_MST_ADDR + 1; port_id++) {
  port = of_graph_get_port_by_id(np, port_id);
  if (!port)
   continue;

  if (of_device_is_available(port)) {
   found_port = true;
   of_node_put(port);
   break;
  }

  of_node_put(port);
 }

 if (!found_port) {
  DRM_DEV_ERROR(pl->dev, "no available output port\n");
  return ERR_PTR(-ENODEV);
 }

 for (reg = 0; reg < PL_MAX_NEXT_BRIDGES; reg++) {
  remote = of_graph_get_remote_node(np, port_id, reg);
  if (!remote)
   continue;

  if (!of_device_is_available(remote->parent)) {
   DRM_DEV_DEBUG(pl->dev,
          "port%u endpoint%u remote parent is not available\n",
          port_id, reg);
   of_node_put(remote);
   continue;
  }

  next_bridge[ep_cnt] = of_drm_find_bridge(remote);
  if (!next_bridge[ep_cnt]) {
   of_node_put(remote);
   return ERR_PTR(-EPROBE_DEFER);
  }

  /* specially select the next bridge with companion PXL2DPI */
  if (of_property_present(remote, "fsl,companion-pxl2dpi"))
   bridge_sel = ep_cnt;

  ep_cnt++;

  of_node_put(remote);
 }

 pl->mst_addr = port_id - 1;

 return next_bridge[bridge_sel];
}

static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev)
{
 struct imx8qxp_pixel_link *pl;
 struct device *dev = &pdev->dev;
 struct device_node *np = dev->of_node;
 int ret;

 pl = devm_drm_bridge_alloc(dev, struct imx8qxp_pixel_link, bridge,
       &imx8qxp_pixel_link_bridge_funcs);
 if (IS_ERR(pl))
  return PTR_ERR(pl);

 ret = imx_scu_get_handle(&pl->ipc_handle);
 if (ret) {
  if (ret != -EPROBE_DEFER)
   DRM_DEV_ERROR(dev, "failed to get SCU ipc handle: %d\n",
          ret);
  return ret;
 }

 ret = of_property_read_u8(np, "fsl,dc-id", &pl->dc_id);
 if (ret) {
  DRM_DEV_ERROR(dev, "failed to get DC index: %d\n", ret);
  return ret;
 }

 ret = of_property_read_u8(np, "fsl,dc-stream-id", &pl->stream_id);
 if (ret) {
  DRM_DEV_ERROR(dev, "failed to get DC stream index: %d\n", ret);
  return ret;
 }

 pl->dev = dev;

 pl->sink_rsc = pl->dc_id ? IMX_SC_R_DC_1 : IMX_SC_R_DC_0;

 if (pl->stream_id == 0) {
  pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST1_ADDR;
  pl->mst_en_ctrl   = IMX_SC_C_PXL_LINK_MST1_ENB;
  pl->mst_vld_ctrl  = IMX_SC_C_PXL_LINK_MST1_VLD;
  pl->sync_ctrl     = IMX_SC_C_SYNC_CTRL0;
 } else {
  pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST2_ADDR;
  pl->mst_en_ctrl   = IMX_SC_C_PXL_LINK_MST2_ENB;
  pl->mst_vld_ctrl  = IMX_SC_C_PXL_LINK_MST2_VLD;
  pl->sync_ctrl     = IMX_SC_C_SYNC_CTRL1;
 }

 /* disable all controls to POR default */
 ret = imx8qxp_pixel_link_disable_all_controls(pl);
 if (ret)
  return ret;

 pl->next_bridge = imx8qxp_pixel_link_find_next_bridge(pl);
 if (IS_ERR(pl->next_bridge)) {
  ret = PTR_ERR(pl->next_bridge);
  if (ret != -EPROBE_DEFER)
   DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
          ret);
  return ret;
 }

 platform_set_drvdata(pdev, pl);

 pl->bridge.driver_private = pl;
 pl->bridge.of_node = np;

 drm_bridge_add(&pl->bridge);

 return ret;
}

static void imx8qxp_pixel_link_bridge_remove(struct platform_device *pdev)
{
 struct imx8qxp_pixel_link *pl = platform_get_drvdata(pdev);

 drm_bridge_remove(&pl->bridge);
}

static const struct of_device_id imx8qxp_pixel_link_dt_ids[] = {
 { .compatible = "fsl,imx8qm-dc-pixel-link", },
 { .compatible = "fsl,imx8qxp-dc-pixel-link", },
 { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx8qxp_pixel_link_dt_ids);

static struct platform_driver imx8qxp_pixel_link_bridge_driver = {
 .probe = imx8qxp_pixel_link_bridge_probe,
 .remove = imx8qxp_pixel_link_bridge_remove,
 .driver = {
  .of_match_table = imx8qxp_pixel_link_dt_ids,
  .name = DRIVER_NAME,
 },
};
module_platform_driver(imx8qxp_pixel_link_bridge_driver);

MODULE_DESCRIPTION("i.MX8QXP/QM display pixel link bridge driver");
MODULE_AUTHOR("Liu Ying ");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);

Messung V0.5
C=86 H=98 G=91

¤ Dauer der Verarbeitung: 0.1 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge