cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fsl-ldb.c (9756B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2022 Marek Vasut <marex@denx.de>
      4 */
      5
      6#include <linux/clk.h>
      7#include <linux/mfd/syscon.h>
      8#include <linux/module.h>
      9#include <linux/of.h>
     10#include <linux/of_device.h>
     11#include <linux/of_graph.h>
     12#include <linux/platform_device.h>
     13#include <linux/regmap.h>
     14
     15#include <drm/drm_atomic_helper.h>
     16#include <drm/drm_bridge.h>
     17#include <drm/drm_of.h>
     18#include <drm/drm_panel.h>
     19
     20#define LDB_CTRL				0x5c
     21#define LDB_CTRL_CH0_ENABLE			BIT(0)
     22#define LDB_CTRL_CH0_DI_SELECT			BIT(1)
     23#define LDB_CTRL_CH1_ENABLE			BIT(2)
     24#define LDB_CTRL_CH1_DI_SELECT			BIT(3)
     25#define LDB_CTRL_SPLIT_MODE			BIT(4)
     26#define LDB_CTRL_CH0_DATA_WIDTH			BIT(5)
     27#define LDB_CTRL_CH0_BIT_MAPPING		BIT(6)
     28#define LDB_CTRL_CH1_DATA_WIDTH			BIT(7)
     29#define LDB_CTRL_CH1_BIT_MAPPING		BIT(8)
     30#define LDB_CTRL_DI0_VSYNC_POLARITY		BIT(9)
     31#define LDB_CTRL_DI1_VSYNC_POLARITY		BIT(10)
     32#define LDB_CTRL_REG_CH0_FIFO_RESET		BIT(11)
     33#define LDB_CTRL_REG_CH1_FIFO_RESET		BIT(12)
     34#define LDB_CTRL_ASYNC_FIFO_ENABLE		BIT(24)
     35#define LDB_CTRL_ASYNC_FIFO_THRESHOLD_MASK	GENMASK(27, 25)
     36
     37#define LVDS_CTRL				0x128
     38#define LVDS_CTRL_CH0_EN			BIT(0)
     39#define LVDS_CTRL_CH1_EN			BIT(1)
     40#define LVDS_CTRL_VBG_EN			BIT(2)
     41#define LVDS_CTRL_HS_EN				BIT(3)
     42#define LVDS_CTRL_PRE_EMPH_EN			BIT(4)
     43#define LVDS_CTRL_PRE_EMPH_ADJ(n)		(((n) & 0x7) << 5)
     44#define LVDS_CTRL_PRE_EMPH_ADJ_MASK		GENMASK(7, 5)
     45#define LVDS_CTRL_CM_ADJ(n)			(((n) & 0x7) << 8)
     46#define LVDS_CTRL_CM_ADJ_MASK			GENMASK(10, 8)
     47#define LVDS_CTRL_CC_ADJ(n)			(((n) & 0x7) << 11)
     48#define LVDS_CTRL_CC_ADJ_MASK			GENMASK(13, 11)
     49#define LVDS_CTRL_SLEW_ADJ(n)			(((n) & 0x7) << 14)
     50#define LVDS_CTRL_SLEW_ADJ_MASK			GENMASK(16, 14)
     51#define LVDS_CTRL_VBG_ADJ(n)			(((n) & 0x7) << 17)
     52#define LVDS_CTRL_VBG_ADJ_MASK			GENMASK(19, 17)
     53
     54struct fsl_ldb {
     55	struct device *dev;
     56	struct drm_bridge bridge;
     57	struct drm_bridge *panel_bridge;
     58	struct clk *clk;
     59	struct regmap *regmap;
     60	bool lvds_dual_link;
     61};
     62
     63static inline struct fsl_ldb *to_fsl_ldb(struct drm_bridge *bridge)
     64{
     65	return container_of(bridge, struct fsl_ldb, bridge);
     66}
     67
     68static int fsl_ldb_attach(struct drm_bridge *bridge,
     69			  enum drm_bridge_attach_flags flags)
     70{
     71	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
     72
     73	return drm_bridge_attach(bridge->encoder, fsl_ldb->panel_bridge,
     74				 bridge, flags);
     75}
     76
     77static int fsl_ldb_atomic_check(struct drm_bridge *bridge,
     78				struct drm_bridge_state *bridge_state,
     79				struct drm_crtc_state *crtc_state,
     80				struct drm_connector_state *conn_state)
     81{
     82	/* Invert DE signal polarity. */
     83	bridge_state->input_bus_cfg.flags &= ~(DRM_BUS_FLAG_DE_LOW |
     84					       DRM_BUS_FLAG_DE_HIGH);
     85	if (bridge_state->output_bus_cfg.flags & DRM_BUS_FLAG_DE_LOW)
     86		bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH;
     87	else if (bridge_state->output_bus_cfg.flags & DRM_BUS_FLAG_DE_HIGH)
     88		bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_LOW;
     89
     90	return 0;
     91}
     92
     93static void fsl_ldb_atomic_enable(struct drm_bridge *bridge,
     94				  struct drm_bridge_state *old_bridge_state)
     95{
     96	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
     97	struct drm_atomic_state *state = old_bridge_state->base.state;
     98	const struct drm_bridge_state *bridge_state;
     99	const struct drm_crtc_state *crtc_state;
    100	const struct drm_display_mode *mode;
    101	struct drm_connector *connector;
    102	struct drm_crtc *crtc;
    103	bool lvds_format_24bpp;
    104	bool lvds_format_jeida;
    105	u32 reg;
    106
    107	/* Get the LVDS format from the bridge state. */
    108	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
    109
    110	switch (bridge_state->output_bus_cfg.format) {
    111	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
    112		lvds_format_24bpp = false;
    113		lvds_format_jeida = true;
    114		break;
    115	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
    116		lvds_format_24bpp = true;
    117		lvds_format_jeida = true;
    118		break;
    119	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
    120		lvds_format_24bpp = true;
    121		lvds_format_jeida = false;
    122		break;
    123	default:
    124		/*
    125		 * Some bridges still don't set the correct LVDS bus pixel
    126		 * format, use SPWG24 default format until those are fixed.
    127		 */
    128		lvds_format_24bpp = true;
    129		lvds_format_jeida = false;
    130		dev_warn(fsl_ldb->dev,
    131			 "Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
    132			 bridge_state->output_bus_cfg.format);
    133		break;
    134	}
    135
    136	/*
    137	 * Retrieve the CRTC adjusted mode. This requires a little dance to go
    138	 * from the bridge to the encoder, to the connector and to the CRTC.
    139	 */
    140	connector = drm_atomic_get_new_connector_for_encoder(state,
    141							     bridge->encoder);
    142	crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
    143	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
    144	mode = &crtc_state->adjusted_mode;
    145
    146	if (fsl_ldb->lvds_dual_link)
    147		clk_set_rate(fsl_ldb->clk, mode->clock * 3500);
    148	else
    149		clk_set_rate(fsl_ldb->clk, mode->clock * 7000);
    150	clk_prepare_enable(fsl_ldb->clk);
    151
    152	/* Program LDB_CTRL */
    153	reg = LDB_CTRL_CH0_ENABLE;
    154
    155	if (fsl_ldb->lvds_dual_link)
    156		reg |= LDB_CTRL_CH1_ENABLE;
    157
    158	if (lvds_format_24bpp) {
    159		reg |= LDB_CTRL_CH0_DATA_WIDTH;
    160		if (fsl_ldb->lvds_dual_link)
    161			reg |= LDB_CTRL_CH1_DATA_WIDTH;
    162	}
    163
    164	if (lvds_format_jeida) {
    165		reg |= LDB_CTRL_CH0_BIT_MAPPING;
    166		if (fsl_ldb->lvds_dual_link)
    167			reg |= LDB_CTRL_CH1_BIT_MAPPING;
    168	}
    169
    170	if (mode->flags & DRM_MODE_FLAG_PVSYNC) {
    171		reg |= LDB_CTRL_DI0_VSYNC_POLARITY;
    172		if (fsl_ldb->lvds_dual_link)
    173			reg |= LDB_CTRL_DI1_VSYNC_POLARITY;
    174	}
    175
    176	regmap_write(fsl_ldb->regmap, LDB_CTRL, reg);
    177
    178	/* Program LVDS_CTRL */
    179	reg = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN |
    180	      LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN;
    181	regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg);
    182
    183	/* Wait for VBG to stabilize. */
    184	usleep_range(15, 20);
    185
    186	reg |= LVDS_CTRL_CH0_EN;
    187	if (fsl_ldb->lvds_dual_link)
    188		reg |= LVDS_CTRL_CH1_EN;
    189
    190	regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg);
    191}
    192
    193static void fsl_ldb_atomic_disable(struct drm_bridge *bridge,
    194				   struct drm_bridge_state *old_bridge_state)
    195{
    196	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
    197
    198	/* Stop both channels. */
    199	regmap_write(fsl_ldb->regmap, LVDS_CTRL, 0);
    200	regmap_write(fsl_ldb->regmap, LDB_CTRL, 0);
    201
    202	clk_disable_unprepare(fsl_ldb->clk);
    203}
    204
    205#define MAX_INPUT_SEL_FORMATS 1
    206static u32 *
    207fsl_ldb_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
    208				  struct drm_bridge_state *bridge_state,
    209				  struct drm_crtc_state *crtc_state,
    210				  struct drm_connector_state *conn_state,
    211				  u32 output_fmt,
    212				  unsigned int *num_input_fmts)
    213{
    214	u32 *input_fmts;
    215
    216	*num_input_fmts = 0;
    217
    218	input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
    219			     GFP_KERNEL);
    220	if (!input_fmts)
    221		return NULL;
    222
    223	input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
    224	*num_input_fmts = MAX_INPUT_SEL_FORMATS;
    225
    226	return input_fmts;
    227}
    228
    229static enum drm_mode_status
    230fsl_ldb_mode_valid(struct drm_bridge *bridge,
    231		   const struct drm_display_info *info,
    232		   const struct drm_display_mode *mode)
    233{
    234	struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
    235
    236	if (mode->clock > (fsl_ldb->lvds_dual_link ? 80000 : 160000))
    237		return MODE_CLOCK_HIGH;
    238
    239	return MODE_OK;
    240}
    241
    242static const struct drm_bridge_funcs funcs = {
    243	.attach = fsl_ldb_attach,
    244	.atomic_check = fsl_ldb_atomic_check,
    245	.atomic_enable = fsl_ldb_atomic_enable,
    246	.atomic_disable = fsl_ldb_atomic_disable,
    247	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
    248	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
    249	.atomic_get_input_bus_fmts = fsl_ldb_atomic_get_input_bus_fmts,
    250	.atomic_reset = drm_atomic_helper_bridge_reset,
    251	.mode_valid = fsl_ldb_mode_valid,
    252};
    253
    254static int fsl_ldb_probe(struct platform_device *pdev)
    255{
    256	struct device *dev = &pdev->dev;
    257	struct device_node *panel_node;
    258	struct device_node *port1, *port2;
    259	struct drm_panel *panel;
    260	struct fsl_ldb *fsl_ldb;
    261	int dual_link;
    262
    263	fsl_ldb = devm_kzalloc(dev, sizeof(*fsl_ldb), GFP_KERNEL);
    264	if (!fsl_ldb)
    265		return -ENOMEM;
    266
    267	fsl_ldb->dev = &pdev->dev;
    268	fsl_ldb->bridge.funcs = &funcs;
    269	fsl_ldb->bridge.of_node = dev->of_node;
    270
    271	fsl_ldb->clk = devm_clk_get(dev, "ldb");
    272	if (IS_ERR(fsl_ldb->clk))
    273		return PTR_ERR(fsl_ldb->clk);
    274
    275	fsl_ldb->regmap = syscon_node_to_regmap(dev->of_node->parent);
    276	if (IS_ERR(fsl_ldb->regmap))
    277		return PTR_ERR(fsl_ldb->regmap);
    278
    279	/* Locate the panel DT node. */
    280	panel_node = of_graph_get_remote_node(dev->of_node, 1, 0);
    281	if (!panel_node)
    282		return -ENXIO;
    283
    284	panel = of_drm_find_panel(panel_node);
    285	of_node_put(panel_node);
    286	if (IS_ERR(panel))
    287		return PTR_ERR(panel);
    288
    289	fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
    290	if (IS_ERR(fsl_ldb->panel_bridge))
    291		return PTR_ERR(fsl_ldb->panel_bridge);
    292
    293	/* Determine whether this is dual-link configuration */
    294	port1 = of_graph_get_port_by_id(dev->of_node, 1);
    295	port2 = of_graph_get_port_by_id(dev->of_node, 2);
    296	dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
    297	of_node_put(port1);
    298	of_node_put(port2);
    299
    300	if (dual_link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
    301		dev_err(dev, "LVDS channel pixel swap not supported.\n");
    302		return -EINVAL;
    303	}
    304
    305	if (dual_link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
    306		fsl_ldb->lvds_dual_link = true;
    307
    308	platform_set_drvdata(pdev, fsl_ldb);
    309
    310	drm_bridge_add(&fsl_ldb->bridge);
    311
    312	return 0;
    313}
    314
    315static int fsl_ldb_remove(struct platform_device *pdev)
    316{
    317	struct fsl_ldb *fsl_ldb = platform_get_drvdata(pdev);
    318
    319	drm_bridge_remove(&fsl_ldb->bridge);
    320
    321	return 0;
    322}
    323
    324static const struct of_device_id fsl_ldb_match[] = {
    325	{ .compatible = "fsl,imx8mp-ldb", },
    326	{ /* sentinel */ },
    327};
    328MODULE_DEVICE_TABLE(of, fsl_ldb_match);
    329
    330static struct platform_driver fsl_ldb_driver = {
    331	.probe	= fsl_ldb_probe,
    332	.remove	= fsl_ldb_remove,
    333	.driver		= {
    334		.name		= "fsl-ldb",
    335		.of_match_table	= fsl_ldb_match,
    336	},
    337};
    338module_platform_driver(fsl_ldb_driver);
    339
    340MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
    341MODULE_DESCRIPTION("Freescale i.MX8MP LDB");
    342MODULE_LICENSE("GPL");