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