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