mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	drm: bridge: ldb: Implement simple Freescale i.MX8MP LDB bridge
The i.MX8MP contains two syscon registers which are responsible
for configuring the on-SoC DPI-to-LVDS serializer. Implement a
simple bridge driver for this serializer.
--
    - Add sentinel of_device_table
    - Add RB from Sam
    - Rename to fsl-ldb altogether
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Lucas Stach <l.stach@pengutronix.de>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Peng Fan <peng.fan@nxp.com>
Cc: Robby Cai <robby.cai@nxp.com>
Cc: Robert Foss <robert.foss@linaro.org>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
To: dri-devel@lists.freedesktop.org
V2: - Rename syscon to fsl,syscon
V3: - Consistently use MX8MP
V4: - Fix MODULE_DESCRIPTION to also use MX8MP
Signed-off-by: Robert Foss <robert.foss@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220426193645.244792-2-marex@denx.de
			
			
This commit is contained in:
		
							parent
							
								
									666518676d
								
							
						
					
					
						commit
						463db5c2ed
					
				
					 3 changed files with 351 additions and 0 deletions
				
			
		| 
						 | 
					@ -75,6 +75,14 @@ config DRM_DISPLAY_CONNECTOR
 | 
				
			||||||
	  on ARM-based platforms. Saying Y here when this driver is not needed
 | 
						  on ARM-based platforms. Saying Y here when this driver is not needed
 | 
				
			||||||
	  will not cause any issue.
 | 
						  will not cause any issue.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config DRM_FSL_LDB
 | 
				
			||||||
 | 
						tristate "Freescale i.MX8MP LDB bridge"
 | 
				
			||||||
 | 
						depends on OF
 | 
				
			||||||
 | 
						select DRM_KMS_HELPER
 | 
				
			||||||
 | 
						select DRM_PANEL_BRIDGE
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  Support for i.MX8MP DPI-to-LVDS on-SoC encoder.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config DRM_ITE_IT6505
 | 
					config DRM_ITE_IT6505
 | 
				
			||||||
        tristate "ITE IT6505 DisplayPort bridge"
 | 
					        tristate "ITE IT6505 DisplayPort bridge"
 | 
				
			||||||
        depends on OF
 | 
					        depends on OF
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
 | 
				
			||||||
obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
 | 
					obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
 | 
				
			||||||
obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
 | 
					obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
 | 
				
			||||||
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
 | 
					obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_DRM_FSL_LDB) += fsl-ldb.o
 | 
				
			||||||
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
 | 
					obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
 | 
				
			||||||
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
 | 
					obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
 | 
				
			||||||
obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
 | 
					obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										342
									
								
								drivers/gpu/drm/bridge/fsl-ldb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								drivers/gpu/drm/bridge/fsl-ldb.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,342 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2022 Marek Vasut <marex@denx.de>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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/platform_device.h>
 | 
				
			||||||
 | 
					#include <linux/regmap.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <drm/drm_atomic_helper.h>
 | 
				
			||||||
 | 
					#include <drm/drm_bridge.h>
 | 
				
			||||||
 | 
					#include <drm/drm_of.h>
 | 
				
			||||||
 | 
					#include <drm/drm_panel.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LDB_CTRL				0x5c
 | 
				
			||||||
 | 
					#define LDB_CTRL_CH0_ENABLE			BIT(0)
 | 
				
			||||||
 | 
					#define LDB_CTRL_CH0_DI_SELECT			BIT(1)
 | 
				
			||||||
 | 
					#define LDB_CTRL_CH1_ENABLE			BIT(2)
 | 
				
			||||||
 | 
					#define LDB_CTRL_CH1_DI_SELECT			BIT(3)
 | 
				
			||||||
 | 
					#define LDB_CTRL_SPLIT_MODE			BIT(4)
 | 
				
			||||||
 | 
					#define LDB_CTRL_CH0_DATA_WIDTH			BIT(5)
 | 
				
			||||||
 | 
					#define LDB_CTRL_CH0_BIT_MAPPING		BIT(6)
 | 
				
			||||||
 | 
					#define LDB_CTRL_CH1_DATA_WIDTH			BIT(7)
 | 
				
			||||||
 | 
					#define LDB_CTRL_CH1_BIT_MAPPING		BIT(8)
 | 
				
			||||||
 | 
					#define LDB_CTRL_DI0_VSYNC_POLARITY		BIT(9)
 | 
				
			||||||
 | 
					#define LDB_CTRL_DI1_VSYNC_POLARITY		BIT(10)
 | 
				
			||||||
 | 
					#define LDB_CTRL_REG_CH0_FIFO_RESET		BIT(11)
 | 
				
			||||||
 | 
					#define LDB_CTRL_REG_CH1_FIFO_RESET		BIT(12)
 | 
				
			||||||
 | 
					#define LDB_CTRL_ASYNC_FIFO_ENABLE		BIT(24)
 | 
				
			||||||
 | 
					#define LDB_CTRL_ASYNC_FIFO_THRESHOLD_MASK	GENMASK(27, 25)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LVDS_CTRL				0x128
 | 
				
			||||||
 | 
					#define LVDS_CTRL_CH0_EN			BIT(0)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_CH1_EN			BIT(1)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_VBG_EN			BIT(2)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_HS_EN				BIT(3)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_PRE_EMPH_EN			BIT(4)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_PRE_EMPH_ADJ(n)		(((n) & 0x7) << 5)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_PRE_EMPH_ADJ_MASK		GENMASK(7, 5)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_CM_ADJ(n)			(((n) & 0x7) << 8)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_CM_ADJ_MASK			GENMASK(10, 8)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_CC_ADJ(n)			(((n) & 0x7) << 11)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_CC_ADJ_MASK			GENMASK(13, 11)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_SLEW_ADJ(n)			(((n) & 0x7) << 14)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_SLEW_ADJ_MASK			GENMASK(16, 14)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_VBG_ADJ(n)			(((n) & 0x7) << 17)
 | 
				
			||||||
 | 
					#define LVDS_CTRL_VBG_ADJ_MASK			GENMASK(19, 17)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct fsl_ldb {
 | 
				
			||||||
 | 
						struct device *dev;
 | 
				
			||||||
 | 
						struct drm_bridge bridge;
 | 
				
			||||||
 | 
						struct drm_bridge *panel_bridge;
 | 
				
			||||||
 | 
						struct clk *clk;
 | 
				
			||||||
 | 
						struct regmap *regmap;
 | 
				
			||||||
 | 
						bool lvds_dual_link;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct fsl_ldb *to_fsl_ldb(struct drm_bridge *bridge)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return container_of(bridge, struct fsl_ldb, bridge);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int fsl_ldb_attach(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
								  enum drm_bridge_attach_flags flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return drm_bridge_attach(bridge->encoder, fsl_ldb->panel_bridge,
 | 
				
			||||||
 | 
									 bridge, flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int fsl_ldb_atomic_check(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
									struct drm_bridge_state *bridge_state,
 | 
				
			||||||
 | 
									struct drm_crtc_state *crtc_state,
 | 
				
			||||||
 | 
									struct drm_connector_state *conn_state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Invert DE signal polarity. */
 | 
				
			||||||
 | 
						bridge_state->input_bus_cfg.flags &= ~(DRM_BUS_FLAG_DE_LOW |
 | 
				
			||||||
 | 
										       DRM_BUS_FLAG_DE_HIGH);
 | 
				
			||||||
 | 
						if (bridge_state->output_bus_cfg.flags & DRM_BUS_FLAG_DE_LOW)
 | 
				
			||||||
 | 
							bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH;
 | 
				
			||||||
 | 
						else if (bridge_state->output_bus_cfg.flags & DRM_BUS_FLAG_DE_HIGH)
 | 
				
			||||||
 | 
							bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_LOW;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void fsl_ldb_atomic_enable(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
									  struct drm_bridge_state *old_bridge_state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
 | 
				
			||||||
 | 
						struct drm_atomic_state *state = old_bridge_state->base.state;
 | 
				
			||||||
 | 
						const struct drm_bridge_state *bridge_state;
 | 
				
			||||||
 | 
						const struct drm_crtc_state *crtc_state;
 | 
				
			||||||
 | 
						const struct drm_display_mode *mode;
 | 
				
			||||||
 | 
						struct drm_connector *connector;
 | 
				
			||||||
 | 
						struct drm_crtc *crtc;
 | 
				
			||||||
 | 
						bool lvds_format_24bpp;
 | 
				
			||||||
 | 
						bool lvds_format_jeida;
 | 
				
			||||||
 | 
						u32 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Get the LVDS format from the bridge state. */
 | 
				
			||||||
 | 
						bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (bridge_state->output_bus_cfg.format) {
 | 
				
			||||||
 | 
						case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
 | 
				
			||||||
 | 
							lvds_format_24bpp = false;
 | 
				
			||||||
 | 
							lvds_format_jeida = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
 | 
				
			||||||
 | 
							lvds_format_24bpp = true;
 | 
				
			||||||
 | 
							lvds_format_jeida = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
 | 
				
			||||||
 | 
							lvds_format_24bpp = true;
 | 
				
			||||||
 | 
							lvds_format_jeida = false;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Some bridges still don't set the correct LVDS bus pixel
 | 
				
			||||||
 | 
							 * format, use SPWG24 default format until those are fixed.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							lvds_format_24bpp = true;
 | 
				
			||||||
 | 
							lvds_format_jeida = false;
 | 
				
			||||||
 | 
							dev_warn(fsl_ldb->dev,
 | 
				
			||||||
 | 
								 "Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
 | 
				
			||||||
 | 
								 bridge_state->output_bus_cfg.format);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Retrieve the CRTC adjusted mode. This requires a little dance to go
 | 
				
			||||||
 | 
						 * from the bridge to the encoder, to the connector and to the CRTC.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						connector = drm_atomic_get_new_connector_for_encoder(state,
 | 
				
			||||||
 | 
												     bridge->encoder);
 | 
				
			||||||
 | 
						crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
 | 
				
			||||||
 | 
						crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
 | 
				
			||||||
 | 
						mode = &crtc_state->adjusted_mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fsl_ldb->lvds_dual_link)
 | 
				
			||||||
 | 
							clk_set_rate(fsl_ldb->clk, mode->clock * 3500);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							clk_set_rate(fsl_ldb->clk, mode->clock * 7000);
 | 
				
			||||||
 | 
						clk_prepare_enable(fsl_ldb->clk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Program LDB_CTRL */
 | 
				
			||||||
 | 
						reg = LDB_CTRL_CH0_ENABLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fsl_ldb->lvds_dual_link)
 | 
				
			||||||
 | 
							reg |= LDB_CTRL_CH1_ENABLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (lvds_format_24bpp) {
 | 
				
			||||||
 | 
							reg |= LDB_CTRL_CH0_DATA_WIDTH;
 | 
				
			||||||
 | 
							if (fsl_ldb->lvds_dual_link)
 | 
				
			||||||
 | 
								reg |= LDB_CTRL_CH1_DATA_WIDTH;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (lvds_format_jeida) {
 | 
				
			||||||
 | 
							reg |= LDB_CTRL_CH0_BIT_MAPPING;
 | 
				
			||||||
 | 
							if (fsl_ldb->lvds_dual_link)
 | 
				
			||||||
 | 
								reg |= LDB_CTRL_CH1_BIT_MAPPING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mode->flags & DRM_MODE_FLAG_PVSYNC) {
 | 
				
			||||||
 | 
							reg |= LDB_CTRL_DI0_VSYNC_POLARITY;
 | 
				
			||||||
 | 
							if (fsl_ldb->lvds_dual_link)
 | 
				
			||||||
 | 
								reg |= LDB_CTRL_DI1_VSYNC_POLARITY;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						regmap_write(fsl_ldb->regmap, LDB_CTRL, reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Program LVDS_CTRL */
 | 
				
			||||||
 | 
						reg = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN |
 | 
				
			||||||
 | 
						      LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN;
 | 
				
			||||||
 | 
						regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Wait for VBG to stabilize. */
 | 
				
			||||||
 | 
						usleep_range(15, 20);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg |= LVDS_CTRL_CH0_EN;
 | 
				
			||||||
 | 
						if (fsl_ldb->lvds_dual_link)
 | 
				
			||||||
 | 
							reg |= LVDS_CTRL_CH1_EN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void fsl_ldb_atomic_disable(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
									   struct drm_bridge_state *old_bridge_state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Stop both channels. */
 | 
				
			||||||
 | 
						regmap_write(fsl_ldb->regmap, LVDS_CTRL, 0);
 | 
				
			||||||
 | 
						regmap_write(fsl_ldb->regmap, LDB_CTRL, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_disable_unprepare(fsl_ldb->clk);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_INPUT_SEL_FORMATS 1
 | 
				
			||||||
 | 
					static u32 *
 | 
				
			||||||
 | 
					fsl_ldb_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
									  struct drm_bridge_state *bridge_state,
 | 
				
			||||||
 | 
									  struct drm_crtc_state *crtc_state,
 | 
				
			||||||
 | 
									  struct drm_connector_state *conn_state,
 | 
				
			||||||
 | 
									  u32 output_fmt,
 | 
				
			||||||
 | 
									  unsigned int *num_input_fmts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 *input_fmts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*num_input_fmts = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
 | 
				
			||||||
 | 
								     GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!input_fmts)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
 | 
				
			||||||
 | 
						*num_input_fmts = MAX_INPUT_SEL_FORMATS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return input_fmts;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static enum drm_mode_status
 | 
				
			||||||
 | 
					fsl_ldb_mode_valid(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
							   const struct drm_display_info *info,
 | 
				
			||||||
 | 
							   const struct drm_display_mode *mode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mode->clock > (fsl_ldb->lvds_dual_link ? 80000 : 160000))
 | 
				
			||||||
 | 
							return MODE_CLOCK_HIGH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return MODE_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct drm_bridge_funcs funcs = {
 | 
				
			||||||
 | 
						.attach = fsl_ldb_attach,
 | 
				
			||||||
 | 
						.atomic_check = fsl_ldb_atomic_check,
 | 
				
			||||||
 | 
						.atomic_enable = fsl_ldb_atomic_enable,
 | 
				
			||||||
 | 
						.atomic_disable = fsl_ldb_atomic_disable,
 | 
				
			||||||
 | 
						.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
 | 
				
			||||||
 | 
						.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
 | 
				
			||||||
 | 
						.atomic_get_input_bus_fmts = fsl_ldb_atomic_get_input_bus_fmts,
 | 
				
			||||||
 | 
						.atomic_reset = drm_atomic_helper_bridge_reset,
 | 
				
			||||||
 | 
						.mode_valid = fsl_ldb_mode_valid,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int fsl_ldb_probe(struct platform_device *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct device *dev = &pdev->dev;
 | 
				
			||||||
 | 
						struct device_node *panel_node;
 | 
				
			||||||
 | 
						struct device_node *port1, *port2;
 | 
				
			||||||
 | 
						struct drm_panel *panel;
 | 
				
			||||||
 | 
						struct fsl_ldb *fsl_ldb;
 | 
				
			||||||
 | 
						int dual_link;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fsl_ldb = devm_kzalloc(dev, sizeof(*fsl_ldb), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!fsl_ldb)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fsl_ldb->dev = &pdev->dev;
 | 
				
			||||||
 | 
						fsl_ldb->bridge.funcs = &funcs;
 | 
				
			||||||
 | 
						fsl_ldb->bridge.of_node = dev->of_node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fsl_ldb->clk = devm_clk_get(dev, "ldb");
 | 
				
			||||||
 | 
						if (IS_ERR(fsl_ldb->clk))
 | 
				
			||||||
 | 
							return PTR_ERR(fsl_ldb->clk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fsl_ldb->regmap = syscon_node_to_regmap(dev->of_node->parent);
 | 
				
			||||||
 | 
						if (IS_ERR(fsl_ldb->regmap))
 | 
				
			||||||
 | 
							return PTR_ERR(fsl_ldb->regmap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Locate the panel DT node. */
 | 
				
			||||||
 | 
						panel_node = of_graph_get_remote_node(dev->of_node, 1, 0);
 | 
				
			||||||
 | 
						if (!panel_node)
 | 
				
			||||||
 | 
							return -ENXIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						panel = of_drm_find_panel(panel_node);
 | 
				
			||||||
 | 
						of_node_put(panel_node);
 | 
				
			||||||
 | 
						if (IS_ERR(panel))
 | 
				
			||||||
 | 
							return PTR_ERR(panel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
 | 
				
			||||||
 | 
						if (IS_ERR(fsl_ldb->panel_bridge))
 | 
				
			||||||
 | 
							return PTR_ERR(fsl_ldb->panel_bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Determine whether this is dual-link configuration */
 | 
				
			||||||
 | 
						port1 = of_graph_get_port_by_id(dev->of_node, 1);
 | 
				
			||||||
 | 
						port2 = of_graph_get_port_by_id(dev->of_node, 2);
 | 
				
			||||||
 | 
						dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
 | 
				
			||||||
 | 
						of_node_put(port1);
 | 
				
			||||||
 | 
						of_node_put(port2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dual_link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
 | 
				
			||||||
 | 
							dev_err(dev, "LVDS channel pixel swap not supported.\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dual_link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
 | 
				
			||||||
 | 
							fsl_ldb->lvds_dual_link = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						platform_set_drvdata(pdev, fsl_ldb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drm_bridge_add(&fsl_ldb->bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int fsl_ldb_remove(struct platform_device *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct fsl_ldb *fsl_ldb = platform_get_drvdata(pdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drm_bridge_remove(&fsl_ldb->bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct of_device_id fsl_ldb_match[] = {
 | 
				
			||||||
 | 
						{ .compatible = "fsl,imx8mp-ldb", },
 | 
				
			||||||
 | 
						{ /* sentinel */ },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MODULE_DEVICE_TABLE(of, fsl_ldb_match);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct platform_driver fsl_ldb_driver = {
 | 
				
			||||||
 | 
						.probe	= fsl_ldb_probe,
 | 
				
			||||||
 | 
						.remove	= fsl_ldb_remove,
 | 
				
			||||||
 | 
						.driver		= {
 | 
				
			||||||
 | 
							.name		= "fsl-ldb",
 | 
				
			||||||
 | 
							.of_match_table	= fsl_ldb_match,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					module_platform_driver(fsl_ldb_driver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 | 
				
			||||||
 | 
					MODULE_DESCRIPTION("Freescale i.MX8MP LDB");
 | 
				
			||||||
 | 
					MODULE_LICENSE("GPL");
 | 
				
			||||||
		Loading…
	
		Reference in a new issue