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