From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:55:20
Hi,
This is the v4 series to add some DRM bridge drivers support
for i.MX8qm/qxp SoCs.
The bridges may chain one by one to form display pipes to support
LVDS displays. The relevant display controller is DPU embedded in
i.MX8qm/qxp SoCs.
The DPU KMS driver can be found at:
https://www.spinics.net/lists/arm-kernel/msg871357.html
This series supports the following display pipes:
1) i.MX8qxp:
prefetch eng -> DPU -> pixel combiner -> pixel link ->
pixel link to DPI(PXL2DPI) -> LVDS display bridge(LDB)
2) i.MX8qm:
prefetch eng -> DPU -> pixel combiner -> pixel link -> LVDS display bridge(LDB)
Patch 1/14 adds LVDS PHY configuration options, which has already been sent
with the following series to add Mixel combo PHY found in i.MX8qxp:
https://www.spinics.net/lists/arm-kernel/msg862560.html
Patch 2/14 and 3/14 add bus formats used by PXL2DPI.
Patch 4/14 ~ 13/14 add drm bridge drivers and dt-bindings support for the bridges.
Patch 14/14 updates MAINTAINERS.
I've tested this series with a koe,tx26d202vm0bwa dual link LVDS panel and
a LVDS to HDMI bridge(with a downstream drm bridge driver).
Welcome comments, thanks.
v3->v4:
* Use 'fsl,sc-resource' DT property to get the SCU resource ID associated with
the PXL2DPI instance instead of using alias ID. (Rob)
* Add Rob's R-b tag on patch 11/14.
v2->v3:
* Drop 'fsl,syscon' DT properties from fsl,imx8qxp-ldb.yaml and
fsl,imx8qxp-pxl2dpi.yaml. (Rob)
* Mention the CSR module controls LDB and PXL2DPI in fsl,imx8qxp-ldb.yaml and
fsl,imx8qxp-pxl2dpi.yaml.
* Call syscon_node_to_regmap() to get regmaps from LDB bridge helper driver
and PXL2DPI bridger driver instead of syscon_regmap_lookup_by_phandle().
* Drop two macros from pixel link bridge driver which help define functions
and define them directly.
* Properly disable all pixel link controls to POR value by calling
imx8qxp_pixel_link_disable_all_controls() from
imx8qxp_pixel_link_bridge_probe().
* Add Rob's R-b tags on patch 4/14 and 6/14.
v1->v2:
* Rebase the series upon the latest drm-misc-next branch(5.11-rc2 based).
* Use graph schema in the dt-bindings of the bridges. (Laurent)
* Require all four pixel link output ports in fsl,imx8qxp-pixel-link.yaml.
(Laurent)
* Side note i.MX8qm/qxp LDB official name 'pixel mapper' in fsl,imx8qxp-ldb.yaml.
(Laurent)
* Mention pixel link is accessed via SCU firmware in fsl,imx8qxp-pixel-link.yaml.
(Rob)
* Use enum instead of oneOf + const for the reg property of pixel combiner
channels in fsl,imx8qxp-pixel-combiner.yaml. (Rob)
* Rewrite the function to find the next bridge in pixel link bridge driver
by properly using OF APIs and dropping unnecessary DT validation. (Rob)
* Drop unnecessary port availability check in i.MX8qxp pixel link to DPI
bridge driver.
* Drop unnecessary DT validation from i.MX8qxp LDB bridge driver.
* Use of_graph_get_endpoint_by_regs() and of_graph_get_remote_endpoint() to
get the input remote endpoint in imx8qxp_ldb_set_di_id() of i.MX8qxp LDB
bridge driver.
* Avoid using companion_port OF node after putting it in
imx8qxp_ldb_parse_dt_companion() of i.MX8qxp LDB bridge driver.
* Drop unnecessary check for maximum available LDB channels from
i.MX8qm LDB bridge driver.
* Mention i.MX8qm/qxp LDB official name 'pixel mapper' in i.MX8qm/qxp LDB
bridge drivers and Kconfig help messages.
Liu Ying (14):
phy: Add LVDS configuration options
media: uapi: Add some RGB bus formats for i.MX8qm/qxp pixel combiner
media: docs: Add some RGB bus formats for i.MX8qm/qxp pixel combiner
dt-bindings: display: bridge: Add i.MX8qm/qxp pixel combiner binding
drm/bridge: imx: Add i.MX8qm/qxp pixel combiner support
dt-bindings: display: bridge: Add i.MX8qm/qxp display pixel link
binding
drm/bridge: imx: Add i.MX8qm/qxp display pixel link support
dt-bindings: display: bridge: Add i.MX8qxp pixel link to DPI binding
drm/bridge: imx: Add i.MX8qxp pixel link to DPI support
drm/bridge: imx: Add LDB driver helper support
dt-bindings: display: bridge: Add i.MX8qm/qxp LVDS display bridge
binding
drm/bridge: imx: Add LDB support for i.MX8qxp
drm/bridge: imx: Add LDB support for i.MX8qm
MAINTAINERS: add maintainer for DRM bridge drivers for i.MX SoCs
.../bindings/display/bridge/fsl,imx8qxp-ldb.yaml | 173 +++++
.../display/bridge/fsl,imx8qxp-pixel-combiner.yaml | 144 +++++
.../display/bridge/fsl,imx8qxp-pixel-link.yaml | 106 +++
.../display/bridge/fsl,imx8qxp-pxl2dpi.yaml | 108 ++++
.../userspace-api/media/v4l/subdev-formats.rst | 156 +++++
MAINTAINERS | 10 +
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/imx/Kconfig | 52 ++
drivers/gpu/drm/bridge/imx/Makefile | 6 +
drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 248 +++++++
drivers/gpu/drm/bridge/imx/imx8qm-ldb.c | 585 +++++++++++++++++
drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c | 719 +++++++++++++++++++++
.../gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c | 452 +++++++++++++
drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c | 426 ++++++++++++
drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c | 485 ++++++++++++++
include/drm/bridge/imx_ldb_helper.h | 98 +++
include/linux/phy/phy-lvds.h | 48 ++
include/linux/phy/phy.h | 4 +
include/uapi/linux/media-bus-format.h | 6 +-
20 files changed, 3828 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-ldb.yaml
create mode 100644 Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-combiner.yaml
create mode 100644 Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-link.yaml
create mode 100644 Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pxl2dpi.yaml
create mode 100644 drivers/gpu/drm/bridge/imx/Kconfig
create mode 100644 drivers/gpu/drm/bridge/imx/Makefile
create mode 100644 drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qm-ldb.c
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
create mode 100644 include/drm/bridge/imx_ldb_helper.h
create mode 100644 include/linux/phy/phy-lvds.h
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:55:42
This patch adds RGB666_1X30_CPADLO, RGB888_1X30_CPADLO, RGB666_1X36_CPADLO
and RGB888_1X36_CPADLO bus formats used by i.MX8qm/qxp pixel combiner.
The RGB pixels with padding low per component are transmitted on a 30-bit
input bus(10-bit per component) from a display controller or a 36-bit
output bus(12-bit per component) to a pixel link.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
include/uapi/linux/media-bus-format.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:56:09
This patch allows LVDS PHYs to be configured through
the generic functions and through a custom structure
added to the generic union.
The parameters added here are based on common LVDS PHY
implementation practices. The set of parameters
should cover all potential users.
Cc: Kishon Vijay Abraham I <redacted>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: NXP Linux Team <redacted>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
include/linux/phy/phy-lvds.h | 48 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/phy/phy.h | 4 ++++
2 files changed, 52 insertions(+)
create mode 100644 include/linux/phy/phy-lvds.h
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:56:10
This patch adds documentations for RGB666_1X30_CPADLO, RGB888_1X30_CPADLO,
RGB666_1X36_CPADLO and RGB888_1X36_CPADLO bus formats used by i.MX8qm/qxp
pixel combiner. The RGB pixels with padding low per component are
transmitted on a 30-bit input bus(10-bit per component) from a display
controller or a 36-bit output bus(12-bit per component) to a pixel link.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
.../userspace-api/media/v4l/subdev-formats.rst | 156 +++++++++++++++++++++
1 file changed, 156 insertions(+)
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:56:54
This patch adds a drm bridge driver for i.MX8qm/qxp pixel combiner.
The pixel combiner takes two output streams from a single display
controller and manipulates the two streams to support a number
of modes(bypass, pixel combine, YUV444 to YUV422, split_RGB) configured
as either one screen, two screens, or virtual screens. The pixel
combiner is also responsible for generating some of the control signals
for the pixel link output channel. For now, the driver only supports
the bypass mode.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
.../gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c | 452 +++++++++++++++++++++
5 files changed, 464 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/Kconfig
create mode 100644 drivers/gpu/drm/bridge/imx/Makefile
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
@@ -0,0 +1,452 @@+// SPDX-License-Identifier: GPL-2.0+++/*+*Copyright2020NXP+*/++#include<linux/bitfield.h>+#include<linux/clk.h>+#include<linux/delay.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/of_graph.h>+#include<linux/platform_device.h>+#include<linux/pm_runtime.h>++#include<drm/drm_atomic_state_helper.h>+#include<drm/drm_bridge.h>+#include<drm/drm_print.h>++#define PC_CTRL_REG 0x0+#define PC_COMBINE_ENABLE BIT(0)+#define PC_DISP_BYPASS(n) BIT(1 + 21 * (n))+#define PC_DISP_HSYNC_POLARITY(n) BIT(2 + 11 * (n))+#define PC_DISP_HSYNC_POLARITY_POS(n) DISP_HSYNC_POLARITY(n)+#define PC_DISP_VSYNC_POLARITY(n) BIT(3 + 11 * (n))+#define PC_DISP_VSYNC_POLARITY_POS(n) DISP_VSYNC_POLARITY(n)+#define PC_DISP_DVALID_POLARITY(n) BIT(4 + 11 * (n))+#define PC_DISP_DVALID_POLARITY_POS(n) DISP_DVALID_POLARITY(n)+#define PC_VSYNC_MASK_ENABLE BIT(5)+#define PC_SKIP_MODE BIT(6)+#define PC_SKIP_NUMBER_MASK GENMASK(12, 7)+#define PC_SKIP_NUMBER(n) FIELD_PREP(PC_SKIP_NUMBER_MASK, (n))+#define PC_DISP0_PIX_DATA_FORMAT_MASK GENMASK(18, 16)+#define PC_DISP0_PIX_DATA_FORMAT(fmt) \+FIELD_PREP(PC_DISP0_PIX_DATA_FORMAT_MASK,(fmt))+#define PC_DISP1_PIX_DATA_FORMAT_MASK GENMASK(21, 19)+#define PC_DISP1_PIX_DATA_FORMAT(fmt) \+FIELD_PREP(PC_DISP1_PIX_DATA_FORMAT_MASK,(fmt))++#define PC_BUF_PARA_REG 0x10+#define PC_BUF_ACTIVE_DEPTH_MASK GENMASK(10, 0)+#define PC_BUF_ACTIVE_DEPTH(n) FIELD_PREP(PC_BUF_ACTIVE_DEPTH_MASK, (n))++#define PC_SW_RESET_REG 0x20+#define PC_SW_RESET_N BIT(0)+#define PC_DISP_SW_RESET_N(n) BIT(1 + (n))+#define PC_FULL_RESET_N (PC_SW_RESET_N | \+PC_DISP_SW_RESET_N(0)|\+PC_DISP_SW_RESET_N(1))++#define PC_REG_SET 0x4+#define PC_REG_CLR 0x8++#define DRIVER_NAME "imx8qxp-pixel-combiner"++enumimx8qxp_pc_pix_data_format{+RGB,+YUV444,+YUV422,+SPLIT_RGB,+};++structimx8qxp_pc_channel{+structdrm_bridgebridge;+structdrm_bridge*next_bridge;+structimx8qxp_pc*pc;+unsignedintstream_id;+boolis_available;+};++structimx8qxp_pc{+structdevice*dev;+structimx8qxp_pc_channelch[2];+structclk*clk_apb;+void__iomem*base;+};++staticinlineu32imx8qxp_pc_read(structimx8qxp_pc*pc,unsignedintoffset)+{+returnreadl(pc->base+offset);+}++staticinlinevoid+imx8qxp_pc_write(structimx8qxp_pc*pc,unsignedintoffset,u32value)+{+writel(value,pc->base+offset);+}++staticinlinevoid+imx8qxp_pc_write_set(structimx8qxp_pc*pc,unsignedintoffset,u32value)+{+imx8qxp_pc_write(pc,offset+PC_REG_SET,value);+}++staticinlinevoid+imx8qxp_pc_write_clr(structimx8qxp_pc*pc,unsignedintoffset,u32value)+{+imx8qxp_pc_write(pc,offset+PC_REG_CLR,value);+}++staticenumdrm_mode_status+imx8qxp_pc_bridge_mode_valid(structdrm_bridge*bridge,+conststructdrm_display_info*info,+conststructdrm_display_mode*mode)+{+if(mode->hdisplay>2560)+returnMODE_BAD_HVALUE;++returnMODE_OK;+}++staticintimx8qxp_pc_bridge_attach(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structimx8qxp_pc_channel*ch=bridge->driver_private;+structimx8qxp_pc*pc=ch->pc;++if(!(flags&DRM_BRIDGE_ATTACH_NO_CONNECTOR)){+DRM_DEV_ERROR(pc->dev,+"do not support creating a drm_connector\n");+return-EINVAL;+}++if(!bridge->encoder){+DRM_DEV_ERROR(pc->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+ch->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}++staticvoid+imx8qxp_pc_bridge_mode_set(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structimx8qxp_pc_channel*ch=bridge->driver_private;+structimx8qxp_pc*pc=ch->pc;+u32val;+intret;++ret=pm_runtime_get_sync(pc->dev);+if(ret<0)+DRM_DEV_ERROR(pc->dev,+"failed to get runtime PM sync: %d\n",ret);++ret=clk_prepare_enable(pc->clk_apb);+if(ret)+DRM_DEV_ERROR(pc->dev,"%s: failed to enable apb clock: %d\n",+__func__,ret);++/* HSYNC to pixel link is active low. */+imx8qxp_pc_write_clr(pc,PC_CTRL_REG,+PC_DISP_HSYNC_POLARITY(ch->stream_id));++/* VSYNC to pixel link is active low. */+imx8qxp_pc_write_clr(pc,PC_CTRL_REG,+PC_DISP_VSYNC_POLARITY(ch->stream_id));++/* Data enable to pixel link is active high. */+imx8qxp_pc_write_set(pc,PC_CTRL_REG,+PC_DISP_DVALID_POLARITY(ch->stream_id));++/* Mask the first frame output which may be incomplete. */+imx8qxp_pc_write_set(pc,PC_CTRL_REG,PC_VSYNC_MASK_ENABLE);++/* Only support RGB currently. */+val=imx8qxp_pc_read(pc,PC_CTRL_REG);+if(ch->stream_id==0){+val&=~PC_DISP0_PIX_DATA_FORMAT_MASK;+val|=PC_DISP0_PIX_DATA_FORMAT(RGB);+}else{+val&=~PC_DISP1_PIX_DATA_FORMAT_MASK;+val|=PC_DISP1_PIX_DATA_FORMAT(RGB);+}+imx8qxp_pc_write(pc,PC_CTRL_REG,val);++/* Only support bypass mode currently. */+imx8qxp_pc_write_set(pc,PC_CTRL_REG,PC_DISP_BYPASS(ch->stream_id));++clk_disable_unprepare(pc->clk_apb);+}++staticvoidimx8qxp_pc_bridge_atomic_disable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structimx8qxp_pc_channel*ch=bridge->driver_private;+structimx8qxp_pc*pc=ch->pc;+intret;++ret=pm_runtime_put(pc->dev);+if(ret<0)+DRM_DEV_ERROR(pc->dev,"failed to put runtime PM: %d\n",ret);+}++staticconstu32imx8qxp_pc_bus_output_fmts[]={+MEDIA_BUS_FMT_RGB888_1X36_CPADLO,+MEDIA_BUS_FMT_RGB666_1X36_CPADLO,+};++staticboolimx8qxp_pc_bus_output_fmt_supported(u32fmt)+{+inti;++for(i=0;i<ARRAY_SIZE(imx8qxp_pc_bus_output_fmts);i++){+if(imx8qxp_pc_bus_output_fmts[i]==fmt)+returntrue;+}++returnfalse;+}++staticu32*+imx8qxp_pc_bridge_atomic_get_input_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+u32output_fmt,+unsignedint*num_input_fmts)+{+u32*input_fmts;++if(!imx8qxp_pc_bus_output_fmt_supported(output_fmt))+returnNULL;++*num_input_fmts=1;++input_fmts=kmalloc(sizeof(*input_fmts),GFP_KERNEL);+if(!input_fmts)+returnNULL;++switch(output_fmt){+caseMEDIA_BUS_FMT_RGB888_1X36_CPADLO:+input_fmts[0]=MEDIA_BUS_FMT_RGB888_1X30_CPADLO;+break;+caseMEDIA_BUS_FMT_RGB666_1X36_CPADLO:+input_fmts[0]=MEDIA_BUS_FMT_RGB666_1X30_CPADLO;+break;+default:+kfree(input_fmts);+input_fmts=NULL;+break;+}++returninput_fmts;+}++staticu32*+imx8qxp_pc_bridge_atomic_get_output_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+unsignedint*num_output_fmts)+{+*num_output_fmts=ARRAY_SIZE(imx8qxp_pc_bus_output_fmts);+returnkmemdup(imx8qxp_pc_bus_output_fmts,+sizeof(imx8qxp_pc_bus_output_fmts),GFP_KERNEL);+}++staticconststructdrm_bridge_funcsimx8qxp_pc_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,+.mode_valid=imx8qxp_pc_bridge_mode_valid,+.attach=imx8qxp_pc_bridge_attach,+.mode_set=imx8qxp_pc_bridge_mode_set,+.atomic_disable=imx8qxp_pc_bridge_atomic_disable,+.atomic_get_input_bus_fmts=+imx8qxp_pc_bridge_atomic_get_input_bus_fmts,+.atomic_get_output_bus_fmts=+imx8qxp_pc_bridge_atomic_get_output_bus_fmts,+};++staticintimx8qxp_pc_bridge_probe(structplatform_device*pdev)+{+structimx8qxp_pc*pc;+structimx8qxp_pc_channel*ch;+structdevice*dev=&pdev->dev;+structdevice_node*np=dev->of_node;+structdevice_node*child,*remote;+u32i;+intret;++pc=devm_kzalloc(dev,sizeof(*pc),GFP_KERNEL);+if(!pc)+return-ENOMEM;++pc->base=devm_platform_ioremap_resource(pdev,0);+if(IS_ERR(pc->base))+returnPTR_ERR(pc->base);++pc->dev=dev;++pc->clk_apb=devm_clk_get(dev,"apb");+if(IS_ERR(pc->clk_apb)){+ret=PTR_ERR(pc->clk_apb);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get apb clock: %d\n",+ret);+returnret;+}++platform_set_drvdata(pdev,pc);+pm_runtime_enable(dev);++for_each_available_child_of_node(np,child){+ret=of_property_read_u32(child,"reg",&i);+if(ret||i>1){+ret=-EINVAL;+DRM_DEV_ERROR(dev,+"invalid channel(%u) node address\n",i);+gotofree_child;+}++ch=&pc->ch[i];+ch->pc=pc;+ch->stream_id=i;++remote=of_graph_get_remote_node(child,1,0);+if(!remote){+ret=-ENODEV;+DRM_DEV_ERROR(dev,+"channel%u failed to get port1's remote node: %d\n",+i,ret);+gotofree_child;+}++ch->next_bridge=of_drm_find_bridge(remote);+if(!ch->next_bridge){+of_node_put(remote);+ret=-EPROBE_DEFER;+DRM_DEV_DEBUG_DRIVER(dev,+"channel%u failed to find next bridge: %d\n",+i,ret);+gotofree_child;+}++of_node_put(remote);++ch->bridge.driver_private=ch;+ch->bridge.funcs=&imx8qxp_pc_bridge_funcs;+ch->bridge.of_node=child;+ch->is_available=true;++drm_bridge_add(&ch->bridge);+}++return0;++free_child:+of_node_put(child);++if(i==1&&pc->ch[0].next_bridge)+drm_bridge_remove(&pc->ch[0].bridge);++pm_runtime_disable(dev);+returnret;+}++staticintimx8qxp_pc_bridge_remove(structplatform_device*pdev)+{+structimx8qxp_pc*pc=platform_get_drvdata(pdev);+structimx8qxp_pc_channel*ch;+inti;++for(i=0;i<2;i++){+ch=&pc->ch[i];++if(!ch->is_available)+continue;++drm_bridge_remove(&ch->bridge);+ch->is_available=false;+}++pm_runtime_disable(&pdev->dev);++return0;+}++staticint__maybe_unusedimx8qxp_pc_runtime_suspend(structdevice*dev)+{+structplatform_device*pdev=to_platform_device(dev);+structimx8qxp_pc*pc=platform_get_drvdata(pdev);+intret;++ret=clk_prepare_enable(pc->clk_apb);+if(ret)+DRM_DEV_ERROR(pc->dev,"%s: failed to enable apb clock: %d\n",+__func__,ret);++/* Disable pixel combiner by full reset. */+imx8qxp_pc_write_clr(pc,PC_SW_RESET_REG,PC_FULL_RESET_N);++clk_disable_unprepare(pc->clk_apb);++/* Ensure the reset takes effect. */+usleep_range(10,20);++returnret;+}++staticint__maybe_unusedimx8qxp_pc_runtime_resume(structdevice*dev)+{+structplatform_device*pdev=to_platform_device(dev);+structimx8qxp_pc*pc=platform_get_drvdata(pdev);+intret;++ret=clk_prepare_enable(pc->clk_apb);+if(ret){+DRM_DEV_ERROR(pc->dev,"%s: failed to enable apb clock: %d\n",+__func__,ret);+returnret;+}++/* out of reset */+imx8qxp_pc_write_set(pc,PC_SW_RESET_REG,PC_FULL_RESET_N);++clk_disable_unprepare(pc->clk_apb);++returnret;+}++staticconststructdev_pm_opsimx8qxp_pc_pm_ops={+SET_RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend,+imx8qxp_pc_runtime_resume,NULL)+};++staticconststructof_device_idimx8qxp_pc_dt_ids[]={+{.compatible="fsl,imx8qm-pixel-combiner",},+{.compatible="fsl,imx8qxp-pixel-combiner",},+{/* sentinel */}+};+MODULE_DEVICE_TABLE(of,imx8qxp_pc_dt_ids);++staticstructplatform_driverimx8qxp_pc_bridge_driver={+.probe=imx8qxp_pc_bridge_probe,+.remove=imx8qxp_pc_bridge_remove,+.driver={+.pm=&imx8qxp_pc_pm_ops,+.name=DRIVER_NAME,+.of_match_table=imx8qxp_pc_dt_ids,+},+};+module_platform_driver(imx8qxp_pc_bridge_driver);++MODULE_DESCRIPTION("i.MX8QM/QXP pixel combiner bridge driver");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:"DRIVER_NAME);
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:57:42
This patch adds a drm bridge driver for i.MX8qm LVDS display bridge(LDB)
which is officially named as pixel mapper. The LDB has two channels.
Each of them supports up to 30bpp parallel input color format and can
map the input to VESA or JEIDA standards. The two channels can be used
simultaneously, either in dual mode or split mode. In dual mode, the
two channels output identical data. In split mode, channel0 outputs
odd pixels and channel1 outputs even pixels. This patch supports the
LDB single mode and split mode.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* Drop unnecessary check for maximum available LDB channels.
* Mention i.MX8qm LDB official name 'pixel mapper' in the bridge driver
and Kconfig help message.
drivers/gpu/drm/bridge/imx/Kconfig | 10 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx8qm-ldb.c | 585 ++++++++++++++++++++++++++++++++
3 files changed, 596 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qm-ldb.c
@@ -0,0 +1,585 @@+// SPDX-License-Identifier: GPL-2.0+++/*+*Copyright2020NXP+*/++#include<linux/clk.h>+#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/of_device.h>+#include<linux/of_graph.h>+#include<linux/phy/phy.h>+#include<linux/pm_runtime.h>+#include<linux/regmap.h>++#include<drm/bridge/imx_ldb_helper.h>+#include<drm/drm_atomic_state_helper.h>+#include<drm/drm_bridge.h>+#include<drm/drm_connector.h>+#include<drm/drm_fourcc.h>+#include<drm/drm_of.h>+#include<drm/drm_print.h>++#define LDB_CH0_10BIT_EN (1 << 22)+#define LDB_CH1_10BIT_EN (1 << 23)+#define LDB_CH0_DATA_WIDTH_24BIT (1 << 24)+#define LDB_CH1_DATA_WIDTH_24BIT (1 << 26)+#define LDB_CH0_DATA_WIDTH_30BIT (2 << 24)+#define LDB_CH1_DATA_WIDTH_30BIT (2 << 26)++#define SS_CTRL 0x20+#define CH_HSYNC_M(id) BIT(0 + ((id) * 2))+#define CH_VSYNC_M(id) BIT(1 + ((id) * 2))+#define CH_PHSYNC(id) BIT(0 + ((id) * 2))+#define CH_PVSYNC(id) BIT(1 + ((id) * 2))++#define DRIVER_NAME "imx8qm-ldb"++structimx8qm_ldb_channel{+structldb_channelbase;+structphy*phy;+};++structimx8qm_ldb{+structldbbase;+structdevice*dev;+structimx8qm_ldb_channelchannel[MAX_LDB_CHAN_NUM];+structclk*clk_pixel;+structclk*clk_bypass;+intactive_chno;+};++staticinlinestructimx8qm_ldb_channel*+base_to_imx8qm_ldb_channel(structldb_channel*base)+{+returncontainer_of(base,structimx8qm_ldb_channel,base);+}++staticinlinestructimx8qm_ldb*base_to_imx8qm_ldb(structldb*base)+{+returncontainer_of(base,structimx8qm_ldb,base);+}++staticvoidimx8qm_ldb_set_phy_cfg(structimx8qm_ldb*imx8qm_ldb,+unsignedlongdi_clk,+boolis_split,boolis_slave,+structphy_configure_opts_lvds*phy_cfg)+{+phy_cfg->bits_per_lane_and_dclk_cycle=7;+phy_cfg->lanes=4;+phy_cfg->differential_clk_rate=is_split?di_clk/2:di_clk;+phy_cfg->is_slave=is_slave;+}++staticintimx8qm_ldb_bridge_atomic_check(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+structimx8qm_ldb_channel*imx8qm_ldb_ch=+base_to_imx8qm_ldb_channel(ldb_ch);+structimx8qm_ldb*imx8qm_ldb=base_to_imx8qm_ldb(ldb);+structdrm_display_mode*adj=&crtc_state->adjusted_mode;+unsignedlongdi_clk=adj->clock*1000;+boolis_split=ldb_channel_is_split_link(ldb_ch);+unionphy_configure_optsopts={};+structphy_configure_opts_lvds*phy_cfg=&opts.lvds;+intret;++ret=ldb_bridge_atomic_check_helper(bridge,bridge_state,+crtc_state,conn_state);+if(ret)+returnret;++imx8qm_ldb_set_phy_cfg(imx8qm_ldb,di_clk,is_split,false,phy_cfg);+ret=phy_validate(imx8qm_ldb_ch->phy,PHY_MODE_LVDS,0,&opts);+if(ret<0){+DRM_DEV_DEBUG_DRIVER(imx8qm_ldb->dev,+"failed to validate PHY: %d\n",ret);+returnret;+}++if(is_split){+imx8qm_ldb_ch=+&imx8qm_ldb->channel[imx8qm_ldb->active_chno^1];+imx8qm_ldb_set_phy_cfg(imx8qm_ldb,di_clk,is_split,true,+phy_cfg);+ret=phy_validate(imx8qm_ldb_ch->phy,PHY_MODE_LVDS,0,&opts);+if(ret<0){+DRM_DEV_DEBUG_DRIVER(imx8qm_ldb->dev,+"failed to validate slave PHY: %d\n",ret);+returnret;+}+}++returnret;+}++staticvoid+imx8qm_ldb_bridge_mode_set(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+structimx8qm_ldb_channel*imx8qm_ldb_ch=+base_to_imx8qm_ldb_channel(ldb_ch);+structimx8qm_ldb*imx8qm_ldb=base_to_imx8qm_ldb(ldb);+structdevice*dev=imx8qm_ldb->dev;+unsignedlongdi_clk=adjusted_mode->clock*1000;+boolis_split=ldb_channel_is_split_link(ldb_ch);+unionphy_configure_optsopts={};+structphy_configure_opts_lvds*phy_cfg=&opts.lvds;+u32chno=ldb_ch->chno;+intret;++ret=pm_runtime_get_sync(dev);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to get runtime PM sync: %d\n",ret);++ret=phy_init(imx8qm_ldb_ch->phy);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to initialize PHY: %d\n",ret);++clk_set_rate(imx8qm_ldb->clk_bypass,di_clk);+clk_set_rate(imx8qm_ldb->clk_pixel,di_clk);++imx8qm_ldb_set_phy_cfg(imx8qm_ldb,di_clk,is_split,false,phy_cfg);+ret=phy_configure(imx8qm_ldb_ch->phy,&opts);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to configure PHY: %d\n",ret);++if(is_split){+imx8qm_ldb_ch=+&imx8qm_ldb->channel[imx8qm_ldb->active_chno^1];+imx8qm_ldb_set_phy_cfg(imx8qm_ldb,di_clk,is_split,true,+phy_cfg);+ret=phy_configure(imx8qm_ldb_ch->phy,&opts);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to configure slave PHY: %d\n",+ret);+}++/* input VSYNC signal from pixel link is active low */+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DI0_VS_POL_ACT_LOW;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DI1_VS_POL_ACT_LOW;++switch(ldb_ch->out_bus_format){+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_CH0_DATA_WIDTH_24BIT;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_CH1_DATA_WIDTH_24BIT;+break;+}++ldb_bridge_mode_set_helper(bridge,mode,adjusted_mode);++if(adjusted_mode->flags&DRM_MODE_FLAG_NVSYNC)+regmap_update_bits(ldb->regmap,SS_CTRL,CH_VSYNC_M(chno),0);+elseif(adjusted_mode->flags&DRM_MODE_FLAG_PVSYNC)+regmap_update_bits(ldb->regmap,SS_CTRL,+CH_VSYNC_M(chno),CH_PVSYNC(chno));++if(adjusted_mode->flags&DRM_MODE_FLAG_NHSYNC)+regmap_update_bits(ldb->regmap,SS_CTRL,CH_HSYNC_M(chno),0);+elseif(adjusted_mode->flags&DRM_MODE_FLAG_PHSYNC)+regmap_update_bits(ldb->regmap,SS_CTRL,+CH_HSYNC_M(chno),CH_PHSYNC(chno));+}++staticvoid+imx8qm_ldb_bridge_atomic_enable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+structimx8qm_ldb_channel*imx8qm_ldb_ch=+base_to_imx8qm_ldb_channel(ldb_ch);+structimx8qm_ldb*imx8qm_ldb=base_to_imx8qm_ldb(ldb);+structdevice*dev=imx8qm_ldb->dev;+boolis_split=ldb_channel_is_split_link(ldb_ch);+intret;++clk_prepare_enable(imx8qm_ldb->clk_pixel);+clk_prepare_enable(imx8qm_ldb->clk_bypass);++/* both DI0 and DI1 connect with pixel link, so ok to use DI0 only */+if(ldb_ch->chno==0||is_split){+ldb->ldb_ctrl&=~LDB_CH0_MODE_EN_MASK;+ldb->ldb_ctrl|=LDB_CH0_MODE_EN_TO_DI0;+}+if(ldb_ch->chno==1||is_split){+ldb->ldb_ctrl&=~LDB_CH1_MODE_EN_MASK;+ldb->ldb_ctrl|=LDB_CH1_MODE_EN_TO_DI0;+}++if(is_split){+ret=phy_power_on(imx8qm_ldb->channel[0].phy);+if(ret)+DRM_DEV_ERROR(dev,+"failed to power on channel0 PHY: %d\n",+ret);++ret=phy_power_on(imx8qm_ldb->channel[1].phy);+if(ret)+DRM_DEV_ERROR(dev,+"failed to power on channel1 PHY: %d\n",+ret);+}else{+ret=phy_power_on(imx8qm_ldb_ch->phy);+if(ret)+DRM_DEV_ERROR(dev,"failed to power on PHY: %d\n",ret);+}++ldb_bridge_enable_helper(bridge);+}++staticvoid+imx8qm_ldb_bridge_atomic_disable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+structimx8qm_ldb_channel*imx8qm_ldb_ch=+base_to_imx8qm_ldb_channel(ldb_ch);+structimx8qm_ldb*imx8qm_ldb=base_to_imx8qm_ldb(ldb);+structdevice*dev=imx8qm_ldb->dev;+boolis_split=ldb_channel_is_split_link(ldb_ch);+intret;++ldb_bridge_disable_helper(bridge);++if(is_split){+ret=phy_power_off(imx8qm_ldb->channel[0].phy);+if(ret)+DRM_DEV_ERROR(dev,+"failed to power off channel0 PHY: %d\n",+ret);+ret=phy_power_off(imx8qm_ldb->channel[1].phy);+if(ret)+DRM_DEV_ERROR(dev,+"failed to power off channel1 PHY: %d\n",+ret);+}else{+ret=phy_power_off(imx8qm_ldb_ch->phy);+if(ret)+DRM_DEV_ERROR(dev,"failed to power off PHY: %d\n",ret);+}++clk_disable_unprepare(imx8qm_ldb->clk_bypass);+clk_disable_unprepare(imx8qm_ldb->clk_pixel);++ret=pm_runtime_put(dev);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to put runtime PM: %d\n",ret);+}++staticconstu32imx8qm_ldb_bus_output_fmts[]={+MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,+MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,+MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,+MEDIA_BUS_FMT_FIXED,+};++staticboolimx8qm_ldb_bus_output_fmt_supported(u32fmt)+{+inti;++for(i=0;i<ARRAY_SIZE(imx8qm_ldb_bus_output_fmts);i++){+if(imx8qm_ldb_bus_output_fmts[i]==fmt)+returntrue;+}++returnfalse;+}++staticu32*+imx8qm_ldb_bridge_atomic_get_input_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+u32output_fmt,+unsignedint*num_input_fmts)+{+structdrm_display_info*di;+conststructdrm_format_info*finfo;+u32*input_fmts;++if(!imx8qm_ldb_bus_output_fmt_supported(output_fmt))+returnNULL;++*num_input_fmts=1;++input_fmts=kmalloc(sizeof(*input_fmts),GFP_KERNEL);+if(!input_fmts)+returnNULL;++switch(output_fmt){+caseMEDIA_BUS_FMT_FIXED:+di=&conn_state->connector->display_info;++/*+*Lookatthefirstbusformattodetermineinputformat.+*DefaulttoMEDIA_BUS_FMT_RGB888_1X36_CPADLO,ifnomatch.+*/+if(di->num_bus_formats){+finfo=drm_format_info(di->bus_formats[0]);++input_fmts[0]=finfo->depth==18?+MEDIA_BUS_FMT_RGB666_1X36_CPADLO:+MEDIA_BUS_FMT_RGB888_1X36_CPADLO;+}else{+input_fmts[0]=MEDIA_BUS_FMT_RGB888_1X36_CPADLO;+}+break;+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+input_fmts[0]=MEDIA_BUS_FMT_RGB666_1X36_CPADLO;+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+input_fmts[0]=MEDIA_BUS_FMT_RGB888_1X36_CPADLO;+break;+default:+kfree(input_fmts);+input_fmts=NULL;+break;+}++returninput_fmts;+}++staticu32*+imx8qm_ldb_bridge_atomic_get_output_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+unsignedint*num_output_fmts)+{+*num_output_fmts=ARRAY_SIZE(imx8qm_ldb_bus_output_fmts);+returnkmemdup(imx8qm_ldb_bus_output_fmts,+sizeof(imx8qm_ldb_bus_output_fmts),GFP_KERNEL);+}++staticenumdrm_mode_status+imx8qm_ldb_bridge_mode_valid(structdrm_bridge*bridge,+conststructdrm_display_info*info,+conststructdrm_display_mode*mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+boolis_single=ldb_channel_is_single_link(ldb_ch);++if(mode->clock>300000)+returnMODE_CLOCK_HIGH;++if(mode->clock>150000&&is_single)+returnMODE_CLOCK_HIGH;++returnMODE_OK;+}++staticconststructdrm_bridge_funcsimx8qm_ldb_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,+.mode_valid=imx8qm_ldb_bridge_mode_valid,+.attach=ldb_bridge_attach_helper,+.atomic_check=imx8qm_ldb_bridge_atomic_check,+.mode_set=imx8qm_ldb_bridge_mode_set,+.atomic_enable=imx8qm_ldb_bridge_atomic_enable,+.atomic_disable=imx8qm_ldb_bridge_atomic_disable,+.atomic_get_input_bus_fmts=+imx8qm_ldb_bridge_atomic_get_input_bus_fmts,+.atomic_get_output_bus_fmts=+imx8qm_ldb_bridge_atomic_get_output_bus_fmts,+};++staticintimx8qm_ldb_get_phy(structimx8qm_ldb*imx8qm_ldb)+{+structimx8qm_ldb_channel*imx8qm_ldb_ch;+structldb_channel*ldb_ch;+structdevice*dev=imx8qm_ldb->dev;+inti,ret;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+imx8qm_ldb_ch=&imx8qm_ldb->channel[i];+ldb_ch=&imx8qm_ldb_ch->base;++if(!ldb_ch->is_available)+continue;++imx8qm_ldb_ch->phy=devm_of_phy_get(dev,ldb_ch->np,+"lvds_phy");+if(IS_ERR(imx8qm_ldb_ch->phy)){+ret=PTR_ERR(imx8qm_ldb_ch->phy);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to get channel%d PHY: %d\n",+i,ret);+returnret;+}+}++return0;+}++staticintimx8qm_ldb_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structimx8qm_ldb*imx8qm_ldb;+structimx8qm_ldb_channel*imx8qm_ldb_ch;+structldb*ldb;+structldb_channel*ldb_ch;+structdevice_node*port1,*port2;+intpixel_order;+intret,i;++imx8qm_ldb=devm_kzalloc(dev,sizeof(*imx8qm_ldb),GFP_KERNEL);+if(!imx8qm_ldb)+return-ENOMEM;++imx8qm_ldb->clk_pixel=devm_clk_get(dev,"pixel");+if(IS_ERR(imx8qm_ldb->clk_pixel)){+ret=PTR_ERR(imx8qm_ldb->clk_pixel);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to get pixel clock: %d\n",ret);+returnret;+}++imx8qm_ldb->clk_bypass=devm_clk_get(dev,"bypass");+if(IS_ERR(imx8qm_ldb->clk_bypass)){+ret=PTR_ERR(imx8qm_ldb->clk_bypass);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to get bypass clock: %d\n",ret);+returnret;+}++imx8qm_ldb->dev=dev;++ldb=&imx8qm_ldb->base;+ldb->dev=dev;+ldb->ctrl_reg=0xe0;++for(i=0;i<MAX_LDB_CHAN_NUM;i++)+ldb->channel[i]=&imx8qm_ldb->channel[i].base;++ret=ldb_init_helper(ldb);+if(ret)+returnret;++if(ldb->available_ch_cnt==0){+DRM_DEV_DEBUG_DRIVER(dev,"no available channel\n");+return0;+}++if(ldb->available_ch_cnt==2){+port1=of_graph_get_port_by_id(ldb->channel[0]->np,1);+port2=of_graph_get_port_by_id(ldb->channel[1]->np,1);+pixel_order=+drm_of_lvds_get_dual_link_pixel_order(port1,port2);+of_node_put(port1);+of_node_put(port2);++if(pixel_order!=DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS){+DRM_DEV_ERROR(dev,"invalid dual link pixel order: %d\n",+pixel_order);+return-EINVAL;+}++imx8qm_ldb->active_chno=0;+imx8qm_ldb_ch=&imx8qm_ldb->channel[0];+ldb_ch=&imx8qm_ldb_ch->base;+ldb_ch->link_type=pixel_order;+}else{+for(i=0;i<MAX_LDB_CHAN_NUM;i++){+imx8qm_ldb_ch=&imx8qm_ldb->channel[i];+ldb_ch=&imx8qm_ldb_ch->base;++if(ldb_ch->is_available){+imx8qm_ldb->active_chno=ldb_ch->chno;+break;+}+}+}++ret=imx8qm_ldb_get_phy(imx8qm_ldb);+if(ret)+returnret;++ret=ldb_find_next_bridge_helper(ldb);+if(ret)+returnret;++platform_set_drvdata(pdev,imx8qm_ldb);+pm_runtime_enable(dev);++ldb_add_bridge_helper(ldb,&imx8qm_ldb_bridge_funcs);++returnret;+}++staticintimx8qm_ldb_remove(structplatform_device*pdev)+{+structimx8qm_ldb*imx8qm_ldb=platform_get_drvdata(pdev);+structldb*ldb=&imx8qm_ldb->base;++ldb_remove_bridge_helper(ldb);++pm_runtime_disable(&pdev->dev);++return0;+}++staticint__maybe_unusedimx8qm_ldb_runtime_suspend(structdevice*dev)+{+return0;+}++staticint__maybe_unusedimx8qm_ldb_runtime_resume(structdevice*dev)+{+structimx8qm_ldb*imx8qm_ldb=dev_get_drvdata(dev);+structldb*ldb=&imx8qm_ldb->base;++/* disable LDB by resetting the control register to POR default */+regmap_write(ldb->regmap,ldb->ctrl_reg,0);++return0;+}++staticconststructdev_pm_opsimx8qm_ldb_pm_ops={+SET_RUNTIME_PM_OPS(imx8qm_ldb_runtime_suspend,+imx8qm_ldb_runtime_resume,NULL)+};++staticconststructof_device_idimx8qm_ldb_dt_ids[]={+{.compatible="fsl,imx8qm-ldb"},+{/* sentinel */}+};+MODULE_DEVICE_TABLE(of,imx8qm_ldb_dt_ids);++staticstructplatform_driverimx8qm_ldb_driver={+.probe=imx8qm_ldb_probe,+.remove=imx8qm_ldb_remove,+.driver={+.pm=&imx8qm_ldb_pm_ops,+.name=DRIVER_NAME,+.of_match_table=imx8qm_ldb_dt_ids,+},+};+module_platform_driver(imx8qm_ldb_driver);++MODULE_DESCRIPTION("i.MX8QM LVDS Display Bridge(LDB)/Pixel Mapper bridge driver");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:"DRIVER_NAME);
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
@@ -0,0 +1,106 @@+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)+%YAML1.2+---+$id:http://devicetree.org/schemas/display/bridge/fsl,imx8qxp-pixel-link.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Freescale i.MX8qm/qxp Display Pixel Link++maintainers:+-Liu Ying <victor.liu@nxp.com>++description:|+The Freescale i.MX8qm/qxp Display Pixel Link(DPL) forms a standard+asynchronous linkage between pixel sources(display controller or+camera module) and pixel consumers(imaging or displays).+It consists of two distinct functions, a pixel transfer function and a+control interface. Multiple pixel channels can exist per one control channel.+This binding documentation is only for pixel links whose pixel sources are+display controllers.++The i.MX8qm/qxp Display Pixel Link is accessed via System Controller Unit(SCU)+firmware.++properties:+compatible:+enum:+-fsl,imx8qm-dc-pixel-link+-fsl,imx8qxp-dc-pixel-link++ports:+$ref:/schemas/graph.yaml#/properties/ports++properties:+port@0:+$ref:/schemas/graph.yaml#/properties/port+description:The pixel link input port node from upstream video source.++patternProperties:+"^port@[1-4]$":+$ref:/schemas/graph.yaml#/properties/port+description:The pixel link output port node to downstream bridge.++required:+-port@0+-port@1+-port@2+-port@3+-port@4++required:+-compatible+-ports++additionalProperties:false++examples:+-|+dc0-pixel-link0 {+compatible = "fsl,imx8qxp-dc-pixel-link";++ports {+#address-cells = <1>;+#size-cells = <0>;++/* from dc0 pixel combiner channel0 */+port@0 {+reg = <0>;++dc0_pixel_link0_dc0_pixel_combiner_ch0:endpoint {+remote-endpoint = <&dc0_pixel_combiner_ch0_dc0_pixel_link0>;+};+};++/* to PXL2DPIs in MIPI/LVDS combo subsystems */+port@1 {+#address-cells = <1>;+#size-cells = <0>;+reg = <1>;++dc0_pixel_link0_mipi_lvds_0_pxl2dpi:endpoint@0 {+reg = <0>;+remote-endpoint = <&mipi_lvds_0_pxl2dpi_dc0_pixel_link0>;+};++dc0_pixel_link0_mipi_lvds_1_pxl2dpi:endpoint@1 {+reg = <1>;+remote-endpoint = <&mipi_lvds_1_pxl2dpi_dc0_pixel_link0>;+};+};++/* unused */+port@2 {+reg = <2>;+};++/* unused */+port@3 {+reg = <3>;+};++/* to imaging subsystem */+port@4 {+reg = <4>;+};+};+};
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
@@ -0,0 +1,108 @@+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)+%YAML1.2+---+$id:http://devicetree.org/schemas/display/bridge/fsl,imx8qxp-pxl2dpi.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Freescale i.MX8qxp Pixel Link to Display Pixel Interface++maintainers:+-Liu Ying <victor.liu@nxp.com>++description:|+The Freescale i.MX8qxp Pixel Link to Display Pixel Interface(PXL2DPI)+interfaces the pixel link 36-bit data output and the DSI controller’s+MIPI-DPI 24-bit data input, and inputs of LVDS Display Bridge(LDB) module+used in LVDS mode, to remap the pixel color codings between those modules.+This module is purely combinatorial.++The i.MX8qxp PXL2DPI is controlled by Control and Status Registers(CSR) module.+The CSR module, as a system controller, contains the PXL2DPI's configuration+register.++properties:+compatible:+const:fsl,imx8qxp-pxl2dpi++fsl,sc-resource:+$ref:/schemas/types.yaml#/definitions/uint32+description:The SCU resource ID associated with this PXL2DPI instance.++power-domains:+maxItems:1++fsl,companion-pxl2dpi:+$ref:/schemas/types.yaml#/definitions/phandle+description:|+A phandle which points to companion PXL2DPI which is used by downstream+LVDS Display Bridge(LDB) in split mode.++ports:+$ref:/schemas/graph.yaml#/properties/ports++properties:+port@0:+$ref:/schemas/graph.yaml#/properties/port+description:The PXL2DPI input port node from pixel link.++port@1:+$ref:/schemas/graph.yaml#/properties/port+description:The PXL2DPI output port node to downstream bridge.++required:+-port@0+-port@1++required:+-compatible+-fsl,sc-resource+-power-domains+-ports++additionalProperties:false++examples:+-|+#include <dt-bindings/firmware/imx/rsrc.h>+pxl2dpi {+compatible = "fsl,imx8qxp-pxl2dpi";+fsl,sc-resource = <IMX_SC_R_MIPI_0>;+power-domains = <&pd IMX_SC_R_MIPI_0>;++ports {+#address-cells = <1>;+#size-cells = <0>;++port@0 {+#address-cells = <1>;+#size-cells = <0>;+reg = <0>;++mipi_lvds_0_pxl2dpi_dc_pixel_link0:endpoint@0 {+reg = <0>;+remote-endpoint = <&dc_pixel_link0_mipi_lvds_0_pxl2dpi>;+};++mipi_lvds_0_pxl2dpi_dc_pixel_link1:endpoint@1 {+reg = <1>;+remote-endpoint = <&dc_pixel_link1_mipi_lvds_0_pxl2dpi>;+};+};++port@1 {+#address-cells = <1>;+#size-cells = <0>;+reg = <1>;++mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch0:endpoint@0 {+reg = <0>;+remote-endpoint = <&mipi_lvds_0_ldb_ch0_mipi_lvds_0_pxl2dpi>;+};++mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch1:endpoint@1 {+reg = <1>;+remote-endpoint = <&mipi_lvds_0_ldb_ch1_mipi_lvds_0_pxl2dpi>;+};+};+};+};
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:59:04
This patch adds a drm bridge driver for i.MX8qm/qxp display pixel link.
The pixel link forms a standard asynchronous linkage between
pixel sources(display controller or camera module) and pixel
consumers(imaging or displays). It consists of two distinct
functions, a pixel transfer function and a control interface.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Drop two macros which help define functions and define them directly.
* Properly disable all pixel link controls to POR value by calling
imx8qxp_pixel_link_disable_all_controls() from
imx8qxp_pixel_link_bridge_probe().
v1->v2:
* Rewrite the function to find the next bridge by properly using OF APIs
and dropping unnecessary DT validation. (Rob)
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c | 426 ++++++++++++++++++++++++
3 files changed, 435 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
@@ -0,0 +1,426 @@+// SPDX-License-Identifier: GPL-2.0+++/*+*Copyright2020NXP+*/++#include<linux/firmware/imx/svc/misc.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++structimx8qxp_pixel_link{+structdrm_bridgebridge;+structdrm_bridge*next_bridge;+structdevice*dev;+structimx_sc_ipc*ipc_handle;+intid;+intstream_id;+intdc_id;+u32sink_rsc;+u32mst_addr;+u8mst_addr_ctrl;+u8mst_en_ctrl;+u8mst_vld_ctrl;+u8sync_ctrl;+};++staticvoidimx8qxp_pixel_link_enable_mst_en(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_en: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticvoidimx8qxp_pixel_link_enable_mst_vld(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_vld: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticvoidimx8qxp_pixel_link_enable_sync(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link sync: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticintimx8qxp_pixel_link_disable_mst_en(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_en: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticintimx8qxp_pixel_link_disable_mst_vld(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_vld: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticintimx8qxp_pixel_link_disable_sync(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link sync: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticvoidimx8qxp_pixel_link_set_mst_addr(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst addr(%u): %d\n",+pl->dc_id,pl->stream_id,pl->mst_addr,ret);+}++staticintimx8qxp_pixel_link_bridge_attach(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structimx8qxp_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;+}++if(!bridge->encoder){+DRM_DEV_ERROR(pl->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+pl->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}++staticvoid+imx8qxp_pixel_link_bridge_mode_set(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structimx8qxp_pixel_link*pl=bridge->driver_private;++imx8qxp_pixel_link_set_mst_addr(pl);+}++staticvoidimx8qxp_pixel_link_bridge_atomic_enable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structimx8qxp_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);+}++staticvoidimx8qxp_pixel_link_bridge_atomic_disable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structimx8qxp_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);+}++staticconstu32imx8qxp_pixel_link_bus_output_fmts[]={+MEDIA_BUS_FMT_RGB888_1X36_CPADLO,+MEDIA_BUS_FMT_RGB666_1X36_CPADLO,+};++staticboolimx8qxp_pixel_link_bus_output_fmt_supported(u32fmt)+{+inti;++for(i=0;i<ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);i++){+if(imx8qxp_pixel_link_bus_output_fmts[i]==fmt)+returntrue;+}++returnfalse;+}++staticu32*+imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+u32output_fmt,+unsignedint*num_input_fmts)+{+u32*input_fmts;++if(!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))+returnNULL;++*num_input_fmts=1;++input_fmts=kmalloc(sizeof(*input_fmts),GFP_KERNEL);+if(!input_fmts)+returnNULL;++input_fmts[0]=output_fmt;++returninput_fmts;+}++staticu32*+imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+unsignedint*num_output_fmts)+{+*num_output_fmts=ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);+returnkmemdup(imx8qxp_pixel_link_bus_output_fmts,+sizeof(imx8qxp_pixel_link_bus_output_fmts),GFP_KERNEL);+}++staticconststructdrm_bridge_funcsimx8qxp_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,+};++staticintimx8qxp_pixel_link_disable_all_controls(structimx8qxp_pixel_link*pl)+{+intret;++ret=imx8qxp_pixel_link_disable_mst_en(pl);+if(ret)+returnret;++ret=imx8qxp_pixel_link_disable_mst_vld(pl);+if(ret)+returnret;++returnimx8qxp_pixel_link_disable_sync(pl);+}++staticstructdrm_bridge*+imx8qxp_pixel_link_find_next_bridge(structimx8qxp_pixel_link*pl)+{+structdevice_node*np=pl->dev->of_node;+structdevice_node*port,*remote;+structdrm_bridge*next_bridge[PL_MAX_NEXT_BRIDGES];+u32port_id;+boolfound_port=false;+intreg,ep_cnt=0;+intbridge_sel=0;/* select the first next bridge by default */++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");+returnERR_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);+returnERR_PTR(-EPROBE_DEFER);+}++/* specially select the next bridge with companion PXL2DPI */+if(of_find_property(remote,"fsl,companion-pxl2dpi",NULL))+bridge_sel=ep_cnt;++ep_cnt++;++of_node_put(remote);+}++pl->mst_addr=port_id-1;++returnnext_bridge[bridge_sel];+}++staticintimx8qxp_pixel_link_bridge_probe(structplatform_device*pdev)+{+structimx8qxp_pixel_link*pl;+structdevice*dev=&pdev->dev;+structdevice_node*np=dev->of_node;+intret;++pl=devm_kzalloc(dev,sizeof(*pl),GFP_KERNEL);+if(!pl)+return-ENOMEM;++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);+returnret;+}++pl->id=of_alias_get_id(np,"dc_pl");+if(pl->id<0){+DRM_DEV_ERROR(dev,+"failed to get pixel link node alias id: %d\n",+pl->id);+returnpl->id;+}++pl->dev=dev;++pl->dc_id=pl->id/2;+pl->stream_id=pl->id%2;++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)+returnret;++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);+returnret;+}++platform_set_drvdata(pdev,pl);++pl->bridge.driver_private=pl;+pl->bridge.funcs=&imx8qxp_pixel_link_bridge_funcs;+pl->bridge.of_node=np;++drm_bridge_add(&pl->bridge);++returnret;+}++staticintimx8qxp_pixel_link_bridge_remove(structplatform_device*pdev)+{+structimx8qxp_pixel_link*pl=platform_get_drvdata(pdev);++drm_bridge_remove(&pl->bridge);++return0;+}++staticconststructof_device_idimx8qxp_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);++staticstructplatform_driverimx8qxp_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 <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:"DRIVER_NAME);
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:59:12
Add myself as the maintainer of DRM bridge drivers for i.MX SoCs.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:59:36
This patch adds a drm bridge driver for i.MX8qxp pixel link to display
pixel interface(PXL2DPI). The PXL2DPI interfaces the pixel link 36-bit
data output and the DSI controller’s MIPI-DPI 24-bit data input, and
inputs of LVDS Display Bridge(LDB) module used in LVDS mode, to remap
the pixel color codings between those modules. The PXL2DPI is purely
combinatorial.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* Use 'fsl,sc-resource' DT property to get the SCU resource ID associated with
the PXL2DPI instance instead of using alias ID. (Rob)
v2->v3:
* Call syscon_node_to_regmap() to get regmap instead of
syscon_regmap_lookup_by_phandle().
v1->v2:
* Drop unnecessary port availability check.
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c | 485 +++++++++++++++++++++++++++
3 files changed, 494 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
@@ -0,0 +1,485 @@+// SPDX-License-Identifier: GPL-2.0+++/*+*Copyright2020NXP+*/++#include<linux/firmware/imx/svc/misc.h>+#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/of_device.h>+#include<linux/of_graph.h>+#include<linux/platform_device.h>+#include<linux/pm_runtime.h>+#include<linux/regmap.h>++#include<drm/drm_atomic_state_helper.h>+#include<drm/drm_bridge.h>+#include<drm/drm_of.h>+#include<drm/drm_print.h>++#include<dt-bindings/firmware/imx/rsrc.h>++#define PXL2DPI_CTRL 0x40+#define CFG1_16BIT 0x0+#define CFG2_16BIT 0x1+#define CFG3_16BIT 0x2+#define CFG1_18BIT 0x3+#define CFG2_18BIT 0x4+#define CFG_24BIT 0x5++#define DRIVER_NAME "imx8qxp-pxl2dpi"++structimx8qxp_pxl2dpi{+structregmap*regmap;+structdrm_bridgebridge;+structdrm_bridge*next_bridge;+structdrm_bridge*companion;+structdevice*dev;+structimx_sc_ipc*ipc_handle;+u32sc_resource;+u32in_bus_format;+u32out_bus_format;+u32pl_sel;+};++#define bridge_to_p2d(b) container_of(b, struct imx8qxp_pxl2dpi, bridge)++staticintimx8qxp_pxl2dpi_bridge_attach(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structimx8qxp_pxl2dpi*p2d=bridge->driver_private;++if(!(flags&DRM_BRIDGE_ATTACH_NO_CONNECTOR)){+DRM_DEV_ERROR(p2d->dev,+"do not support creating a drm_connector\n");+return-EINVAL;+}++if(!bridge->encoder){+DRM_DEV_ERROR(p2d->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+p2d->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}++staticint+imx8qxp_pxl2dpi_bridge_atomic_check(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structimx8qxp_pxl2dpi*p2d=bridge->driver_private;++p2d->in_bus_format=bridge_state->input_bus_cfg.format;+p2d->out_bus_format=bridge_state->output_bus_cfg.format;++return0;+}++staticvoid+imx8qxp_pxl2dpi_bridge_mode_set(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structimx8qxp_pxl2dpi*p2d=bridge->driver_private;+structimx8qxp_pxl2dpi*companion_p2d;+intret;++ret=pm_runtime_get_sync(p2d->dev);+if(ret<0)+DRM_DEV_ERROR(p2d->dev,+"failed to get runtime PM sync: %d\n",ret);++ret=imx_sc_misc_set_control(p2d->ipc_handle,p2d->sc_resource,+IMX_SC_C_PXL_LINK_SEL,p2d->pl_sel);+if(ret)+DRM_DEV_ERROR(p2d->dev,+"failed to set pixel link selection(%u): %d\n",+p2d->pl_sel,ret);++switch(p2d->out_bus_format){+caseMEDIA_BUS_FMT_RGB888_1X24:+regmap_write(p2d->regmap,PXL2DPI_CTRL,CFG_24BIT);+break;+caseMEDIA_BUS_FMT_RGB666_1X24_CPADHI:+regmap_write(p2d->regmap,PXL2DPI_CTRL,CFG2_18BIT);+break;+default:+DRM_DEV_ERROR(p2d->dev,+"unsupported output bus format 0x%08x\n",+p2d->out_bus_format);+}++if(p2d->companion){+companion_p2d=bridge_to_p2d(p2d->companion);++companion_p2d->in_bus_format=p2d->in_bus_format;+companion_p2d->out_bus_format=p2d->out_bus_format;++p2d->companion->funcs->mode_set(p2d->companion,mode,+adjusted_mode);+}+}++staticvoid+imx8qxp_pxl2dpi_bridge_atomic_disable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structimx8qxp_pxl2dpi*p2d=bridge->driver_private;+intret;++ret=pm_runtime_put(p2d->dev);+if(ret<0)+DRM_DEV_ERROR(p2d->dev,"failed to put runtime PM: %d\n",ret);++if(p2d->companion)+p2d->companion->funcs->atomic_disable(p2d->companion,+old_bridge_state);+}++staticconstu32imx8qxp_pxl2dpi_bus_output_fmts[]={+MEDIA_BUS_FMT_RGB888_1X24,+MEDIA_BUS_FMT_RGB666_1X24_CPADHI,+};++staticboolimx8qxp_pxl2dpi_bus_output_fmt_supported(u32fmt)+{+inti;++for(i=0;i<ARRAY_SIZE(imx8qxp_pxl2dpi_bus_output_fmts);i++){+if(imx8qxp_pxl2dpi_bus_output_fmts[i]==fmt)+returntrue;+}++returnfalse;+}++staticu32*+imx8qxp_pxl2dpi_bridge_atomic_get_input_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+u32output_fmt,+unsignedint*num_input_fmts)+{+u32*input_fmts;++if(!imx8qxp_pxl2dpi_bus_output_fmt_supported(output_fmt))+returnNULL;++*num_input_fmts=1;++input_fmts=kmalloc(sizeof(*input_fmts),GFP_KERNEL);+if(!input_fmts)+returnNULL;++switch(output_fmt){+caseMEDIA_BUS_FMT_RGB888_1X24:+input_fmts[0]=MEDIA_BUS_FMT_RGB888_1X36_CPADLO;+break;+caseMEDIA_BUS_FMT_RGB666_1X24_CPADHI:+input_fmts[0]=MEDIA_BUS_FMT_RGB666_1X36_CPADLO;+break;+default:+kfree(input_fmts);+input_fmts=NULL;+break;+}++returninput_fmts;+}++staticu32*+imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+unsignedint*num_output_fmts)+{+*num_output_fmts=ARRAY_SIZE(imx8qxp_pxl2dpi_bus_output_fmts);+returnkmemdup(imx8qxp_pxl2dpi_bus_output_fmts,+sizeof(imx8qxp_pxl2dpi_bus_output_fmts),GFP_KERNEL);+}++staticconststructdrm_bridge_funcsimx8qxp_pxl2dpi_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_pxl2dpi_bridge_attach,+.atomic_check=imx8qxp_pxl2dpi_bridge_atomic_check,+.mode_set=imx8qxp_pxl2dpi_bridge_mode_set,+.atomic_disable=imx8qxp_pxl2dpi_bridge_atomic_disable,+.atomic_get_input_bus_fmts=+imx8qxp_pxl2dpi_bridge_atomic_get_input_bus_fmts,+.atomic_get_output_bus_fmts=+imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts,+};++staticstructdevice_node*+imx8qxp_pxl2dpi_get_available_ep_from_port(structimx8qxp_pxl2dpi*p2d,+u32port_id)+{+structdevice_node*port,*ep;+intep_cnt;++port=of_graph_get_port_by_id(p2d->dev->of_node,port_id);+if(!port){+DRM_DEV_ERROR(p2d->dev,"failed to get port@%u\n",port_id);+returnERR_PTR(-ENODEV);+}++ep_cnt=of_get_available_child_count(port);+if(ep_cnt==0){+DRM_DEV_ERROR(p2d->dev,"no available endpoints of port@%u\n",+port_id);+ep=ERR_PTR(-ENODEV);+gotoout;+}elseif(ep_cnt>1){+DRM_DEV_ERROR(p2d->dev,+"invalid available endpoints of port@%u\n",port_id);+ep=ERR_PTR(-ENOTSUPP);+gotoout;+}++ep=of_get_next_available_child(port,NULL);+if(!ep){+DRM_DEV_ERROR(p2d->dev,+"failed to get available endpoint of port@%u\n",+port_id);+ep=ERR_PTR(-ENODEV);+gotoout;+}+out:+of_node_put(port);+returnep;+}++staticstructdrm_bridge*+imx8qxp_pxl2dpi_find_next_bridge(structimx8qxp_pxl2dpi*p2d)+{+structdevice_node*ep,*remote;+structdrm_bridge*next_bridge;+intret;++ep=imx8qxp_pxl2dpi_get_available_ep_from_port(p2d,1);+if(IS_ERR(ep)){+ret=PTR_ERR(ep);+returnERR_PTR(ret);+}++remote=of_graph_get_remote_port_parent(ep);+if(!remote||!of_device_is_available(remote)){+DRM_DEV_ERROR(p2d->dev,"no available remote\n");+next_bridge=ERR_PTR(-ENODEV);+gotoout;+}elseif(!of_device_is_available(remote->parent)){+DRM_DEV_ERROR(p2d->dev,"remote parent is not available\n");+next_bridge=ERR_PTR(-ENODEV);+gotoout;+}++next_bridge=of_drm_find_bridge(remote);+if(!next_bridge){+next_bridge=ERR_PTR(-EPROBE_DEFER);+gotoout;+}+out:+of_node_put(remote);+of_node_put(ep);++returnnext_bridge;+}++staticintimx8qxp_pxl2dpi_set_pixel_link_sel(structimx8qxp_pxl2dpi*p2d)+{+structdevice_node*ep;+structof_endpointendpoint;+intret;++ep=imx8qxp_pxl2dpi_get_available_ep_from_port(p2d,0);+if(IS_ERR(ep))+returnPTR_ERR(ep);++ret=of_graph_parse_endpoint(ep,&endpoint);+if(ret){+DRM_DEV_ERROR(p2d->dev,+"failed to parse endpoint of port@0: %d\n",ret);+gotoout;+}++p2d->pl_sel=endpoint.id;+out:+of_node_put(ep);++returnret;+}++staticintimx8qxp_pxl2dpi_parse_dt_companion(structimx8qxp_pxl2dpi*p2d)+{+structimx8qxp_pxl2dpi*companion_p2d;+structdevice*dev=p2d->dev;+structdevice_node*companion;+structdevice_node*port1,*port2;+conststructof_device_id*match;+intdual_link;+intret=0;++/* Locate the companion PXL2DPI for dual-link operation, if any. */+companion=of_parse_phandle(dev->of_node,"fsl,companion-pxl2dpi",0);+if(!companion)+return0;++if(!of_device_is_available(companion)){+DRM_DEV_ERROR(dev,"companion PXL2DPI is not available\n");+ret=-ENODEV;+gotoout;+}++/*+*Sanitycheck:thecompanionbridgemusthavethesamecompatible+*string.+*/+match=of_match_device(dev->driver->of_match_table,dev);+if(!of_device_is_compatible(companion,match->compatible)){+DRM_DEV_ERROR(dev,"companion PXL2DPI is incompatible\n");+ret=-ENXIO;+gotoout;+}++p2d->companion=of_drm_find_bridge(companion);+if(!p2d->companion){+ret=-EPROBE_DEFER;+DRM_DEV_DEBUG_DRIVER(p2d->dev,+"failed to find companion bridge: %d\n",ret);+gotoout;+}++companion_p2d=bridge_to_p2d(p2d->companion);++/*+*Weneedtoworkoutifthesinkisexpectingustofunctionin+*dual-linkmode.WedothisbylookingattheDTportnodesthat+*thenextbridgesareconnectedto.Iftheyaremarkedasexpecting+*evenpixelsandoddpixelsthanweneedtousethecompanionPXL2DPI.+*/+port1=of_graph_get_port_by_id(p2d->next_bridge->of_node,1);+port2=of_graph_get_port_by_id(companion_p2d->next_bridge->of_node,1);+dual_link=drm_of_lvds_get_dual_link_pixel_order(port1,port2);+of_node_put(port1);+of_node_put(port2);++if(dual_link<0){+ret=dual_link;+DRM_DEV_ERROR(dev,"failed to get dual link pixel order: %d\n",+ret);+gotoout;+}++DRM_DEV_DEBUG_DRIVER(dev,+"dual-link configuration detected (companion bridge %pOF)\n",+companion);+out:+of_node_put(companion);+returnret;+}++staticintimx8qxp_pxl2dpi_bridge_probe(structplatform_device*pdev)+{+structimx8qxp_pxl2dpi*p2d;+structdevice*dev=&pdev->dev;+structdevice_node*np=dev->of_node;+intret;++p2d=devm_kzalloc(dev,sizeof(*p2d),GFP_KERNEL);+if(!p2d)+return-ENOMEM;++p2d->regmap=syscon_node_to_regmap(np->parent);+if(IS_ERR(p2d->regmap)){+ret=PTR_ERR(p2d->regmap);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get regmap: %d\n",ret);+returnret;+}++ret=imx_scu_get_handle(&p2d->ipc_handle);+if(ret){+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get SCU ipc handle: %d\n",+ret);+returnret;+}++p2d->dev=dev;++ret=of_property_read_u32(np,"fsl,sc-resource",&p2d->sc_resource);+if(ret){+DRM_DEV_ERROR(dev,"failed to get SC resource %d\n",ret);+returnret;+}++p2d->next_bridge=imx8qxp_pxl2dpi_find_next_bridge(p2d);+if(IS_ERR(p2d->next_bridge)){+ret=PTR_ERR(p2d->next_bridge);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to find next bridge: %d\n",+ret);+returnret;+}++ret=imx8qxp_pxl2dpi_set_pixel_link_sel(p2d);+if(ret)+returnret;++ret=imx8qxp_pxl2dpi_parse_dt_companion(p2d);+if(ret)+returnret;++platform_set_drvdata(pdev,p2d);+pm_runtime_enable(dev);++p2d->bridge.driver_private=p2d;+p2d->bridge.funcs=&imx8qxp_pxl2dpi_bridge_funcs;+p2d->bridge.of_node=np;++drm_bridge_add(&p2d->bridge);++returnret;+}++staticintimx8qxp_pxl2dpi_bridge_remove(structplatform_device*pdev)+{+structimx8qxp_pxl2dpi*p2d=platform_get_drvdata(pdev);++drm_bridge_remove(&p2d->bridge);++pm_runtime_disable(&pdev->dev);++return0;+}++staticconststructof_device_idimx8qxp_pxl2dpi_dt_ids[]={+{.compatible="fsl,imx8qxp-pxl2dpi",},+{/* sentinel */}+};+MODULE_DEVICE_TABLE(of,imx8qxp_pxl2dpi_dt_ids);++staticstructplatform_driverimx8qxp_pxl2dpi_bridge_driver={+.probe=imx8qxp_pxl2dpi_bridge_probe,+.remove=imx8qxp_pxl2dpi_bridge_remove,+.driver={+.of_match_table=imx8qxp_pxl2dpi_dt_ids,+.name=DRIVER_NAME,+},+};+module_platform_driver(imx8qxp_pxl2dpi_bridge_driver);++MODULE_DESCRIPTION("i.MX8QXP pixel link to DPI bridge driver");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:"DRIVER_NAME);
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:59:58
This patch adds a helper to support LDB drm bridge drivers for
i.MX SoCs. Helper functions exported from this driver should
implement common logics for all LDB modules embedded in i.MX SoCs.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Call syscon_node_to_regmap() to get regmap instead of
syscon_regmap_lookup_by_phandle().
v1->v2:
* No change.
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 248 ++++++++++++++++++++++++++++
include/drm/bridge/imx_ldb_helper.h | 98 +++++++++++
4 files changed, 355 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
create mode 100644 include/drm/bridge/imx_ldb_helper.h
@@ -0,0 +1,248 @@+// SPDX-License-Identifier: GPL-2.0++/*+*Copyright(C)2012SaschaHauer,Pengutronix+*Copyright2019,2020NXP+*/++#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/regmap.h>++#include<drm/bridge/imx_ldb_helper.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>+#include<drm/drm_print.h>++boolldb_channel_is_single_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_SINGLE_LINK;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_single_link);++boolldb_channel_is_split_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS||+ldb_ch->link_type==LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_split_link);++intldb_bridge_atomic_check_helper(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structldb_channel*ldb_ch=bridge->driver_private;++ldb_ch->in_bus_format=bridge_state->input_bus_cfg.format;+ldb_ch->out_bus_format=bridge_state->output_bus_cfg.format;++return0;+}+EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper);++voidldb_bridge_mode_set_helper(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(is_split)+ldb->ldb_ctrl|=LDB_SPLIT_MODE_EN;++switch(ldb_ch->out_bus_format){+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24;+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24|+LDB_BIT_MAP_CH0_JEIDA;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24|+LDB_BIT_MAP_CH1_JEIDA;+break;+}+}+EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper);++voidldb_bridge_enable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++/*+*Platformspecificbridgedriversshouldsetldb_ctrlproperly+*fortheenablement,sojustwritethectrl_reghere.+*/+regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper);++voidldb_bridge_disable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl&=~LDB_CH0_MODE_EN_MASK;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl&=~LDB_CH1_MODE_EN_MASK;++regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper);++intldb_bridge_attach_helper(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++if(!(flags&DRM_BRIDGE_ATTACH_NO_CONNECTOR)){+DRM_DEV_ERROR(ldb->dev,+"do not support creating a drm_connector\n");+return-EINVAL;+}++if(!bridge->encoder){+DRM_DEV_ERROR(ldb->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+ldb_ch->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}+EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper);++intldb_init_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structdevice_node*np=dev->of_node;+structdevice_node*child;+intret;+u32i;++ldb->regmap=syscon_node_to_regmap(np->parent);+if(IS_ERR(ldb->regmap)){+ret=PTR_ERR(ldb->regmap);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get regmap: %d\n",ret);+returnret;+}++for_each_available_child_of_node(np,child){+structldb_channel*ldb_ch;++ret=of_property_read_u32(child,"reg",&i);+if(ret||i>MAX_LDB_CHAN_NUM-1){+ret=-EINVAL;+DRM_DEV_ERROR(dev,+"invalid channel node address: %u\n",i);+of_node_put(child);+returnret;+}++ldb_ch=ldb->channel[i];+ldb_ch->ldb=ldb;+ldb_ch->chno=i;+ldb_ch->is_available=true;+ldb_ch->np=child;++ldb->available_ch_cnt++;+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_init_helper);++intldb_find_next_bridge_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structldb_channel*ldb_ch;+intret,i;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ret=drm_of_find_panel_or_bridge(ldb_ch->np,1,0,+&ldb_ch->panel,+&ldb_ch->next_bridge);+if(ret){+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to find panel or bridge: %d\n",+ret);+returnret;+}++if(ldb_ch->panel){+ldb_ch->next_bridge=devm_drm_panel_bridge_add(dev,+ldb_ch->panel);+if(IS_ERR(ldb_ch->next_bridge)){+ret=PTR_ERR(ldb_ch->next_bridge);+DRM_DEV_ERROR(dev,+"failed to add panel bridge: %d\n",+ret);+returnret;+}+}+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper);++voidldb_add_bridge_helper(structldb*ldb,+conststructdrm_bridge_funcs*bridge_funcs)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ldb_ch->bridge.driver_private=ldb_ch;+ldb_ch->bridge.funcs=bridge_funcs;+ldb_ch->bridge.of_node=ldb_ch->np;++drm_bridge_add(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_add_bridge_helper);++voidldb_remove_bridge_helper(structldb*ldb)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++drm_bridge_remove(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper);++MODULE_DESCRIPTION("Freescale i.MX LVDS Display Bridge driver helper");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:imx-ldb-helper");
From: Liu Ying <victor.liu@nxp.com> Date: 2021-02-18 03:59:58
This patch adds a drm bridge driver for i.MX8qxp LVDS display bridge(LDB)
which is officially named as pixel mapper. The LDB has two channels.
Each of them supports up to 24bpp parallel input color format and can map
the input to VESA or JEIDA standards. The two channels cannot be used
simultaneously, that is to say, the user should pick one of them to use.
Two LDB channels from two LDB instances can work together in LDB split
mode to support a dual link LVDS display. The channel indexes have to be
different. Channel0 outputs odd pixels and channel1 outputs even pixels.
This patch supports the LDB single mode and split mode.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* Drop unnecessary DT validation.
* Use of_graph_get_endpoint_by_regs() and of_graph_get_remote_endpoint() to
get the input remote endpoint in imx8qxp_ldb_set_di_id().
* Avoid using companion_port OF node after putting it in
imx8qxp_ldb_parse_dt_companion().
* Mention i.MX8qxp LDB official name 'pixel mapper' in the bridge driver
and Kconfig help message.
drivers/gpu/drm/bridge/imx/Kconfig | 10 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c | 719 +++++++++++++++++++++++++++++++
3 files changed, 730 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
@@ -0,0 +1,719 @@+// SPDX-License-Identifier: GPL-2.0+++/*+*Copyright2020NXP+*/++#include<linux/clk.h>+#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/of_device.h>+#include<linux/of_graph.h>+#include<linux/phy/phy.h>+#include<linux/pm_runtime.h>+#include<linux/regmap.h>++#include<drm/bridge/imx_ldb_helper.h>+#include<drm/drm_atomic_state_helper.h>+#include<drm/drm_bridge.h>+#include<drm/drm_connector.h>+#include<drm/drm_fourcc.h>+#include<drm/drm_of.h>+#include<drm/drm_print.h>++#define LDB_CH_SEL (1 << 28)++#define SS_CTRL 0x20+#define CH_HSYNC_M(id) BIT(0 + ((id) * 2))+#define CH_VSYNC_M(id) BIT(1 + ((id) * 2))+#define CH_PHSYNC(id) BIT(0 + ((id) * 2))+#define CH_PVSYNC(id) BIT(1 + ((id) * 2))++#define DRIVER_NAME "imx8qxp-ldb"++structimx8qxp_ldb_channel{+structldb_channelbase;+structphy*phy;+unsignedintdi_id;+};++structimx8qxp_ldb{+structldbbase;+structdevice*dev;+structimx8qxp_ldb_channelchannel[MAX_LDB_CHAN_NUM];+structclk*clk_pixel;+structclk*clk_bypass;+structdrm_bridge*companion;+intactive_chno;+};++staticinlinestructimx8qxp_ldb_channel*+base_to_imx8qxp_ldb_channel(structldb_channel*base)+{+returncontainer_of(base,structimx8qxp_ldb_channel,base);+}++staticinlinestructimx8qxp_ldb*base_to_imx8qxp_ldb(structldb*base)+{+returncontainer_of(base,structimx8qxp_ldb,base);+}++staticvoidimx8qxp_ldb_set_phy_cfg(structimx8qxp_ldb*imx8qxp_ldb,+unsignedlongdi_clk,boolis_split,+structphy_configure_opts_lvds*phy_cfg)+{+phy_cfg->bits_per_lane_and_dclk_cycle=7;+phy_cfg->lanes=4;++if(is_split){+phy_cfg->differential_clk_rate=di_clk/2;+phy_cfg->is_slave=!imx8qxp_ldb->companion;+}else{+phy_cfg->differential_clk_rate=di_clk;+phy_cfg->is_slave=false;+}+}++staticintimx8qxp_ldb_bridge_atomic_check(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+structimx8qxp_ldb_channel*imx8qxp_ldb_ch=+base_to_imx8qxp_ldb_channel(ldb_ch);+structimx8qxp_ldb*imx8qxp_ldb=base_to_imx8qxp_ldb(ldb);+structdrm_bridge*companion=imx8qxp_ldb->companion;+structdrm_display_mode*adj=&crtc_state->adjusted_mode;+unsignedlongdi_clk=adj->clock*1000;+boolis_split=ldb_channel_is_split_link(ldb_ch);+unionphy_configure_optsopts={};+structphy_configure_opts_lvds*phy_cfg=&opts.lvds;+intret;++ret=ldb_bridge_atomic_check_helper(bridge,bridge_state,+crtc_state,conn_state);+if(ret)+returnret;++imx8qxp_ldb_set_phy_cfg(imx8qxp_ldb,di_clk,is_split,phy_cfg);+ret=phy_validate(imx8qxp_ldb_ch->phy,PHY_MODE_LVDS,0,&opts);+if(ret<0){+DRM_DEV_DEBUG_DRIVER(imx8qxp_ldb->dev,+"failed to validate PHY: %d\n",ret);+returnret;+}++if(is_split&&companion){+ret=companion->funcs->atomic_check(companion,+bridge_state,crtc_state,conn_state);+if(ret)+returnret;+}++returnret;+}++staticvoid+imx8qxp_ldb_bridge_mode_set(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb_channel*companion_ldb_ch;+structldb*ldb=ldb_ch->ldb;+structimx8qxp_ldb_channel*imx8qxp_ldb_ch=+base_to_imx8qxp_ldb_channel(ldb_ch);+structimx8qxp_ldb*imx8qxp_ldb=base_to_imx8qxp_ldb(ldb);+structdrm_bridge*companion=imx8qxp_ldb->companion;+structdevice*dev=imx8qxp_ldb->dev;+unsignedlongdi_clk=adjusted_mode->clock*1000;+boolis_split=ldb_channel_is_split_link(ldb_ch);+unionphy_configure_optsopts={};+structphy_configure_opts_lvds*phy_cfg=&opts.lvds;+u32chno=ldb_ch->chno;+intret;++ret=pm_runtime_get_sync(dev);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to get runtime PM sync: %d\n",ret);++ret=phy_init(imx8qxp_ldb_ch->phy);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to initialize PHY: %d\n",ret);++ret=phy_set_mode(imx8qxp_ldb_ch->phy,PHY_MODE_LVDS);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to set PHY mode: %d\n",ret);++if(is_split&&companion){+companion_ldb_ch=bridge_to_ldb_ch(companion);++companion_ldb_ch->in_bus_format=ldb_ch->in_bus_format;+companion_ldb_ch->out_bus_format=ldb_ch->out_bus_format;+}++clk_set_rate(imx8qxp_ldb->clk_bypass,di_clk);+clk_set_rate(imx8qxp_ldb->clk_pixel,di_clk);++imx8qxp_ldb_set_phy_cfg(imx8qxp_ldb,di_clk,is_split,phy_cfg);+ret=phy_configure(imx8qxp_ldb_ch->phy,&opts);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to configure PHY: %d\n",ret);++if(chno==0)+ldb->ldb_ctrl&=~LDB_CH_SEL;+else+ldb->ldb_ctrl|=LDB_CH_SEL;++/* input VSYNC signal from pixel link is active low */+if(imx8qxp_ldb_ch->di_id==0)+ldb->ldb_ctrl|=LDB_DI0_VS_POL_ACT_LOW;+else+ldb->ldb_ctrl|=LDB_DI1_VS_POL_ACT_LOW;++/*+*Forsplitmode,settleinputVSYNCsignalpolarityand+*channelselectiondownearly.+*/+if(is_split)+regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);++ldb_bridge_mode_set_helper(bridge,mode,adjusted_mode);++if(adjusted_mode->flags&DRM_MODE_FLAG_NVSYNC)+regmap_update_bits(ldb->regmap,SS_CTRL,CH_VSYNC_M(chno),0);+elseif(adjusted_mode->flags&DRM_MODE_FLAG_PVSYNC)+regmap_update_bits(ldb->regmap,SS_CTRL,+CH_VSYNC_M(chno),CH_PVSYNC(chno));++if(adjusted_mode->flags&DRM_MODE_FLAG_NHSYNC)+regmap_update_bits(ldb->regmap,SS_CTRL,CH_HSYNC_M(chno),0);+elseif(adjusted_mode->flags&DRM_MODE_FLAG_PHSYNC)+regmap_update_bits(ldb->regmap,SS_CTRL,+CH_HSYNC_M(chno),CH_PHSYNC(chno));++if(is_split&&companion)+companion->funcs->mode_set(companion,mode,adjusted_mode);+}++staticvoid+imx8qxp_ldb_bridge_atomic_pre_enable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+structimx8qxp_ldb*imx8qxp_ldb=base_to_imx8qxp_ldb(ldb);+structdrm_bridge*companion=imx8qxp_ldb->companion;+boolis_split=ldb_channel_is_split_link(ldb_ch);++clk_prepare_enable(imx8qxp_ldb->clk_pixel);+clk_prepare_enable(imx8qxp_ldb->clk_bypass);++if(is_split&&companion)+companion->funcs->atomic_pre_enable(companion,old_bridge_state);+}++staticvoid+imx8qxp_ldb_bridge_atomic_enable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+structimx8qxp_ldb_channel*imx8qxp_ldb_ch=+base_to_imx8qxp_ldb_channel(ldb_ch);+structimx8qxp_ldb*imx8qxp_ldb=base_to_imx8qxp_ldb(ldb);+structdrm_bridge*companion=imx8qxp_ldb->companion;+structdevice*dev=imx8qxp_ldb->dev;+boolis_split=ldb_channel_is_split_link(ldb_ch);+intret;++if(ldb_ch->chno==0||is_split){+ldb->ldb_ctrl&=~LDB_CH0_MODE_EN_MASK;+ldb->ldb_ctrl|=imx8qxp_ldb_ch->di_id==0?+LDB_CH0_MODE_EN_TO_DI0:LDB_CH0_MODE_EN_TO_DI1;+}+if(ldb_ch->chno==1||is_split){+ldb->ldb_ctrl&=~LDB_CH1_MODE_EN_MASK;+ldb->ldb_ctrl|=imx8qxp_ldb_ch->di_id==0?+LDB_CH1_MODE_EN_TO_DI0:LDB_CH1_MODE_EN_TO_DI1;+}++ldb_bridge_enable_helper(bridge);++ret=phy_power_on(imx8qxp_ldb_ch->phy);+if(ret)+DRM_DEV_ERROR(dev,"failed to power on PHY: %d\n",ret);++if(is_split&&companion)+companion->funcs->atomic_enable(companion,old_bridge_state);+}++staticvoid+imx8qxp_ldb_bridge_atomic_disable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+structimx8qxp_ldb_channel*imx8qxp_ldb_ch=+base_to_imx8qxp_ldb_channel(ldb_ch);+structimx8qxp_ldb*imx8qxp_ldb=base_to_imx8qxp_ldb(ldb);+structdrm_bridge*companion=imx8qxp_ldb->companion;+structdevice*dev=imx8qxp_ldb->dev;+boolis_split=ldb_channel_is_split_link(ldb_ch);+intret;++ret=phy_power_off(imx8qxp_ldb_ch->phy);+if(ret)+DRM_DEV_ERROR(dev,"failed to power off PHY: %d\n",ret);++ret=phy_exit(imx8qxp_ldb_ch->phy);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to teardown PHY: %d\n",ret);++ldb_bridge_disable_helper(bridge);++clk_disable_unprepare(imx8qxp_ldb->clk_bypass);+clk_disable_unprepare(imx8qxp_ldb->clk_pixel);++if(is_split&&companion)+companion->funcs->atomic_disable(companion,old_bridge_state);++ret=pm_runtime_put(dev);+if(ret<0)+DRM_DEV_ERROR(dev,"failed to put runtime PM: %d\n",ret);+}++staticconstu32imx8qxp_ldb_bus_output_fmts[]={+MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,+MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,+MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,+MEDIA_BUS_FMT_FIXED,+};++staticboolimx8qxp_ldb_bus_output_fmt_supported(u32fmt)+{+inti;++for(i=0;i<ARRAY_SIZE(imx8qxp_ldb_bus_output_fmts);i++){+if(imx8qxp_ldb_bus_output_fmts[i]==fmt)+returntrue;+}++returnfalse;+}++staticu32*+imx8qxp_ldb_bridge_atomic_get_input_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+u32output_fmt,+unsignedint*num_input_fmts)+{+structdrm_display_info*di;+conststructdrm_format_info*finfo;+u32*input_fmts;++if(!imx8qxp_ldb_bus_output_fmt_supported(output_fmt))+returnNULL;++*num_input_fmts=1;++input_fmts=kmalloc(sizeof(*input_fmts),GFP_KERNEL);+if(!input_fmts)+returnNULL;++switch(output_fmt){+caseMEDIA_BUS_FMT_FIXED:+di=&conn_state->connector->display_info;++/*+*Lookatthefirstbusformattodetermineinputformat.+*DefaulttoMEDIA_BUS_FMT_RGB888_1X24,ifnomatch.+*/+if(di->num_bus_formats){+finfo=drm_format_info(di->bus_formats[0]);++input_fmts[0]=finfo->depth==18?+MEDIA_BUS_FMT_RGB666_1X24_CPADHI:+MEDIA_BUS_FMT_RGB888_1X24;+}else{+input_fmts[0]=MEDIA_BUS_FMT_RGB888_1X24;+}+break;+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+input_fmts[0]=MEDIA_BUS_FMT_RGB666_1X24_CPADHI;+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+input_fmts[0]=MEDIA_BUS_FMT_RGB888_1X24;+break;+default:+kfree(input_fmts);+input_fmts=NULL;+break;+}++returninput_fmts;+}++staticu32*+imx8qxp_ldb_bridge_atomic_get_output_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+unsignedint*num_output_fmts)+{+*num_output_fmts=ARRAY_SIZE(imx8qxp_ldb_bus_output_fmts);+returnkmemdup(imx8qxp_ldb_bus_output_fmts,+sizeof(imx8qxp_ldb_bus_output_fmts),GFP_KERNEL);+}++staticenumdrm_mode_status+imx8qxp_ldb_bridge_mode_valid(structdrm_bridge*bridge,+conststructdrm_display_info*info,+conststructdrm_display_mode*mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+boolis_single=ldb_channel_is_single_link(ldb_ch);++if(mode->clock>170000)+returnMODE_CLOCK_HIGH;++if(mode->clock>150000&&is_single)+returnMODE_CLOCK_HIGH;++returnMODE_OK;+}++staticconststructdrm_bridge_funcsimx8qxp_ldb_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,+.mode_valid=imx8qxp_ldb_bridge_mode_valid,+.attach=ldb_bridge_attach_helper,+.atomic_check=imx8qxp_ldb_bridge_atomic_check,+.mode_set=imx8qxp_ldb_bridge_mode_set,+.atomic_pre_enable=imx8qxp_ldb_bridge_atomic_pre_enable,+.atomic_enable=imx8qxp_ldb_bridge_atomic_enable,+.atomic_disable=imx8qxp_ldb_bridge_atomic_disable,+.atomic_get_input_bus_fmts=+imx8qxp_ldb_bridge_atomic_get_input_bus_fmts,+.atomic_get_output_bus_fmts=+imx8qxp_ldb_bridge_atomic_get_output_bus_fmts,+};++staticintimx8qxp_ldb_set_di_id(structimx8qxp_ldb*imx8qxp_ldb)+{+structimx8qxp_ldb_channel*imx8qxp_ldb_ch=+&imx8qxp_ldb->channel[imx8qxp_ldb->active_chno];+structldb_channel*ldb_ch=&imx8qxp_ldb_ch->base;+structdevice_node*ep,*remote;+structdevice*dev=imx8qxp_ldb->dev;+structof_endpointendpoint;+intret;++ep=of_graph_get_endpoint_by_regs(ldb_ch->np,0,-1);+if(!ep){+DRM_DEV_ERROR(dev,"failed to get port0 endpoint\n");+return-EINVAL;+}++remote=of_graph_get_remote_endpoint(ep);+of_node_put(ep);+if(!remote){+DRM_DEV_ERROR(dev,"failed to get port0 remote endpoint\n");+return-EINVAL;+}++ret=of_graph_parse_endpoint(remote,&endpoint);+of_node_put(remote);+if(ret){+DRM_DEV_ERROR(dev,"failed to parse port0 remote endpoint: %d\n",+ret);+returnret;+}++imx8qxp_ldb_ch->di_id=endpoint.id;++return0;+}++staticint+imx8qxp_ldb_check_chno_and_dual_link(structldb_channel*ldb_ch,intlink)+{+if((link==DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS&&ldb_ch->chno!=0)||+(link==DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS&&ldb_ch->chno!=1))+return-EINVAL;++return0;+}++staticintimx8qxp_ldb_parse_dt_companion(structimx8qxp_ldb*imx8qxp_ldb)+{+structimx8qxp_ldb_channel*imx8qxp_ldb_ch=+&imx8qxp_ldb->channel[imx8qxp_ldb->active_chno];+structldb_channel*ldb_ch=&imx8qxp_ldb_ch->base;+structldb_channel*companion_ldb_ch;+structdevice_node*companion;+structdevice_node*child;+structdevice_node*companion_port=NULL;+structdevice_node*port1,*port2;+structdevice*dev=imx8qxp_ldb->dev;+conststructof_device_id*match;+u32i;+intdual_link;+intret;++/* Locate the companion LDB for dual-link operation, if any. */+companion=of_parse_phandle(dev->of_node,"fsl,companion-ldb",0);+if(!companion)+return0;++if(!of_device_is_available(companion)){+DRM_DEV_ERROR(dev,"companion LDB is not available\n");+ret=-ENODEV;+gotoout;+}++/*+*Sanitycheck:thecompanionbridgemusthavethesamecompatible+*string.+*/+match=of_match_device(dev->driver->of_match_table,dev);+if(!of_device_is_compatible(companion,match->compatible)){+DRM_DEV_ERROR(dev,"companion LDB is incompatible\n");+ret=-ENXIO;+gotoout;+}++for_each_available_child_of_node(companion,child){+ret=of_property_read_u32(child,"reg",&i);+if(ret||i>MAX_LDB_CHAN_NUM-1){+DRM_DEV_ERROR(dev,+"invalid channel node address: %u\n",i);+ret=-EINVAL;+of_node_put(child);+gotoout;+}++/*+*Channelnumbershavetobedifferent,becausechannel0+*transmitsoddpixelsandchannel1transmitsevenpixels.+*/+if(i==(ldb_ch->chno^0x1)){+companion_port=child;+break;+}+}++if(companion_port==NULL){+DRM_DEV_ERROR(dev,+"failed to find companion LDB channel port\n");+ret=-EINVAL;+gotoout;+}++/*+*Weneedtoworkoutifthesinkisexpectingustofunctionin+*dual-linkmode.WedothisbylookingattheDTportnodesweare+*connectedto.Iftheyaremarkedasexpectingoddpixelsand+*evenpixelsthanweneedtoenableLDBsplitmode.+*/+port1=of_graph_get_port_by_id(ldb_ch->np,1);+port2=of_graph_get_port_by_id(companion_port,1);+dual_link=drm_of_lvds_get_dual_link_pixel_order(port1,port2);+of_node_put(port1);+of_node_put(port2);++switch(dual_link){+caseDRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:+ldb_ch->link_type=LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;+break;+caseDRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:+ldb_ch->link_type=LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS;+break;+default:+ret=dual_link;+DRM_DEV_ERROR(dev,+"failed to get dual link pixel order: %d\n",ret);+gotoout;+}++ret=imx8qxp_ldb_check_chno_and_dual_link(ldb_ch,dual_link);+if(ret<0){+DRM_DEV_ERROR(dev,+"unmatched channel number(%u) vs dual link(%d)\n",+ldb_ch->chno,dual_link);+gotoout;+}++imx8qxp_ldb->companion=of_drm_find_bridge(companion_port);+if(!imx8qxp_ldb->companion){+ret=-EPROBE_DEFER;+DRM_DEV_DEBUG_DRIVER(dev,+"failed to find bridge for companion bridge: %d\n",ret);+gotoout;+}++DRM_DEV_DEBUG_DRIVER(dev,+"dual-link configuration detected (companion bridge %pOF)\n",+companion);++companion_ldb_ch=bridge_to_ldb_ch(imx8qxp_ldb->companion);+companion_ldb_ch->link_type=ldb_ch->link_type;+out:+of_node_put(companion_port);+of_node_put(companion);+returnret;+}++staticintimx8qxp_ldb_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structimx8qxp_ldb*imx8qxp_ldb;+structimx8qxp_ldb_channel*imx8qxp_ldb_ch;+structldb*ldb;+structldb_channel*ldb_ch;+intret,i;++imx8qxp_ldb=devm_kzalloc(dev,sizeof(*imx8qxp_ldb),GFP_KERNEL);+if(!imx8qxp_ldb)+return-ENOMEM;++imx8qxp_ldb->clk_pixel=devm_clk_get(dev,"pixel");+if(IS_ERR(imx8qxp_ldb->clk_pixel)){+ret=PTR_ERR(imx8qxp_ldb->clk_pixel);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to get pixel clock: %d\n",ret);+returnret;+}++imx8qxp_ldb->clk_bypass=devm_clk_get(dev,"bypass");+if(IS_ERR(imx8qxp_ldb->clk_bypass)){+ret=PTR_ERR(imx8qxp_ldb->clk_bypass);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to get bypass clock: %d\n",ret);+returnret;+}++imx8qxp_ldb->dev=dev;++ldb=&imx8qxp_ldb->base;+ldb->dev=dev;+ldb->ctrl_reg=0xe0;++for(i=0;i<MAX_LDB_CHAN_NUM;i++)+ldb->channel[i]=&imx8qxp_ldb->channel[i].base;++ret=ldb_init_helper(ldb);+if(ret)+returnret;++if(ldb->available_ch_cnt==0){+DRM_DEV_DEBUG_DRIVER(dev,"no available channel\n");+return0;+}elseif(ldb->available_ch_cnt>1){+DRM_DEV_ERROR(dev,"invalid available channel number(%u)\n",+ldb->available_ch_cnt);+return-ENOTSUPP;+}++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+imx8qxp_ldb_ch=&imx8qxp_ldb->channel[i];+ldb_ch=&imx8qxp_ldb_ch->base;++if(ldb_ch->is_available){+imx8qxp_ldb->active_chno=ldb_ch->chno;+break;+}+}++imx8qxp_ldb_ch->phy=devm_of_phy_get(dev,ldb_ch->np,"lvds_phy");+if(IS_ERR(imx8qxp_ldb_ch->phy)){+ret=PTR_ERR(imx8qxp_ldb_ch->phy);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get channel%d PHY: %d\n",+imx8qxp_ldb->active_chno,ret);+returnret;+}++ret=ldb_find_next_bridge_helper(ldb);+if(ret)+returnret;++ret=imx8qxp_ldb_set_di_id(imx8qxp_ldb);+if(ret)+returnret;++ret=imx8qxp_ldb_parse_dt_companion(imx8qxp_ldb);+if(ret)+returnret;++platform_set_drvdata(pdev,imx8qxp_ldb);+pm_runtime_enable(dev);++ldb_add_bridge_helper(ldb,&imx8qxp_ldb_bridge_funcs);++returnret;+}++staticintimx8qxp_ldb_remove(structplatform_device*pdev)+{+structimx8qxp_ldb*imx8qxp_ldb=platform_get_drvdata(pdev);+structldb*ldb=&imx8qxp_ldb->base;++ldb_remove_bridge_helper(ldb);++pm_runtime_disable(&pdev->dev);++return0;+}++staticint__maybe_unusedimx8qxp_ldb_runtime_suspend(structdevice*dev)+{+return0;+}++staticint__maybe_unusedimx8qxp_ldb_runtime_resume(structdevice*dev)+{+structimx8qxp_ldb*imx8qxp_ldb=dev_get_drvdata(dev);+structldb*ldb=&imx8qxp_ldb->base;++/* disable LDB by resetting the control register to POR default */+regmap_write(ldb->regmap,ldb->ctrl_reg,0);++return0;+}++staticconststructdev_pm_opsimx8qxp_ldb_pm_ops={+SET_RUNTIME_PM_OPS(imx8qxp_ldb_runtime_suspend,+imx8qxp_ldb_runtime_resume,NULL)+};++staticconststructof_device_idimx8qxp_ldb_dt_ids[]={+{.compatible="fsl,imx8qxp-ldb"},+{/* sentinel */}+};+MODULE_DEVICE_TABLE(of,imx8qxp_ldb_dt_ids);++staticstructplatform_driverimx8qxp_ldb_driver={+.probe=imx8qxp_ldb_probe,+.remove=imx8qxp_ldb_remove,+.driver={+.pm=&imx8qxp_ldb_pm_ops,+.name=DRIVER_NAME,+.of_match_table=imx8qxp_ldb_dt_ids,+},+};+module_platform_driver(imx8qxp_ldb_driver);++MODULE_DESCRIPTION("i.MX8QXP LVDS Display Bridge(LDB)/Pixel Mapper bridge driver");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:"DRIVER_NAME);
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
@@ -0,0 +1,173 @@+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)+%YAML1.2+---+$id:http://devicetree.org/schemas/display/bridge/fsl,imx8qxp-ldb.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Freescale i.MX8qm/qxp LVDS Display Bridge++maintainers:+-Liu Ying <victor.liu@nxp.com>++description:|+The Freescale i.MX8qm/qxp LVDS Display Bridge(LDB) has two channels.++The i.MX8qm/qxp LDB is controlled by Control and Status Registers(CSR) module.+The CSR module, as a system controller, contains the LDB's configuration+registers.++For i.MX8qxp LDB, each channel supports up to 24bpp parallel input color+format and can map the input to VESA or JEIDA standards. The two channels+cannot be used simultaneously, that is to say, the user should pick one of+them to use. Two LDB channels from two LDB instances can work together in+LDB split mode to support a dual link LVDS display. The channel indexes+have to be different. Channel0 outputs odd pixels and channel1 outputs+even pixels.++For i.MX8qm LDB, each channel additionally supports up to 30bpp parallel+input color format. The two channels can be used simultaneously, either+in dual mode or split mode. In dual mode, the two channels output identical+data. In split mode, channel0 outputs odd pixels and channel1 outputs even+pixels.++A side note is that i.MX8qm/qxp LDB is officially called pixel mapper in+the SoC reference manuals. The pixel mapper uses logic of LDBs embedded in+i.MX6qdl/sx SoCs, i.e., it is essentially based on them. To keep the naming+consistency, this binding calls it LDB.++properties:+compatible:+enum:+-fsl,imx8qm-ldb+-fsl,imx8qxp-ldb++"#address-cells":+const:1++"#size-cells":+const:0++clocks:+items:+-description:pixel clock+-description:bypass clock++clock-names:+items:+-const:pixel+-const:bypass++power-domains:+maxItems:1++fsl,companion-ldb:+$ref:/schemas/types.yaml#/definitions/phandle+description:|+A phandle which points to companion LDB which is used in LDB split mode.++patternProperties:+"^channel@[0-1]$":+type:object+description:Represents a channel of LDB.++properties:+"#address-cells":+const:1++"#size-cells":+const:0++reg:+description:The channel index.+enum:[0,1]++phys:+description:A phandle to the phy module representing the LVDS PHY.+maxItems:1++phy-names:+const:lvds_phy++port@0:+$ref:/schemas/graph.yaml#/properties/port+description:Input port of the channel.++port@1:+$ref:/schemas/graph.yaml#/properties/port+description:Output port of the channel.++required:+-"#address-cells"+-"#size-cells"+-reg+-phys+-phy-names++additionalProperties:false++required:+-compatible+-"#address-cells"+-"#size-cells"+-clocks+-clock-names+-power-domains+-channel@0+-channel@1++allOf:+-if:+properties:+compatible:+contains:+const:fsl,imx8qm-ldb+then:+properties:+fsl,companion-ldb:false++additionalProperties:false++examples:+-|+#include <dt-bindings/firmware/imx/rsrc.h>+ldb {+#address-cells = <1>;+#size-cells = <0>;+compatible = "fsl,imx8qxp-ldb";+clocks = <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_MISC2>,+<&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_BYPASS>;+clock-names = "pixel", "bypass";+power-domains = <&pd IMX_SC_R_LVDS_0>;++channel@0 {+#address-cells = <1>;+#size-cells = <0>;+reg = <0>;+phys = <&mipi_lvds_0_phy>;+phy-names = "lvds_phy";++port@0 {+reg = <0>;++mipi_lvds_0_ldb_ch0_mipi_lvds_0_pxl2dpi:endpoint {+remote-endpoint = <&mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch0>;+};+};+};++channel@1 {+#address-cells = <1>;+#size-cells = <0>;+reg = <1>;+phys = <&mipi_lvds_0_phy>;+phy-names = "lvds_phy";++port@0 {+reg = <0>;++mipi_lvds_0_ldb_ch1_mipi_lvds_0_pxl2dpi:endpoint {+remote-endpoint = <&mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch1>;+};+};+};+};
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Robert Foss <hidden> Date: 2021-02-26 11:53:53
Hey Liu,
Thanks for submitting this series.
This patch looks good to me.
Reviewed-by: Robert Foss <redacted>
On Thu, 18 Feb 2021 at 04:56, Liu Ying [off-list ref] wrote:
quoted hunk
This patch allows LVDS PHYs to be configured through
the generic functions and through a custom structure
added to the generic union.
The parameters added here are based on common LVDS PHY
implementation practices. The set of parameters
should cover all potential users.
Cc: Kishon Vijay Abraham I <redacted>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: NXP Linux Team <redacted>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
include/linux/phy/phy-lvds.h | 48 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/phy/phy.h | 4 ++++
2 files changed, 52 insertions(+)
create mode 100644 include/linux/phy/phy-lvds.h
From: Robert Foss <hidden> Date: 2021-02-26 12:01:07
Hey Liu,
This patch looks good to me
Reviewed-by: Robert Foss <redacted>
On Thu, 18 Feb 2021 at 04:56, Liu Ying [off-list ref] wrote:
quoted hunk
This patch adds RGB666_1X30_CPADLO, RGB888_1X30_CPADLO, RGB666_1X36_CPADLO
and RGB888_1X36_CPADLO bus formats used by i.MX8qm/qxp pixel combiner.
The RGB pixels with padding low per component are transmitted on a 30-bit
input bus(10-bit per component) from a display controller or a 36-bit
output bus(12-bit per component) to a pixel link.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
include/uapi/linux/media-bus-format.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
From: Robert Foss <hidden> Date: 2021-02-26 12:45:31
Hey Liu,
This patch looks good to me.
Reviewed-by: Robert Foss <redacted>
On Thu, 18 Feb 2021 at 04:56, Liu Ying [off-list ref] wrote:
quoted hunk
This patch adds documentations for RGB666_1X30_CPADLO, RGB888_1X30_CPADLO,
RGB666_1X36_CPADLO and RGB888_1X36_CPADLO bus formats used by i.MX8qm/qxp
pixel combiner. The RGB pixels with padding low per component are
transmitted on a 30-bit input bus(10-bit per component) from a display
controller or a 36-bit output bus(12-bit per component) to a pixel link.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
.../userspace-api/media/v4l/subdev-formats.rst | 156 +++++++++++++++++++++
1 file changed, 156 insertions(+)
From: Robert Foss <hidden> Date: 2021-02-26 13:09:12
Hey Liu,
With the below nit straightened out, feel free to add my r-b.
Reviewed-by: Robert Foss <redacted>
On Thu, 18 Feb 2021 at 04:58, Liu Ying [off-list ref] wrote:
quoted hunk
This patch adds a drm bridge driver for i.MX8qm/qxp pixel combiner.
The pixel combiner takes two output streams from a single display
controller and manipulates the two streams to support a number
of modes(bypass, pixel combine, YUV444 to YUV422, split_RGB) configured
as either one screen, two screens, or virtual screens. The pixel
combiner is also responsible for generating some of the control signals
for the pixel link output channel. For now, the driver only supports
the bypass mode.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
.../gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c | 452 +++++++++++++++++++++
5 files changed, 464 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/Kconfig
create mode 100644 drivers/gpu/drm/bridge/imx/Makefile
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
YUV444, YUV422 & SPLIT_RGB are also unused, but if their values are
compatible with the PC_DISP0_PIX_DATA_FORMAT macro I think keeping
them around for future reference is a good idea.
From: Liu Ying <victor.liu@nxp.com> Date: 2021-03-01 09:08:46
Hi Robert,
On Fri, 2021-02-26 at 14:07 +0100, Robert Foss wrote:
Hey Liu,
With the below nit straightened out, feel free to add my r-b.
Reviewed-by: Robert Foss <redacted>
Thanks for reviewing this patch.
On Thu, 18 Feb 2021 at 04:58, Liu Ying [off-list ref] wrote:
quoted
This patch adds a drm bridge driver for i.MX8qm/qxp pixel combiner.
The pixel combiner takes two output streams from a single display
controller and manipulates the two streams to support a number
of modes(bypass, pixel combine, YUV444 to YUV422, split_RGB) configured
as either one screen, two screens, or virtual screens. The pixel
combiner is also responsible for generating some of the control signals
for the pixel link output channel. For now, the driver only supports
the bypass mode.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
.../gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c | 452 +++++++++++++++++++++
5 files changed, 464 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/Kconfig
create mode 100644 drivers/gpu/drm/bridge/imx/Makefile
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
This register is unused, keeping it in here to avoid future headaches
seems like a good idea.
Yes, for now, this register is unused.
It will be used to set the below PC_BUF_ACTIVE_DEPTH field when
non-bypass modes are enabled, I think.
Though you said 'keeping it', you actually want me to drop it for now,
right?
YUV444, YUV422 & SPLIT_RGB are also unused, but if their values are
compatible with the PC_DISP0_PIX_DATA_FORMAT macro I think keeping
them around for future reference is a good idea.
Yes, YUV444, YUV422 & SPLIT_RGB are compatible with the
PC_DISP{0, 1}_PIX_DATA_FORMAT macros, so I'll keep them here.
Regards,
Liu Ying
From: Robert Foss <hidden> Date: 2021-03-01 10:57:51
On Mon, 1 Mar 2021 at 10:07, Liu Ying [off-list ref] wrote:
Hi Robert,
On Fri, 2021-02-26 at 14:07 +0100, Robert Foss wrote:
quoted
Hey Liu,
With the below nit straightened out, feel free to add my r-b.
Reviewed-by: Robert Foss <redacted>
Thanks for reviewing this patch.
quoted
On Thu, 18 Feb 2021 at 04:58, Liu Ying [off-list ref] wrote:
quoted
This patch adds a drm bridge driver for i.MX8qm/qxp pixel combiner.
The pixel combiner takes two output streams from a single display
controller and manipulates the two streams to support a number
of modes(bypass, pixel combine, YUV444 to YUV422, split_RGB) configured
as either one screen, two screens, or virtual screens. The pixel
combiner is also responsible for generating some of the control signals
for the pixel link output channel. For now, the driver only supports
the bypass mode.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
.../gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c | 452 +++++++++++++++++++++
5 files changed, 464 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/Kconfig
create mode 100644 drivers/gpu/drm/bridge/imx/Makefile
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
This register is unused, keeping it in here to avoid future headaches
seems like a good idea.
Yes, for now, this register is unused.
It will be used to set the below PC_BUF_ACTIVE_DEPTH field when
non-bypass modes are enabled, I think.
Though you said 'keeping it', you actually want me to drop it for now,
right?
If there is a 2nd series coming which will enable non-bypass modes,
then maybe add it in that series instead.
YUV444, YUV422 & SPLIT_RGB are also unused, but if their values are
compatible with the PC_DISP0_PIX_DATA_FORMAT macro I think keeping
them around for future reference is a good idea.
Yes, YUV444, YUV422 & SPLIT_RGB are compatible with the
PC_DISP{0, 1}_PIX_DATA_FORMAT macros, so I'll keep them here.
Regards,
Liu Ying
From: Liu Ying <victor.liu@nxp.com> Date: 2021-03-02 07:47:26
On Mon, 2021-03-01 at 11:56 +0100, Robert Foss wrote:
On Mon, 1 Mar 2021 at 10:07, Liu Ying [off-list ref] wrote:
quoted
Hi Robert,
On Fri, 2021-02-26 at 14:07 +0100, Robert Foss wrote:
quoted
Hey Liu,
With the below nit straightened out, feel free to add my r-b.
Reviewed-by: Robert Foss <redacted>
Thanks for reviewing this patch.
quoted
On Thu, 18 Feb 2021 at 04:58, Liu Ying [off-list ref] wrote:
quoted
This patch adds a drm bridge driver for i.MX8qm/qxp pixel combiner.
The pixel combiner takes two output streams from a single display
controller and manipulates the two streams to support a number
of modes(bypass, pixel combine, YUV444 to YUV422, split_RGB) configured
as either one screen, two screens, or virtual screens. The pixel
combiner is also responsible for generating some of the control signals
for the pixel link output channel. For now, the driver only supports
the bypass mode.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* No change.
v1->v2:
* No change.
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
.../gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c | 452 +++++++++++++++++++++
5 files changed, 464 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/Kconfig
create mode 100644 drivers/gpu/drm/bridge/imx/Makefile
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
This register is unused, keeping it in here to avoid future headaches
seems like a good idea.
Yes, for now, this register is unused.
It will be used to set the below PC_BUF_ACTIVE_DEPTH field when
non-bypass modes are enabled, I think.
Though you said 'keeping it', you actually want me to drop it for now,
right?
If there is a 2nd series coming which will enable non-bypass modes,
then maybe add it in that series instead.
Ok, I'll drop PC_BUF_PARA_REG for now in the next version.
Thanks,
Liu Ying
YUV444, YUV422 & SPLIT_RGB are also unused, but if their values are
compatible with the PC_DISP0_PIX_DATA_FORMAT macro I think keeping
them around for future reference is a good idea.
Yes, YUV444, YUV422 & SPLIT_RGB are compatible with the
PC_DISP{0, 1}_PIX_DATA_FORMAT macros, so I'll keep them here.
Regards,
Liu Ying
From: Robert Foss <hidden> Date: 2021-03-02 16:30:30
Hey Liu,
Thanks for submitting this patch.
I only have one comment below. With that addressed, feel free to add my r-b.
Reviewed-by: Robert Foss <redacted>
On Thu, 18 Feb 2021 at 04:59, Liu Ying [off-list ref] wrote:
quoted hunk
This patch adds a drm bridge driver for i.MX8qm/qxp display pixel link.
The pixel link forms a standard asynchronous linkage between
pixel sources(display controller or camera module) and pixel
consumers(imaging or displays). It consists of two distinct
functions, a pixel transfer function and a control interface.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Drop two macros which help define functions and define them directly.
* Properly disable all pixel link controls to POR value by calling
imx8qxp_pixel_link_disable_all_controls() from
imx8qxp_pixel_link_bridge_probe().
v1->v2:
* Rewrite the function to find the next bridge by properly using OF APIs
and dropping unnecessary DT validation. (Rob)
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c | 426 ++++++++++++++++++++++++
3 files changed, 435 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
@@ -0,0 +1,426 @@+// SPDX-License-Identifier: GPL-2.0+++/*+*Copyright2020NXP+*/++#include<linux/firmware/imx/svc/misc.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++structimx8qxp_pixel_link{+structdrm_bridgebridge;+structdrm_bridge*next_bridge;+structdevice*dev;+structimx_sc_ipc*ipc_handle;+intid;+intstream_id;+intdc_id;+u32sink_rsc;+u32mst_addr;+u8mst_addr_ctrl;+u8mst_en_ctrl;+u8mst_vld_ctrl;+u8sync_ctrl;+};++staticvoidimx8qxp_pixel_link_enable_mst_en(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_en: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticvoidimx8qxp_pixel_link_enable_mst_vld(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_vld: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticvoidimx8qxp_pixel_link_enable_sync(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link sync: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticintimx8qxp_pixel_link_disable_mst_en(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_en: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticintimx8qxp_pixel_link_disable_mst_vld(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_vld: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticintimx8qxp_pixel_link_disable_sync(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link sync: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticvoidimx8qxp_pixel_link_set_mst_addr(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst addr(%u): %d\n",+pl->dc_id,pl->stream_id,pl->mst_addr,ret);+}++staticintimx8qxp_pixel_link_bridge_attach(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structimx8qxp_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;+}++if(!bridge->encoder){+DRM_DEV_ERROR(pl->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+pl->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}++staticvoid+imx8qxp_pixel_link_bridge_mode_set(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structimx8qxp_pixel_link*pl=bridge->driver_private;++imx8qxp_pixel_link_set_mst_addr(pl);+}++staticvoidimx8qxp_pixel_link_bridge_atomic_enable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structimx8qxp_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);+}++staticvoidimx8qxp_pixel_link_bridge_atomic_disable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structimx8qxp_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);+}++staticconstu32imx8qxp_pixel_link_bus_output_fmts[]={+MEDIA_BUS_FMT_RGB888_1X36_CPADLO,+MEDIA_BUS_FMT_RGB666_1X36_CPADLO,+};++staticboolimx8qxp_pixel_link_bus_output_fmt_supported(u32fmt)+{+inti;++for(i=0;i<ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);i++){+if(imx8qxp_pixel_link_bus_output_fmts[i]==fmt)+returntrue;+}++returnfalse;+}++staticu32*+imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+u32output_fmt,+unsignedint*num_input_fmts)+{+u32*input_fmts;++if(!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))+returnNULL;++*num_input_fmts=1;++input_fmts=kmalloc(sizeof(*input_fmts),GFP_KERNEL);+if(!input_fmts)+returnNULL;++input_fmts[0]=output_fmt;++returninput_fmts;+}++staticu32*+imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+unsignedint*num_output_fmts)+{+*num_output_fmts=ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);+returnkmemdup(imx8qxp_pixel_link_bus_output_fmts,+sizeof(imx8qxp_pixel_link_bus_output_fmts),GFP_KERNEL);+}++staticconststructdrm_bridge_funcsimx8qxp_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,+};++staticintimx8qxp_pixel_link_disable_all_controls(structimx8qxp_pixel_link*pl)+{+intret;++ret=imx8qxp_pixel_link_disable_mst_en(pl);+if(ret)+returnret;++ret=imx8qxp_pixel_link_disable_mst_vld(pl);+if(ret)+returnret;++returnimx8qxp_pixel_link_disable_sync(pl);+}++staticstructdrm_bridge*+imx8qxp_pixel_link_find_next_bridge(structimx8qxp_pixel_link*pl)+{+structdevice_node*np=pl->dev->of_node;+structdevice_node*port,*remote;+structdrm_bridge*next_bridge[PL_MAX_NEXT_BRIDGES];+u32port_id;+boolfound_port=false;+intreg,ep_cnt=0;+intbridge_sel=0;/* select the first next bridge by default */
From: Robert Foss <hidden> Date: 2021-03-02 17:00:14
Hey Liu,
Thanks for submitting this patch.
On Thu, 18 Feb 2021 at 04:59, Liu Ying [off-list ref] wrote:
quoted hunk
This patch adds a helper to support LDB drm bridge drivers for
i.MX SoCs. Helper functions exported from this driver should
implement common logics for all LDB modules embedded in i.MX SoCs.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Call syscon_node_to_regmap() to get regmap instead of
syscon_regmap_lookup_by_phandle().
v1->v2:
* No change.
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 248 ++++++++++++++++++++++++++++
include/drm/bridge/imx_ldb_helper.h | 98 +++++++++++
4 files changed, 355 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
create mode 100644 include/drm/bridge/imx_ldb_helper.h
@@ -0,0 +1,248 @@+// SPDX-License-Identifier: GPL-2.0++/*+*Copyright(C)2012SaschaHauer,Pengutronix+*Copyright2019,2020NXP+*/++#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/regmap.h>++#include<drm/bridge/imx_ldb_helper.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>+#include<drm/drm_print.h>++boolldb_channel_is_single_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_SINGLE_LINK;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_single_link);++boolldb_channel_is_split_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS||+ldb_ch->link_type==LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_split_link);++intldb_bridge_atomic_check_helper(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structldb_channel*ldb_ch=bridge->driver_private;++ldb_ch->in_bus_format=bridge_state->input_bus_cfg.format;+ldb_ch->out_bus_format=bridge_state->output_bus_cfg.format;++return0;+}+EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper);++voidldb_bridge_mode_set_helper(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(is_split)+ldb->ldb_ctrl|=LDB_SPLIT_MODE_EN;++switch(ldb_ch->out_bus_format){+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24;+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24|+LDB_BIT_MAP_CH0_JEIDA;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24|+LDB_BIT_MAP_CH1_JEIDA;+break;+}+}+EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper);++voidldb_bridge_enable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++/*+*Platformspecificbridgedriversshouldsetldb_ctrlproperly+*fortheenablement,sojustwritethectrl_reghere.+*/+regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper);++voidldb_bridge_disable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl&=~LDB_CH0_MODE_EN_MASK;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl&=~LDB_CH1_MODE_EN_MASK;++regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper);++intldb_bridge_attach_helper(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++if(!(flags&DRM_BRIDGE_ATTACH_NO_CONNECTOR)){+DRM_DEV_ERROR(ldb->dev,+"do not support creating a drm_connector\n");+return-EINVAL;+}++if(!bridge->encoder){+DRM_DEV_ERROR(ldb->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+ldb_ch->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}+EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper);++intldb_init_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structdevice_node*np=dev->of_node;+structdevice_node*child;+intret;+u32i;++ldb->regmap=syscon_node_to_regmap(np->parent);+if(IS_ERR(ldb->regmap)){+ret=PTR_ERR(ldb->regmap);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get regmap: %d\n",ret);+returnret;+}++for_each_available_child_of_node(np,child){+structldb_channel*ldb_ch;++ret=of_property_read_u32(child,"reg",&i);+if(ret||i>MAX_LDB_CHAN_NUM-1){+ret=-EINVAL;+DRM_DEV_ERROR(dev,+"invalid channel node address: %u\n",i);+of_node_put(child);+returnret;+}++ldb_ch=ldb->channel[i];+ldb_ch->ldb=ldb;+ldb_ch->chno=i;+ldb_ch->is_available=true;+ldb_ch->np=child;++ldb->available_ch_cnt++;+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_init_helper);++intldb_find_next_bridge_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structldb_channel*ldb_ch;+intret,i;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ret=drm_of_find_panel_or_bridge(ldb_ch->np,1,0,+&ldb_ch->panel,+&ldb_ch->next_bridge);+if(ret){+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to find panel or bridge: %d\n",+ret);+returnret;+}++if(ldb_ch->panel){+ldb_ch->next_bridge=devm_drm_panel_bridge_add(dev,+ldb_ch->panel);+if(IS_ERR(ldb_ch->next_bridge)){+ret=PTR_ERR(ldb_ch->next_bridge);+DRM_DEV_ERROR(dev,+"failed to add panel bridge: %d\n",+ret);+returnret;+}+}+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper);++voidldb_add_bridge_helper(structldb*ldb,+conststructdrm_bridge_funcs*bridge_funcs)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ldb_ch->bridge.driver_private=ldb_ch;+ldb_ch->bridge.funcs=bridge_funcs;+ldb_ch->bridge.of_node=ldb_ch->np;++drm_bridge_add(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_add_bridge_helper);++voidldb_remove_bridge_helper(structldb*ldb)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++drm_bridge_remove(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper);++MODULE_DESCRIPTION("Freescale i.MX LVDS Display Bridge driver helper");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:imx-ldb-helper");
I'm not entirely sure why this set of helper functions should be a
module. It's not a driver, but rather a toolbox for the LDB driver,
which is fine, but there is no situation I can see where this module
would be unloaded and the LDB driver would be loaded.
This header is specific to this driver, and I would expect it to not
be useful to other drivers. Additionally the filename has a different
format than the .c file it corresponds to. I would change the name and
path to "drivers/gpu/drm/bridge/imx/imx-ldb-helper.h".
From: Liu Ying <victor.liu@nxp.com> Date: 2021-03-03 12:58:48
Hi Robert,
On Tue, 2021-03-02 at 14:53 +0100, Robert Foss wrote:
Hey Liu,
Thanks for submitting this patch.
I only have one comment below. With that addressed, feel free to add my r-b.
Reviewed-by: Robert Foss <redacted>
Thanks for reviewing this patch.
On Thu, 18 Feb 2021 at 04:59, Liu Ying [off-list ref] wrote:
quoted
This patch adds a drm bridge driver for i.MX8qm/qxp display pixel link.
The pixel link forms a standard asynchronous linkage between
pixel sources(display controller or camera module) and pixel
consumers(imaging or displays). It consists of two distinct
functions, a pixel transfer function and a control interface.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Drop two macros which help define functions and define them directly.
* Properly disable all pixel link controls to POR value by calling
imx8qxp_pixel_link_disable_all_controls() from
imx8qxp_pixel_link_bridge_probe().
v1->v2:
* Rewrite the function to find the next bridge by properly using OF APIs
and dropping unnecessary DT validation. (Rob)
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c | 426 ++++++++++++++++++++++++
3 files changed, 435 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
@@ -0,0 +1,426 @@+// SPDX-License-Identifier: GPL-2.0+++/*+*Copyright2020NXP+*/++#include<linux/firmware/imx/svc/misc.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++structimx8qxp_pixel_link{+structdrm_bridgebridge;+structdrm_bridge*next_bridge;+structdevice*dev;+structimx_sc_ipc*ipc_handle;+intid;+intstream_id;+intdc_id;+u32sink_rsc;+u32mst_addr;+u8mst_addr_ctrl;+u8mst_en_ctrl;+u8mst_vld_ctrl;+u8sync_ctrl;+};++staticvoidimx8qxp_pixel_link_enable_mst_en(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_en: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticvoidimx8qxp_pixel_link_enable_mst_vld(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_vld: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticvoidimx8qxp_pixel_link_enable_sync(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link sync: %d\n",+pl->dc_id,pl->stream_id,ret);+}++staticintimx8qxp_pixel_link_disable_mst_en(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_en: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticintimx8qxp_pixel_link_disable_mst_vld(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst_vld: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticintimx8qxp_pixel_link_disable_sync(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link sync: %d\n",+pl->dc_id,pl->stream_id,ret);++returnret;+}++staticvoidimx8qxp_pixel_link_set_mst_addr(structimx8qxp_pixel_link*pl)+{+intret;++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%d stream%d pixel link mst addr(%u): %d\n",+pl->dc_id,pl->stream_id,pl->mst_addr,ret);+}++staticintimx8qxp_pixel_link_bridge_attach(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structimx8qxp_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;+}++if(!bridge->encoder){+DRM_DEV_ERROR(pl->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+pl->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}++staticvoid+imx8qxp_pixel_link_bridge_mode_set(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structimx8qxp_pixel_link*pl=bridge->driver_private;++imx8qxp_pixel_link_set_mst_addr(pl);+}++staticvoidimx8qxp_pixel_link_bridge_atomic_enable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structimx8qxp_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);+}++staticvoidimx8qxp_pixel_link_bridge_atomic_disable(structdrm_bridge*bridge,+structdrm_bridge_state*old_bridge_state)+{+structimx8qxp_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);+}++staticconstu32imx8qxp_pixel_link_bus_output_fmts[]={+MEDIA_BUS_FMT_RGB888_1X36_CPADLO,+MEDIA_BUS_FMT_RGB666_1X36_CPADLO,+};++staticboolimx8qxp_pixel_link_bus_output_fmt_supported(u32fmt)+{+inti;++for(i=0;i<ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);i++){+if(imx8qxp_pixel_link_bus_output_fmts[i]==fmt)+returntrue;+}++returnfalse;+}++staticu32*+imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+u32output_fmt,+unsignedint*num_input_fmts)+{+u32*input_fmts;++if(!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))+returnNULL;++*num_input_fmts=1;++input_fmts=kmalloc(sizeof(*input_fmts),GFP_KERNEL);+if(!input_fmts)+returnNULL;++input_fmts[0]=output_fmt;++returninput_fmts;+}++staticu32*+imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state,+unsignedint*num_output_fmts)+{+*num_output_fmts=ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);+returnkmemdup(imx8qxp_pixel_link_bus_output_fmts,+sizeof(imx8qxp_pixel_link_bus_output_fmts),GFP_KERNEL);+}++staticconststructdrm_bridge_funcsimx8qxp_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,+};++staticintimx8qxp_pixel_link_disable_all_controls(structimx8qxp_pixel_link*pl)+{+intret;++ret=imx8qxp_pixel_link_disable_mst_en(pl);+if(ret)+returnret;++ret=imx8qxp_pixel_link_disable_mst_vld(pl);+if(ret)+returnret;++returnimx8qxp_pixel_link_disable_sync(pl);+}++staticstructdrm_bridge*+imx8qxp_pixel_link_find_next_bridge(structimx8qxp_pixel_link*pl)+{+structdevice_node*np=pl->dev->of_node;+structdevice_node*port,*remote;+structdrm_bridge*next_bridge[PL_MAX_NEXT_BRIDGES];+u32port_id;+boolfound_port=false;+intreg,ep_cnt=0;+intbridge_sel=0;/* select the first next bridge by default */
Is this comment on the wrong line?
I'll make this comment occupy a line just prior to this line in the
next version.
Thanks,
Liu Ying
From: Liu Ying <victor.liu@nxp.com> Date: 2021-03-03 13:06:27
Hi Robert,
On Tue, 2021-03-02 at 15:22 +0100, Robert Foss wrote:
Hey Liu,
Thanks for submitting this patch.
Thanks for reviewing this patch.
On Thu, 18 Feb 2021 at 04:59, Liu Ying [off-list ref] wrote:
quoted
This patch adds a helper to support LDB drm bridge drivers for
i.MX SoCs. Helper functions exported from this driver should
implement common logics for all LDB modules embedded in i.MX SoCs.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Call syscon_node_to_regmap() to get regmap instead of
syscon_regmap_lookup_by_phandle().
v1->v2:
* No change.
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 248 ++++++++++++++++++++++++++++
include/drm/bridge/imx_ldb_helper.h | 98 +++++++++++
4 files changed, 355 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
create mode 100644 include/drm/bridge/imx_ldb_helper.h
@@ -0,0 +1,248 @@+// SPDX-License-Identifier: GPL-2.0++/*+*Copyright(C)2012SaschaHauer,Pengutronix+*Copyright2019,2020NXP+*/++#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/regmap.h>++#include<drm/bridge/imx_ldb_helper.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>+#include<drm/drm_print.h>++boolldb_channel_is_single_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_SINGLE_LINK;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_single_link);++boolldb_channel_is_split_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS||+ldb_ch->link_type==LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_split_link);++intldb_bridge_atomic_check_helper(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structldb_channel*ldb_ch=bridge->driver_private;++ldb_ch->in_bus_format=bridge_state->input_bus_cfg.format;+ldb_ch->out_bus_format=bridge_state->output_bus_cfg.format;++return0;+}+EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper);++voidldb_bridge_mode_set_helper(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(is_split)+ldb->ldb_ctrl|=LDB_SPLIT_MODE_EN;++switch(ldb_ch->out_bus_format){+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24;+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24|+LDB_BIT_MAP_CH0_JEIDA;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24|+LDB_BIT_MAP_CH1_JEIDA;+break;+}+}+EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper);++voidldb_bridge_enable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++/*+*Platformspecificbridgedriversshouldsetldb_ctrlproperly+*fortheenablement,sojustwritethectrl_reghere.+*/+regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper);++voidldb_bridge_disable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl&=~LDB_CH0_MODE_EN_MASK;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl&=~LDB_CH1_MODE_EN_MASK;++regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper);++intldb_bridge_attach_helper(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++if(!(flags&DRM_BRIDGE_ATTACH_NO_CONNECTOR)){+DRM_DEV_ERROR(ldb->dev,+"do not support creating a drm_connector\n");+return-EINVAL;+}++if(!bridge->encoder){+DRM_DEV_ERROR(ldb->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+ldb_ch->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}+EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper);++intldb_init_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structdevice_node*np=dev->of_node;+structdevice_node*child;+intret;+u32i;++ldb->regmap=syscon_node_to_regmap(np->parent);+if(IS_ERR(ldb->regmap)){+ret=PTR_ERR(ldb->regmap);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get regmap: %d\n",ret);+returnret;+}++for_each_available_child_of_node(np,child){+structldb_channel*ldb_ch;++ret=of_property_read_u32(child,"reg",&i);+if(ret||i>MAX_LDB_CHAN_NUM-1){+ret=-EINVAL;+DRM_DEV_ERROR(dev,+"invalid channel node address: %u\n",i);+of_node_put(child);+returnret;+}++ldb_ch=ldb->channel[i];+ldb_ch->ldb=ldb;+ldb_ch->chno=i;+ldb_ch->is_available=true;+ldb_ch->np=child;++ldb->available_ch_cnt++;+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_init_helper);++intldb_find_next_bridge_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structldb_channel*ldb_ch;+intret,i;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ret=drm_of_find_panel_or_bridge(ldb_ch->np,1,0,+&ldb_ch->panel,+&ldb_ch->next_bridge);+if(ret){+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to find panel or bridge: %d\n",+ret);+returnret;+}++if(ldb_ch->panel){+ldb_ch->next_bridge=devm_drm_panel_bridge_add(dev,+ldb_ch->panel);+if(IS_ERR(ldb_ch->next_bridge)){+ret=PTR_ERR(ldb_ch->next_bridge);+DRM_DEV_ERROR(dev,+"failed to add panel bridge: %d\n",+ret);+returnret;+}+}+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper);++voidldb_add_bridge_helper(structldb*ldb,+conststructdrm_bridge_funcs*bridge_funcs)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ldb_ch->bridge.driver_private=ldb_ch;+ldb_ch->bridge.funcs=bridge_funcs;+ldb_ch->bridge.of_node=ldb_ch->np;++drm_bridge_add(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_add_bridge_helper);++voidldb_remove_bridge_helper(structldb*ldb)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++drm_bridge_remove(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper);++MODULE_DESCRIPTION("Freescale i.MX LVDS Display Bridge driver helper");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:imx-ldb-helper");
I'm not entirely sure why this set of helper functions should be a
module. It's not a driver, but rather a toolbox for the LDB driver,
which is fine, but there is no situation I can see where this module
would be unloaded and the LDB driver would be loaded.
I can see drivers/gpu/drm/drm_mipi_dbi.c is also a module and
essentially provides helpers to MIPI DBI drivers, but it is not a
driver. I don't see this imx-ldb-helper can be anything else other
than a module.
Or, do you mean that imx-ldb-helper should be only built-in?
This header is specific to this driver, and I would expect it to not
be useful to other drivers. Additionally the filename has a different
format than the .c file it corresponds to. I would change the name and
path to "drivers/gpu/drm/bridge/imx/imx-ldb-helper.h".
The i.MX53/6qdl LDB driver(drivers/gpu/drm/imx/imx-ldb.c) can
potentially use this header, but it's a DRM encoder driver.
So, maybe, it's a good idea to move this header to the 'drivers' folder
and rename it to 'imx-ldb-helper.h' ofc. If no objections, I'll do as
what you're suggesting here in the next version.
Regards,
Liu Ying
From: Robert Foss <hidden> Date: 2021-03-03 18:52:52
On Wed, 3 Mar 2021 at 08:23, Liu Ying [off-list ref] wrote:
Hi Robert,
On Tue, 2021-03-02 at 15:22 +0100, Robert Foss wrote:
quoted
Hey Liu,
Thanks for submitting this patch.
Thanks for reviewing this patch.
quoted
On Thu, 18 Feb 2021 at 04:59, Liu Ying [off-list ref] wrote:
quoted
This patch adds a helper to support LDB drm bridge drivers for
i.MX SoCs. Helper functions exported from this driver should
implement common logics for all LDB modules embedded in i.MX SoCs.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Call syscon_node_to_regmap() to get regmap instead of
syscon_regmap_lookup_by_phandle().
v1->v2:
* No change.
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 248 ++++++++++++++++++++++++++++
include/drm/bridge/imx_ldb_helper.h | 98 +++++++++++
4 files changed, 355 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
create mode 100644 include/drm/bridge/imx_ldb_helper.h
@@ -0,0 +1,248 @@+// SPDX-License-Identifier: GPL-2.0++/*+*Copyright(C)2012SaschaHauer,Pengutronix+*Copyright2019,2020NXP+*/++#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/regmap.h>++#include<drm/bridge/imx_ldb_helper.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>+#include<drm/drm_print.h>++boolldb_channel_is_single_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_SINGLE_LINK;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_single_link);++boolldb_channel_is_split_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS||+ldb_ch->link_type==LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_split_link);++intldb_bridge_atomic_check_helper(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structldb_channel*ldb_ch=bridge->driver_private;++ldb_ch->in_bus_format=bridge_state->input_bus_cfg.format;+ldb_ch->out_bus_format=bridge_state->output_bus_cfg.format;++return0;+}+EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper);++voidldb_bridge_mode_set_helper(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(is_split)+ldb->ldb_ctrl|=LDB_SPLIT_MODE_EN;++switch(ldb_ch->out_bus_format){+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24;+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24|+LDB_BIT_MAP_CH0_JEIDA;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24|+LDB_BIT_MAP_CH1_JEIDA;+break;+}+}+EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper);++voidldb_bridge_enable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++/*+*Platformspecificbridgedriversshouldsetldb_ctrlproperly+*fortheenablement,sojustwritethectrl_reghere.+*/+regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper);++voidldb_bridge_disable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl&=~LDB_CH0_MODE_EN_MASK;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl&=~LDB_CH1_MODE_EN_MASK;++regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper);++intldb_bridge_attach_helper(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++if(!(flags&DRM_BRIDGE_ATTACH_NO_CONNECTOR)){+DRM_DEV_ERROR(ldb->dev,+"do not support creating a drm_connector\n");+return-EINVAL;+}++if(!bridge->encoder){+DRM_DEV_ERROR(ldb->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+ldb_ch->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}+EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper);++intldb_init_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structdevice_node*np=dev->of_node;+structdevice_node*child;+intret;+u32i;++ldb->regmap=syscon_node_to_regmap(np->parent);+if(IS_ERR(ldb->regmap)){+ret=PTR_ERR(ldb->regmap);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get regmap: %d\n",ret);+returnret;+}++for_each_available_child_of_node(np,child){+structldb_channel*ldb_ch;++ret=of_property_read_u32(child,"reg",&i);+if(ret||i>MAX_LDB_CHAN_NUM-1){+ret=-EINVAL;+DRM_DEV_ERROR(dev,+"invalid channel node address: %u\n",i);+of_node_put(child);+returnret;+}++ldb_ch=ldb->channel[i];+ldb_ch->ldb=ldb;+ldb_ch->chno=i;+ldb_ch->is_available=true;+ldb_ch->np=child;++ldb->available_ch_cnt++;+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_init_helper);++intldb_find_next_bridge_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structldb_channel*ldb_ch;+intret,i;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ret=drm_of_find_panel_or_bridge(ldb_ch->np,1,0,+&ldb_ch->panel,+&ldb_ch->next_bridge);+if(ret){+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to find panel or bridge: %d\n",+ret);+returnret;+}++if(ldb_ch->panel){+ldb_ch->next_bridge=devm_drm_panel_bridge_add(dev,+ldb_ch->panel);+if(IS_ERR(ldb_ch->next_bridge)){+ret=PTR_ERR(ldb_ch->next_bridge);+DRM_DEV_ERROR(dev,+"failed to add panel bridge: %d\n",+ret);+returnret;+}+}+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper);++voidldb_add_bridge_helper(structldb*ldb,+conststructdrm_bridge_funcs*bridge_funcs)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ldb_ch->bridge.driver_private=ldb_ch;+ldb_ch->bridge.funcs=bridge_funcs;+ldb_ch->bridge.of_node=ldb_ch->np;++drm_bridge_add(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_add_bridge_helper);++voidldb_remove_bridge_helper(structldb*ldb)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++drm_bridge_remove(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper);++MODULE_DESCRIPTION("Freescale i.MX LVDS Display Bridge driver helper");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:imx-ldb-helper");
I'm not entirely sure why this set of helper functions should be a
module. It's not a driver, but rather a toolbox for the LDB driver,
which is fine, but there is no situation I can see where this module
would be unloaded and the LDB driver would be loaded.
I can see drivers/gpu/drm/drm_mipi_dbi.c is also a module and
essentially provides helpers to MIPI DBI drivers, but it is not a
driver. I don't see this imx-ldb-helper can be anything else other
than a module.
Or, do you mean that imx-ldb-helper should be only built-in?
My thinking was that it should just be linked together with the rest
of the imx8qxp-ldb driver. But this ties in to my next comment.
This header is specific to this driver, and I would expect it to not
be useful to other drivers. Additionally the filename has a different
format than the .c file it corresponds to. I would change the name and
path to "drivers/gpu/drm/bridge/imx/imx-ldb-helper.h".
The i.MX53/6qdl LDB driver(drivers/gpu/drm/imx/imx-ldb.c) can
potentially use this header, but it's a DRM encoder driver.
So, maybe, it's a good idea to move this header to the 'drivers' folder
and rename it to 'imx-ldb-helper.h' ofc. If no objections, I'll do as
what you're suggesting here in the next version.
Ah I see. If ldb-helper is indeed used by two drivers, making it a
module seems reasonable.
I think we have two options then.
#1 Make imx-ldb-helper an object that is just linked with the
imx8qxp-ldb driver.
#2 Keep imx-ldb-helper as a module, and implement support for using it
in the imx-ldb driver. Ideally I'd like to see the imx-ldb-helper
module patch in the same series as as imx53/6qdl switching to using
the module. These things have a tendency of not happening if not done
right away :)
From: Liu Ying <victor.liu@nxp.com> Date: 2021-03-04 03:31:41
Hi Robert,
On Wed, 2021-03-03 at 16:34 +0100, Robert Foss wrote:
On Wed, 3 Mar 2021 at 08:23, Liu Ying [off-list ref] wrote:
quoted
Hi Robert,
On Tue, 2021-03-02 at 15:22 +0100, Robert Foss wrote:
quoted
Hey Liu,
Thanks for submitting this patch.
Thanks for reviewing this patch.
quoted
On Thu, 18 Feb 2021 at 04:59, Liu Ying [off-list ref] wrote:
quoted
This patch adds a helper to support LDB drm bridge drivers for
i.MX SoCs. Helper functions exported from this driver should
implement common logics for all LDB modules embedded in i.MX SoCs.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Call syscon_node_to_regmap() to get regmap instead of
syscon_regmap_lookup_by_phandle().
v1->v2:
* No change.
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 248 ++++++++++++++++++++++++++++
include/drm/bridge/imx_ldb_helper.h | 98 +++++++++++
4 files changed, 355 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
create mode 100644 include/drm/bridge/imx_ldb_helper.h
@@ -0,0 +1,248 @@+// SPDX-License-Identifier: GPL-2.0++/*+*Copyright(C)2012SaschaHauer,Pengutronix+*Copyright2019,2020NXP+*/++#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/regmap.h>++#include<drm/bridge/imx_ldb_helper.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>+#include<drm/drm_print.h>++boolldb_channel_is_single_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_SINGLE_LINK;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_single_link);++boolldb_channel_is_split_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS||+ldb_ch->link_type==LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_split_link);++intldb_bridge_atomic_check_helper(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structldb_channel*ldb_ch=bridge->driver_private;++ldb_ch->in_bus_format=bridge_state->input_bus_cfg.format;+ldb_ch->out_bus_format=bridge_state->output_bus_cfg.format;++return0;+}+EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper);++voidldb_bridge_mode_set_helper(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(is_split)+ldb->ldb_ctrl|=LDB_SPLIT_MODE_EN;++switch(ldb_ch->out_bus_format){+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24;+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24|+LDB_BIT_MAP_CH0_JEIDA;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24|+LDB_BIT_MAP_CH1_JEIDA;+break;+}+}+EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper);++voidldb_bridge_enable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++/*+*Platformspecificbridgedriversshouldsetldb_ctrlproperly+*fortheenablement,sojustwritethectrl_reghere.+*/+regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper);++voidldb_bridge_disable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl&=~LDB_CH0_MODE_EN_MASK;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl&=~LDB_CH1_MODE_EN_MASK;++regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper);++intldb_bridge_attach_helper(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++if(!(flags&DRM_BRIDGE_ATTACH_NO_CONNECTOR)){+DRM_DEV_ERROR(ldb->dev,+"do not support creating a drm_connector\n");+return-EINVAL;+}++if(!bridge->encoder){+DRM_DEV_ERROR(ldb->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+ldb_ch->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}+EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper);++intldb_init_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structdevice_node*np=dev->of_node;+structdevice_node*child;+intret;+u32i;++ldb->regmap=syscon_node_to_regmap(np->parent);+if(IS_ERR(ldb->regmap)){+ret=PTR_ERR(ldb->regmap);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get regmap: %d\n",ret);+returnret;+}++for_each_available_child_of_node(np,child){+structldb_channel*ldb_ch;++ret=of_property_read_u32(child,"reg",&i);+if(ret||i>MAX_LDB_CHAN_NUM-1){+ret=-EINVAL;+DRM_DEV_ERROR(dev,+"invalid channel node address: %u\n",i);+of_node_put(child);+returnret;+}++ldb_ch=ldb->channel[i];+ldb_ch->ldb=ldb;+ldb_ch->chno=i;+ldb_ch->is_available=true;+ldb_ch->np=child;++ldb->available_ch_cnt++;+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_init_helper);++intldb_find_next_bridge_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structldb_channel*ldb_ch;+intret,i;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ret=drm_of_find_panel_or_bridge(ldb_ch->np,1,0,+&ldb_ch->panel,+&ldb_ch->next_bridge);+if(ret){+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to find panel or bridge: %d\n",+ret);+returnret;+}++if(ldb_ch->panel){+ldb_ch->next_bridge=devm_drm_panel_bridge_add(dev,+ldb_ch->panel);+if(IS_ERR(ldb_ch->next_bridge)){+ret=PTR_ERR(ldb_ch->next_bridge);+DRM_DEV_ERROR(dev,+"failed to add panel bridge: %d\n",+ret);+returnret;+}+}+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper);++voidldb_add_bridge_helper(structldb*ldb,+conststructdrm_bridge_funcs*bridge_funcs)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ldb_ch->bridge.driver_private=ldb_ch;+ldb_ch->bridge.funcs=bridge_funcs;+ldb_ch->bridge.of_node=ldb_ch->np;++drm_bridge_add(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_add_bridge_helper);++voidldb_remove_bridge_helper(structldb*ldb)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++drm_bridge_remove(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper);++MODULE_DESCRIPTION("Freescale i.MX LVDS Display Bridge driver helper");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:imx-ldb-helper");
I'm not entirely sure why this set of helper functions should be a
module. It's not a driver, but rather a toolbox for the LDB driver,
which is fine, but there is no situation I can see where this module
would be unloaded and the LDB driver would be loaded.
I can see drivers/gpu/drm/drm_mipi_dbi.c is also a module and
essentially provides helpers to MIPI DBI drivers, but it is not a
driver. I don't see this imx-ldb-helper can be anything else other
than a module.
Or, do you mean that imx-ldb-helper should be only built-in?
My thinking was that it should just be linked together with the rest
of the imx8qxp-ldb driver. But this ties in to my next comment.
This patch set contains _two_ modules which use the imx-ldb-helper,
i.e., the imx8qxp-ldb driver(patch 12/14) and the imx8qm-ldb
driver(patch 13/14). It looks that we cannot link the imx-ldb-helper
with them respectively. So, I think keeping imx-ldb-helper as a module
is the only option.
This header is specific to this driver, and I would expect it to not
be useful to other drivers. Additionally the filename has a different
format than the .c file it corresponds to. I would change the name and
path to "drivers/gpu/drm/bridge/imx/imx-ldb-helper.h".
The i.MX53/6qdl LDB driver(drivers/gpu/drm/imx/imx-ldb.c) can
potentially use this header, but it's a DRM encoder driver.
So, maybe, it's a good idea to move this header to the 'drivers' folder
and rename it to 'imx-ldb-helper.h' ofc. If no objections, I'll do as
what you're suggesting here in the next version.
Ah I see. If ldb-helper is indeed used by two drivers, making it a
module seems reasonable.
Yes, for now, two drivers(imx8qxp-ldb and imx8qm-ldb) use
imx-ldb-helper.
I think we have two options then.
#1 Make imx-ldb-helper an object that is just linked with the
imx8qxp-ldb driver.
I don't think #1 is a valid option, as we cannot link imx-ldb-helper
object with imx8qxp-ldb driver and imx8qm-ldb driver respectively.
#2 Keep imx-ldb-helper as a module, and implement support for using it
in the imx-ldb driver. Ideally I'd like to see the imx-ldb-helper
module patch in the same series as as imx53/6qdl switching to using
the module. These things have a tendency of not happening if not done
right away :)
As I mentioned before, the i.MX53/6qdl LDB driver(imx-ldb) is a DRM
encoder driver. It doesn't look straight-forward to include a header
for bridge drivers there.
An alternative is to first convert imx-ldb to be a pure bridge
driver(called imx53-ldb.c, perhaps) and to put it in
drivers/gpu/drm/bridge/imx folder. But, it's not an easy task, because
the imx-drm for i.MX51/53/6dql IPU display controller needs to create
DRM encoders & connectors instead, and the other relevant encoder
drivers (imx-tve, parallel-display and dw_hdmi-imx) needs to be
converted to bridge drivers as well. This is not what this patch set
can cover, IMHO. Perhaps, it will be done later on.
In all, it looks ok to keep imx-ldb-helper as a module and move the
header(imx-ldb-helper.h) to the 'drivers' folder. Agree?
Regards,
Liu Ying
@@ -0,0 +1,108 @@+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)+%YAML1.2+---+$id:http://devicetree.org/schemas/display/bridge/fsl,imx8qxp-pxl2dpi.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Freescale i.MX8qxp Pixel Link to Display Pixel Interface++maintainers:+-Liu Ying <victor.liu@nxp.com>++description:|+The Freescale i.MX8qxp Pixel Link to Display Pixel Interface(PXL2DPI)+interfaces the pixel link 36-bit data output and the DSI controller’s+MIPI-DPI 24-bit data input, and inputs of LVDS Display Bridge(LDB) module+used in LVDS mode, to remap the pixel color codings between those modules.+This module is purely combinatorial.++The i.MX8qxp PXL2DPI is controlled by Control and Status Registers(CSR) module.+The CSR module, as a system controller, contains the PXL2DPI's configuration+register.
So this node should be a child of the CSR. Ideally, this schema is also
referenced from the CSR's schema (and if that doesn't exist, it should
be there first).
@@ -0,0 +1,108 @@+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)+%YAML1.2+---+$id:https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fdevicetree.org%2Fschemas%2Fdisplay%2Fbridge%2Ffsl%2Cimx8qxp-pxl2dpi.yaml%23&data=04%7C01%7Cvictor.liu%40nxp.com%7Ca37ec67ba3274bcea5c408d8e027f69b%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637505809544037562%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=UN2IQps7q5vK6uNG8fQTn1Klgn0cVyuYnUeqxrjCWHo%3D&reserved=0+$schema:https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fdevicetree.org%2Fmeta-schemas%2Fcore.yaml%23&data=04%7C01%7Cvictor.liu%40nxp.com%7Ca37ec67ba3274bcea5c408d8e027f69b%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637505809544037562%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=cvJVL3Fp1hwbjj1jO1YAozKdZATt5DJ78E7vGT%2F25Oc%3D&reserved=0++title:Freescale i.MX8qxp Pixel Link to Display Pixel Interface++maintainers:+-Liu Ying <victor.liu@nxp.com>++description:|+The Freescale i.MX8qxp Pixel Link to Display Pixel Interface(PXL2DPI)+interfaces the pixel link 36-bit data output and the DSI controller’s+MIPI-DPI 24-bit data input, and inputs of LVDS Display Bridge(LDB) module+used in LVDS mode, to remap the pixel color codings between those modules.+This module is purely combinatorial.++The i.MX8qxp PXL2DPI is controlled by Control and Status Registers(CSR) module.+The CSR module, as a system controller, contains the PXL2DPI's configuration+register.
So this node should be a child of the CSR. Ideally, this schema is also
referenced from the CSR's schema (and if that doesn't exist, it should
be there first).
I can add a patch to introduce a schema for the CSR in this series,
just prior to this patch. Do you think if that will be fine?
Thanks,
Liu Ying
From: Liu Ying <victor.liu@nxp.com> Date: 2021-03-08 10:12:43
Hi Robert,
On Thu, 2021-03-04 at 11:27 +0800, Liu Ying wrote:
Hi Robert,
On Wed, 2021-03-03 at 16:34 +0100, Robert Foss wrote:
quoted
On Wed, 3 Mar 2021 at 08:23, Liu Ying [off-list ref] wrote:
quoted
Hi Robert,
On Tue, 2021-03-02 at 15:22 +0100, Robert Foss wrote:
quoted
Hey Liu,
Thanks for submitting this patch.
Thanks for reviewing this patch.
quoted
On Thu, 18 Feb 2021 at 04:59, Liu Ying [off-list ref] wrote:
quoted
This patch adds a helper to support LDB drm bridge drivers for
i.MX SoCs. Helper functions exported from this driver should
implement common logics for all LDB modules embedded in i.MX SoCs.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
v3->v4:
* No change.
v2->v3:
* Call syscon_node_to_regmap() to get regmap instead of
syscon_regmap_lookup_by_phandle().
v1->v2:
* No change.
drivers/gpu/drm/bridge/imx/Kconfig | 8 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx-ldb-helper.c | 248 ++++++++++++++++++++++++++++
include/drm/bridge/imx_ldb_helper.h | 98 +++++++++++
4 files changed, 355 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
create mode 100644 include/drm/bridge/imx_ldb_helper.h
@@ -0,0 +1,248 @@+// SPDX-License-Identifier: GPL-2.0++/*+*Copyright(C)2012SaschaHauer,Pengutronix+*Copyright2019,2020NXP+*/++#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of.h>+#include<linux/regmap.h>++#include<drm/bridge/imx_ldb_helper.h>+#include<drm/drm_of.h>+#include<drm/drm_panel.h>+#include<drm/drm_print.h>++boolldb_channel_is_single_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_SINGLE_LINK;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_single_link);++boolldb_channel_is_split_link(structldb_channel*ldb_ch)+{+returnldb_ch->link_type==LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS||+ldb_ch->link_type==LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;+}+EXPORT_SYMBOL_GPL(ldb_channel_is_split_link);++intldb_bridge_atomic_check_helper(structdrm_bridge*bridge,+structdrm_bridge_state*bridge_state,+structdrm_crtc_state*crtc_state,+structdrm_connector_state*conn_state)+{+structldb_channel*ldb_ch=bridge->driver_private;++ldb_ch->in_bus_format=bridge_state->input_bus_cfg.format;+ldb_ch->out_bus_format=bridge_state->output_bus_cfg.format;++return0;+}+EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper);++voidldb_bridge_mode_set_helper(structdrm_bridge*bridge,+conststructdrm_display_mode*mode,+conststructdrm_display_mode*adjusted_mode)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(is_split)+ldb->ldb_ctrl|=LDB_SPLIT_MODE_EN;++switch(ldb_ch->out_bus_format){+caseMEDIA_BUS_FMT_RGB666_1X7X3_SPWG:+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_SPWG:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24;+break;+caseMEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:+if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH0_24|+LDB_BIT_MAP_CH0_JEIDA;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl|=LDB_DATA_WIDTH_CH1_24|+LDB_BIT_MAP_CH1_JEIDA;+break;+}+}+EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper);++voidldb_bridge_enable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++/*+*Platformspecificbridgedriversshouldsetldb_ctrlproperly+*fortheenablement,sojustwritethectrl_reghere.+*/+regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper);++voidldb_bridge_disable_helper(structdrm_bridge*bridge)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;+boolis_split=ldb_channel_is_split_link(ldb_ch);++if(ldb_ch->chno==0||is_split)+ldb->ldb_ctrl&=~LDB_CH0_MODE_EN_MASK;+if(ldb_ch->chno==1||is_split)+ldb->ldb_ctrl&=~LDB_CH1_MODE_EN_MASK;++regmap_write(ldb->regmap,ldb->ctrl_reg,ldb->ldb_ctrl);+}+EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper);++intldb_bridge_attach_helper(structdrm_bridge*bridge,+enumdrm_bridge_attach_flagsflags)+{+structldb_channel*ldb_ch=bridge->driver_private;+structldb*ldb=ldb_ch->ldb;++if(!(flags&DRM_BRIDGE_ATTACH_NO_CONNECTOR)){+DRM_DEV_ERROR(ldb->dev,+"do not support creating a drm_connector\n");+return-EINVAL;+}++if(!bridge->encoder){+DRM_DEV_ERROR(ldb->dev,"missing encoder\n");+return-ENODEV;+}++returndrm_bridge_attach(bridge->encoder,+ldb_ch->next_bridge,bridge,+DRM_BRIDGE_ATTACH_NO_CONNECTOR);+}+EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper);++intldb_init_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structdevice_node*np=dev->of_node;+structdevice_node*child;+intret;+u32i;++ldb->regmap=syscon_node_to_regmap(np->parent);+if(IS_ERR(ldb->regmap)){+ret=PTR_ERR(ldb->regmap);+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,"failed to get regmap: %d\n",ret);+returnret;+}++for_each_available_child_of_node(np,child){+structldb_channel*ldb_ch;++ret=of_property_read_u32(child,"reg",&i);+if(ret||i>MAX_LDB_CHAN_NUM-1){+ret=-EINVAL;+DRM_DEV_ERROR(dev,+"invalid channel node address: %u\n",i);+of_node_put(child);+returnret;+}++ldb_ch=ldb->channel[i];+ldb_ch->ldb=ldb;+ldb_ch->chno=i;+ldb_ch->is_available=true;+ldb_ch->np=child;++ldb->available_ch_cnt++;+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_init_helper);++intldb_find_next_bridge_helper(structldb*ldb)+{+structdevice*dev=ldb->dev;+structldb_channel*ldb_ch;+intret,i;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ret=drm_of_find_panel_or_bridge(ldb_ch->np,1,0,+&ldb_ch->panel,+&ldb_ch->next_bridge);+if(ret){+if(ret!=-EPROBE_DEFER)+DRM_DEV_ERROR(dev,+"failed to find panel or bridge: %d\n",+ret);+returnret;+}++if(ldb_ch->panel){+ldb_ch->next_bridge=devm_drm_panel_bridge_add(dev,+ldb_ch->panel);+if(IS_ERR(ldb_ch->next_bridge)){+ret=PTR_ERR(ldb_ch->next_bridge);+DRM_DEV_ERROR(dev,+"failed to add panel bridge: %d\n",+ret);+returnret;+}+}+}++return0;+}+EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper);++voidldb_add_bridge_helper(structldb*ldb,+conststructdrm_bridge_funcs*bridge_funcs)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++ldb_ch->bridge.driver_private=ldb_ch;+ldb_ch->bridge.funcs=bridge_funcs;+ldb_ch->bridge.of_node=ldb_ch->np;++drm_bridge_add(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_add_bridge_helper);++voidldb_remove_bridge_helper(structldb*ldb)+{+structldb_channel*ldb_ch;+inti;++for(i=0;i<MAX_LDB_CHAN_NUM;i++){+ldb_ch=ldb->channel[i];++if(!ldb_ch->is_available)+continue;++drm_bridge_remove(&ldb_ch->bridge);+}+}+EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper);++MODULE_DESCRIPTION("Freescale i.MX LVDS Display Bridge driver helper");+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:imx-ldb-helper");
I'm not entirely sure why this set of helper functions should be a
module. It's not a driver, but rather a toolbox for the LDB driver,
which is fine, but there is no situation I can see where this module
would be unloaded and the LDB driver would be loaded.
I can see drivers/gpu/drm/drm_mipi_dbi.c is also a module and
essentially provides helpers to MIPI DBI drivers, but it is not a
driver. I don't see this imx-ldb-helper can be anything else other
than a module.
Or, do you mean that imx-ldb-helper should be only built-in?
My thinking was that it should just be linked together with the rest
of the imx8qxp-ldb driver. But this ties in to my next comment.
This patch set contains _two_ modules which use the imx-ldb-helper,
i.e., the imx8qxp-ldb driver(patch 12/14) and the imx8qm-ldb
driver(patch 13/14). It looks that we cannot link the imx-ldb-helper
with them respectively. So, I think keeping imx-ldb-helper as a module
is the only option.
I need to change my saying here. I think we can link the
imx-ldb-helper with those two drivers respectively.
So, I assume imx-ldb-helper won't be a module, then.
And, I still don't think this series should touch the i.MX53/6qdl LDB
driver(imx-ldb).
If no strong opinions, I would try the updated option#1 for the next
version:
#1(updated) Make imx-ldb-helper an object that is just linked with the
imx8qxp-ldb driver and the imx8qm-ldb driver.
Regards,
Liu Ying
This header is specific to this driver, and I would expect it to not
be useful to other drivers. Additionally the filename has a different
format than the .c file it corresponds to. I would change the name and
path to "drivers/gpu/drm/bridge/imx/imx-ldb-helper.h".
The i.MX53/6qdl LDB driver(drivers/gpu/drm/imx/imx-ldb.c) can
potentially use this header, but it's a DRM encoder driver.
So, maybe, it's a good idea to move this header to the 'drivers' folder
and rename it to 'imx-ldb-helper.h' ofc. If no objections, I'll do as
what you're suggesting here in the next version.
Ah I see. If ldb-helper is indeed used by two drivers, making it a
module seems reasonable.
Yes, for now, two drivers(imx8qxp-ldb and imx8qm-ldb) use
imx-ldb-helper.
quoted
I think we have two options then.
#1 Make imx-ldb-helper an object that is just linked with the
imx8qxp-ldb driver.
I don't think #1 is a valid option, as we cannot link imx-ldb-helper
object with imx8qxp-ldb driver and imx8qm-ldb driver respectively.
quoted
#2 Keep imx-ldb-helper as a module, and implement support for using it
in the imx-ldb driver. Ideally I'd like to see the imx-ldb-helper
module patch in the same series as as imx53/6qdl switching to using
the module. These things have a tendency of not happening if not done
right away :)
As I mentioned before, the i.MX53/6qdl LDB driver(imx-ldb) is a DRM
encoder driver. It doesn't look straight-forward to include a header
for bridge drivers there.
An alternative is to first convert imx-ldb to be a pure bridge
driver(called imx53-ldb.c, perhaps) and to put it in
drivers/gpu/drm/bridge/imx folder. But, it's not an easy task, because
the imx-drm for i.MX51/53/6dql IPU display controller needs to create
DRM encoders & connectors instead, and the other relevant encoder
drivers (imx-tve, parallel-display and dw_hdmi-imx) needs to be
converted to bridge drivers as well. This is not what this patch set
can cover, IMHO. Perhaps, it will be done later on.
In all, it looks ok to keep imx-ldb-helper as a module and move the
header(imx-ldb-helper.h) to the 'drivers' folder. Agree?
Regards,
Liu Ying