1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
235ac89ddSJacob Chen /*
335ac89ddSJacob Chen  * Copyright 2016 Rockchip Inc.
435ac89ddSJacob Chen  */
535ac89ddSJacob Chen 
635ac89ddSJacob Chen #include <common.h>
735ac89ddSJacob Chen #include <display.h>
835ac89ddSJacob Chen #include <dm.h>
935ac89ddSJacob Chen #include <edid.h>
1035ac89ddSJacob Chen #include <panel.h>
1135ac89ddSJacob Chen #include <regmap.h>
1235ac89ddSJacob Chen #include <syscon.h>
1335ac89ddSJacob Chen #include <asm/gpio.h>
1435ac89ddSJacob Chen #include <asm/io.h>
1535ac89ddSJacob Chen #include <asm/arch/clock.h>
1635ac89ddSJacob Chen #include <asm/arch/lvds_rk3288.h>
1735ac89ddSJacob Chen #include <asm/arch/grf_rk3288.h>
1835ac89ddSJacob Chen #include <dt-bindings/clock/rk3288-cru.h>
1935ac89ddSJacob Chen #include <dt-bindings/video/rk3288.h>
2035ac89ddSJacob Chen 
2135ac89ddSJacob Chen DECLARE_GLOBAL_DATA_PTR;
2235ac89ddSJacob Chen 
2335ac89ddSJacob Chen /**
2435ac89ddSJacob Chen  * struct rk_lvds_priv - private rockchip lvds display driver info
2535ac89ddSJacob Chen  *
2635ac89ddSJacob Chen  * @reg: LVDS register address
2735ac89ddSJacob Chen  * @grf: GRF register
2835ac89ddSJacob Chen  * @panel: Panel device that is used in driver
2935ac89ddSJacob Chen  *
3035ac89ddSJacob Chen  * @output: Output mode, decided single or double channel,
3135ac89ddSJacob Chen  *		LVDS or LVTLL
3235ac89ddSJacob Chen  * @format: Data format that RGB data will packing as
3335ac89ddSJacob Chen  */
3435ac89ddSJacob Chen struct rk_lvds_priv {
3535ac89ddSJacob Chen 	void __iomem *regs;
3635ac89ddSJacob Chen 	struct rk3288_grf *grf;
3735ac89ddSJacob Chen 	struct udevice *panel;
3835ac89ddSJacob Chen 
3935ac89ddSJacob Chen 	int output;
4035ac89ddSJacob Chen 	int format;
4135ac89ddSJacob Chen };
4235ac89ddSJacob Chen 
lvds_writel(struct rk_lvds_priv * lvds,u32 offset,u32 val)4335ac89ddSJacob Chen static inline void lvds_writel(struct rk_lvds_priv *lvds, u32 offset, u32 val)
4435ac89ddSJacob Chen {
4535ac89ddSJacob Chen 	writel(val, lvds->regs + offset);
4635ac89ddSJacob Chen 
4735ac89ddSJacob Chen 	writel(val, lvds->regs + offset + 0x100);
4835ac89ddSJacob Chen }
4935ac89ddSJacob Chen 
rk_lvds_enable(struct udevice * dev,int panel_bpp,const struct display_timing * edid)5035ac89ddSJacob Chen int rk_lvds_enable(struct udevice *dev, int panel_bpp,
5135ac89ddSJacob Chen 		   const struct display_timing *edid)
5235ac89ddSJacob Chen {
5335ac89ddSJacob Chen 	struct rk_lvds_priv *priv = dev_get_priv(dev);
5435ac89ddSJacob Chen 	struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
5535ac89ddSJacob Chen 	int ret = 0;
5635ac89ddSJacob Chen 	unsigned int val = 0;
5735ac89ddSJacob Chen 
5835ac89ddSJacob Chen 	ret = panel_enable_backlight(priv->panel);
5935ac89ddSJacob Chen 	if (ret) {
6035ac89ddSJacob Chen 		debug("%s: backlight error: %d\n", __func__, ret);
6135ac89ddSJacob Chen 		return ret;
6235ac89ddSJacob Chen 	}
6335ac89ddSJacob Chen 
6435ac89ddSJacob Chen 	/* Select the video source */
6535ac89ddSJacob Chen 	if (uc_plat->source_id)
6635ac89ddSJacob Chen 		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
6735ac89ddSJacob Chen 		    (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
6835ac89ddSJacob Chen 	else
6935ac89ddSJacob Chen 		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
7035ac89ddSJacob Chen 	rk_setreg(&priv->grf->soc_con6, val);
7135ac89ddSJacob Chen 
7235ac89ddSJacob Chen 	/* Select data transfer format */
7335ac89ddSJacob Chen 	val = priv->format;
7435ac89ddSJacob Chen 	if (priv->output == LVDS_OUTPUT_DUAL)
7535ac89ddSJacob Chen 		val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
7635ac89ddSJacob Chen 	else if (priv->output == LVDS_OUTPUT_SINGLE)
7735ac89ddSJacob Chen 		val |= LVDS_CH0_EN;
7835ac89ddSJacob Chen 	else if (priv->output == LVDS_OUTPUT_RGB)
7935ac89ddSJacob Chen 		val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
8035ac89ddSJacob Chen 	val |= (0xffff << 16);
8135ac89ddSJacob Chen 	rk_setreg(&priv->grf->soc_con7, val);
8235ac89ddSJacob Chen 
8335ac89ddSJacob Chen 	/* Enable LVDS PHY */
8435ac89ddSJacob Chen 	if (priv->output == LVDS_OUTPUT_RGB) {
8535ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG0,
8635ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_TTL_EN |
8735ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANECK_EN |
8835ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE4_EN |
8935ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE3_EN |
9035ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE2_EN |
9135ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE1_EN |
9235ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE0_EN);
9335ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG2,
9435ac89ddSJacob Chen 			    RK3288_LVDS_PLL_FBDIV_REG2(0x46));
9535ac89ddSJacob Chen 
9635ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG3,
9735ac89ddSJacob Chen 			    RK3288_LVDS_PLL_FBDIV_REG3(0x46));
9835ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG4,
9935ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
10035ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
10135ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
10235ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
10335ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
10435ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
10535ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG5,
10635ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
10735ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
10835ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
10935ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
11035ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
11135ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
11235ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REGD,
11335ac89ddSJacob Chen 			    RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
11435ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG20,
11535ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG20_LSB);
11635ac89ddSJacob Chen 	} else {
11735ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG0,
11835ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LVDS_EN |
11935ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANECK_EN |
12035ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE4_EN |
12135ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE3_EN |
12235ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE2_EN |
12335ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE1_EN |
12435ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG0_LANE0_EN);
12535ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG1,
12635ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG1_LANECK_BIAS |
12735ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG1_LANE4_BIAS |
12835ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG1_LANE3_BIAS |
12935ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG1_LANE2_BIAS |
13035ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG1_LANE1_BIAS |
13135ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG1_LANE0_BIAS);
13235ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG2,
13335ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG2_RESERVE_ON |
13435ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
13535ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
13635ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
13735ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
13835ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
13935ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
14035ac89ddSJacob Chen 			    RK3288_LVDS_PLL_FBDIV_REG2(0x46));
14135ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG3,
14235ac89ddSJacob Chen 			    RK3288_LVDS_PLL_FBDIV_REG3(0x46));
14335ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG4, 0x00);
14435ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG5, 0x00);
14535ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REGD,
14635ac89ddSJacob Chen 			    RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
14735ac89ddSJacob Chen 		lvds_writel(priv, RK3288_LVDS_CH0_REG20,
14835ac89ddSJacob Chen 			    RK3288_LVDS_CH0_REG20_LSB);
14935ac89ddSJacob Chen 	}
15035ac89ddSJacob Chen 
15135ac89ddSJacob Chen 	/* Power on */
15235ac89ddSJacob Chen 	writel(RK3288_LVDS_CFG_REGC_PLL_ENABLE,
15335ac89ddSJacob Chen 	       priv->regs + RK3288_LVDS_CFG_REGC);
15435ac89ddSJacob Chen 
15535ac89ddSJacob Chen 	writel(RK3288_LVDS_CFG_REG21_TX_ENABLE,
15635ac89ddSJacob Chen 	       priv->regs + RK3288_LVDS_CFG_REG21);
15735ac89ddSJacob Chen 
15835ac89ddSJacob Chen 	return 0;
15935ac89ddSJacob Chen }
16035ac89ddSJacob Chen 
rk_lvds_read_timing(struct udevice * dev,struct display_timing * timing)16135ac89ddSJacob Chen int rk_lvds_read_timing(struct udevice *dev, struct display_timing *timing)
16235ac89ddSJacob Chen {
16335ac89ddSJacob Chen 	if (fdtdec_decode_display_timing
164e160f7d4SSimon Glass 	    (gd->fdt_blob, dev_of_offset(dev), 0, timing)) {
16535ac89ddSJacob Chen 		debug("%s: Failed to decode display timing\n", __func__);
16635ac89ddSJacob Chen 		return -EINVAL;
16735ac89ddSJacob Chen 	}
16835ac89ddSJacob Chen 
16935ac89ddSJacob Chen 	return 0;
17035ac89ddSJacob Chen }
17135ac89ddSJacob Chen 
rk_lvds_ofdata_to_platdata(struct udevice * dev)17235ac89ddSJacob Chen static int rk_lvds_ofdata_to_platdata(struct udevice *dev)
17335ac89ddSJacob Chen {
17435ac89ddSJacob Chen 	struct rk_lvds_priv *priv = dev_get_priv(dev);
17535ac89ddSJacob Chen 	const void *blob = gd->fdt_blob;
176e160f7d4SSimon Glass 	int node = dev_of_offset(dev);
17735ac89ddSJacob Chen 	int ret;
178a821c4afSSimon Glass 	priv->regs = (void *)devfdt_get_addr(dev);
17935ac89ddSJacob Chen 	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
18035ac89ddSJacob Chen 
18135ac89ddSJacob Chen 	ret = fdtdec_get_int(blob, node, "rockchip,output", -1);
18235ac89ddSJacob Chen 	if (ret != -1) {
18335ac89ddSJacob Chen 		priv->output = ret;
18435ac89ddSJacob Chen 		debug("LVDS output : %d\n", ret);
18535ac89ddSJacob Chen 	} else {
18635ac89ddSJacob Chen 		/* default set it as output rgb */
18735ac89ddSJacob Chen 		priv->output = LVDS_OUTPUT_RGB;
18835ac89ddSJacob Chen 	}
18935ac89ddSJacob Chen 
19035ac89ddSJacob Chen 	ret = fdtdec_get_int(blob, node, "rockchip,data-mapping", -1);
19135ac89ddSJacob Chen 	if (ret != -1) {
19235ac89ddSJacob Chen 		priv->format = ret;
19335ac89ddSJacob Chen 		debug("LVDS data-mapping : %d\n", ret);
19435ac89ddSJacob Chen 	} else {
19535ac89ddSJacob Chen 		/* default set it as format jeida */
19635ac89ddSJacob Chen 		priv->format = LVDS_FORMAT_JEIDA;
19735ac89ddSJacob Chen 	}
19835ac89ddSJacob Chen 
19935ac89ddSJacob Chen 	ret = fdtdec_get_int(blob, node, "rockchip,data-width", -1);
20035ac89ddSJacob Chen 	if (ret != -1) {
20135ac89ddSJacob Chen 		debug("LVDS data-width : %d\n", ret);
20235ac89ddSJacob Chen 		if (ret == 24) {
20335ac89ddSJacob Chen 			priv->format |= LVDS_24BIT;
20435ac89ddSJacob Chen 		} else if (ret == 18) {
20535ac89ddSJacob Chen 			priv->format |= LVDS_18BIT;
20635ac89ddSJacob Chen 		} else {
20735ac89ddSJacob Chen 			debug("rockchip-lvds unsupport data-width[%d]\n", ret);
20835ac89ddSJacob Chen 			ret = -EINVAL;
20935ac89ddSJacob Chen 			return ret;
21035ac89ddSJacob Chen 		}
21135ac89ddSJacob Chen 	} else {
21235ac89ddSJacob Chen 		priv->format |= LVDS_24BIT;
21335ac89ddSJacob Chen 	}
21435ac89ddSJacob Chen 
21535ac89ddSJacob Chen 	return 0;
21635ac89ddSJacob Chen }
21735ac89ddSJacob Chen 
rk_lvds_probe(struct udevice * dev)21835ac89ddSJacob Chen int rk_lvds_probe(struct udevice *dev)
21935ac89ddSJacob Chen {
22035ac89ddSJacob Chen 	struct rk_lvds_priv *priv = dev_get_priv(dev);
22135ac89ddSJacob Chen 	int ret;
22235ac89ddSJacob Chen 
22335ac89ddSJacob Chen 	ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "rockchip,panel",
22435ac89ddSJacob Chen 					   &priv->panel);
22535ac89ddSJacob Chen 	if (ret) {
22635ac89ddSJacob Chen 		debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
22735ac89ddSJacob Chen 		      dev->name, ret);
22835ac89ddSJacob Chen 		return ret;
22935ac89ddSJacob Chen 	}
23035ac89ddSJacob Chen 
23135ac89ddSJacob Chen 	return 0;
23235ac89ddSJacob Chen }
23335ac89ddSJacob Chen 
23435ac89ddSJacob Chen static const struct dm_display_ops lvds_rockchip_ops = {
23535ac89ddSJacob Chen 	.read_timing = rk_lvds_read_timing,
23635ac89ddSJacob Chen 	.enable = rk_lvds_enable,
23735ac89ddSJacob Chen };
23835ac89ddSJacob Chen 
23935ac89ddSJacob Chen static const struct udevice_id rockchip_lvds_ids[] = {
24035ac89ddSJacob Chen 	{.compatible = "rockchip,rk3288-lvds"},
24135ac89ddSJacob Chen 	{}
24235ac89ddSJacob Chen };
24335ac89ddSJacob Chen 
24435ac89ddSJacob Chen U_BOOT_DRIVER(lvds_rockchip) = {
24535ac89ddSJacob Chen 	.name	= "lvds_rockchip",
24635ac89ddSJacob Chen 	.id	= UCLASS_DISPLAY,
24735ac89ddSJacob Chen 	.of_match = rockchip_lvds_ids,
24835ac89ddSJacob Chen 	.ops	= &lvds_rockchip_ops,
24935ac89ddSJacob Chen 	.ofdata_to_platdata	= rk_lvds_ofdata_to_platdata,
25035ac89ddSJacob Chen 	.probe	= rk_lvds_probe,
25135ac89ddSJacob Chen 	.priv_auto_alloc_size	= sizeof(struct rk_lvds_priv),
25235ac89ddSJacob Chen };
253