-  Pegatron Chagall, Samsung Galaxy R (GT-I9103) and Captivate Glide
   (SGH-i927) support
This commit is contained in:
Tom Rini 2025-08-04 08:22:04 -06:00
commit 488ae65f39
67 changed files with 4403 additions and 310 deletions

View File

@ -92,6 +92,8 @@ dtb-$(CONFIG_ARCH_TEGRA) += \
tegra20-motorola-olympus.dtb \
tegra20-paz00.dtb \
tegra20-plutux.dtb \
tegra20-samsung-bose.dtb \
tegra20-samsung-n1.dtb \
tegra20-seaboard.dtb \
tegra20-tec.dtb \
tegra20-trimslice.dtb \
@ -117,6 +119,7 @@ dtb-$(CONFIG_ARCH_TEGRA) += \
tegra30-lg-p895.dtb \
tegra30-microsoft-surface-rt.dtb \
tegra30-ouya.dtb \
tegra30-pegatron-chagall.dtb \
tegra30-tec-ng.dtb \
tegra30-wexler-qc750.dtb \
tegra114-asus-tf701t.dtb \

View File

@ -40,6 +40,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000014 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -36,6 +36,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000014 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -46,6 +46,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000014 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -62,6 +62,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000014 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
#include "tegra20-samsung-n1-common.dtsi"
/ {
model = "Samsung Captivate Glide (SGH-i927)";
compatible = "samsung,bose", "nvidia,tegra20";
aliases {
spi0 = &panel_spi;
};
host1x@50000000 {
dc@54200000 {
rgb {
status = "okay";
port {
dpi_output: endpoint {
remote-endpoint = <&panel_input>;
bus-width = <24>;
};
};
};
};
};
pinmux@70000014 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;
state_default: pinmux {
conf-dtf {
nvidia,pins = "dtf", "spdi", "spib", "spih";
nvidia,pull = <TEGRA_PIN_PULL_UP>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
conf-gpv {
nvidia,pins = "gpv";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
conf-kbcd {
nvidia,pins = "kbcd";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
drive-dap {
nvidia,pins = "drive_dap2", "drive_dap3";
nvidia,high-speed-mode = <TEGRA_PIN_ENABLE>;
nvidia,schmitt = <TEGRA_PIN_ENABLE>;
nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <31>;
nvidia,pull-up-strength = <31>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_SLOWEST>;
nvidia,slew-rate-falling = <TEGRA_PIN_SLEW_RATE_SLOWEST>;
};
};
};
panel_spi: spi@7000d800 {
status = "okay";
spi-max-frequency = <1000000>;
panel: panel@2 {
/* 480x800 AMOLED panel */
compatible = "samsung,bose-panel", "samsung,s6e63m0";
reg = <2>;
spi-max-frequency = <1000000>;
spi-cpol;
spi-cpha;
reset-gpios = <&gpio TEGRA_GPIO(C, 1) GPIO_ACTIVE_LOW>;
vdd3-supply = <&vlcd_1v8_reg>;
vci-supply = <&vlcd_3v0_reg>;
panel-width-mm = <52>;
panel-height-mm = <87>;
port {
panel_input: endpoint {
remote-endpoint = <&dpi_output>;
};
};
};
};
sdhci@c8000400 {
broken-cd;
};
gpio-keys {
compatible = "gpio-keys";
switch-hall {
label = "Keyboard Slide";
gpios = <&gpio TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
linux,input-type = <EV_SW>;
linux,code = <SW_KEYPAD_SLIDE>;
};
};
leds {
compatible = "gpio-leds";
led-kbd {
label = "Keyboard backlight";
gpios = <&gpio TEGRA_GPIO(L, 5) GPIO_ACTIVE_HIGH>;
default-state = "off";
};
};
};

View File

@ -0,0 +1,428 @@
// SPDX-License-Identifier: GPL-2.0
#include <dt-bindings/input/input.h>
#include "tegra20.dtsi"
/ {
chosen {
stdout-path = &uartb;
};
aliases {
i2c0 = &pwr_i2c;
mmc0 = &sdmmc4; /* eMMC */
mmc1 = &sdmmc3; /* uSD slot */
rtc0 = &pmic;
rtc1 = "/rtc@7000e000";
usb0 = &micro_usb;
};
memory {
device_type = "memory";
reg = <0x00000000 0x40000000>; /* 1 GB */
};
pinmux@70000014 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;
state_default: pinmux {
ata {
nvidia,pins = "ata", "atc", "atd", "ate",
"gmb", "gmd", "irrx", "irtx",
"spid", "spie";
nvidia,function = "gmi";
};
atb {
nvidia,pins = "atb", "gma", "gme";
nvidia,function = "sdio4";
};
cdev1 {
nvidia,pins = "cdev1";
nvidia,function = "plla_out";
};
cdev2 {
nvidia,pins = "cdev2";
nvidia,function = "pllp_out4";
};
crtp {
nvidia,pins = "crtp";
nvidia,function = "crt";
};
csus {
nvidia,pins = "csus";
nvidia,function = "vi_sensor_clk";
};
dap1 {
nvidia,pins = "dap1";
nvidia,function = "dap1";
};
dap2 {
nvidia,pins = "dap2";
nvidia,function = "dap2";
};
dap3 {
nvidia,pins = "dap3";
nvidia,function = "dap3";
};
dap4 {
nvidia,pins = "dap4";
nvidia,function = "dap4";
};
ddc {
nvidia,pins = "ddc";
nvidia,function = "rsvd4";
};
pta {
nvidia,pins = "pta";
nvidia,function = "i2c2";
};
spif {
nvidia,pins = "spif", "uac";
nvidia,function = "rsvd4";
};
dta {
nvidia,pins = "dta", "dtb", "dtc", "dtd", "dte";
nvidia,function = "vi";
};
dtf {
nvidia,pins = "dtf";
nvidia,function = "i2c3";
};
gmc {
nvidia,pins = "gmc";
nvidia,function = "uartd";
};
gpu {
nvidia,pins = "gpu", "uaa", "uab";
nvidia,function = "uarta";
};
gpu7 {
nvidia,pins = "gpu7";
nvidia,function = "rtck";
};
gpv {
nvidia,pins = "gpv", "slxa", "slxk";
nvidia,function = "pcie";
};
hdint {
nvidia,pins = "hdint", "spdi", "spdo";
nvidia,function = "rsvd2";
};
i2cp {
nvidia,pins = "i2cp";
nvidia,function = "i2cp";
};
kbca {
nvidia,pins = "kbca", "kbcb", "kbcc", "kbcd",
"kbce", "kbcf";
nvidia,function = "kbc";
};
lcsn {
nvidia,pins = "lcsn", "lsck", "lsda", "lsdi";
nvidia,function = "spi3";
};
ld0 {
nvidia,pins = "ld0", "ld1", "ld2", "ld3", "ld4",
"ld5", "ld6", "ld7", "ld8", "ld9",
"ld10", "ld11", "ld12", "ld13", "ld14",
"ld15", "ld16", "ld17", "ldc", "ldi",
"lhp0", "lhp1", "lhp2", "lhs", "lm0",
"lm1", "lpp", "lpw0", "lpw1", "lpw2",
"lsc0", "lsc1", "lspi", "lvp0", "lvp1",
"lvs";
nvidia,function = "displaya";
};
owc {
nvidia,pins = "owc";
nvidia,function = "owr";
};
pmc {
nvidia,pins = "pmc";
nvidia,function = "pwr_on";
};
rm {
nvidia,pins = "rm";
nvidia,function = "i2c1";
};
sdb {
nvidia,pins = "sdb", "sdc", "sdd";
nvidia,function = "sdio3";
};
sdio1 {
nvidia,pins = "sdio1";
nvidia,function = "sdio1";
};
slxc {
nvidia,pins = "slxc", "slxd";
nvidia,function = "spi4";
};
spig {
nvidia,pins = "spig", "spih";
nvidia,function = "spi2_alt";
};
uad {
nvidia,pins = "uad";
nvidia,function = "irda";
};
uca {
nvidia,pins = "uca", "ucb";
nvidia,function = "uartc";
};
uda {
nvidia,pins = "uda";
nvidia,function = "spi1";
};
spia {
nvidia,pins = "spia", "spib", "spic";
nvidia,function = "spi2";
};
conf-cdev1 {
nvidia,pins = "cdev1", "cdev2", "dap1", "dap2",
"dap3", "dap4", "ddc", "dte", "gma",
"gmc", "gmd", "gme", "gpu7", "hdint",
"i2cp", "lcsn", "lhs", "lm0", "lm1",
"lpw1", "lsc0", "lsck", "lsda", "lsdi",
"lspi", "lvs", "pmc", "pta", "rm",
"sdb", "sdio1", "uac", "uda", "ck32",
"ddrc", "pmca", "pmcb", "pmcc", "pmcd",
"xm2c", "xm2d";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
conf-crtp {
nvidia,pins = "crtp", "lvp0";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_ENABLE>;
};
conf-csus {
nvidia,pins = "csus", "spid";
nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
nvidia,tristate = <TEGRA_PIN_ENABLE>;
};
conf-ata {
nvidia,pins = "ata", "atb", "atc", "ate",
"gmb", "gpu", "irrx", "irtx",
"kbca", "kbcc", "kbce", "kbcf",
"ldc", "lpw0", "lpw2", "lsc1", "sdc",
"sdd", "spig", "uaa", "uab",
"uad", "uca", "ucb", "pmce";
nvidia,pull = <TEGRA_PIN_PULL_UP>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
conf-owc {
nvidia,pins = "owc";
nvidia,pull = <TEGRA_PIN_PULL_UP>;
nvidia,tristate = <TEGRA_PIN_ENABLE>;
};
conf-atd {
nvidia,pins = "atd", "dta", "dtb", "dtc", "dtd",
"kbcb", "ld0", "ld1", "ld10", "ld11",
"ld12", "ld13", "ld14", "ld15", "ld16",
"ld17", "ld2", "ld3", "ld4", "ld5",
"ld6", "ld7", "ld8", "ld9", "ldi",
"lhp0", "lhp1", "lhp2", "lpp", "lvp1",
"slxa", "slxc", "slxd", "slxk", "spdo",
"spia", "spic", "spie", "spif";
nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
drive-ao1 {
nvidia,pins = "drive_ao1", "drive_at1", "drive_dbg",
"drive_vi1", "drive_vi2";
nvidia,high-speed-mode = <TEGRA_PIN_DISABLE>;
nvidia,schmitt = <TEGRA_PIN_ENABLE>;
nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <31>;
nvidia,pull-up-strength = <31>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_SLOWEST>;
nvidia,slew-rate-falling = <TEGRA_PIN_SLEW_RATE_SLOWEST>;
};
drive-sdio1 {
nvidia,pins = "drive_sdio1";
nvidia,high-speed-mode = <TEGRA_PIN_DISABLE>;
nvidia,schmitt = <TEGRA_PIN_ENABLE>;
nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <31>;
nvidia,pull-up-strength = <31>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_SLOWEST>;
nvidia,slew-rate-falling = <TEGRA_PIN_SLEW_RATE_SLOWEST>;
};
drive-ddc {
nvidia,pins = "drive_ddc";
nvidia,high-speed-mode = <TEGRA_PIN_DISABLE>;
nvidia,schmitt = <TEGRA_PIN_ENABLE>;
nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <31>;
nvidia,pull-up-strength = <31>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_FASTEST>;
nvidia,slew-rate-falling = <TEGRA_PIN_SLEW_RATE_FASTEST>;
};
};
};
uartb: serial@70006040 {
clocks = <&tegra_car 7>;
status = "okay";
};
pwr_i2c: i2c@7000d000 {
status = "okay";
clock-frequency = <400000>;
pmic: max8907@3c {
compatible = "maxim,max8907";
reg = <0x3c>;
interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
#interrupt-cells = <2>;
interrupt-controller;
#gpio-cells = <2>;
gpio-controller;
maxim,system-power-controller;
regulators {
vlcd_1v8_reg: ldo3 {
regulator-name = "vlcd_1v8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
regulator-boot-on;
};
usb_phy_reg: ldo4 {
regulator-name = "vap_usb_3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-boot-on;
};
vlcd_3v0_reg: ldo12 {
regulator-name = "vlcd_3v0";
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
};
vmmc_usd_reg: ldo16 {
regulator-name = "vmmc_usd";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
};
};
};
micro_usb: usb@c5000000 {
status = "okay";
dr_mode = "otg";
};
usb-phy@c5000000 {
status = "okay";
vbus-supply = <&usb_phy_reg>;
};
sdmmc3: sdhci@c8000400 {
status = "okay";
bus-width = <4>;
vmmc-supply = <&vmmc_usd_reg>;
vqmmc-supply = <&vdd_3v3_sys>;
};
sdmmc4: sdhci@c8000600 {
status = "okay";
bus-width = <8>;
non-removable;
vmmc-supply = <&vdd_3v3_sys>;
vqmmc-supply = <&vdd_3v3_sys>;
};
/* 32KHz oscillator which is used by PMC */
clk32k_in: clock-32k-in {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <32768>;
clock-output-names = "ref-oscillator";
};
gpio-keys {
compatible = "gpio-keys";
key-power {
label = "Power";
gpios = <&gpio TEGRA_GPIO(U, 5) GPIO_ACTIVE_HIGH>;
linux,code = <KEY_ENTER>;
};
key-volume-up {
label = "Volume Up";
gpios = <&gpio TEGRA_GPIO(Q, 1) GPIO_ACTIVE_LOW>;
linux,code = <KEY_UP>;
};
key-volume-down {
label = "Volume Down";
gpios = <&gpio TEGRA_GPIO(Q, 2) GPIO_ACTIVE_LOW>;
linux,code = <KEY_DOWN>;
};
};
vdd_3v3_sys: regulator-3v3 {
compatible = "regulator-fixed";
regulator-name = "vdd_3v3_vs";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
};

View File

@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
#include "tegra20-samsung-n1-common.dtsi"
/ {
model = "Samsung Galaxy R (GT-I9103)";
compatible = "samsung,n1", "nvidia,tegra20";
aliases {
i2c10 = &cmc_i2c;
spi0 = &panel_spi;
};
host1x@50000000 {
dc@54200000 {
rgb {
status = "okay";
port {
dpi_output: endpoint {
remote-endpoint = <&bridge_input>;
bus-width = <24>;
};
};
};
};
};
pinmux@70000014 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;
state_default: pinmux {
conf-dtf {
nvidia,pins = "dtf", "spdi", "spih";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
conf-gpv {
nvidia,pins = "gpv", "spib";
nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
conf-kbcd {
nvidia,pins = "kbcd";
nvidia,pull = <TEGRA_PIN_PULL_UP>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
};
};
i2c@7000d000 {
max8907@3c {
regulators {
vcmc623_io_1v8: ldo15 {
regulator-name = "vcmc623_io_1v8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
};
};
};
};
cmc_i2c: i2c-10 {
compatible = "i2c-gpio";
sda-gpios = <&gpio TEGRA_GPIO(Z, 3) GPIO_ACTIVE_HIGH>;
scl-gpios = <&gpio TEGRA_GPIO(C, 6) GPIO_ACTIVE_HIGH>;
i2c-gpio,scl-output-only;
i2c-gpio,delay-us = <1>;
#address-cells = <1>;
#size-cells = <0>;
cmc623: bridge@38 {
compatible = "samsung,cmc623";
reg = <0x38>;
enable-gpios = <&gpio TEGRA_GPIO(Q, 4) GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio TEGRA_GPIO(J, 6) GPIO_ACTIVE_HIGH>;
bypass-gpios = <&gpio TEGRA_GPIO(BB, 5) GPIO_ACTIVE_LOW>;
sleep-gpios = <&gpio TEGRA_GPIO(W, 0) GPIO_ACTIVE_HIGH>;
vdd3v0-supply = <&vcmc623_3v0>;
vdd1v2-supply = <&vcmc623_1v2>;
vddio1v8-supply = <&vcmc623_io_1v8>;
cmc623_backlight: backlight {
compatible = "samsung,cmc623-backlight";
enable-gpios = <&gpio TEGRA_GPIO(R, 3) GPIO_ACTIVE_HIGH>;
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
bridge_input: endpoint {
remote-endpoint = <&dpi_output>;
};
};
port@1 {
reg = <1>;
bridge_output: endpoint {
remote-endpoint = <&panel_input>;
};
};
};
};
};
panel_spi: spi@7000d800 {
status = "okay";
spi-max-frequency = <1000000>;
panel: panel@2 {
/* 480x800 TFT LCD panel */
compatible = "sony,l4f00430t01";
reg = <2>;
spi-max-frequency = <1000000>;
spi-cpol;
spi-cpha;
reset-gpios = <&gpio TEGRA_GPIO(C, 1) GPIO_ACTIVE_LOW>;
vdd1v8-supply = <&vlcd_1v8_reg>;
vdd3v0-supply = <&vlcd_3v0_reg>;
panel-width-mm = <55>;
panel-height-mm = <91>;
backlight = <&cmc623_backlight>;
port {
panel_input: endpoint {
remote-endpoint = <&bridge_output>;
};
};
};
};
sdhci@c8000400 {
/* battery blocks the sdcard slot and the device lacks CD pin */
non-removable;
};
gpio-keys {
compatible = "gpio-keys";
key-home {
label = "Home";
gpios = <&gpio TEGRA_GPIO(O, 5) GPIO_ACTIVE_LOW>;
linux,code = <KEY_ENTER>;
};
};
vcmc623_3v0: regulator-cmc623-3v0 {
compatible = "regulator-fixed";
regulator-name = "vcmc623_3v0";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
vcmc623_1v2: regulator-cmc623-1v2 {
compatible = "regulator-fixed";
regulator-name = "vcmc623_1v2";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
gpio = <&gpio TEGRA_GPIO(L, 5) GPIO_ACTIVE_HIGH>;
enable-active-high;
};
};

View File

@ -249,6 +249,35 @@
*/
};
/* Audio Bitstream Engine */
bsea@60011000 {
compatible = "nvidia,tegra20-bsea";
reg = <0x60011000 0x1000>, <0x4000c000 0x4000>;
reg-names = "bsea", "iram-buffer";
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "bsea";
clocks = <&tegra_car TEGRA20_CLK_BSEA>;
resets = <&tegra_car 62>;
reset-names = "bsea";
status = "disabled";
};
/* Video Bitstream Engine */
bsev@6001b000 {
compatible = "nvidia,tegra20-bsev";
reg = <0x6001b000 0x1000>, <0x40008000 0x4000>;
reg-names = "bsev", "iram-buffer";
interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "bsev";
clocks = <&tegra_car TEGRA20_CLK_BSEV>,
<&tegra_car TEGRA20_CLK_VDE>;
clock-names = "bsev", "vde";
resets = <&tegra_car 63>,
<&tegra_car 61>;
reset-names = "bsev", "vde";
status = "disabled";
};
apbmisc@70000800 {
compatible = "nvidia,tegra20-apbmisc";
reg = <0x70000800 0x64 /* Chip revision */

View File

@ -44,6 +44,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -50,6 +50,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -53,6 +53,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -37,6 +37,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -60,6 +60,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -43,6 +43,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -42,6 +42,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -56,6 +56,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

File diff suppressed because it is too large Load Diff

View File

@ -44,6 +44,10 @@
};
};
bsev@6001b000 {
status = "okay";
};
pinmux@70000868 {
pinctrl-names = "default";
pinctrl-0 = <&state_default>;

View File

@ -373,6 +373,35 @@
*/
};
/* Audio Bitstream Engine */
bsea@60011000 {
compatible = "nvidia,tegra30-bsea";
reg = <0x60011000 0x1000>, <0x4000c000 0x4000>;
reg-names = "bsea", "iram-buffer";
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "bsea";
clocks = <&tegra_car TEGRA30_CLK_BSEA>;
resets = <&tegra_car 62>;
reset-names = "bsea";
status = "disabled";
};
/* Video Bitstream Engine */
bsev@6001b000 {
compatible = "nvidia,tegra30-bsev";
reg = <0x6001b000 0x1000>, <0x40008000 0x4000>;
reg-names = "bsev", "iram-buffer";
interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "bsev";
clocks = <&tegra_car TEGRA30_CLK_BSEV>,
<&tegra_car TEGRA30_CLK_VDE>;
clock-names = "bsev", "vde";
resets = <&tegra_car 63>,
<&tegra_car 61>;
reset-names = "bsev", "vde";
status = "disabled";
};
apbmisc@70000800 {
compatible = "nvidia,tegra30-apbmisc", "nvidia,tegra20-apbmisc";
reg = <0x70000800 0x64 /* Chip revision */

View File

@ -54,6 +54,13 @@ int tegra_get_chip_sku(void);
*/
int tegra_get_chip(void);
/**
* Returns the pure SOC major version from the HIDREV register
*
* Return: SOC major version
*/
u32 tegra_get_major_version(void);
/**
* Returns the SKU ID from the sku_info register
*

View File

@ -7,41 +7,36 @@
#ifndef _CRYPTO_H_
#define _CRYPTO_H_
#define TEGRA_AES_SLOT_SBK 0
/**
* Sign a block of data
* sign_data_block - Sign a block of data
*
* \param source Source data
* \param length Size of source data
* \param signature Destination address for signature, AES_KEY_LENGTH bytes
* @source Source data
* @length Size of source data in bytes
* @signature Destination address for signature, AES_KEY_LENGTH bytes
* Return: 0 on success, negative value on failure
*/
int sign_data_block(u8 *source, unsigned int length, u8 *signature);
/**
* Sign an encrypted block of data
* encrypt_data_block - Encrypt a block of data
*
* \param source Source data
* \param length Size of source data
* \param signature Destination address for signature, AES_KEY_LENGTH bytes
* \param key AES128 encryption key
* @source Source data
* @dest Destination data
* @length Size of source data in bytes
* Return: 0 on success, negative value on failure
*/
int sign_enc_data_block(u8 *source, unsigned int length, u8 *signature, u8 *key);
int encrypt_data_block(u8 *source, u8 *dest, unsigned int length);
/**
* Encrypt a block of data
* decrypt_data_block - Decrypt a block of data
*
* \param source Source data
* \param length Size of source data
* \param key AES128 encryption key
* @source Source data
* @dest Destination data
* @length Size of source data in bytes
* Return: 0 on success, negative value on failure
*/
int encrypt_data_block(u8 *source, unsigned int length, u8 *key);
/**
* Decrypt a block of data
*
* \param source Source data
* \param length Size of source data
* \param key AES128 encryption key
*/
int decrypt_data_block(u8 *source, unsigned int length, u8 *key);
int decrypt_data_block(u8 *source, u8 *dest, unsigned int length);
#endif /* #ifndef _CRYPTO_H_ */

View File

@ -448,6 +448,9 @@ enum win_color_depth_id {
#define LVS_OUTPUT_POLARITY_LOW BIT(28)
#define LSC0_OUTPUT_POLARITY_LOW BIT(24)
/* DC_COM_PIN_OUTPUT_POLARITY3 0x309 */
#define LSPI_OUTPUT_POLARITY_LOW BIT(8)
/* DC_COM_PIN_OUTPUT_SELECT6 0x31a */
#define LDC_OUTPUT_SELECT_V_PULSE1 BIT(14) /* 100b */

View File

@ -17,8 +17,22 @@ struct fuse_regs {
u32 fa; /* 0x148: FUSE_FA */
u32 reserved3[21]; /* 0x14C - 0x19C: */
u32 security_mode; /* 0x1A0: FUSE_SECURITY_MODE */
u32 sbk[4]; /* 0x1A4 - 0x1B4 */
};
/* Defines the supported operating modes */
enum fuse_operating_mode {
MODE_UNDEFINED = 0,
MODE_PRODUCTION = 3,
MODE_ODM_PRODUCTION_SECURE = 4,
MODE_ODM_PRODUCTION_OPEN = 5,
};
/**
* Initializes fuse hardware
*/
void tegra_fuse_init(void);
/**
* Calculate SoC UID
*
@ -26,4 +40,11 @@ struct fuse_regs {
*/
unsigned long long tegra_chip_uid(void);
/**
* Gives the current operating mode from fuses
*
* @return current operating mode
*/
enum fuse_operating_mode tegra_fuse_get_operation_mode(void);
#endif /* ifndef _FUSE_H_ */

View File

@ -10,12 +10,6 @@
#define STRAP_OPT_A_RAM_CODE_SHIFT 4
#define STRAP_OPT_A_RAM_CODE_MASK (0xf << STRAP_OPT_A_RAM_CODE_SHIFT)
/* Defines the supported operating modes */
enum fuse_operating_mode {
MODE_PRODUCTION = 3,
MODE_UNDEFINED,
};
/* Defines the CMAC-AES-128 hash length in 32 bit words. (128 bits = 4 words) */
enum {
HASH_LENGTH = 4
@ -125,7 +119,6 @@ union scratch3_reg {
int warmboot_save_sdram_params(void);
int warmboot_prepare_code(u32 seg_address, u32 seg_length);
int sign_data_block(u8 *source, u32 length, u8 *signature);
void wb_start(void); /* Start of WB assembly code */
void wb_end(void); /* End of WB assembly code */

View File

@ -17,7 +17,9 @@ config TEGRA_CLKRST
config TEGRA_CRYPTO
bool "Tegra AES128 crypto module"
select DM_AES
select AES
select TEGRA_AES
config TEGRA_GP_PADCTRL
bool

View File

@ -37,6 +37,14 @@ int tegra_get_chip(void)
return rev;
}
u32 tegra_get_major_version(void)
{
struct apb_misc_gp_ctlr *gp =
(struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE;
return (readl(&gp->hidrev) & HIDREV_MAJORPREV_MASK) >> HIDREV_MAJORPREV_SHIFT;
}
int tegra_get_sku_info(void)
{
int sku_id;

View File

@ -71,6 +71,7 @@ void powerup_cpu(void);
void reset_A9_cpu(int reset);
void start_cpu(u32 reset_vector);
int tegra_get_chip(void);
u32 tegra_get_major_version(void);
int tegra_get_sku_info(void);
int tegra_get_chip_sku(void);
void adjust_pllp_out_freqs(void);

View File

@ -4,164 +4,68 @@
* (C) Copyright 2010 - 2011 NVIDIA Corporation <www.nvidia.com>
*/
#include <dm.h>
#include <log.h>
#include <linux/errno.h>
#include <asm/arch-tegra/crypto.h>
#include "uboot_aes.h"
static u8 zero_key[16];
#define AES_CMAC_CONST_RB 0x87 /* from RFC 4493, Figure 2.2 */
enum security_op {
SECURITY_SIGN = 1 << 0, /* Sign the data */
SECURITY_ENCRYPT = 1 << 1, /* Encrypt the data */
SECURITY_DECRYPT = 1 << 2, /* Dectypt the data */
};
/**
* Shift a vector left by one bit
*
* \param in Input vector
* \param out Output vector
* \param size Length of vector in bytes
*/
static void left_shift_vector(u8 *in, u8 *out, int size)
{
int carry = 0;
int i;
for (i = size - 1; i >= 0; i--) {
out[i] = (in[i] << 1) | carry;
carry = in[i] >> 7; /* get most significant bit */
}
}
/**
* Sign a block of data, putting the result into dst.
*
* \param key Input AES key, length AES128_KEY_LENGTH
* \param key_schedule Expanded key to use
* \param src Source data of length 'num_aes_blocks' blocks
* \param dst Destination buffer, length AES128_KEY_LENGTH
* \param num_aes_blocks Number of AES blocks to encrypt
*/
static void sign_object(u8 *key, u8 *key_schedule, u8 *src, u8 *dst,
u32 num_aes_blocks)
{
u8 tmp_data[AES128_KEY_LENGTH];
u8 iv[AES128_KEY_LENGTH] = {0};
u8 left[AES128_KEY_LENGTH];
u8 k1[AES128_KEY_LENGTH];
u8 *cbc_chain_data;
unsigned int i;
cbc_chain_data = zero_key; /* Convenient array of 0's for IV */
/* compute K1 constant needed by AES-CMAC calculation */
for (i = 0; i < AES128_KEY_LENGTH; i++)
tmp_data[i] = 0;
aes_cbc_encrypt_blocks(AES128_KEY_LENGTH, key_schedule, iv,
tmp_data, left, 1);
left_shift_vector(left, k1, sizeof(left));
if ((left[0] >> 7) != 0) /* get MSB of L */
k1[AES128_KEY_LENGTH - 1] ^= AES_CMAC_CONST_RB;
/* compute the AES-CMAC value */
for (i = 0; i < num_aes_blocks; i++) {
/* Apply the chain data */
aes_apply_cbc_chain_data(cbc_chain_data, src, tmp_data);
/* for the final block, XOR K1 into the IV */
if (i == num_aes_blocks - 1)
aes_apply_cbc_chain_data(tmp_data, k1, tmp_data);
/* encrypt the AES block */
aes_encrypt(AES128_KEY_LENGTH, tmp_data,
key_schedule, dst);
debug("sign_obj: block %d of %d\n", i, num_aes_blocks);
/* Update pointers for next loop. */
cbc_chain_data = dst;
src += AES128_KEY_LENGTH;
}
}
/**
* Decrypt, encrypt or sign a block of data (depending on security mode).
*
* \param key Input AES key, length AES128_KEY_LENGTH
* \param oper Security operations mask to perform (enum security_op)
* \param src Source data
* \param length Size of source data
* \param sig_dst Destination address for signature, AES128_KEY_LENGTH bytes
*/
static int tegra_crypto_core(u8 *key, enum security_op oper, u8 *src,
u32 length, u8 *sig_dst)
{
u32 num_aes_blocks;
u8 key_schedule[AES128_EXPAND_KEY_LENGTH];
u8 iv[AES128_KEY_LENGTH] = {0};
debug("%s: length = %d\n", __func__, length);
aes_expand_key(key, AES128_KEY_LENGTH, key_schedule);
num_aes_blocks = (length + AES128_KEY_LENGTH - 1) / AES128_KEY_LENGTH;
if (oper & SECURITY_DECRYPT) {
/* Perform this in place, resulting in src being decrypted. */
debug("%s: begin decryption\n", __func__);
aes_cbc_decrypt_blocks(AES128_KEY_LENGTH, key_schedule, iv, src,
src, num_aes_blocks);
debug("%s: end decryption\n", __func__);
}
if (oper & SECURITY_ENCRYPT) {
/* Perform this in place, resulting in src being encrypted. */
debug("%s: begin encryption\n", __func__);
aes_cbc_encrypt_blocks(AES128_KEY_LENGTH, key_schedule, iv, src,
src, num_aes_blocks);
debug("%s: end encryption\n", __func__);
}
if (oper & SECURITY_SIGN) {
/* encrypt the data, overwriting the result in signature. */
debug("%s: begin signing\n", __func__);
sign_object(key, key_schedule, src, sig_dst, num_aes_blocks);
debug("%s: end signing\n", __func__);
}
return 0;
}
/**
* Tegra crypto group
*/
int sign_data_block(u8 *source, unsigned int length, u8 *signature)
{
return tegra_crypto_core(zero_key, SECURITY_SIGN, source,
length, signature);
struct udevice *dev;
int ret;
/* Only one AES engine should be present */
ret = uclass_get_device(UCLASS_AES, 0, &dev);
if (ret) {
log_err("%s: failed to get tegra_aes: %d\n", __func__, ret);
return ret;
}
ret = dm_aes_select_key_slot(dev, 128, TEGRA_AES_SLOT_SBK);
if (ret)
return ret;
return dm_aes_cmac(dev, source, signature,
DIV_ROUND_UP(length, AES_BLOCK_LENGTH));
}
int sign_enc_data_block(u8 *source, unsigned int length, u8 *signature, u8 *key)
int encrypt_data_block(u8 *source, u8 *dest, unsigned int length)
{
return tegra_crypto_core(key, SECURITY_SIGN, source,
length, signature);
struct udevice *dev;
int ret;
/* Only one AES engine should be present */
ret = uclass_get_device(UCLASS_AES, 0, &dev);
if (ret) {
log_err("%s: failed to get tegra_aes: %d\n", __func__, ret);
return ret;
}
ret = dm_aes_select_key_slot(dev, 128, TEGRA_AES_SLOT_SBK);
if (ret)
return ret;
return dm_aes_cbc_encrypt(dev, (u8 *)AES_ZERO_BLOCK, source, dest,
DIV_ROUND_UP(length, AES_BLOCK_LENGTH));
}
int encrypt_data_block(u8 *source, unsigned int length, u8 *key)
int decrypt_data_block(u8 *source, u8 *dest, unsigned int length)
{
return tegra_crypto_core(key, SECURITY_ENCRYPT, source,
length, NULL);
}
struct udevice *dev;
int ret;
int decrypt_data_block(u8 *source, unsigned int length, u8 *key)
{
return tegra_crypto_core(key, SECURITY_DECRYPT, source,
length, NULL);
/* Only one AES engine should be present */
ret = uclass_get_device(UCLASS_AES, 0, &dev);
if (ret) {
log_err("%s: failed to get tegra_aes: %d\n", __func__, ret);
return ret;
}
ret = dm_aes_select_key_slot(dev, 128, TEGRA_AES_SLOT_SBK);
if (ret)
return ret;
return dm_aes_cbc_decrypt(dev, (u8 *)AES_ZERO_BLOCK, source, dest,
DIV_ROUND_UP(length, AES_BLOCK_LENGTH));
}

View File

@ -39,7 +39,7 @@ static u32 tegra_fuse_readl(unsigned long offset)
return readl(NV_PA_FUSE_BASE + offset);
}
static void tegra_fuse_init(void)
void tegra_fuse_init(void)
{
u32 reg;
@ -49,8 +49,11 @@ static void tegra_fuse_init(void)
* this bit fuse region will not work.
*/
reg = readl_relaxed(NV_PA_CLK_RST_BASE + 0x48);
reg |= BIT(28);
writel(reg, NV_PA_CLK_RST_BASE + 0x48);
if (reg & BIT(28))
return;
writel(reg | BIT(28), NV_PA_CLK_RST_BASE + 0x48);
clock_enable(PERIPH_ID_FUSE);
udelay(2);
@ -148,3 +151,57 @@ unsigned long long tegra_chip_uid(void)
return uid;
}
static int tegra_is_production_mode_fuse_set(struct fuse_regs *fuse)
{
return readl(&fuse->production_mode);
}
static int tegra_is_odm_production_mode_fuse_set(struct fuse_regs *fuse)
{
return readl(&fuse->security_mode);
}
static int tegra_is_failure_analysis_mode(struct fuse_regs *fuse)
{
return readl(&fuse->fa);
}
static int tegra_is_sbk_zeroes(struct fuse_regs *fuse)
{
int i;
for (i = 0; i < 4; i++)
if (readl(&fuse->sbk[i]))
return 0;
return 1;
}
static int tegra_is_production_mode(struct fuse_regs *fuse)
{
if (!tegra_get_major_version())
return 1;
return !tegra_is_failure_analysis_mode(fuse) &&
tegra_is_production_mode_fuse_set(fuse);
}
enum fuse_operating_mode tegra_fuse_get_operation_mode(void)
{
struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE;
tegra_fuse_init();
if (tegra_is_production_mode(fuse)) {
if (!tegra_is_odm_production_mode_fuse_set(fuse))
return MODE_PRODUCTION;
else
if (tegra_is_sbk_zeroes(fuse))
return MODE_ODM_PRODUCTION_OPEN;
else
return MODE_ODM_PRODUCTION_SECURE;
}
return MODE_UNDEFINED;
}

View File

@ -9,12 +9,10 @@
#include <vsprintf.h>
#include <linux/string.h>
#include <asm/arch-tegra/crypto.h>
#include <asm/arch-tegra/fuse.h>
#include "bct.h"
#include "uboot_aes.h"
/* Device with "sbk burned: false" will expose zero key */
const u8 nosbk[AES128_KEY_LENGTH] = { 0 };
/*
* @param bct boot config table start in RAM
* @param ect bootloader start in RAM
@ -26,29 +24,25 @@ static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size)
struct nvboot_config_table *bct_tbl = NULL;
u8 ebt_hash[AES128_KEY_LENGTH] = { 0 };
u8 bct_hash[AES128_KEY_LENGTH] = { 0 };
u8 sbk[AES128_KEY_LENGTH] = { 0 };
u8 *sbct = bct + UBCT_LENGTH;
bool encrypted;
int ret;
ebt_size = roundup(ebt_size, EBT_ALIGNMENT);
memcpy(sbk, (u8 *)(bct + UBCT_LENGTH + SBCT_LENGTH),
NVBOOT_CMAC_AES_HASH_LENGTH * 4);
encrypted = memcmp(&sbk, &nosbk, AES128_KEY_LENGTH);
encrypted = tegra_fuse_get_operation_mode() == MODE_ODM_PRODUCTION_SECURE;
if (encrypted) {
ret = decrypt_data_block(sbct, SBCT_LENGTH, sbk);
ret = decrypt_data_block(sbct, sbct, SBCT_LENGTH);
if (ret)
return 1;
ret = encrypt_data_block(ebt, ebt_size, sbk);
ret = encrypt_data_block(ebt, ebt, ebt_size);
if (ret)
return 1;
}
ret = sign_enc_data_block(ebt, ebt_size, ebt_hash, sbk);
ret = sign_data_block(ebt, ebt_size, ebt_hash);
if (ret)
return 1;
@ -61,12 +55,12 @@ static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size)
bct_tbl->bootloader[0].length = ebt_size;
if (encrypted) {
ret = encrypt_data_block(sbct, SBCT_LENGTH, sbk);
ret = encrypt_data_block(sbct, sbct, SBCT_LENGTH);
if (ret)
return 1;
}
ret = sign_enc_data_block(sbct, SBCT_LENGTH, bct_hash, sbk);
ret = sign_data_block(sbct, SBCT_LENGTH, bct_hash);
if (ret)
return 1;

View File

@ -54,6 +54,10 @@ config TARGET_SEABOARD
select TEGRA_LP0
select TEGRA_PMU
config TARGET_SAMSUNG_N1
bool "Samsung Tegra20 N1 board"
select BOARD_LATE_INIT
config TARGET_STAR
bool "LG Tegra20 Star board"
select BOARD_LATE_INIT
@ -92,6 +96,7 @@ source "board/compal/paz00/Kconfig"
source "board/acer/picasso/Kconfig"
source "board/avionic-design/plutux/Kconfig"
source "board/nvidia/seaboard/Kconfig"
source "board/samsung/n1/Kconfig"
source "board/lg/star/Kconfig"
source "board/avionic-design/tec/Kconfig"
source "board/asus/transformer-t20/Kconfig"

View File

@ -9,12 +9,10 @@
#include <vsprintf.h>
#include <linux/string.h>
#include <asm/arch-tegra/crypto.h>
#include <asm/arch-tegra/fuse.h>
#include "bct.h"
#include "uboot_aes.h"
/* Device with "sbk burned: false" will expose zero key */
const u8 nosbk[AES128_KEY_LENGTH] = { 0 };
/*
* @param bct boot config table start in RAM
* @param ect bootloader start in RAM
@ -25,7 +23,6 @@ static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size)
{
struct nvboot_config_table *bct_tbl = NULL;
u8 ebt_hash[AES128_KEY_LENGTH] = { 0 };
u8 sbk[AES128_KEY_LENGTH] = { 0 };
u8 *bct_hash = bct;
bool encrypted;
int ret;
@ -34,22 +31,19 @@ static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size)
ebt_size = roundup(ebt_size, EBT_ALIGNMENT);
memcpy(sbk, (u8 *)(bct + BCT_LENGTH),
NVBOOT_CMAC_AES_HASH_LENGTH * 4);
encrypted = memcmp(&sbk, &nosbk, AES128_KEY_LENGTH);
encrypted = tegra_fuse_get_operation_mode() == MODE_ODM_PRODUCTION_SECURE;
if (encrypted) {
ret = decrypt_data_block(bct, BCT_LENGTH, sbk);
ret = decrypt_data_block(bct, bct, BCT_LENGTH);
if (ret)
return 1;
ret = encrypt_data_block(ebt, ebt_size, sbk);
ret = encrypt_data_block(ebt, ebt, ebt_size);
if (ret)
return 1;
}
ret = sign_enc_data_block(ebt, ebt_size, ebt_hash, sbk);
ret = sign_data_block(ebt, ebt_size, ebt_hash);
if (ret)
return 1;
@ -62,12 +56,12 @@ static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size)
bct_tbl->bootloader[0].length = ebt_size;
if (encrypted) {
ret = encrypt_data_block(bct, BCT_LENGTH, sbk);
ret = encrypt_data_block(bct, bct, BCT_LENGTH);
if (ret)
return 1;
}
ret = sign_enc_data_block(bct, BCT_LENGTH, bct_hash, sbk);
ret = sign_data_block(bct, BCT_LENGTH, bct_hash);
if (ret)
return 1;

View File

@ -19,6 +19,7 @@
#include <asm/arch-tegra/pmc.h>
#include <asm/arch-tegra/fuse.h>
#include <asm/arch-tegra/warmboot.h>
#include <asm/arch-tegra/crypto.h>
DECLARE_GLOBAL_DATA_PTR;
@ -182,98 +183,36 @@ int warmboot_save_sdram_params(void)
return 0;
}
static u32 get_major_version(void)
static void determine_crypto_options(int *is_encrypted, int *is_signed)
{
u32 major_id;
struct apb_misc_gp_ctlr *gp =
(struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE;
major_id = (readl(&gp->hidrev) & HIDREV_MAJORPREV_MASK) >>
HIDREV_MAJORPREV_SHIFT;
return major_id;
}
static int is_production_mode_fuse_set(struct fuse_regs *fuse)
{
return readl(&fuse->production_mode);
}
static int is_odm_production_mode_fuse_set(struct fuse_regs *fuse)
{
return readl(&fuse->security_mode);
}
static int is_failure_analysis_mode(struct fuse_regs *fuse)
{
return readl(&fuse->fa);
}
static int ap20_is_odm_production_mode(void)
{
struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE;
if (!is_failure_analysis_mode(fuse) &&
is_odm_production_mode_fuse_set(fuse))
return 1;
else
return 0;
}
static int ap20_is_production_mode(void)
{
struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE;
if (get_major_version() == 0)
return 1;
if (!is_failure_analysis_mode(fuse) &&
is_production_mode_fuse_set(fuse) &&
!is_odm_production_mode_fuse_set(fuse))
return 1;
else
return 0;
}
static enum fuse_operating_mode fuse_get_operation_mode(void)
{
u32 chip_id;
struct apb_misc_gp_ctlr *gp =
(struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE;
chip_id = (readl(&gp->hidrev) & HIDREV_CHIPID_MASK) >>
HIDREV_CHIPID_SHIFT;
if (chip_id == CHIPID_TEGRA20) {
if (ap20_is_odm_production_mode()) {
printf("!! odm_production_mode is not supported !!\n");
return MODE_UNDEFINED;
} else
if (ap20_is_production_mode())
return MODE_PRODUCTION;
else
return MODE_UNDEFINED;
}
return MODE_UNDEFINED;
}
static void determine_crypto_options(int *is_encrypted, int *is_signed,
int *use_zero_key)
{
switch (fuse_get_operation_mode()) {
switch (tegra_fuse_get_operation_mode()) {
case MODE_ODM_PRODUCTION_SECURE:
*is_encrypted = 1;
*is_signed = 1;
break;
case MODE_ODM_PRODUCTION_OPEN:
case MODE_PRODUCTION:
*is_encrypted = 0;
*is_signed = 1;
*use_zero_key = 1;
break;
case MODE_UNDEFINED:
default:
*is_encrypted = 0;
*is_signed = 0;
*use_zero_key = 0;
break;
}
}
static int sign_wb_code(u32 start, u32 length, int use_zero_key)
static int encrypt_wb_code(u8 *source, u8 *destination, u32 length)
{
source += offsetof(struct wb_header, random_aes_block);
destination += offsetof(struct wb_header, random_aes_block);
length -= offsetof(struct wb_header, random_aes_block);
return encrypt_data_block(source, destination, length);
}
static int sign_wb_code(u32 start, u32 length)
{
int err;
u8 *source; /* Pointer to source */
@ -295,10 +234,9 @@ int warmboot_prepare_code(u32 seg_address, u32 seg_length)
struct wb_header *dst_header; /* Pointer to dest WB header */
int is_encrypted; /* Segment is encrypted */
int is_signed; /* Segment is signed */
int use_zero_key; /* Use key of all zeros */
/* Determine crypto options. */
determine_crypto_options(&is_encrypted, &is_signed, &use_zero_key);
determine_crypto_options(&is_encrypted, &is_signed);
/* Get the actual code limits. */
length = roundup(((u32)wb_end - (u32)wb_start), 16);
@ -346,18 +284,15 @@ int warmboot_prepare_code(u32 seg_address, u32 seg_length)
dst_header->entry_point = NV_WB_RUN_ADDRESS;
dst_header->code_length = length;
if (is_encrypted) {
printf("!!!! Encryption is not supported !!!!\n");
dst_header->length_insecure = 0;
err = -EACCES;
goto fail;
} else
/* copy the wb code directly following dst_header. */
memcpy((char *)(dst_header+1), (char *)wb_start, length);
if (is_encrypted)
encrypt_wb_code((u8 *)wb_start, (u8 *)dst_header,
length + sizeof(struct wb_header));
else
/* copy the wb code directly following dst_header */
memcpy((char *)(dst_header + 1), (char *)wb_start, length);
if (is_signed)
err = sign_wb_code(seg_address, dst_header->length_insecure,
use_zero_key);
err = sign_wb_code(seg_address, dst_header->length_insecure);
fail:
if (err)

View File

@ -16,6 +16,10 @@ config TARGET_CARDHU
bool "NVIDIA Tegra30 Cardhu evaluation board"
select BOARD_LATE_INIT
config TARGET_CHAGALL
bool "Pegatron Tegra30 Chagall board"
select BOARD_LATE_INIT
config TARGET_COLIBRI_T30
bool "Toradex Colibri T30 board"
select BOARD_LATE_INIT
@ -64,6 +68,7 @@ config SYS_SOC
source "board/toradex/apalis_t30/Kconfig"
source "board/nvidia/beaver/Kconfig"
source "board/nvidia/cardhu/Kconfig"
source "board/pegatron/chagall/Kconfig"
source "board/toradex/colibri_t30/Kconfig"
source "board/htc/endeavoru/Kconfig"
source "board/asus/grouper/Kconfig"

View File

@ -9,12 +9,10 @@
#include <vsprintf.h>
#include <linux/string.h>
#include <asm/arch-tegra/crypto.h>
#include <asm/arch-tegra/fuse.h>
#include "bct.h"
#include "uboot_aes.h"
/* Device with "sbk burned: false" will expose zero key */
const u8 nosbk[AES128_KEY_LENGTH] = { 0 };
/*
* @param bct boot config table start in RAM
* @param ect bootloader start in RAM
@ -25,7 +23,6 @@ static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size)
{
struct nvboot_config_table *bct_tbl = NULL;
u8 ebt_hash[AES128_KEY_LENGTH] = { 0 };
u8 sbk[AES128_KEY_LENGTH] = { 0 };
u8 *bct_hash = bct;
bool encrypted;
int ret;
@ -34,22 +31,19 @@ static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size)
ebt_size = roundup(ebt_size, EBT_ALIGNMENT);
memcpy(sbk, (u8 *)(bct + BCT_LENGTH),
NVBOOT_CMAC_AES_HASH_LENGTH * 4);
encrypted = memcmp(&sbk, &nosbk, AES128_KEY_LENGTH);
encrypted = tegra_fuse_get_operation_mode() == MODE_ODM_PRODUCTION_SECURE;
if (encrypted) {
ret = decrypt_data_block(bct, BCT_LENGTH, sbk);
ret = decrypt_data_block(bct, bct, BCT_LENGTH);
if (ret)
return 1;
ret = encrypt_data_block(ebt, ebt_size, sbk);
ret = encrypt_data_block(ebt, ebt, ebt_size);
if (ret)
return 1;
}
ret = sign_enc_data_block(ebt, ebt_size, ebt_hash, sbk);
ret = sign_data_block(ebt, ebt_size, ebt_hash);
if (ret)
return 1;
@ -62,12 +56,12 @@ static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size)
bct_tbl->bootloader[0].length = ebt_size;
if (encrypted) {
ret = encrypt_data_block(bct, BCT_LENGTH, sbk);
ret = encrypt_data_block(bct, bct, BCT_LENGTH);
if (ret)
return 1;
}
ret = sign_enc_data_block(bct, BCT_LENGTH, bct_hash, sbk);
ret = sign_data_block(bct, BCT_LENGTH, bct_hash);
if (ret)
return 1;

View File

@ -0,0 +1,13 @@
if TARGET_CHAGALL
config SYS_BOARD
default "chagall"
config SYS_VENDOR
default "pegatron"
config TEGRA_BOARD_STRING
string "Default Tegra board name"
default "Pegatron Chagall"
endif

View File

@ -0,0 +1,7 @@
CHAGALL BOARD
M: Svyatoslav Ryhel <clamor95@gmail.com>
S: Maintained
F: arch/arm/dts/tegra30-pegatron-chagall.dts
F: board/pegatron/chagall/
F: configs/chagall_defconfig
F: doc/board/pegatron/chagall.rst

View File

@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2010-2012
# NVIDIA Corporation <www.nvidia.com>
#
# (C) Copyright 2021
# Svyatoslav Ryhel <clamor95@gmail.com>
obj-$(CONFIG_XPL_BUILD) += chagall-spl.o
obj-y += chagall.o

View File

@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Chagall SPL stage configuration
*
* (C) Copyright 2010-2013
* NVIDIA Corporation <www.nvidia.com>
*
* (C) Copyright 2021
* Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <asm/arch/tegra.h>
#include <asm/arch-tegra/tegra_i2c.h>
#include <linux/delay.h>
#define TPS65911_I2C_ADDR (0x2D << 1)
#define TPS65911_VDDCTRL_OP_REG 0x28
#define TPS65911_VDDCTRL_SR_REG 0x27
#define TPS65911_VDDCTRL_OP_DATA (0x2400 | TPS65911_VDDCTRL_OP_REG)
#define TPS65911_VDDCTRL_SR_DATA (0x0100 | TPS65911_VDDCTRL_SR_REG)
#define TPS62361B_I2C_ADDR (0x60 << 1)
#define TPS62361B_SET3_REG 0x03
#define TPS62361B_SET3_DATA (0x4600 | TPS62361B_SET3_REG)
void pmic_enable_cpu_vdd(void)
{
/* Set VDD_CORE to 1.200V. */
tegra_i2c_ll_write(TPS62361B_I2C_ADDR, TPS62361B_SET3_DATA);
udelay(1000);
/*
* Bring up CPU VDD via the TPS65911x PMIC on the DVC I2C bus.
* First set VDD to 1.0125V, then enable the VDD regulator.
*/
tegra_i2c_ll_write(TPS65911_I2C_ADDR, TPS65911_VDDCTRL_OP_DATA);
udelay(1000);
tegra_i2c_ll_write(TPS65911_I2C_ADDR, TPS65911_VDDCTRL_SR_DATA);
udelay(10 * 1000);
}

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2010-2013
* NVIDIA Corporation <www.nvidia.com>
*
* (C) Copyright 2021
* Svyatoslav Ryhel <clamor95@gmail.com>
*/
/* Chagall derives from Cardhu board */
#include <fdt_support.h>
#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP)
int ft_board_setup(void *blob, struct bd_info *bd)
{
/* Remove TrustZone nodes */
fdt_del_node_and_alias(blob, "/firmware");
fdt_del_node_and_alias(blob, "/reserved-memory/trustzone@bfe00000");
return 0;
}
#endif

View File

@ -0,0 +1,9 @@
button_cmd_0_name=Volume Down
button_cmd_0=bootmenu
bootmenu_0=mount external storage=usb start && ums 0 mmc 1; bootmenu
bootmenu_1=fastboot=echo Starting Fastboot protocol ...; fastboot usb 0; bootmenu
bootmenu_2=reboot RCM=enterrcm
bootmenu_3=reboot=reset
bootmenu_4=power off=poweroff
bootmenu_delay=-1

13
board/samsung/n1/Kconfig Normal file
View File

@ -0,0 +1,13 @@
if TARGET_SAMSUNG_N1
config SYS_BOARD
default "n1"
config SYS_VENDOR
default "samsung"
config TEGRA_BOARD_STRING
string "Default Tegra board name"
default "Samsung N1"
endif

View File

@ -0,0 +1,9 @@
N1 BOARD
M: Ion Agorria <ion@agorria.com>
M: Svyatoslav Ryhel <clamor95@gmail.com>
S: Maintained
F: arch/arm/dts/tegra20-samsung-bose.dts
F: arch/arm/dts/tegra20-samsung-n1.dts
F: board/samsung/n1/
F: configs/n1_defconfig
F: doc/board/samsung/n1.rst

View File

@ -0,0 +1,6 @@
CONFIG_DEFAULT_DEVICE_TREE="tegra20-samsung-bose"
CONFIG_SYS_PROMPT="Tegra20 (Bose) # "
# CONFIG_VIDEO_BRIDGE_SAMSUNG_CMC623 is not set
# CONFIG_BACKLIGHT_SAMSUNG_CMC623 is not set
# CONFIG_VIDEO_LCD_SONY_L4F00430T01 is not set
CONFIG_VIDEO_LCD_SAMSUNG_S6E63M0=y

16
board/samsung/n1/n1.env Normal file
View File

@ -0,0 +1,16 @@
#include <env/nvidia/prod_upd.env>
button_cmd_0_name=Volume Down
button_cmd_0=bootmenu
boot_block_size_r=0x80000
boot_block_size=0x400
boot_dev=1
bootmenu_0=mount internal storage=usb start && ums 0 mmc 0; bootmenu
bootmenu_1=mount external storage=usb start && ums 0 mmc 1; bootmenu
bootmenu_2=fastboot=echo Starting Fastboot protocol ...; fastboot usb 0; bootmenu
bootmenu_3=reboot RCM=enterrcm
bootmenu_4=reboot=reset
bootmenu_5=power off=poweroff
bootmenu_delay=-1

80
configs/chagall_defconfig Normal file
View File

@ -0,0 +1,80 @@
CONFIG_ARM=y
CONFIG_ARCH_TEGRA=y
CONFIG_SUPPORT_PASSING_ATAGS=y
CONFIG_CMDLINE_TAG=y
CONFIG_INITRD_TAG=y
CONFIG_TEXT_BASE=0x80110000
CONFIG_NR_DRAM_BANKS=2
CONFIG_ENV_SOURCE_FILE="chagall"
CONFIG_ENV_SIZE=0x3000
CONFIG_ENV_OFFSET=0xFFFFD000
CONFIG_DEFAULT_DEVICE_TREE="tegra30-pegatron-chagall"
CONFIG_SPL_STACK=0x800ffffc
CONFIG_SPL_TEXT_BASE=0x80108000
CONFIG_SYS_LOAD_ADDR=0x82000000
CONFIG_TEGRA30=y
CONFIG_TARGET_CHAGALL=y
CONFIG_BUTTON_CMD=y
CONFIG_BOOTDELAY=0
CONFIG_AUTOBOOT_KEYED=y
CONFIG_AUTOBOOT_KEYED_CTRLC=y
CONFIG_OF_BOARD_SETUP=y
CONFIG_OF_SYSTEM_SETUP=y
CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff"
CONFIG_SYS_PBSIZE=2084
CONFIG_SPL_FOOTPRINT_LIMIT=y
CONFIG_SPL_MAX_FOOTPRINT=0x8000
# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set
CONFIG_SPL_HAVE_INIT_STACK=y
CONFIG_SPL_SYS_MALLOC=y
CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y
CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x80090000
CONFIG_SPL_SYS_MALLOC_SIZE=0x10000
CONFIG_SYS_PROMPT="Tegra30 (Chagall) # "
# CONFIG_CMD_BOOTEFI_BOOTMGR is not set
CONFIG_CMD_BOOTMENU=y
# CONFIG_CMD_IMI is not set
CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_POWEROFF=y
CONFIG_CMD_USB=y
CONFIG_CMD_USB_MASS_STORAGE=y
CONFIG_CMD_UMS_ABORT_KEYED=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_CMD_PAUSE=y
CONFIG_CMD_REGULATOR=y
CONFIG_CMD_EXT4_WRITE=y
# CONFIG_SPL_DOS_PARTITION is not set
# CONFIG_SPL_EFI_PARTITION is not set
CONFIG_ENV_OVERWRITE=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_SYS_MMC_ENV_PART=2
CONFIG_BUTTON=y
CONFIG_USB_FUNCTION_FASTBOOT=y
CONFIG_FASTBOOT_BUF_ADDR=0x91000000
CONFIG_FASTBOOT_BUF_SIZE=0x10000000
CONFIG_FASTBOOT_FLASH=y
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
CONFIG_FASTBOOT_CMD_OEM_FORMAT=y
CONFIG_SYS_I2C_TEGRA=y
CONFIG_BUTTON_KEYBOARD=y
CONFIG_DM_PMIC=y
CONFIG_DM_PMIC_TPS65910=y
CONFIG_DM_REGULATOR=y
CONFIG_DM_REGULATOR_FIXED=y
CONFIG_DM_REGULATOR_TPS65911=y
CONFIG_PWM_TEGRA=y
CONFIG_SYS_NS16550=y
CONFIG_SYSRESET_TPS65910=y
CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_TEGRA=y
CONFIG_USB_KEYBOARD=y
CONFIG_USB_GADGET=y
CONFIG_CI_UDC=y
CONFIG_VIDEO=y
# CONFIG_VIDEO_LOGO is not set
CONFIG_VIDEO_BRIDGE=y
CONFIG_VIDEO_BRIDGE_LVDS_CODEC=y
CONFIG_VIDEO_TEGRA=y

92
configs/n1_defconfig Normal file
View File

@ -0,0 +1,92 @@
CONFIG_ARM=y
CONFIG_ARCH_TEGRA=y
CONFIG_SUPPORT_PASSING_ATAGS=y
CONFIG_CMDLINE_TAG=y
CONFIG_INITRD_TAG=y
CONFIG_TEXT_BASE=0x00110000
CONFIG_NR_DRAM_BANKS=2
CONFIG_ENV_SOURCE_FILE="n1"
CONFIG_ENV_SIZE=0x3000
CONFIG_ENV_OFFSET=0xFFFFD000
CONFIG_DEFAULT_DEVICE_TREE="tegra20-samsung-n1"
CONFIG_SPL_STACK=0xffffc
CONFIG_SPL_TEXT_BASE=0x00108000
CONFIG_SYS_LOAD_ADDR=0x2000000
CONFIG_TEGRA20=y
CONFIG_TARGET_SAMSUNG_N1=y
CONFIG_TEGRA_ENABLE_UARTB=y
CONFIG_CMD_EBTUPDATE=y
CONFIG_BUTTON_CMD=y
CONFIG_BOOTDELAY=0
CONFIG_AUTOBOOT_KEYED=y
CONFIG_AUTOBOOT_KEYED_CTRLC=y
CONFIG_OF_SYSTEM_SETUP=y
CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff"
CONFIG_SYS_PBSIZE=2085
CONFIG_SPL_FOOTPRINT_LIMIT=y
CONFIG_SPL_MAX_FOOTPRINT=0x8000
# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set
CONFIG_SPL_HAVE_INIT_STACK=y
CONFIG_SPL_SYS_MALLOC=y
CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y
CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x90000
CONFIG_SPL_SYS_MALLOC_SIZE=0x10000
CONFIG_SYS_PROMPT="Tegra20 (N1) # "
# CONFIG_CMD_BOOTEFI_BOOTMGR is not set
CONFIG_CMD_BOOTMENU=y
# CONFIG_CMD_IMI is not set
CONFIG_CMD_GPIO=y
CONFIG_CMD_GPT=y
CONFIG_CMD_GPT_RENAME=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_POWEROFF=y
CONFIG_CMD_USB=y
CONFIG_CMD_USB_MASS_STORAGE=y
CONFIG_CMD_UMS_ABORT_KEYED=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_CMD_PAUSE=y
CONFIG_CMD_EXT4_WRITE=y
# CONFIG_SPL_DOS_PARTITION is not set
# CONFIG_SPL_EFI_PARTITION is not set
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_MMC=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_SYS_MMC_ENV_PART=2
CONFIG_BUTTON=y
CONFIG_USB_FUNCTION_FASTBOOT=y
CONFIG_FASTBOOT_BUF_ADDR=0x11000000
CONFIG_FASTBOOT_BUF_SIZE=0x5000000
CONFIG_FASTBOOT_FLASH=y
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
CONFIG_FASTBOOT_CMD_OEM_FORMAT=y
CONFIG_DM_I2C_GPIO=y
CONFIG_SYS_I2C_TEGRA=y
CONFIG_BUTTON_KEYBOARD=y
CONFIG_DM_PMIC=y
CONFIG_DM_PMIC_MAX8907=y
CONFIG_DM_REGULATOR=y
CONFIG_DM_REGULATOR_MAX8907=y
CONFIG_DM_REGULATOR_FIXED=y
CONFIG_SYS_NS16550=y
CONFIG_TEGRA20_SLINK=y
CONFIG_SYSRESET_MAX8907=y
CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_TEGRA=y
CONFIG_USB_ULPI_VIEWPORT=y
CONFIG_USB_ULPI=y
CONFIG_USB_KEYBOARD=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_MANUFACTURER="Samsung"
CONFIG_USB_GADGET_VENDOR_NUM=0x04e8
CONFIG_USB_GADGET_PRODUCT_NUM=0x685F
CONFIG_CI_UDC=y
CONFIG_VIDEO=y
# CONFIG_VIDEO_LOGO is not set
# CONFIG_VIDEO_BPP8 is not set
CONFIG_VIDEO_BRIDGE=y
CONFIG_VIDEO_TEGRA=y
CONFIG_VIDEO_BRIDGE_SAMSUNG_CMC623=y
CONFIG_BACKLIGHT_SAMSUNG_CMC623=y
CONFIG_VIDEO_LCD_SONY_L4F00430T01=y

View File

@ -47,6 +47,7 @@ Board-specific doc
nxp/index
openpiton/index
ouya/index
pegatron/index
phytec/index
purism/index
qualcomm/index

View File

@ -0,0 +1,41 @@
.. SPDX-License-Identifier: GPL-2.0+
U-Boot for the Pegatron Chagall tablet
======================================
Quick Start
-----------
- Build U-Boot
- Boot
Build U-Boot
------------
.. code-block:: bash
$ export CROSS_COMPILE=arm-none-eabi-
$ make chagall_defconfig
$ make
After the build succeeds, you will obtain the final ``u-boot-dtb-tegra.bin``
image, ready for loading.
Boot
----
Currently, U-Boot can be preloaded into RAM via the Fusée Gelée. To enter
RCM protocol use ``power`` and ``volume up`` key combination from powered
off device. The host PC should recognize an APX device.
Built U-Boot ``u-boot-dtb-tegra.bin`` can be loaded from fusee-tools
directory with
.. code-block:: bash
$ ./run_bootloader.sh -s T30 -t ./bct/chagall.bct
To boot Linux, U-Boot will look for an ``extlinux.conf`` on MicroSD and then on
eMMC. Additionally, if the Volume Down button is pressed while loading, the
device will enter bootmenu. Bootmenu contains entries to mount MicroSD and eMMC
as mass storage, fastboot, reboot, reboot RCM and poweroff.

View File

@ -0,0 +1,9 @@
.. SPDX-License-Identifier: GPL-2.0+
Pegatron
========
.. toctree::
:maxdepth: 2
chagall

View File

@ -8,3 +8,4 @@ Samsung
axy17lte
e850-96
n1

51
doc/board/samsung/n1.rst Normal file
View File

@ -0,0 +1,51 @@
.. SPDX-License-Identifier: GPL-2.0+
U-Boot for the Samsung N1 device family
=======================================
``DISCLAMER!`` Moving your Samsung Galaxy R (GT-I9103) or Samsung Captivate Glide
(SGH-i927) to use U-Boot assumes replacement of the sboot. Vendor android firmwares
will no longer be able to run on the device. This replacement IS reversible.
Quick Start
-----------
- Build U-Boot
- Boot
Build U-Boot
------------
Device support is implemented by applying config fragment to a generic board
defconfig. Generic board defconfig is suitable for Samsung Galaxy R (GT-I9103)
while Samsung Captivate Glide (SGH-i927) support is provided by applying
``bose.config`` fragment.
.. code-block:: bash
$ export CROSS_COMPILE=arm-none-eabi-
$ make n1_defconfig bose.config # For Captivate Glide
$ make
After the build succeeds, you will obtain the final ``u-boot-dtb-tegra.bin``
image, ready for further processing.
Boot
----
Currently, U-Boot can be preloaded into RAM via the NvFlash. To enter
RCM protocol use ``home`` and ``volume up`` for Galaxy R and ``volume down``
and ``volume up`` Captivate Glide key combination plus plugging usb in.
The host PC should recognize an APX device.
Built U-Boot ``u-boot-dtb-tegra.bin`` can be loaded from fusee-tools
directory with
.. code-block:: bash
$ ./utils/nvflash_t20 --setbct --bct ./bct/i927.bct --configfile ./utils/flash.cfg --bl u-boot-dtb-tegra.bin --sbk (in form of 0xABCDABCD 4 times) --sync # For Captivate Glide
To boot Linux, U-Boot will look for an ``extlinux.conf`` on MicroSD and then on
eMMC. Additionally, if the Volume Down button is pressed while loading, the
device will enter bootmenu. Bootmenu contains entries to mount MicroSD and eMMC
as mass storage, fastboot, reboot, reboot RCM and poweroff.

View File

@ -10,4 +10,6 @@ source "drivers/crypto/aspeed/Kconfig"
source "drivers/crypto/nuvoton/Kconfig"
source "drivers/crypto/tegra/Kconfig"
endmenu

View File

@ -10,3 +10,4 @@ obj-y += fsl/
obj-y += hash/
obj-y += aspeed/
obj-y += nuvoton/
obj-y += tegra/

View File

@ -0,0 +1,7 @@
config TEGRA_AES
bool "Support the Tegra AES"
depends on DM_AES
help
This provides a means to encrypt and decrypt data using the Tegra
Bit Stream Engine for Video/Audio. Also may provide a mean to
encrypt/decrypt/sign using already loaded device SBK.

View File

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_$(PHASE_)TEGRA_AES) += tegra_aes.o

View File

@ -0,0 +1,591 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for NVIDIA Tegra AES hardware engine residing inside the Bit Stream Engine
* for Video (BSEV) hardware block and Bit Stream Engine for Audio (BSEA).
*
* The programming sequence for this engine is with the help of commands which travel
* via a command queue residing between the CPU and the BSEV/BSEA block.
*
* The hardware key table length is 64 bytes and each key slot divided as follows:
* 1. Key - 32 bytes
* 2. Original IV - 16 bytes
* 3. Updated IV - 16 bytes
*
* The engine has 4 slots in T20/T30 in which 0th contains SBK loaded by bootrom,
* vendor bootloaders tend to clear this slot so that anything booted after can't
* use the SBK. This is relevant for U-Boot's chainloaded from these vendor bootloaders.
*
* Copyright (c) 2010-2011, NVIDIA Corporation
* Copyright (c) 2025, Ion Agorria.
*/
#include <dm.h>
#include <asm/io.h>
#include <malloc.h>
#include <time.h>
#include <linux/delay.h>
#include <clk.h>
#include <reset.h>
#include <uboot_aes.h>
#include <asm/arch-tegra/crypto.h>
#include <asm/arch-tegra/fuse.h>
/* Make sure pointers will fit register size for AES engine */
static_assert(sizeof(void *) == sizeof(u32));
#define IRAM_BASE 0x40000000
#define TEGRA_AES_DMA_BUFFER_SIZE (0x4000 / AES_BLOCK_LENGTH)
#define TEGRA_AES_HW_MAX_KEY_LENGTH AES256_KEY_LENGTH
#define TEGRA_AES_HW_TABLE_LENGTH (TEGRA_AES_HW_MAX_KEY_LENGTH + AES_BLOCK_LENGTH * 2)
#define TEGRA_AES_IRAM_MAX_ADDR (IRAM_BASE | TEGRA_AES_KEYTABLEADDR_FIELD)
#define TEGRA_AES_BUSY_TIMEOUT_MS 1000
/* Registers */
#define TEGRA_AES_ICMDQUE_WR 0x000
#define TEGRA_AES_CMDQUE_CONTROL 0x008
#define TEGRA_AES_INTR_STATUS 0x018
#define TEGRA_AES_INT_ENB 0x040
#define TEGRA_AES_BSE_CFG 0x044
#define TEGRA_AES_IRAM_ACCESS_CFG 0x0a0
#define TEGRA_AES_SECURE_DEST_ADDR 0x100
#define TEGRA_AES_SECURE_INPUT_SELECT 0x104
#define TEGRA_AES_SECURE_CONFIG 0x108
#define TEGRA_AES_SECURE_CONFIG_EXT 0x10c
/* Register field macros */
#define TEGRA_AES_ENGINE_BUSY_FIELD BIT(0)
#define TEGRA_AES_ICQ_EMPTY_FIELD BIT(3)
#define TEGRA_AES_DMA_BUSY_FIELD BIT(23)
#define TEGRA_AES_SECURE_KEY_SCH_DIS_FIELD BIT(15)
#define TEGRA_AES_KEYTABLEADDR_FIELD (BIT(17) - 1)
#define TEGRA_AES_SECURE_KEY_INDEX_SHIFT 20
#define TEGRA_AES_SECURE_KEY_INDEX_FIELD (0x1f << TEGRA_AES_SECURE_KEY_INDEX_SHIFT)
#define TEGRA_AES_SECURE_CTR_CNT_SHIFT 16
#define TEGRA_AES_SECURE_CTR_CNT_FIELD (0xffff << TEGRA_AES_SECURE_CTR_CNT_SHIFT)
#define TEGRA_AES_BSE_MODE_FIELD 0x1f
#define TEGRA_AES_BSE_LITTLE_ENDIAN_FIELD BIT(10)
#define TEGRA_AES_CMDQ_OPCODE_SHIFT 26
#define TEGRA_AES_CMDQ_CTRL_ICMDQEN_FIELD BIT(1)
#define TEGRA_AES_CMDQ_CTRL_SRC_STM_SEL_FIELD BIT(4)
#define TEGRA_AES_CMDQ_CTRL_DST_STM_SEL_FIELD BIT(5)
#define TEGRA_AES_SECURE_INPUT_ALG_SEL_SHIFT 28
#define TEGRA_AES_SECURE_INPUT_KEY_LEN_SHIFT 16
#define TEGRA_AES_SECURE_INPUT_IV_FIELD BIT(10)
#define TEGRA_AES_SECURE_INPUT_HASH_ENB_FIELD BIT(2)
#define TEGRA_AES_SECURE_CORE_SEL_SHIFT 9
#define TEGRA_AES_SECURE_VCTRAM_SEL_SHIFT 7
#define TEGRA_AES_SECURE_XOR_POS_SHIFT 3
#define TEGRA_AES_INT_ERROR_MASK 0x6ff000
/* Commands for BSEV/BSEA */
#define TEGRA_AES_CMD_BLKSTARTENGINE 0x0e
#define TEGRA_AES_CMD_DMASETUP 0x10
#define TEGRA_AES_CMD_DMACOMPLETE 0x11
#define TEGRA_AES_CMD_SETTABLE 0x15
/* Flags for mode */
#define TEGRA_AES_MODE_ENCRYPT BIT(0)
#define TEGRA_AES_MODE_CBC BIT(1)
#define TEGRA_AES_MODE_UPDATE_IV BIT(2)
#define TEGRA_AES_MODE_HASH BIT(3)
struct tegra_aes_priv {
void *regs;
void *iram_addr;
struct reset_ctl reset_ctl;
struct reset_ctl reset_ctl_vde;
struct clk *clk;
struct clk *clk_parent;
u8 current_key_size;
bool sbk_available;
};
static bool tegra_aes_is_busy(struct tegra_aes_priv *priv, bool dma_wait)
{
u32 value = readl(priv->regs + TEGRA_AES_INTR_STATUS);
bool engine_busy = value & TEGRA_AES_ENGINE_BUSY_FIELD;
bool non_empty_queue = !(value & TEGRA_AES_ICQ_EMPTY_FIELD);
bool dma_busy = dma_wait && (value & TEGRA_AES_DMA_BUSY_FIELD);
log_debug("%s - e:%d q:%d dma:%d\n", __func__, engine_busy, non_empty_queue, dma_busy);
return engine_busy || non_empty_queue || dma_busy;
}
static u32 tegra_aes_check_error(struct tegra_aes_priv *priv)
{
u32 value = readl(priv->regs + TEGRA_AES_INTR_STATUS) & TEGRA_AES_INT_ERROR_MASK;
if (value) {
writel(TEGRA_AES_INT_ERROR_MASK, priv->regs + TEGRA_AES_INTR_STATUS);
log_debug("%s 0x%x\n", __func__, value);
}
return value;
}
static int tegra_aes_wait_for_idle_dma(struct tegra_aes_priv *priv, bool dma_wait)
{
ulong start = get_timer(0);
while (tegra_aes_is_busy(priv, dma_wait)) {
if (get_timer(start) > TEGRA_AES_BUSY_TIMEOUT_MS) {
log_err("%s: TIMEOUT!!!\n", __func__);
break;
}
mdelay(5);
}
if (tegra_aes_check_error(priv))
return -1;
return 0;
}
static int tegra_aes_wait_for_idle(struct tegra_aes_priv *priv)
{
return tegra_aes_wait_for_idle_dma(priv, 1);
}
static int tegra_aes_configure(struct tegra_aes_priv *priv)
{
u32 value;
if (tegra_aes_wait_for_idle(priv))
return -1;
/* IRAM config */
writel(0, priv->regs + TEGRA_AES_IRAM_ACCESS_CFG);
/* Reset interrupts bits, or engine will hang on next operation */
writel(0xFFFFFFFF, priv->regs + TEGRA_AES_INTR_STATUS);
/* Set interrupts */
writel(0, priv->regs + TEGRA_AES_INT_ENB);
/* Configure CMDQUE */
value = readl(priv->regs + TEGRA_AES_CMDQUE_CONTROL);
value |= TEGRA_AES_CMDQ_CTRL_SRC_STM_SEL_FIELD |
TEGRA_AES_CMDQ_CTRL_DST_STM_SEL_FIELD |
TEGRA_AES_CMDQ_CTRL_ICMDQEN_FIELD;
writel(value, priv->regs + TEGRA_AES_CMDQUE_CONTROL);
value = readl(priv->regs + TEGRA_AES_SECURE_CONFIG_EXT);
value &= ~TEGRA_AES_SECURE_CTR_CNT_FIELD;
writel(value, priv->regs + TEGRA_AES_SECURE_CONFIG_EXT);
/* Configure BSE */
value = readl(priv->regs + TEGRA_AES_BSE_CFG);
value &= ~TEGRA_AES_BSE_MODE_FIELD;
value |= TEGRA_AES_BSE_LITTLE_ENDIAN_FIELD;
writel(value, priv->regs + TEGRA_AES_BSE_CFG);
return 0;
}
static int tegra_aes_select_key_slot(struct tegra_aes_priv *priv, u32 key_size, u8 slot)
{
if (tegra_aes_wait_for_idle(priv))
return -1;
if (key_size < (AES128_KEY_LENGTH * 8) ||
key_size > (TEGRA_AES_HW_MAX_KEY_LENGTH * 8))
return -EINVAL;
priv->current_key_size = key_size;
/* Select the key slot */
u32 value = readl(priv->regs + TEGRA_AES_SECURE_CONFIG);
value &= ~TEGRA_AES_SECURE_KEY_INDEX_FIELD;
value |= (slot << TEGRA_AES_SECURE_KEY_INDEX_SHIFT);
writel(value, priv->regs + TEGRA_AES_SECURE_CONFIG);
return 0;
}
static int tegra_aes_call_engine(struct tegra_aes_priv *priv, u8 *src, u8 *dst,
u32 nblocks, u32 mode)
{
u32 value;
const u32 ICMDQ_LENGTH = 4;
u32 cmdq[ICMDQ_LENGTH];
log_debug("%s: 0x%p -> 0x%p blocks %d mode 0x%x\n", __func__,
src, dst, nblocks, mode);
if (!nblocks) {
log_warning("%s: called with 0 blocks!\n", __func__);
return -1;
}
if (tegra_aes_configure(priv))
return -1;
/* Configure Secure Input */
value = 1 << TEGRA_AES_SECURE_INPUT_ALG_SEL_SHIFT |
priv->current_key_size << TEGRA_AES_SECURE_INPUT_KEY_LEN_SHIFT;
if (mode & TEGRA_AES_MODE_UPDATE_IV)
value |= TEGRA_AES_SECURE_INPUT_IV_FIELD;
if (mode & TEGRA_AES_MODE_HASH)
value |= TEGRA_AES_SECURE_INPUT_HASH_ENB_FIELD;
if (mode & TEGRA_AES_MODE_CBC) {
value |= ((mode & TEGRA_AES_MODE_ENCRYPT) ? 2 : 3) <<
TEGRA_AES_SECURE_XOR_POS_SHIFT;
value |= ((mode & TEGRA_AES_MODE_ENCRYPT) ? 2 : 3) <<
TEGRA_AES_SECURE_VCTRAM_SEL_SHIFT;
value |= ((mode & TEGRA_AES_MODE_ENCRYPT) ? 1 : 0) <<
TEGRA_AES_SECURE_CORE_SEL_SHIFT;
} else {
/* ECB */
value |= ((mode & TEGRA_AES_MODE_ENCRYPT) ? 1 : 0) <<
TEGRA_AES_SECURE_CORE_SEL_SHIFT;
}
writel(value, priv->regs + TEGRA_AES_SECURE_INPUT_SELECT);
/* Set destination address (doing in-place at IRAM) */
writel((u32)priv->iram_addr, priv->regs + TEGRA_AES_SECURE_DEST_ADDR);
/* Copy src data to IRAM */
if (src != priv->iram_addr)
memcpy(priv->iram_addr, src, nblocks * AES_BLOCK_LENGTH);
/* Run ICMD commands */
cmdq[0] = TEGRA_AES_CMD_DMASETUP << TEGRA_AES_CMDQ_OPCODE_SHIFT;
cmdq[1] = (u32)priv->iram_addr;
cmdq[2] = TEGRA_AES_CMD_BLKSTARTENGINE << TEGRA_AES_CMDQ_OPCODE_SHIFT | (nblocks - 1);
cmdq[3] = TEGRA_AES_CMD_DMACOMPLETE << TEGRA_AES_CMDQ_OPCODE_SHIFT;
for (int i = 0; i < ICMDQ_LENGTH; i++) {
tegra_aes_wait_for_idle_dma(priv, (ICMDQ_LENGTH - 1) == i);
writel(cmdq[i], priv->regs + TEGRA_AES_ICMDQUE_WR);
}
if (tegra_aes_wait_for_idle(priv))
return -1;
/* Put the result from IRAM to destination if not hashing */
if (dst != priv->iram_addr && !(mode & TEGRA_AES_MODE_HASH))
memcpy(dst, priv->iram_addr, nblocks * AES_BLOCK_LENGTH);
return 0;
}
static int tegra_aes_process_blocks(struct udevice *dev, u8 *iv, u8 *src,
u8 *dst, u32 num_aes_blocks, u32 mode)
{
struct tegra_aes_priv *priv = dev_get_priv(dev);
log_debug("%s: 0x%p -> 0x%p blocks %d mode 0x%x\n",
__func__, src, dst, num_aes_blocks, mode);
if (!num_aes_blocks) {
log_warning("%s: called with 0 blocks!\n", __func__);
return -1;
}
/* Load initial IV if CBC mode */
if (mode & TEGRA_AES_MODE_CBC) {
if (tegra_aes_call_engine(priv, iv, priv->iram_addr, 1, TEGRA_AES_MODE_CBC))
return -1;
/* Add update IV flag */
mode |= TEGRA_AES_MODE_UPDATE_IV;
}
/* Process blocks by calling engine several times per dma buffer size */
while (num_aes_blocks > 0) {
u32 blocks = min(num_aes_blocks, (u32)TEGRA_AES_DMA_BUFFER_SIZE);
if (tegra_aes_call_engine(priv, src, dst, blocks, mode))
return -1;
num_aes_blocks -= blocks;
src += blocks * AES_BLOCK_LENGTH;
dst += blocks * AES_BLOCK_LENGTH;
}
return 0;
}
static int tegra_aes_ops_available_key_slots(struct udevice *dev)
{
return 4; /* 4 slots in Tegra20 and Tegra30 */
}
static int tegra_aes_ops_select_key_slot(struct udevice *dev, u32 key_size, u8 slot)
{
struct tegra_aes_priv *priv = dev_get_priv(dev);
if (slot == TEGRA_AES_SLOT_SBK && !priv->sbk_available) {
log_warning("%s: SBK not available!\n", __func__);
return -1;
}
return tegra_aes_select_key_slot(priv, key_size, slot);
}
static int tegra_aes_ops_set_key_for_key_slot(struct udevice *dev, u32 key_size,
u8 *key, u8 slot)
{
struct tegra_aes_priv *priv = dev_get_priv(dev);
const u8 SUBCMD_CRYPTO_TABLE_SEL = 0x3;
const u8 SUBCMD_KEY_TABLE_SEL = 0x8;
const u8 CMDQ_KEYTABLEADDR_SHIFT = 0;
const u8 CMDQ_KEYTABLEID_SHIFT = 17;
const u8 CMDQ_TABLESEL_SHIFT = 24;
u32 value, addr;
log_debug("%s: slot %d\n", __func__, slot);
if (tegra_aes_configure(priv))
return -1;
if (key_size < (AES128_KEY_LENGTH * 8) ||
key_size > (TEGRA_AES_HW_MAX_KEY_LENGTH * 8))
return -EINVAL;
if (slot == TEGRA_AES_SLOT_SBK)
log_debug("%s: SBK slot being set!\n", __func__);
/* Clear and copy data to IRAM */
memset(priv->iram_addr, 0, TEGRA_AES_HW_TABLE_LENGTH);
memcpy(priv->iram_addr, key, key_size / 8);
/* Mask the addr */
addr = ((u32)priv->iram_addr) & TEGRA_AES_KEYTABLEADDR_FIELD;
/* Command for engine to load AES key from IRAM */
value = TEGRA_AES_CMD_SETTABLE << TEGRA_AES_CMDQ_OPCODE_SHIFT |
SUBCMD_CRYPTO_TABLE_SEL << CMDQ_TABLESEL_SHIFT |
(SUBCMD_KEY_TABLE_SEL | slot) << CMDQ_KEYTABLEID_SHIFT |
addr << CMDQ_KEYTABLEADDR_SHIFT;
writel(value, priv->regs + TEGRA_AES_ICMDQUE_WR);
return tegra_aes_wait_for_idle(priv);
}
static int tegra_aes_ops_aes_ecb_encrypt(struct udevice *dev, u8 *src, u8 *dst,
u32 num_aes_blocks)
{
return tegra_aes_process_blocks(dev, NULL, src, dst, num_aes_blocks,
TEGRA_AES_MODE_ENCRYPT);
}
static int tegra_aes_ops_aes_ecb_decrypt(struct udevice *dev, u8 *src, u8 *dst,
u32 num_aes_blocks)
{
return tegra_aes_process_blocks(dev, NULL, src, dst, num_aes_blocks, 0);
}
static int tegra_aes_ops_aes_cbc_encrypt(struct udevice *dev, u8 *iv, u8 *src,
u8 *dst, u32 num_aes_blocks)
{
return tegra_aes_process_blocks(dev, iv, src, dst, num_aes_blocks,
TEGRA_AES_MODE_CBC | TEGRA_AES_MODE_ENCRYPT);
}
static int tegra_aes_ops_aes_cbc_decrypt(struct udevice *dev, u8 *iv, u8 *src,
u8 *dst, u32 num_aes_blocks)
{
return tegra_aes_process_blocks(dev, iv, src, dst, num_aes_blocks,
TEGRA_AES_MODE_CBC);
}
static void tegra_aes_test_loaded_sbk(struct udevice *dev)
{
struct tegra_aes_priv *priv = dev_get_priv(dev);
enum fuse_operating_mode opmode = tegra_fuse_get_operation_mode();
const u8 ZERO_KEY_CIPHERTEXT[AES_BLOCK_LENGTH] = {
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b,
0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e
};
/* Encrypt a zero block, we use ECB so that we only care about SBK and not the IV */
memset(priv->iram_addr, 0, AES_BLOCK_LENGTH);
tegra_aes_select_key_slot(priv, 128, TEGRA_AES_SLOT_SBK);
tegra_aes_call_engine(priv, priv->iram_addr, priv->iram_addr, 1, TEGRA_AES_MODE_ENCRYPT);
/* Evaluate the result of engine operation */
if (!memcmp(priv->iram_addr, AES_ZERO_BLOCK, AES_BLOCK_LENGTH)) {
log_err("%s: engine is not operational! (opmode 0x%x)\n", __func__, opmode);
} else if (!memcmp(priv->iram_addr, ZERO_KEY_CIPHERTEXT, AES_BLOCK_LENGTH)) {
if (opmode == MODE_ODM_PRODUCTION_SECURE) {
log_warning("%s: SBK is zero or is cleared from engine! (opmode 0x%x)\n",
__func__, opmode);
} else {
log_debug("%s - SBK is zero and available! (opmode 0x%x)\n",
__func__, opmode);
priv->sbk_available = true;
}
} else {
if (opmode == MODE_ODM_PRODUCTION_SECURE) {
log_debug("%s: SBK is available! (opmode 0x%x)\n", __func__, opmode);
priv->sbk_available = true;
} else {
log_warning("%s: SBK is not zero and should be! (opmode 0x%x)\n",
__func__, opmode);
}
}
}
static int tegra_aes_hw_init(struct udevice *dev)
{
struct tegra_aes_priv *priv = dev_get_priv(dev);
u32 value;
int ret;
if (priv->clk_parent) {
ret = reset_assert(&priv->reset_ctl_vde);
if (ret) {
log_debug("%s: VDE reset assert failed: %d\n", __func__, ret);
return ret;
}
}
ret = reset_assert(&priv->reset_ctl);
if (ret) {
log_debug("%s: BSE reset assert failed: %d\n", __func__, ret);
return ret;
}
if (priv->clk_parent) {
ret = clk_enable(priv->clk_parent);
if (ret) {
log_err("%s: VDE clock enable failed: %d\n", __func__, ret);
return ret;
}
ret = clk_set_rate(priv->clk_parent, 50 * 1000000);
if (IS_ERR_VALUE(ret)) {
log_err("%s: VDE clock set rate failed: %d\n", __func__, ret);
return ret;
}
}
ret = clk_enable(priv->clk);
if (ret) {
log_err("%s: BSE clock enable failed: %d\n", __func__, ret);
return ret;
}
if (priv->clk_parent) {
ret = reset_deassert(&priv->reset_ctl_vde);
if (ret) {
log_err("%s: VDE reset deassert failed: %d\n", __func__, ret);
return ret;
}
}
ret = reset_deassert(&priv->reset_ctl);
if (ret) {
log_err("%s: BSE reset deassert failed: %d\n", __func__, ret);
return ret;
}
/* Enable key schedule generation in hardware */
value = readl(priv->regs + TEGRA_AES_SECURE_CONFIG_EXT);
value &= ~TEGRA_AES_SECURE_KEY_SCH_DIS_FIELD;
writel(value, priv->regs + TEGRA_AES_SECURE_CONFIG_EXT);
/* Check if SBK is loaded in SBK slot or was erased */
priv->sbk_available = false;
tegra_aes_test_loaded_sbk(dev);
return 0;
}
static int tegra_aes_probe(struct udevice *dev)
{
struct tegra_aes_priv *priv = dev_get_priv(dev);
fdt_size_t iram_size = 0;
u32 value;
int ret;
priv->current_key_size = AES128_KEY_LENGTH;
priv->regs = dev_read_addr_ptr(dev);
if (!priv->regs) {
log_err("%s: Cannot find aes reg address, binding failed\n", __func__);
return -EINVAL;
}
priv->iram_addr = devfdt_get_addr_size_name_ptr(dev, "iram-buffer", &iram_size);
if (!priv->iram_addr) {
log_debug("%s: Cannot find iram buffer address, binding failed\n", __func__);
return -EINVAL;
}
if (iram_size < TEGRA_AES_DMA_BUFFER_SIZE * AES_BLOCK_LENGTH) {
log_debug("%s: Unsupported iram buffer size: 0x%x required: 0x%x\n",
__func__, iram_size, TEGRA_AES_DMA_BUFFER_SIZE);
return -EINVAL;
}
/* Make sure the IRAM address is kept block aligned and accessible for slot loading */
value = (uint32_t)priv->iram_addr;
if ((value & 0xFFF0000F) != IRAM_BASE || value > TEGRA_AES_IRAM_MAX_ADDR) {
log_debug("%s: iram buffer must be located inside iram,", __func__);
log_debug("AES block aligned and not above 0x%08x, current addr %p\n",
(u32)TEGRA_AES_IRAM_MAX_ADDR, priv->iram_addr);
return -EINVAL;
}
ret = reset_get_by_name(dev, NULL, &priv->reset_ctl);
if (ret) {
log_debug("%s: failed to get BSE reset: %d\n", __func__, ret);
return ret;
}
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
log_err("%s: failed to get BSE clock: %d\n", __func__, ret);
return ret;
}
/* VDE clock and reset required by BSEV */
ret = reset_get_by_name(dev, "vde", &priv->reset_ctl_vde);
if (ret)
log_debug("%s: failed to get VDE reset: %d\n", __func__, ret);
priv->clk_parent = devm_clk_get(dev, "vde");
if (IS_ERR(priv->clk_parent))
log_debug("%s: failed to get BSE clock: %d\n", __func__, ret);
return tegra_aes_hw_init(dev);
}
static const struct aes_ops tegra_aes_ops = {
.available_key_slots = tegra_aes_ops_available_key_slots,
.select_key_slot = tegra_aes_ops_select_key_slot,
.set_key_for_key_slot = tegra_aes_ops_set_key_for_key_slot,
.aes_ecb_encrypt = tegra_aes_ops_aes_ecb_encrypt,
.aes_ecb_decrypt = tegra_aes_ops_aes_ecb_decrypt,
.aes_cbc_encrypt = tegra_aes_ops_aes_cbc_encrypt,
.aes_cbc_decrypt = tegra_aes_ops_aes_cbc_decrypt,
};
static const struct udevice_id tegra_aes_ids[] = {
{ .compatible = "nvidia,tegra20-bsea" },
{ .compatible = "nvidia,tegra20-bsev" },
{ .compatible = "nvidia,tegra30-bsea" },
{ .compatible = "nvidia,tegra30-bsev" },
{ }
};
U_BOOT_DRIVER(tegra_aes) = {
.name = "tegra_aes",
.id = UCLASS_AES,
.of_match = tegra_aes_ids,
.probe = tegra_aes_probe,
.ops = &tegra_aes_ops,
.priv_auto = sizeof(struct tegra_aes_priv),
};

View File

@ -691,6 +691,23 @@ config VIDEO_LCD_HITACHI_TX18D42VM
lcd controller which needs to be initialized over SPI, once that is
done they work like a regular LVDS panel.
config VIDEO_LCD_SONY_L4F00430T01
tristate "Sony L4F00430T01 480x800 LCD panel support"
depends on PANEL
help
Say Y here if you want to enable support for Sony L4F00430T01
LCD module found in Samsung Galaxy R. The panel has a
WVGA resolution (480x800) and is setup over SPI, video
data comes from RGB.
config VIDEO_LCD_SAMSUNG_S6E63M0
tristate "Samsung S6E63M0 controller based panel support"
depends on PANEL && BACKLIGHT
help
Say Y here if you want to enable support for Samsung S6E63M0
controller found in some panels like on Samsung Captivate Glide.
Currently only DBI C panel is implemented.
config VIDEO_LCD_SPI_CS
string "SPI CS pin for LCD related config job"
depends on VIDEO_LCD_SSD2828 || VIDEO_LCD_HITACHI_TX18D42VM
@ -821,6 +838,13 @@ config BACKLIGHT_LP855x
supported for now, PWM mode can be added if there will be any need in
it. Supported backlight level range is from 0 to 255 with step of 1.
config BACKLIGHT_SAMSUNG_CMC623
bool "Backlight Driver for Samsung CMC623"
depends on VIDEO_BRIDGE_SAMSUNG_CMC623
help
Say Y to enable the backlight driver for Samsung CMC623 image converter
chip's PWM output to control backlight brightness.
source "drivers/video/ti/Kconfig"
source "drivers/video/exynos/Kconfig"

View File

@ -33,6 +33,7 @@ obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_backlight.o
obj-$(CONFIG_BACKLIGHT_LM3532) += lm3532_backlight.o
obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o
obj-$(CONFIG_BACKLIGHT_LP855x) += lp855x_backlight.o
obj-$(CONFIG_BACKLIGHT_SAMSUNG_CMC623) += cmc623_backlight.o
obj-${CONFIG_EXYNOS_FB} += exynos/
obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
obj-${CONFIG_VIDEO_STM32} += stm32/
@ -74,6 +75,8 @@ obj-$(CONFIG_VIDEO_LCD_SHARP_LQ079L1SX01) += sharp-lq079l1sx01.o
obj-$(CONFIG_VIDEO_LCD_SHARP_LQ101R1SX01) += sharp-lq101r1sx01.o
obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o
obj-$(CONFIG_VIDEO_LCD_SONY_L4F00430T01) += sony-l4f00430t01.o
obj-$(CONFIG_VIDEO_LCD_SAMSUNG_S6E63M0) += samsung-s6e63m0.o
obj-$(CONFIG_VIDEO_MCDE_SIMPLE) += mcde_simple.o
obj-${CONFIG_VIDEO_MESON} += meson/
obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o

View File

@ -66,3 +66,11 @@ config VIDEO_BRIDGE_LVDS_CODEC
help
Support for transparent LVDS encoders and decoders that don't
require any configuration.
config VIDEO_BRIDGE_SAMSUNG_CMC623
bool "Samsung CMC623 Image Converter driver"
depends on VIDEO_BRIDGE && DM_GPIO
select DM_I2C
help
Samsung CMC623 image converter chip driver.
Found in several Samsung devices such as N1

View File

@ -11,3 +11,4 @@ obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o
obj-$(CONFIG_VIDEO_BRIDGE_SOLOMON_SSD2825) += ssd2825.o
obj-$(CONFIG_VIDEO_BRIDGE_TOSHIBA_TC358768) += tc358768.o
obj-$(CONFIG_VIDEO_BRIDGE_LVDS_CODEC) += lvds-codec.o
obj-$(CONFIG_VIDEO_BRIDGE_SAMSUNG_CMC623) += cmc623.o

View File

@ -0,0 +1,234 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2025 Ion Agorria <ion@agorria.com>
*/
#include <clk.h>
#include <dm.h>
#include <dm/ofnode_graph.h>
#include <i2c.h>
#include <log.h>
#include <backlight.h>
#include <panel.h>
#include <video_bridge.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <power/regulator.h>
#include <asm/gpio.h>
static const char * const cmc623_supplies[] = {
"vdd3v0-supply", "vdd1v2-supply", "vddio1v8-supply"
};
struct cmc623_priv {
struct udevice *panel;
struct display_timing timing;
struct udevice *supplies[ARRAY_SIZE(cmc623_supplies)];
struct gpio_desc enable_gpio; /* also known as FAILSAFE */
struct gpio_desc bypass_gpio;
};
static int cmc623_attach(struct udevice *dev)
{
struct cmc623_priv *priv = dev_get_priv(dev);
int ret;
/* Perform panel setup */
ret = panel_enable_backlight(priv->panel);
if (ret)
return ret;
return 0;
}
static int cmc623_set_backlight(struct udevice *dev, int percent)
{
struct cmc623_priv *priv = dev_get_priv(dev);
return panel_set_backlight(priv->panel, percent);
}
static int cmc623_panel_timings(struct udevice *dev, struct display_timing *timing)
{
struct cmc623_priv *priv = dev_get_priv(dev);
memcpy(timing, &priv->timing, sizeof(*timing));
return 0;
}
static int cmc623_hw_init(struct udevice *dev)
{
struct cmc623_priv *priv = dev_get_priv(dev);
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
int i, ret;
/* enable supplies */
for (i = 0; i < ARRAY_SIZE(cmc623_supplies); i++) {
ret = regulator_set_enable_if_allowed(priv->supplies[i], 1);
if (ret) {
log_debug("%s: cannot enable %s %d\n", __func__,
cmc623_supplies[i], ret);
return ret;
}
}
mdelay(10);
ret = dm_gpio_set_value(&uc_priv->reset, 1);
if (ret) {
log_debug("%s: error at reset = 1 (%d)\n", __func__, ret);
return ret;
}
ret = dm_gpio_set_value(&priv->enable_gpio, 0);
if (ret) {
log_debug("%s: error at enable = 0 (%d)\n", __func__, ret);
return ret;
}
ret = dm_gpio_set_value(&priv->bypass_gpio, 0);
if (ret) {
log_debug("%s: error at bypass = 0 (%d)\n", __func__, ret);
return ret;
}
ret = dm_gpio_set_value(&uc_priv->sleep, 0);
if (ret) {
log_debug("%s: error at sleep = 0 (%d)\n", __func__, ret);
return ret;
}
udelay(2000);
ret = dm_gpio_set_value(&priv->enable_gpio, 1);
if (ret) {
log_debug("%s: error at enable = 1 (%d)\n", __func__, ret);
return ret;
}
udelay(2000);
ret = dm_gpio_set_value(&priv->bypass_gpio, 1);
if (ret) {
log_debug("%s: error at bypass = 1 (%d)\n", __func__, ret);
return ret;
}
udelay(2000);
ret = dm_gpio_set_value(&uc_priv->sleep, 1);
if (ret) {
log_debug("%s: error at sleep = 1 (%d)\n", __func__, ret);
return ret;
}
udelay(2000);
ret = dm_gpio_set_value(&uc_priv->reset, 0);
if (ret) {
log_debug("%s: error at sleep = 0 (%d)\n", __func__, ret);
return ret;
}
mdelay(10);
ret = dm_gpio_set_value(&uc_priv->reset, 1);
if (ret) {
log_debug("%s: error at sleep = 1 (%d)\n", __func__, ret);
return ret;
}
mdelay(10);
return 0;
}
static int cmc623_get_panel(struct udevice *dev)
{
struct cmc623_priv *priv = dev_get_priv(dev);
int i, ret;
u32 num = ofnode_graph_get_port_count(dev_ofnode(dev));
for (i = 0; i < num; i++) {
ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1);
ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel);
if (!ret)
return 0;
}
/* If this point is reached, no panels were found */
return -ENODEV;
}
static int cmc623_probe(struct udevice *dev)
{
struct cmc623_priv *priv = dev_get_priv(dev);
int i, ret;
/* get supplies */
for (i = 0; i < ARRAY_SIZE(cmc623_supplies); i++) {
ret = device_get_supply_regulator(dev, cmc623_supplies[i], &priv->supplies[i]);
if (ret) {
log_debug("%s: cannot get %s %d\n", __func__, cmc623_supplies[i], ret);
if (ret != -ENOENT)
return log_ret(ret);
}
}
/* get control gpios */
ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable_gpio, GPIOD_IS_OUT);
if (ret) {
log_debug("%s: could not get enable-gpios (%d)\n", __func__, ret);
return ret;
}
ret = gpio_request_by_name(dev, "bypass-gpios", 0, &priv->bypass_gpio, GPIOD_IS_OUT);
if (ret) {
log_debug("%s: could not get bypass-gpios (%d)\n", __func__, ret);
return ret;
}
ret = cmc623_hw_init(dev);
if (ret) {
log_debug("%s: error doing hw init, ret %d\n", __func__, ret);
return ret;
}
ret = cmc623_get_panel(dev);
if (ret) {
log_debug("%s: panel not found, ret %d\n", __func__, ret);
return ret;
}
panel_get_display_timing(priv->panel, &priv->timing);
return 0;
}
static const struct video_bridge_ops cmc623_ops = {
.attach = cmc623_attach,
.set_backlight = cmc623_set_backlight,
.get_display_timing = cmc623_panel_timings,
};
static const struct udevice_id cmc623_ids[] = {
{ .compatible = "samsung,cmc623" },
{ }
};
U_BOOT_DRIVER(samsung_cmc623) = {
.name = "samsung_cmc623",
.id = UCLASS_VIDEO_BRIDGE,
.of_match = cmc623_ids,
.ops = &cmc623_ops,
.bind = dm_scan_fdt_dev,
.probe = cmc623_probe,
.priv_auto = sizeof(struct cmc623_priv),
};

View File

@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2025 Ion Agorria <ion@agorria.com>
*/
#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
#include <backlight.h>
#include <dm.h>
#include <i2c.h>
#include <log.h>
#include <linux/err.h>
#include <asm/gpio.h>
#include <power/regulator.h>
#define CMC623_I2C_REG_SELBANK 0x00
#define CMC623_I2C_REG_PWMCTRL 0xb4
#define CMC623_I2C_REG_REGMASK 0x28
#define CMC623_BL_MIN_BRIGHTNESS 0
#define CMC623_BL_DEF_BRIGHTNESS 50
#define CMC623_BL_MAX_BRIGHTNESS 100
struct cmc623_backlight_priv {
struct gpio_desc enable_gpio;
};
static int cmc623_backlight_enable(struct udevice *dev)
{
struct cmc623_backlight_priv *priv = dev_get_priv(dev);
if (dm_gpio_is_valid(&priv->enable_gpio))
dm_gpio_set_value(&priv->enable_gpio, 1);
return 0;
}
static int cmc623_i2c_write(struct udevice *dev, u8 reg, u16 value)
{
u8 data[2];
data[0] = (value >> 8) & 0xff;
data[1] = value & 0xff;
return dm_i2c_write(dev->parent, reg, data, 2);
}
static int cmc623_backlight_set_brightness(struct udevice *dev, int percent)
{
int ret;
u16 brightness;
if (percent == BACKLIGHT_DEFAULT)
percent = CMC623_BL_DEF_BRIGHTNESS;
if (percent < CMC623_BL_MIN_BRIGHTNESS)
percent = CMC623_BL_MIN_BRIGHTNESS;
if (percent > CMC623_BL_MAX_BRIGHTNESS)
percent = CMC623_BL_MAX_BRIGHTNESS;
brightness = 0x4000 | (percent << 4);
ret = cmc623_i2c_write(dev, CMC623_I2C_REG_SELBANK, 0x0000);
if (ret) {
log_debug("%s: error at CMC623_I2C_REG_SELBANK (%d)\n",
__func__, ret);
return ret;
}
ret = cmc623_i2c_write(dev, CMC623_I2C_REG_PWMCTRL, brightness);
if (ret) {
log_debug("%s: error at CMC623_I2C_REG_PWMCTRL (%d)\n",
__func__, ret);
return ret;
}
ret = cmc623_i2c_write(dev, CMC623_I2C_REG_REGMASK, 0x0000);
if (ret) {
log_debug("%s: error at CMC623_I2C_REG_REGMASK (%d)\n",
__func__, ret);
return ret;
}
return 0;
}
static int cmc623_backlight_of_to_plat(struct udevice *dev)
{
struct cmc623_backlight_priv *priv = dev_get_priv(dev);
gpio_request_by_name(dev, "enable-gpios", 0,
&priv->enable_gpio, GPIOD_IS_OUT);
return 0;
}
static int cmc623_backlight_probe(struct udevice *dev)
{
if (device_get_uclass_id(dev->parent) != UCLASS_VIDEO_BRIDGE)
return -EPROTONOSUPPORT;
return 0;
}
static const struct backlight_ops cmc623_backlight_ops = {
.enable = cmc623_backlight_enable,
.set_brightness = cmc623_backlight_set_brightness,
};
static const struct udevice_id cmc623_backlight_ids[] = {
{ .compatible = "samsung,cmc623-backlight" },
{ }
};
U_BOOT_DRIVER(cmc623_backlight) = {
.name = "cmc623_backlight",
.id = UCLASS_PANEL_BACKLIGHT,
.of_match = cmc623_backlight_ids,
.of_to_plat = cmc623_backlight_of_to_plat,
.probe = cmc623_backlight_probe,
.ops = &cmc623_backlight_ops,
.priv_auto = sizeof(struct cmc623_backlight_priv),
};

View File

@ -0,0 +1,393 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2025 Ion Agorria <ion@agorria.com>
*/
#include <backlight.h>
#include <dm.h>
#include <panel.h>
#include <log.h>
#include <malloc.h>
#include <spi.h>
#include <mipi_display.h>
#include <linux/delay.h>
#include <power/regulator.h>
#include <asm/gpio.h>
#define S6E63M0_DCS_CMD 0
#define S6E63M0_DCS_DATA 1
#define S6E63M0_INFO_FLAG_PGAMMACTL BIT(0)
#define S6E63M0_INFO_FLAG_GAMMA_DELTA BIT(1)
#define S6E63M0_GTCON_FLAG_FLIP_H BIT(0)
#define S6E63M0_GTCON_FLAG_FLIP_V BIT(1)
/* Manufacturer Command Set */
#define MCS_PENTILE_1 0xb3
#define MCS_GAMMA_DELTA_Y_RED 0xb5
#define MCS_GAMMA_DELTA_X_RED 0xb6
#define MCS_GAMMA_DELTA_Y_GREEN 0xb7
#define MCS_GAMMA_DELTA_X_GREEN 0xb8
#define MCS_GAMMA_DELTA_Y_BLUE 0xb9
#define MCS_GAMMA_DELTA_X_BLUE 0xba
#define MCS_DISCTL 0xf2
#define MCS_SRCCTL 0xf6
#define MCS_IFCTL 0xf7
#define MCS_PANELCTL 0xf8
#define MCS_PGAMMACTL 0xfa
#define MCS_PANELCTL_LEN 14
#define MCS_IFCTL_LEN 3
#define MCS_PGAMMACTL_LEN 22
#define MCS_GAMMA_DELTA_Y_LEN 32
#define MCS_GAMMA_DELTA_X_LEN 16
struct s6e63m0_priv {
struct udevice *vdd3;
struct udevice *vci;
struct s6e63m0_info *info;
struct gpio_desc reset_gpio;
u8 gtcon;
};
struct s6e63m0_info {
const u32 flags;
struct display_timing timing;
const u8 cmd_mcs_panelctl[MCS_PANELCTL_LEN];
const u8 cmd_mcs_pgammactl_1[MCS_PGAMMACTL_LEN];
const u8 cmd_mcs_pgammactl_2;
const u8 cmd_mcs_gamma_delta_y[MCS_GAMMA_DELTA_Y_LEN];
const u8 cmd_mcs_gamma_delta_x[MCS_GAMMA_DELTA_X_LEN];
};
static const struct s6e63m0_info s6e63m0_generic_info = {
.flags = S6E63M0_INFO_FLAG_GAMMA_DELTA,
.timing = {
.pixelclock.typ = 25628,
.hactive.typ = 480,
.hfront_porch.typ = 16,
.hback_porch.typ = 16,
.hsync_len.typ = 2,
.vactive.typ = 800,
.vfront_porch.typ = 28,
.vback_porch.typ = 1,
.vsync_len.typ = 2,
.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
},
.cmd_mcs_panelctl = {
0x01, /* DOCT */ 0x27, /* CLWEA */
0x27, /* CLWEB*/ 0x07, /* CLTE */
0x07, /* SHE */ 0x54, /* FLTE */
0x9F, /* FLWE */ 0x63, /* SCTE */
0x8F, /* SCWE */ 0x1A, /* INTE */
0x33, /* INWE */ 0x0D, /* EMPS */
0x00, /* E_INTE */ 0x00 /* E_INWE */
},
.cmd_mcs_pgammactl_1 = {
0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xb6,
0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1, 0xc1, 0xb7,
0x00, 0x9c, 0x00, 0x9f, 0x00, 0xd6
},
.cmd_mcs_pgammactl_2 = 0x01,
.cmd_mcs_gamma_delta_y = {
0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 0x13,
0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 0x1a, 0x17,
0x2b, 0x26, 0x22, 0x20, 0x3a, 0x34, 0x30, 0x2c,
0x29, 0x26, 0x25, 0x23, 0x21, 0x20, 0x1e, 0x1e
},
.cmd_mcs_gamma_delta_x = {
0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 0x44,
0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66
},
};
static const struct s6e63m0_info samsung_bose_panel_info = {
.flags = S6E63M0_INFO_FLAG_PGAMMACTL | S6E63M0_INFO_FLAG_GAMMA_DELTA,
.timing = {
.pixelclock.typ = 24000000,
.hactive.typ = 480,
.hfront_porch.typ = 16,
.hback_porch.typ = 14,
.hsync_len.typ = 2,
.vactive.typ = 800,
.vfront_porch.typ = 28,
.vback_porch.typ = 1,
.vsync_len.typ = 2,
.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
DISPLAY_FLAGS_DE_LOW | DISPLAY_FLAGS_PIXDATA_NEGEDGE,
},
.cmd_mcs_panelctl = {
0x01, /* DOCT */ 0x27, /* CLWEA */
0x27, /* CLWEB*/ 0x07, /* CLTE */
0x07, /* SHE */ 0x54, /* FLTE */
0x9F, /* FLWE */ 0x63, /* SCTE */
0x86, /* SCWE */ 0x1A, /* INTE */
0x33, /* INWE */ 0x0D, /* EMPS */
0x00, /* E_INTE */ 0x00 /* E_INWE */
},
.cmd_mcs_pgammactl_1 = {
0x02, 0x18, 0x08, 0x24, 0x70, 0x6e, 0x4e, 0xbc,
0xc0, 0xaf, 0xb3, 0xb8, 0xa5, 0xc5, 0xc7, 0xbb,
0x00, 0xb9, 0x00, 0xb8, 0x00, 0xfc
},
.cmd_mcs_pgammactl_2 = 0x03,
.cmd_mcs_gamma_delta_y = {
0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17, 0x13,
0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b, 0x1a, 0x17,
0x2b, 0x26, 0x22, 0x20, 0x3a, 0x34, 0x30, 0x2c,
0x29, 0x26, 0x25, 0x23, 0x21, 0x20, 0x1e, 0x1e
},
.cmd_mcs_gamma_delta_x = {
0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44, 0x44,
0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66
},
};
static int s6e63m0_dcs_write(struct udevice *dev, u8 cmd, const u8 *seq, size_t len)
{
int ret;
u8 data[2];
int i;
data[0] = S6E63M0_DCS_CMD;
data[1] = cmd;
ret = dm_spi_xfer(dev, 9, &data, NULL, SPI_XFER_ONCE);
if (ret)
return ret;
for (i = 0; i < len; i++) {
data[0] = S6E63M0_DCS_DATA;
data[1] = seq[i];
ret = dm_spi_xfer(dev, 9, &data, NULL, SPI_XFER_ONCE);
if (ret)
return ret;
}
return 0;
}
#define s6e63m0_dcs_write_seq_static(dev, cmd, seq ...) ({ \
static const u8 d[] = { seq }; \
ret = s6e63m0_dcs_write(dev, cmd, d, ARRAY_SIZE(d)); \
if (ret) \
return ret; \
})
static int s6e63m0_enable_backlight(struct udevice *dev)
{
struct s6e63m0_priv *priv = dev_get_priv(dev);
struct s6e63m0_info *info = priv->info;
u8 cmd_mcs_ifctl[MCS_IFCTL_LEN];
int ret;
ret = s6e63m0_dcs_write(dev, MCS_PANELCTL, info->cmd_mcs_panelctl, MCS_PANELCTL_LEN);
if (ret)
return ret;
s6e63m0_dcs_write_seq_static(dev, MCS_DISCTL,
0x02, /* Number of Line */
0x03, /* VBP */
0x1c, /* VFP */
0x10, /* HBP */
0x10); /* HFP */
cmd_mcs_ifctl[0] = priv->gtcon; /* GTCON */
cmd_mcs_ifctl[1] = 0x00; /* Display Mode */
cmd_mcs_ifctl[2] = 0x00; /* Vsync/Hsync, DOCCLK, RGB mode */
ret = s6e63m0_dcs_write(dev, MCS_IFCTL, cmd_mcs_ifctl, MCS_IFCTL_LEN);
if (ret)
return ret;
if (info->flags & S6E63M0_INFO_FLAG_PGAMMACTL) {
ret = s6e63m0_dcs_write(dev, MCS_PGAMMACTL, info->cmd_mcs_pgammactl_1,
MCS_PGAMMACTL_LEN);
if (ret)
return ret;
s6e63m0_dcs_write(dev, MCS_PGAMMACTL, &info->cmd_mcs_pgammactl_2, 1);
}
s6e63m0_dcs_write_seq_static(dev, MCS_SRCCTL, 0x00, 0x8e, 0x07);
s6e63m0_dcs_write_seq_static(dev, MCS_PENTILE_1, 0x6c);
if (info->flags & S6E63M0_INFO_FLAG_GAMMA_DELTA) {
ret = s6e63m0_dcs_write(dev, MCS_GAMMA_DELTA_Y_RED, info->cmd_mcs_gamma_delta_y,
MCS_GAMMA_DELTA_Y_LEN);
if (ret)
return ret;
ret = s6e63m0_dcs_write(dev, MCS_GAMMA_DELTA_X_RED, info->cmd_mcs_gamma_delta_x,
MCS_GAMMA_DELTA_X_LEN);
if (ret)
return ret;
ret = s6e63m0_dcs_write(dev, MCS_GAMMA_DELTA_Y_GREEN, info->cmd_mcs_gamma_delta_y,
MCS_GAMMA_DELTA_Y_LEN);
if (ret)
return ret;
ret = s6e63m0_dcs_write(dev, MCS_GAMMA_DELTA_X_GREEN, info->cmd_mcs_gamma_delta_x,
MCS_GAMMA_DELTA_X_LEN);
if (ret)
return ret;
ret = s6e63m0_dcs_write(dev, MCS_GAMMA_DELTA_Y_BLUE, info->cmd_mcs_gamma_delta_y,
MCS_GAMMA_DELTA_Y_LEN);
if (ret)
return ret;
ret = s6e63m0_dcs_write(dev, MCS_GAMMA_DELTA_X_BLUE, info->cmd_mcs_gamma_delta_x,
MCS_GAMMA_DELTA_X_LEN);
if (ret)
return ret;
}
s6e63m0_dcs_write_seq_static(dev, MIPI_DCS_EXIT_SLEEP_MODE);
s6e63m0_dcs_write_seq_static(dev, MIPI_DCS_SET_DISPLAY_ON);
return 0;
}
static int s6e63m0_set_backlight(struct udevice *dev, int percent)
{
return 0;
}
static int s6e63m0_get_display_timing(struct udevice *dev, struct display_timing *timing)
{
struct s6e63m0_priv *priv = dev_get_priv(dev);
memcpy(timing, &priv->info->timing, sizeof(*timing));
return 0;
}
static int s6e63m0_of_to_plat(struct udevice *dev)
{
struct s6e63m0_priv *priv = dev_get_priv(dev);
int ret;
ret = device_get_supply_regulator(dev, "vdd3-supply", &priv->vdd3);
if (ret) {
log_debug("%s: cannot get vdd3-supply: ret = %d\n",
__func__, ret);
return ret;
}
ret = device_get_supply_regulator(dev, "vci-supply", &priv->vci);
if (ret) {
log_debug("%s: cannot get vci-supply: ret = %d\n",
__func__, ret);
return ret;
}
ret = gpio_request_by_name(dev, "reset-gpios", 0,
&priv->reset_gpio, GPIOD_IS_OUT);
if (ret) {
log_debug("%s: cannot decode reset-gpios (%d)\n",
__func__, ret);
return ret;
}
if (dev_read_bool(dev, "flip-horizontal"))
priv->gtcon |= S6E63M0_GTCON_FLAG_FLIP_H;
if (dev_read_bool(dev, "flip-vertical"))
priv->gtcon |= S6E63M0_GTCON_FLAG_FLIP_V;
return 0;
}
static int s6e63m0_hw_init(struct udevice *dev)
{
struct s6e63m0_priv *priv = dev_get_priv(dev);
int ret;
ret = dm_gpio_set_value(&priv->reset_gpio, 1);
if (ret) {
log_debug("%s: error entering reset (%d)\n", __func__, ret);
return ret;
}
ret = regulator_set_enable_if_allowed(priv->vdd3, 1);
if (ret) {
log_debug("%s: enabling vdd3-supply failed (%d)\n",
__func__, ret);
return ret;
}
mdelay(1);
ret = regulator_set_enable_if_allowed(priv->vci, 1);
if (ret) {
log_debug("%s: enabling vci-supply failed (%d)\n",
__func__, ret);
return ret;
}
mdelay(26);
ret = dm_gpio_set_value(&priv->reset_gpio, 0);
if (ret) {
log_debug("%s: error exiting reset (%d)\n", __func__, ret);
return ret;
}
mdelay(10);
return 0;
}
static int s6e63m0_probe(struct udevice *dev)
{
struct s6e63m0_priv *priv = dev_get_priv(dev);
struct spi_slave *slave = dev_get_parent_priv(dev);
int ret;
if (device_get_uclass_id(dev->parent) != UCLASS_SPI)
return -EPROTONOSUPPORT;
ret = spi_claim_bus(slave);
if (ret) {
log_err("SPI bus allocation failed (%d)\n", ret);
return ret;
}
priv->info = (struct s6e63m0_info *)dev_get_driver_data(dev);
return s6e63m0_hw_init(dev);
}
static const struct panel_ops s6e63m0_ops = {
.enable_backlight = s6e63m0_enable_backlight,
.set_backlight = s6e63m0_set_backlight,
.get_display_timing = s6e63m0_get_display_timing,
};
static const struct udevice_id s6e63m0_ids[] = {
{
.compatible = "samsung,s6e63m0",
.data = (ulong)&s6e63m0_generic_info
},
{
.compatible = "samsung,bose-panel",
.data = (ulong)&samsung_bose_panel_info
},
{ }
};
U_BOOT_DRIVER(samsung_s6e63m0) = {
.name = "samsung_s6e63m0",
.id = UCLASS_PANEL,
.of_match = s6e63m0_ids,
.ops = &s6e63m0_ops,
.of_to_plat = s6e63m0_of_to_plat,
.probe = s6e63m0_probe,
.priv_auto = sizeof(struct s6e63m0_priv),
};

View File

@ -0,0 +1,210 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2025 Ion Agorria <ion@agorria.com>
*/
#include <backlight.h>
#include <dm.h>
#include <panel.h>
#include <log.h>
#include <spi.h>
#include <mipi_display.h>
#include <linux/delay.h>
#include <power/regulator.h>
#include <asm/gpio.h>
#define SONY_L4F00430T01_DCS_CMD 0
#define SONY_L4F00430T01_DCS_DATA 1
struct sony_l4f00430t01_priv {
struct udevice *vdd1v8;
struct udevice *vdd3v0;
struct udevice *backlight;
struct gpio_desc reset_gpio;
};
static struct display_timing default_timing = {
.pixelclock.typ = 24000000,
.hactive.typ = 480,
.hfront_porch.typ = 10,
.hback_porch.typ = 20,
.hsync_len.typ = 10,
.vactive.typ = 800,
.vfront_porch.typ = 3,
.vback_porch.typ = 4,
.vsync_len.typ = 2,
.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_NEGEDGE,
};
static int sony_l4f00430t01_write(struct udevice *dev, u8 cmd, const u8 *seq, int len)
{
u8 data[2];
int i, ret;
data[0] = SONY_L4F00430T01_DCS_CMD;
data[1] = cmd;
ret = dm_spi_xfer(dev, 9, &data, NULL, SPI_XFER_ONCE);
if (ret)
return ret;
for (i = 0; i < len; i++) {
data[0] = SONY_L4F00430T01_DCS_DATA;
data[1] = seq[i];
ret = dm_spi_xfer(dev, 9, &data, NULL, SPI_XFER_ONCE);
if (ret)
return ret;
}
return 0;
}
#define sony_l4f00430t01_write_seq(dev, cmd, seq...) do { \
static const u8 b[] = { seq }; \
sony_l4f00430t01_write(dev, cmd, b, ARRAY_SIZE(b)); \
} while (0)
static int sony_l4f00430t01_enable_backlight(struct udevice *dev)
{
sony_l4f00430t01_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0xd4);
mdelay(25);
sony_l4f00430t01_write_seq(dev, MIPI_DCS_EXIT_SLEEP_MODE);
mdelay(150);
sony_l4f00430t01_write_seq(dev, MIPI_DCS_SET_DISPLAY_ON);
return 0;
}
static int sony_l4f00430t01_set_backlight(struct udevice *dev, int percent)
{
struct sony_l4f00430t01_priv *priv = dev_get_priv(dev);
int ret;
ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
"backlight", &priv->backlight);
if (ret) {
log_debug("%s: cannot get backlight: ret = %d\n",
__func__, ret);
return ret;
}
ret = backlight_enable(priv->backlight);
if (ret)
return ret;
return backlight_set_brightness(priv->backlight, percent);
}
static int sony_l4f00430t01_get_display_timing(struct udevice *dev,
struct display_timing *timing)
{
memcpy(timing, &default_timing, sizeof(*timing));
return 0;
}
static int sony_l4f00430t01_of_to_plat(struct udevice *dev)
{
struct sony_l4f00430t01_priv *priv = dev_get_priv(dev);
int ret;
ret = device_get_supply_regulator(dev, "vdd1v8-supply", &priv->vdd1v8);
if (ret) {
log_debug("%s: cannot get vdd1v8-supply: ret = %d\n",
__func__, ret);
return ret;
}
ret = device_get_supply_regulator(dev, "vdd3v0-supply", &priv->vdd3v0);
if (ret) {
log_debug("%s: cannot get vdd3v0-supply: ret = %d\n",
__func__, ret);
return ret;
}
ret = gpio_request_by_name(dev, "reset-gpios", 0,
&priv->reset_gpio, GPIOD_IS_OUT);
if (ret) {
log_debug("%s: cannot decode reset-gpios (%d)\n",
__func__, ret);
return ret;
}
return 0;
}
static int sony_l4f00430t01_hw_init(struct udevice *dev)
{
struct sony_l4f00430t01_priv *priv = dev_get_priv(dev);
int ret;
ret = dm_gpio_set_value(&priv->reset_gpio, 1);
if (ret) {
log_debug("%s: error entering reset (%d)\n", __func__, ret);
return ret;
}
ret = regulator_set_enable_if_allowed(priv->vdd1v8, 1);
if (ret) {
log_debug("%s: enabling vdd1v8-supply failed (%d)\n",
__func__, ret);
return ret;
}
ret = regulator_set_enable_if_allowed(priv->vdd3v0, 1);
if (ret) {
log_debug("%s: enabling vdd3v0-supply failed (%d)\n",
__func__, ret);
return ret;
}
mdelay(15);
ret = dm_gpio_set_value(&priv->reset_gpio, 0);
if (ret) {
log_debug("%s: error exiting reset (%d)\n", __func__, ret);
return ret;
}
mdelay(100);
return 0;
}
static int sony_l4f00430t01_probe(struct udevice *dev)
{
struct spi_slave *slave = dev_get_parent_priv(dev);
int ret;
ret = spi_claim_bus(slave);
if (ret) {
log_err("SPI bus allocation failed (%d)\n", ret);
return ret;
}
return sony_l4f00430t01_hw_init(dev);
}
static const struct panel_ops sony_l4f00430t01_ops = {
.enable_backlight = sony_l4f00430t01_enable_backlight,
.set_backlight = sony_l4f00430t01_set_backlight,
.get_display_timing = sony_l4f00430t01_get_display_timing,
};
static const struct udevice_id sony_l4f00430t01_ids[] = {
{ .compatible = "sony,l4f00430t01" },
{ }
};
U_BOOT_DRIVER(sony_l4f00430t01) = {
.name = "sony_l4f00430t01",
.id = UCLASS_PANEL,
.of_match = sony_l4f00430t01_ids,
.ops = &sony_l4f00430t01_ops,
.of_to_plat = sony_l4f00430t01_of_to_plat,
.probe = sony_l4f00430t01_probe,
.priv_auto = sizeof(struct sony_l4f00430t01_priv),
};

View File

@ -238,8 +238,24 @@ static void rgb_enable(struct tegra_lcd_priv *priv)
else
value &= ~LVS_OUTPUT_POLARITY_LOW;
/* configure pixel data signal polarity */
if (dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
value &= ~LSC0_OUTPUT_POLARITY_LOW;
else
value |= LSC0_OUTPUT_POLARITY_LOW;
writel(value, &com->pin_output_polarity[1]);
/* configure data enable signal polarity */
value = readl(&com->pin_output_polarity[3]);
if (dt->flags & DISPLAY_FLAGS_DE_LOW)
value |= LSPI_OUTPUT_POLARITY_LOW;
else
value &= ~LSPI_OUTPUT_POLARITY_LOW;
writel(value, &com->pin_output_polarity[3]);
for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
writel(rgb_sel_tab[i], &com->pin_output_sel[i]);
}