1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
4  */
5 
6 #define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
7 
8 #include <backlight.h>
9 #include <common.h>
10 #include <dm.h>
11 #include <i2c.h>
12 #include <log.h>
13 #include <linux/delay.h>
14 #include <linux/err.h>
15 
16 #include <asm/io.h>
17 #include <asm/gpio.h>
18 #include <asm/arch/display.h>
19 
20 #define TEGRA_DISPLAY_A_BASE		0x54200000
21 #define TEGRA_DISPLAY_B_BASE		0x54240000
22 
23 #define TEGRA_PWM_BL_MIN_BRIGHTNESS	0x10
24 #define TEGRA_PWM_BL_MAX_BRIGHTNESS	0xFF
25 
26 #define TEGRA_PWM_BL_PERIOD		0xFF
27 #define TEGRA_PWM_BL_CLK_DIV		0x14
28 #define TEGRA_PWM_BL_CLK_SELECT		0x00
29 
30 #define PM_PERIOD_SHIFT                 18
31 #define PM_CLK_DIVIDER_SHIFT		4
32 
33 #define TEGRA_PWM_PM0			0
34 #define TEGRA_PWM_PM1			1
35 
36 struct tegra_pwm_backlight_priv {
37 	struct dc_ctlr *dc;		/* Display controller regmap */
38 
39 	u32 pwm_source;
40 	u32 period;
41 	u32 clk_div;
42 	u32 clk_select;
43 	u32 dft_brightness;
44 };
45 
tegra_pwm_backlight_set_brightness(struct udevice * dev,int percent)46 static int tegra_pwm_backlight_set_brightness(struct udevice *dev, int percent)
47 {
48 	struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev);
49 	struct dc_cmd_reg *cmd = &priv->dc->cmd;
50 	struct dc_com_reg *com = &priv->dc->com;
51 	unsigned int ctrl;
52 	unsigned long out_sel;
53 	unsigned long cmd_state;
54 
55 	if (percent == BACKLIGHT_DEFAULT)
56 		percent = priv->dft_brightness;
57 
58 	if (percent < TEGRA_PWM_BL_MIN_BRIGHTNESS)
59 		percent = TEGRA_PWM_BL_MIN_BRIGHTNESS;
60 
61 	if (percent > TEGRA_PWM_BL_MAX_BRIGHTNESS)
62 		percent = TEGRA_PWM_BL_MAX_BRIGHTNESS;
63 
64 	ctrl = ((priv->period << PM_PERIOD_SHIFT) |
65 		(priv->clk_div << PM_CLK_DIVIDER_SHIFT) |
66 		 priv->clk_select);
67 
68 	/* The new value should be effected immediately */
69 	cmd_state = readl(&cmd->state_access);
70 	writel((cmd_state | (1 << 2)), &cmd->state_access);
71 
72 	switch (priv->pwm_source) {
73 	case TEGRA_PWM_PM0:
74 		/* Select the LM0 on PM0 */
75 		out_sel = readl(&com->pin_output_sel[5]);
76 		out_sel &= ~(7 << 0);
77 		out_sel |= (3 << 0);
78 		writel(out_sel, &com->pin_output_sel[5]);
79 		writel(ctrl, &com->pm0_ctrl);
80 		writel(percent, &com->pm0_duty_cycle);
81 		break;
82 	case TEGRA_PWM_PM1:
83 		/* Select the LM1 on PM1 */
84 		out_sel = readl(&com->pin_output_sel[5]);
85 		out_sel &= ~(7 << 4);
86 		out_sel |= (3 << 4);
87 		writel(out_sel, &com->pin_output_sel[5]);
88 		writel(ctrl, &com->pm1_ctrl);
89 		writel(percent, &com->pm1_duty_cycle);
90 		break;
91 	default:
92 		break;
93 	}
94 
95 	writel(cmd_state, &cmd->state_access);
96 	return 0;
97 }
98 
tegra_pwm_backlight_enable(struct udevice * dev)99 static int tegra_pwm_backlight_enable(struct udevice *dev)
100 {
101 	struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev);
102 
103 	return tegra_pwm_backlight_set_brightness(dev, priv->dft_brightness);
104 }
105 
tegra_pwm_backlight_probe(struct udevice * dev)106 static int tegra_pwm_backlight_probe(struct udevice *dev)
107 {
108 	struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev);
109 
110 	if (dev_read_bool(dev, "nvidia,display-b-base"))
111 		priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_B_BASE;
112 	else
113 		priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_A_BASE;
114 
115 	if (!priv->dc) {
116 		log_err("no display controller address\n");
117 		return -EINVAL;
118 	}
119 
120 	priv->pwm_source =
121 		dev_read_u32_default(dev, "nvidia,pwm-source",
122 				     TEGRA_PWM_PM0);
123 	priv->period =
124 		dev_read_u32_default(dev, "nvidia,period",
125 				     TEGRA_PWM_BL_PERIOD);
126 	priv->clk_div =
127 		dev_read_u32_default(dev, "nvidia,clock-div",
128 				     TEGRA_PWM_BL_CLK_DIV);
129 	priv->clk_select =
130 		dev_read_u32_default(dev, "nvidia,clock-select",
131 				     TEGRA_PWM_BL_CLK_SELECT);
132 	priv->dft_brightness =
133 		dev_read_u32_default(dev, "nvidia,default-brightness",
134 				     TEGRA_PWM_BL_MAX_BRIGHTNESS);
135 
136 	return 0;
137 }
138 
139 static const struct backlight_ops tegra_pwm_backlight_ops = {
140 	.enable = tegra_pwm_backlight_enable,
141 	.set_brightness = tegra_pwm_backlight_set_brightness,
142 };
143 
144 static const struct udevice_id tegra_pwm_backlight_ids[] = {
145 	{ .compatible = "nvidia,tegra-pwm-backlight" },
146 	{ }
147 };
148 
149 U_BOOT_DRIVER(tegra_pwm_backlight) = {
150 	.name		= "tegra_pwm_backlight",
151 	.id		= UCLASS_PANEL_BACKLIGHT,
152 	.of_match	= tegra_pwm_backlight_ids,
153 	.probe		= tegra_pwm_backlight_probe,
154 	.ops		= &tegra_pwm_backlight_ops,
155 	.priv_auto	= sizeof(struct tegra_pwm_backlight_priv),
156 };
157