summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2016-02-19 14:25:09 (GMT)
committerTom Rini <trini@konsulko.com>2016-02-19 14:25:09 (GMT)
commitcf432dd5953e9ddf06759565c80fed470882da6d (patch)
treef0b5ee32047f37eb87e6e5ba537a50fb0babc1cb /drivers
parenta8c3eca43393cffef16a40e683f7a4d45b37e6ed (diff)
parent5589bc2770a7d7fe346f5972596f26d1ddf57bba (diff)
downloadu-boot-cf432dd5953e9ddf06759565c80fed470882da6d.tar.xz
Merge branch 'master' of git://git.denx.de/u-boot-tegra
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/tegra_gpio.c5
-rw-r--r--drivers/input/tegra-kbc.c2
-rw-r--r--drivers/mmc/tegra_mmc.c4
-rw-r--r--drivers/pwm/Kconfig9
-rw-r--r--drivers/pwm/Makefile3
-rw-r--r--drivers/pwm/tegra_pwm.c85
-rw-r--r--drivers/video/Kconfig10
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/simple_panel.c1
-rw-r--r--drivers/video/tegra.c658
-rw-r--r--drivers/video/tegra124/Makefile1
-rw-r--r--drivers/video/tegra124/display.c184
-rw-r--r--drivers/video/tegra124/dp.c53
-rw-r--r--drivers/video/tegra124/sor.c162
-rw-r--r--drivers/video/tegra124/sor.h45
-rw-r--r--drivers/video/tegra124/tegra124-lcd.c95
-rw-r--r--drivers/video/vidconsole-uclass.c1
17 files changed, 908 insertions, 412 deletions
diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c
index 8e880e2..5a03115 100644
--- a/drivers/gpio/tegra_gpio.c
+++ b/drivers/gpio/tegra_gpio.c
@@ -177,7 +177,10 @@ static int tegra_gpio_get_value(struct udevice *dev, unsigned offset)
debug("%s: pin = %d (port %d:bit %d)\n", __func__,
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
- val = readl(&state->bank->gpio_in[GPIO_PORT(gpio)]);
+ if (get_direction(gpio) == DIRECTION_INPUT)
+ val = readl(&state->bank->gpio_in[GPIO_PORT(gpio)]);
+ else
+ val = readl(&state->bank->gpio_out[GPIO_PORT(gpio)]);
return (val >> GPIO_BIT(gpio)) & 1;
}
diff --git a/drivers/input/tegra-kbc.c b/drivers/input/tegra-kbc.c
index 951cbb4..c77f610 100644
--- a/drivers/input/tegra-kbc.c
+++ b/drivers/input/tegra-kbc.c
@@ -312,6 +312,7 @@ static int tegra_kbd_probe(struct udevice *dev)
__func__, ret);
return ret;
}
+ input_add_tables(input, false);
if (priv->matrix.fn_keycode) {
ret = input_add_table(input, KEY_FN, -1,
priv->matrix.fn_keycode,
@@ -326,7 +327,6 @@ static int tegra_kbd_probe(struct udevice *dev)
priv->input = input;
input->dev = dev;
input->read_keys = tegra_kbc_check;
- input_add_tables(input, false);
strcpy(sdev->name, "tegra-kbc");
ret = input_stdio_register(sdev);
if (ret) {
diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c
index 1584865..573819a 100644
--- a/drivers/mmc/tegra_mmc.c
+++ b/drivers/mmc/tegra_mmc.c
@@ -674,7 +674,7 @@ void tegra_mmc_init(void)
CONFIG_SYS_MMC_MAX_DEVICE);
debug("%s: count of Tegra210 sdhci nodes is %d\n", __func__, count);
if (process_nodes(blob, node_list, count)) {
- printf("%s: Error processing T30 mmc node(s)!\n", __func__);
+ printf("%s: Error processing T210 mmc node(s)!\n", __func__);
return;
}
@@ -684,7 +684,7 @@ void tegra_mmc_init(void)
CONFIG_SYS_MMC_MAX_DEVICE);
debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count);
if (process_nodes(blob, node_list, count)) {
- printf("%s: Error processing T30 mmc node(s)!\n", __func__);
+ printf("%s: Error processing T124 mmc node(s)!\n", __func__);
return;
}
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index cd8f357..6f0d61e 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -17,3 +17,12 @@ config PWM_ROCKCHIP
programmable period and duty cycle. A 32-bit counter is used.
Various options provided in the hardware (such as capture mode and
continuous/single-shot) are not supported by the driver.
+
+config PWM_TEGRA
+ bool "Enable support for the Tegra PWM"
+ depends on DM_PWM
+ help
+ This PWM is found on Tegra 20 and other Nvidia SoCs. It supports
+ four channels with a programmable period and duty cycle. Only a
+ 32KHz clock is supported by the driver but the duty cycle is
+ configurable.
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index b6d8c16..fd414b1 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -13,3 +13,6 @@
obj-$(CONFIG_DM_PWM) += pwm-uclass.o
obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o
+ifdef CONFIG_DM_PWM
+obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o
+endif
diff --git a/drivers/pwm/tegra_pwm.c b/drivers/pwm/tegra_pwm.c
new file mode 100644
index 0000000..10e1fdc
--- /dev/null
+++ b/drivers/pwm/tegra_pwm.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <pwm.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/pwm.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct tegra_pwm_priv {
+ struct pwm_ctlr *regs;
+};
+
+static int tegra_pwm_set_config(struct udevice *dev, uint channel,
+ uint period_ns, uint duty_ns)
+{
+ struct tegra_pwm_priv *priv = dev_get_priv(dev);
+ struct pwm_ctlr *regs = priv->regs;
+ uint pulse_width;
+ u32 reg;
+
+ if (channel >= 4)
+ return -EINVAL;
+ debug("%s: Configure '%s' channel %u\n", __func__, dev->name, channel);
+ /* We ignore the period here and just use 32KHz */
+ clock_start_periph_pll(PERIPH_ID_PWM, CLOCK_ID_SFROM32KHZ, 32768);
+
+ pulse_width = duty_ns * 255 / period_ns;
+
+ reg = pulse_width << PWM_WIDTH_SHIFT;
+ reg |= 1 << PWM_DIVIDER_SHIFT;
+ writel(reg, &regs[channel].control);
+ debug("%s: pulse_width=%u\n", __func__, pulse_width);
+
+ return 0;
+}
+
+static int tegra_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
+{
+ struct tegra_pwm_priv *priv = dev_get_priv(dev);
+ struct pwm_ctlr *regs = priv->regs;
+
+ if (channel >= 4)
+ return -EINVAL;
+ debug("%s: Enable '%s' channel %u\n", __func__, dev->name, channel);
+ clrsetbits_le32(&regs[channel].control, PWM_ENABLE_MASK,
+ enable ? PWM_ENABLE_MASK : 0);
+
+ return 0;
+}
+
+static int tegra_pwm_ofdata_to_platdata(struct udevice *dev)
+{
+ struct tegra_pwm_priv *priv = dev_get_priv(dev);
+
+ priv->regs = (struct pwm_ctlr *)dev_get_addr(dev);
+
+ return 0;
+}
+
+static const struct pwm_ops tegra_pwm_ops = {
+ .set_config = tegra_pwm_set_config,
+ .set_enable = tegra_pwm_set_enable,
+};
+
+static const struct udevice_id tegra_pwm_ids[] = {
+ { .compatible = "nvidia,tegra124-pwm" },
+ { .compatible = "nvidia,tegra20-pwm" },
+ { }
+};
+
+U_BOOT_DRIVER(tegra_pwm) = {
+ .name = "tegra_pwm",
+ .id = UCLASS_PWM,
+ .of_match = tegra_pwm_ids,
+ .ops = &tegra_pwm_ops,
+ .ofdata_to_platdata = tegra_pwm_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct tegra_pwm_priv),
+};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 19f9429..ff4179f 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -381,8 +381,18 @@ config VIDEO_SANDBOX_SDL
console device and can display stdout output. Within U-Boot is is
a normal bitmap display and can display images as well as text.
+config VIDEO_TEGRA20
+ bool "Enable LCD support on Tegra20"
+ depends on OF_CONTROL
+ help
+ Tegra20 supports video output to an attached LCD panel as well as
+ other options such as HDMI. Only the LCD is supported in U-Boot.
+ This option enables this support which can be used on devices which
+ have an LCD display connected.
+
config VIDEO_TEGRA124
bool "Enable video support on Tegra124"
+ depends on DM_VIDEO
help
Tegra124 supports many video output options including eDP and
HDMI. At present only eDP is supported by U-Boot. This option
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index c55e6ed..d19a1d9 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -57,7 +57,7 @@ obj-$(CONFIG_VIDEO_SED13806) += sed13806.o
obj-$(CONFIG_VIDEO_SM501) += sm501.o
obj-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o
obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o videomodes.o
-obj-$(CONFIG_VIDEO_TEGRA) += tegra.o
+obj-$(CONFIG_VIDEO_TEGRA20) += tegra.o
obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
obj-$(CONFIG_VIDEO_VESA) += vesa_fb.o
obj-$(CONFIG_FORMIKE) += formike.o
diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
index b161517..c73f242 100644
--- a/drivers/video/simple_panel.c
+++ b/drivers/video/simple_panel.c
@@ -85,6 +85,7 @@ static const struct panel_ops simple_panel_ops = {
static const struct udevice_id simple_panel_ids[] = {
{ .compatible = "simple-panel" },
+ { .compatible = "auo,b133xtn01" },
{ }
};
diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c
index 8e81346..7fd10e6 100644
--- a/drivers/video/tegra.c
+++ b/drivers/video/tegra.c
@@ -4,11 +4,13 @@
*/
#include <common.h>
+#include <dm.h>
#include <fdtdec.h>
-#include <lcd.h>
-
+#include <pwm.h>
+#include <video.h>
#include <asm/system.h>
#include <asm/gpio.h>
+#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/funcmux.h>
@@ -30,165 +32,347 @@ enum stage_t {
STAGE_DONE,
};
-static enum stage_t stage; /* Current stage we are at */
-static unsigned long timer_next; /* Time we can move onto next stage */
+#define FDT_LCD_TIMINGS 4
+
+enum {
+ FDT_LCD_TIMING_REF_TO_SYNC,
+ FDT_LCD_TIMING_SYNC_WIDTH,
+ FDT_LCD_TIMING_BACK_PORCH,
+ FDT_LCD_TIMING_FRONT_PORCH,
+
+ FDT_LCD_TIMING_COUNT,
+};
+
+enum lcd_cache_t {
+ FDT_LCD_CACHE_OFF = 0,
+ FDT_LCD_CACHE_WRITE_THROUGH = 1 << 0,
+ FDT_LCD_CACHE_WRITE_BACK = 1 << 1,
+ FDT_LCD_CACHE_FLUSH = 1 << 2,
+ FDT_LCD_CACHE_WRITE_BACK_FLUSH = FDT_LCD_CACHE_WRITE_BACK |
+ FDT_LCD_CACHE_FLUSH,
+};
+
+/* Information about the display controller */
+struct tegra_lcd_priv {
+ enum stage_t stage; /* Current stage we are at */
+ unsigned long timer_next; /* Time we can move onto next stage */
+ int width; /* width in pixels */
+ int height; /* height in pixels */
-/* Our LCD config, set up in handle_stage() */
-static struct fdt_panel_config config;
-struct fdt_disp_config *disp_config; /* Display controller config */
+ /*
+ * log2 of number of bpp, in general, unless it bpp is 24 in which
+ * case this field holds 24 also! This is a U-Boot thing.
+ */
+ int log2_bpp;
+ struct disp_ctlr *disp; /* Display controller to use */
+ fdt_addr_t frame_buffer; /* Address of frame buffer */
+ unsigned pixel_clock; /* Pixel clock in Hz */
+ uint horiz_timing[FDT_LCD_TIMING_COUNT]; /* Horizontal timing */
+ uint vert_timing[FDT_LCD_TIMING_COUNT]; /* Vertical timing */
+ struct udevice *pwm;
+ int pwm_channel; /* PWM channel to use for backlight */
+ enum lcd_cache_t cache_type;
+
+ struct gpio_desc backlight_en; /* GPIO for backlight enable */
+ struct gpio_desc lvds_shutdown; /* GPIO for lvds shutdown */
+ struct gpio_desc backlight_vdd; /* GPIO for backlight vdd */
+ struct gpio_desc panel_vdd; /* GPIO for panel vdd */
+ /*
+ * Panel required timings
+ * Timing 1: delay between panel_vdd-rise and data-rise
+ * Timing 2: delay between data-rise and backlight_vdd-rise
+ * Timing 3: delay between backlight_vdd and pwm-rise
+ * Timing 4: delay between pwm-rise and backlight_en-rise
+ */
+ uint panel_timings[FDT_LCD_TIMINGS];
+};
enum {
/* Maximum LCD size we support */
LCD_MAX_WIDTH = 1366,
LCD_MAX_HEIGHT = 768,
- LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
+ LCD_MAX_LOG2_BPP = VIDEO_BPP16,
};
-vidinfo_t panel_info = {
- /* Insert a value here so that we don't end up in the BSS */
- .vl_col = -1,
-};
+static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win)
+{
+ unsigned h_dda, v_dda;
+ unsigned long val;
-#if !CONFIG_IS_ENABLED(OF_CONTROL)
-#error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support"
-#endif
+ val = readl(&dc->cmd.disp_win_header);
+ val |= WINDOW_A_SELECT;
+ writel(val, &dc->cmd.disp_win_header);
-static void update_panel_size(struct fdt_disp_config *config)
-{
- panel_info.vl_col = config->width;
- panel_info.vl_row = config->height;
- panel_info.vl_bpix = config->log2_bpp;
+ writel(win->fmt, &dc->win.color_depth);
+
+ clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK,
+ BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT);
+
+ val = win->out_x << H_POSITION_SHIFT;
+ val |= win->out_y << V_POSITION_SHIFT;
+ writel(val, &dc->win.pos);
+
+ val = win->out_w << H_SIZE_SHIFT;
+ val |= win->out_h << V_SIZE_SHIFT;
+ writel(val, &dc->win.size);
+
+ val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT;
+ val |= win->h << V_PRESCALED_SIZE_SHIFT;
+ writel(val, &dc->win.prescaled_size);
+
+ writel(0, &dc->win.h_initial_dda);
+ writel(0, &dc->win.v_initial_dda);
+
+ h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U);
+ v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U);
+
+ val = h_dda << H_DDA_INC_SHIFT;
+ val |= v_dda << V_DDA_INC_SHIFT;
+ writel(val, &dc->win.dda_increment);
+
+ writel(win->stride, &dc->win.line_stride);
+ writel(0, &dc->win.buf_stride);
+
+ val = WIN_ENABLE;
+ if (win->bpp < 24)
+ val |= COLOR_EXPAND;
+ writel(val, &dc->win.win_opt);
+
+ writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr);
+ writel(win->x, &dc->winbuf.addr_h_offset);
+ writel(win->y, &dc->winbuf.addr_v_offset);
+
+ writel(0xff00, &dc->win.blend_nokey);
+ writel(0xff00, &dc->win.blend_1win);
+
+ val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
+ val |= GENERAL_UPDATE | WIN_A_UPDATE;
+ writel(val, &dc->cmd.state_ctrl);
}
-/*
- * Main init function called by lcd driver.
- * Inits and then prints test pattern if required.
- */
+static void write_pair(struct tegra_lcd_priv *priv, int item, u32 *reg)
+{
+ writel(priv->horiz_timing[item] |
+ (priv->vert_timing[item] << 16), reg);
+}
-void lcd_ctrl_init(void *lcdbase)
+static int update_display_mode(struct dc_disp_reg *disp,
+ struct tegra_lcd_priv *priv)
{
- int type = DCACHE_OFF;
- int size;
+ unsigned long val;
+ unsigned long rate;
+ unsigned long div;
- assert(disp_config);
+ writel(0x0, &disp->disp_timing_opt);
+ write_pair(priv, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync);
+ write_pair(priv, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width);
+ write_pair(priv, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch);
+ write_pair(priv, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch);
- /* Make sure that we can acommodate the selected LCD */
- assert(disp_config->width <= LCD_MAX_WIDTH);
- assert(disp_config->height <= LCD_MAX_HEIGHT);
- assert(disp_config->log2_bpp <= LCD_MAX_LOG2_BPP);
- if (disp_config->width <= LCD_MAX_WIDTH
- && disp_config->height <= LCD_MAX_HEIGHT
- && disp_config->log2_bpp <= LCD_MAX_LOG2_BPP)
- update_panel_size(disp_config);
- size = lcd_get_size(&lcd_line_length);
+ writel(priv->width | (priv->height << 16), &disp->disp_active);
- /* Set up the LCD caching as requested */
- if (config.cache_type & FDT_LCD_CACHE_WRITE_THROUGH)
- type = DCACHE_WRITETHROUGH;
- else if (config.cache_type & FDT_LCD_CACHE_WRITE_BACK)
- type = DCACHE_WRITEBACK;
- mmu_set_region_dcache_behaviour(disp_config->frame_buffer, size, type);
+ val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT;
+ val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT;
+ writel(val, &disp->data_enable_opt);
- /* Enable flushing after LCD writes if requested */
- lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH);
+ val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT;
+ val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
+ val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
+ writel(val, &disp->disp_interface_ctrl);
+
+ /*
+ * The pixel clock divider is in 7.1 format (where the bottom bit
+ * represents 0.5). Here we calculate the divider needed to get from
+ * the display clock (typically 600MHz) to the pixel clock. We round
+ * up or down as requried.
+ */
+ rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL);
+ div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2;
+ debug("Display clock %lu, divider %lu\n", rate, div);
+
+ writel(0x00010001, &disp->shift_clk_opt);
- debug("LCD frame buffer at %pa\n", &disp_config->frame_buffer);
+ val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
+ val |= div << SHIFT_CLK_DIVIDER_SHIFT;
+ writel(val, &disp->disp_clk_ctrl);
+
+ return 0;
}
-ulong calc_fbsize(void)
+/* Start up the display and turn on power to PWMs */
+static void basic_init(struct dc_cmd_reg *cmd)
{
- return (panel_info.vl_col * panel_info.vl_row *
- NBITS(panel_info.vl_bpix)) / 8;
+ u32 val;
+
+ writel(0x00000100, &cmd->gen_incr_syncpt_ctrl);
+ writel(0x0000011a, &cmd->cont_syncpt_vsync);
+ writel(0x00000000, &cmd->int_type);
+ writel(0x00000000, &cmd->int_polarity);
+ writel(0x00000000, &cmd->int_mask);
+ writel(0x00000000, &cmd->int_enb);
+
+ val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE;
+ val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE;
+ val |= PM1_ENABLE;
+ writel(val, &cmd->disp_pow_ctrl);
+
+ val = readl(&cmd->disp_cmd);
+ val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
+ writel(val, &cmd->disp_cmd);
}
-void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
+static void basic_init_timer(struct dc_disp_reg *disp)
{
+ writel(0x00000020, &disp->mem_high_pri);
+ writel(0x00000001, &disp->mem_high_pri_timer);
}
-void tegra_lcd_early_init(const void *blob)
+static const u32 rgb_enb_tab[PIN_REG_COUNT] = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+static const u32 rgb_polarity_tab[PIN_REG_COUNT] = {
+ 0x00000000,
+ 0x01000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+static const u32 rgb_data_tab[PIN_REG_COUNT] = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00210222,
+ 0x00002200,
+ 0x00020000,
+};
+
+static void rgb_enable(struct dc_com_reg *com)
{
- /*
- * Go with the maximum size for now. We will fix this up after
- * relocation. These values are only used for memory alocation.
- */
- panel_info.vl_col = LCD_MAX_WIDTH;
- panel_info.vl_row = LCD_MAX_HEIGHT;
- panel_info.vl_bpix = LCD_MAX_LOG2_BPP;
+ int i;
+
+ for (i = 0; i < PIN_REG_COUNT; i++) {
+ writel(rgb_enb_tab[i], &com->pin_output_enb[i]);
+ writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]);
+ writel(rgb_data_tab[i], &com->pin_output_data[i]);
+ }
+
+ for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
+ writel(rgb_sel_tab[i], &com->pin_output_sel[i]);
+}
+
+static int setup_window(struct disp_ctl_win *win,
+ struct tegra_lcd_priv *priv)
+{
+ win->x = 0;
+ win->y = 0;
+ win->w = priv->width;
+ win->h = priv->height;
+ win->out_x = 0;
+ win->out_y = 0;
+ win->out_w = priv->width;
+ win->out_h = priv->height;
+ win->phys_addr = priv->frame_buffer;
+ win->stride = priv->width * (1 << priv->log2_bpp) / 8;
+ debug("%s: depth = %d\n", __func__, priv->log2_bpp);
+ switch (priv->log2_bpp) {
+ case 5:
+ case 24:
+ win->fmt = COLOR_DEPTH_R8G8B8A8;
+ win->bpp = 32;
+ break;
+ case 4:
+ win->fmt = COLOR_DEPTH_B5G6R5;
+ win->bpp = 16;
+ break;
+
+ default:
+ debug("Unsupported LCD bit depth");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void debug_timing(const char *name, unsigned int timing[])
+{
+#ifdef DEBUG
+ int i;
+
+ debug("%s timing: ", name);
+ for (i = 0; i < FDT_LCD_TIMING_COUNT; i++)
+ debug("%d ", timing[i]);
+ debug("\n");
+#endif
}
/**
- * Decode the panel information from the fdt.
+ * Register a new display based on device tree configuration.
*
- * @param blob fdt blob
- * @param config structure to store fdt config into
- * @return 0 if ok, -ve on error
+ * The frame buffer can be positioned by U-Boot or overriden by the fdt.
+ * You should pass in the U-Boot address here, and check the contents of
+ * struct tegra_lcd_priv to see what was actually chosen.
+ *
+ * @param blob Device tree blob
+ * @param priv Driver's private data
+ * @param default_lcd_base Default address of LCD frame buffer
+ * @return 0 if ok, -1 on error (unsupported bits per pixel)
*/
-static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config)
+static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
+ void *default_lcd_base)
{
- int display_node;
+ struct disp_ctl_win window;
+ struct dc_ctlr *dc;
- disp_config = tegra_display_get_config();
- if (!disp_config) {
- debug("%s: Display controller is not configured\n", __func__);
- return -1;
- }
- display_node = disp_config->panel_node;
- if (display_node < 0) {
- debug("%s: No panel configuration available\n", __func__);
- return -1;
- }
+ priv->frame_buffer = (u32)default_lcd_base;
- config->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm");
- if (config->pwm_channel < 0) {
- debug("%s: Unable to request PWM channel\n", __func__);
- return -1;
- }
+ dc = (struct dc_ctlr *)priv->disp;
- config->cache_type = fdtdec_get_int(blob, display_node,
- "nvidia,cache-type",
- FDT_LCD_CACHE_WRITE_BACK_FLUSH);
+ /*
+ * A header file for clock constants was NAKed upstream.
+ * TODO: Put this into the FDT and fdt_lcd struct when we have clock
+ * support there
+ */
+ clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH,
+ 144 * 1000000);
+ clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL,
+ 600 * 1000000);
+ basic_init(&dc->cmd);
+ basic_init_timer(&dc->disp);
+ rgb_enable(&dc->com);
+
+ if (priv->pixel_clock)
+ update_display_mode(&dc->disp, priv);
+
+ if (setup_window(&window, priv))
+ return -1;
- /* These GPIOs are all optional */
- gpio_request_by_name_nodev(blob, display_node,
- "nvidia,backlight-enable-gpios", 0,
- &config->backlight_en, GPIOD_IS_OUT);
- gpio_request_by_name_nodev(blob, display_node,
- "nvidia,lvds-shutdown-gpios", 0,
- &config->lvds_shutdown, GPIOD_IS_OUT);
- gpio_request_by_name_nodev(blob, display_node,
- "nvidia,backlight-vdd-gpios", 0,
- &config->backlight_vdd, GPIOD_IS_OUT);
- gpio_request_by_name_nodev(blob, display_node,
- "nvidia,panel-vdd-gpios", 0,
- &config->panel_vdd, GPIOD_IS_OUT);
+ update_window(dc, &window);
- return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings",
- config->panel_timings, FDT_LCD_TIMINGS);
+ return 0;
}
/**
* Handle the next stage of device init
*/
-static int handle_stage(const void *blob)
+static int handle_stage(const void *blob, struct tegra_lcd_priv *priv)
{
- debug("%s: stage %d\n", __func__, stage);
+ debug("%s: stage %d\n", __func__, priv->stage);
/* do the things for this stage */
- switch (stage) {
+ switch (priv->stage) {
case STAGE_START:
- /* Initialize the Tegra display controller */
- if (tegra_display_probe(gd->fdt_blob, (void *)gd->fb_base)) {
- printf("%s: Failed to probe display driver\n",
- __func__);
- return -1;
- }
-
- /* get panel details */
- if (fdt_decode_lcd(blob, &config)) {
- printf("No valid LCD information in device tree\n");
- return -1;
- }
-
/*
* It is possible that the FDT has requested that the LCD be
* disabled. We currently don't support this. It would require
@@ -202,52 +386,71 @@ static int handle_stage(const void *blob)
funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
break;
case STAGE_PANEL_VDD:
- if (dm_gpio_is_valid(&config.panel_vdd))
- dm_gpio_set_value(&config.panel_vdd, 1);
+ if (dm_gpio_is_valid(&priv->panel_vdd))
+ dm_gpio_set_value(&priv->panel_vdd, 1);
break;
case STAGE_LVDS:
- if (dm_gpio_is_valid(&config.lvds_shutdown))
- dm_gpio_set_value(&config.lvds_shutdown, 1);
+ if (dm_gpio_is_valid(&priv->lvds_shutdown))
+ dm_gpio_set_value(&priv->lvds_shutdown, 1);
break;
case STAGE_BACKLIGHT_VDD:
- if (dm_gpio_is_valid(&config.backlight_vdd))
- dm_gpio_set_value(&config.backlight_vdd, 1);
+ if (dm_gpio_is_valid(&priv->backlight_vdd))
+ dm_gpio_set_value(&priv->backlight_vdd, 1);
break;
case STAGE_PWM:
/* Enable PWM at 15/16 high, 32768 Hz with divider 1 */
pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM);
pinmux_tristate_disable(PMUX_PINGRP_GPU);
- pwm_enable(config.pwm_channel, 32768, 0xdf, 1);
+ pwm_set_config(priv->pwm, priv->pwm_channel, 0xdf, 0xff);
+ pwm_set_enable(priv->pwm, priv->pwm_channel, true);
break;
case STAGE_BACKLIGHT_EN:
- if (dm_gpio_is_valid(&config.backlight_en))
- dm_gpio_set_value(&config.backlight_en, 1);
+ if (dm_gpio_is_valid(&priv->backlight_en))
+ dm_gpio_set_value(&priv->backlight_en, 1);
break;
case STAGE_DONE:
break;
}
/* set up timer for next stage */
- timer_next = timer_get_us();
- if (stage < FDT_LCD_TIMINGS)
- timer_next += config.panel_timings[stage] * 1000;
+ priv->timer_next = timer_get_us();
+ if (priv->stage < FDT_LCD_TIMINGS)
+ priv->timer_next += priv->panel_timings[priv->stage] * 1000;
/* move to next stage */
- stage++;
+ priv->stage++;
return 0;
}
-int tegra_lcd_check_next_stage(const void *blob, int wait)
+/**
+ * Perform the next stage of the LCD init if it is time to do so.
+ *
+ * LCD init can be time-consuming because of the number of delays we need
+ * while waiting for the backlight power supply, etc. This function can
+ * be called at various times during U-Boot operation to advance the
+ * initialization of the LCD to the next stage if sufficient time has
+ * passed since the last stage. It keeps track of what stage it is up to
+ * and the time that it is permitted to move to the next stage.
+ *
+ * The final call should have wait=1 to complete the init.
+ *
+ * @param blob fdt blob containing LCD information
+ * @param wait 1 to wait until all init is complete, and then return
+ * 0 to return immediately, potentially doing nothing if it is
+ * not yet time for the next init.
+ */
+static int tegra_lcd_check_next_stage(const void *blob,
+ struct tegra_lcd_priv *priv, int wait)
{
- if (stage == STAGE_DONE)
+ if (priv->stage == STAGE_DONE)
return 0;
do {
/* wait if we need to */
- debug("%s: stage %d\n", __func__, stage);
- if (stage != STAGE_START) {
- int delay = timer_next - timer_get_us();
+ debug("%s: stage %d\n", __func__, priv->stage);
+ if (priv->stage != STAGE_START) {
+ int delay = priv->timer_next - timer_get_us();
if (delay > 0) {
if (wait)
@@ -257,29 +460,188 @@ int tegra_lcd_check_next_stage(const void *blob, int wait)
}
}
- if (handle_stage(blob))
+ if (handle_stage(blob, priv))
return -1;
- } while (wait && stage != STAGE_DONE);
- if (stage == STAGE_DONE)
+ } while (wait && priv->stage != STAGE_DONE);
+ if (priv->stage == STAGE_DONE)
debug("%s: LCD init complete\n", __func__);
return 0;
}
-void lcd_enable(void)
+static int tegra_lcd_probe(struct udevice *dev)
{
- /*
- * Backlight and power init will be done separately in
- * tegra_lcd_check_next_stage(), which should be called in
- * board_late_init().
- *
- * U-Boot code supports only colour depth, selected at compile time.
- * The device tree setting should match this. Otherwise the display
- * will not look right, and U-Boot may crash.
- */
- if (disp_config->log2_bpp != LCD_BPP) {
- printf("%s: Error: LCD depth configured in FDT (%d = %dbpp)"
- " must match setting of LCD_BPP (%d)\n", __func__,
- disp_config->log2_bpp, disp_config->bpp, LCD_BPP);
+ struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+ struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct tegra_lcd_priv *priv = dev_get_priv(dev);
+ const void *blob = gd->fdt_blob;
+ int type = DCACHE_OFF;
+
+ /* Initialize the Tegra display controller */
+ if (tegra_display_probe(blob, priv, (void *)plat->base)) {
+ printf("%s: Failed to probe display driver\n", __func__);
+ return -1;
}
+
+ tegra_lcd_check_next_stage(blob, priv, 1);
+
+ /* Set up the LCD caching as requested */
+ if (priv->cache_type & FDT_LCD_CACHE_WRITE_THROUGH)
+ type = DCACHE_WRITETHROUGH;
+ else if (priv->cache_type & FDT_LCD_CACHE_WRITE_BACK)
+ type = DCACHE_WRITEBACK;
+ mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, type);
+
+ /* Enable flushing after LCD writes if requested */
+ video_set_flush_dcache(dev, priv->cache_type & FDT_LCD_CACHE_FLUSH);
+
+ uc_priv->xsize = priv->width;
+ uc_priv->ysize = priv->height;
+ uc_priv->bpix = priv->log2_bpp;
+ debug("LCD frame buffer at %pa, size %x\n", &priv->frame_buffer,
+ plat->size);
+
+ return 0;
}
+
+static int tegra_lcd_ofdata_to_platdata(struct udevice *dev)
+{
+ struct tegra_lcd_priv *priv = dev_get_priv(dev);
+ struct fdtdec_phandle_args args;
+ const void *blob = gd->fdt_blob;
+ int node = dev->of_offset;
+ int front, back, ref;
+ int panel_node;
+ int rgb;
+ int bpp, bit;
+ int ret;
+
+ priv->disp = (struct disp_ctlr *)dev_get_addr(dev);
+ if (!priv->disp) {
+ debug("%s: No display controller address\n", __func__);
+ return -EINVAL;
+ }
+
+ rgb = fdt_subnode_offset(blob, node, "rgb");
+
+ panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel");
+ if (panel_node < 0) {
+ debug("%s: Cannot find panel information\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->width = fdtdec_get_int(blob, panel_node, "xres", -1);
+ priv->height = fdtdec_get_int(blob, panel_node, "yres", -1);
+ priv->pixel_clock = fdtdec_get_int(blob, panel_node, "clock", 0);
+ if (!priv->pixel_clock || priv->width == -1 || priv->height == -1) {
+ debug("%s: Pixel parameters missing\n", __func__);
+ return -EINVAL;
+ }
+
+ back = fdtdec_get_int(blob, panel_node, "left-margin", -1);
+ front = fdtdec_get_int(blob, panel_node, "right-margin", -1);
+ ref = fdtdec_get_int(blob, panel_node, "hsync-len", -1);
+ if ((back | front | ref) == -1) {
+ debug("%s: Horizontal parameters missing\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Use a ref-to-sync of 1 always, and take this from the front porch */
+ priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1;
+ priv->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref;
+ priv->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back;
+ priv->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front -
+ priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC];
+ debug_timing("horiz", priv->horiz_timing);
+
+ back = fdtdec_get_int(blob, panel_node, "upper-margin", -1);
+ front = fdtdec_get_int(blob, panel_node, "lower-margin", -1);
+ ref = fdtdec_get_int(blob, panel_node, "vsync-len", -1);
+ if ((back | front | ref) == -1) {
+ debug("%s: Vertical parameters missing\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1;
+ priv->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref;
+ priv->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back;
+ priv->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front -
+ priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC];
+ debug_timing("vert", priv->vert_timing);
+
+ bpp = fdtdec_get_int(blob, panel_node, "nvidia,bits-per-pixel", -1);
+ bit = ffs(bpp) - 1;
+ if (bpp == (1 << bit))
+ priv->log2_bpp = bit;
+ else
+ priv->log2_bpp = bpp;
+ if (bpp == -1) {
+ debug("%s: Pixel bpp parameters missing\n", __func__);
+ return -EINVAL;
+ }
+
+ if (fdtdec_parse_phandle_with_args(blob, panel_node, "nvidia,pwm",
+ "#pwm-cells", 0, 0, &args)) {
+ debug("%s: Unable to decode PWM\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = uclass_get_device_by_of_offset(UCLASS_PWM, args.node, &priv->pwm);
+ if (ret) {
+ debug("%s: Unable to find PWM\n", __func__);
+ return -EINVAL;
+ }
+ priv->pwm_channel = args.args[0];
+
+ priv->cache_type = fdtdec_get_int(blob, panel_node, "nvidia,cache-type",
+ FDT_LCD_CACHE_WRITE_BACK_FLUSH);
+
+ /* These GPIOs are all optional */
+ gpio_request_by_name_nodev(blob, panel_node,
+ "nvidia,backlight-enable-gpios", 0,
+ &priv->backlight_en, GPIOD_IS_OUT);
+ gpio_request_by_name_nodev(blob, panel_node,
+ "nvidia,lvds-shutdown-gpios", 0,
+ &priv->lvds_shutdown, GPIOD_IS_OUT);
+ gpio_request_by_name_nodev(blob, panel_node,
+ "nvidia,backlight-vdd-gpios", 0,
+ &priv->backlight_vdd, GPIOD_IS_OUT);
+ gpio_request_by_name_nodev(blob, panel_node,
+ "nvidia,panel-vdd-gpios", 0,
+ &priv->panel_vdd, GPIOD_IS_OUT);
+
+ if (fdtdec_get_int_array(blob, panel_node, "nvidia,panel-timings",
+ priv->panel_timings, FDT_LCD_TIMINGS))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra_lcd_bind(struct udevice *dev)
+{
+ struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+
+ plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
+ (1 << LCD_MAX_LOG2_BPP) / 8;
+
+ return 0;
+}
+
+static const struct video_ops tegra_lcd_ops = {
+};
+
+static const struct udevice_id tegra_lcd_ids[] = {
+ { .compatible = "nvidia,tegra20-dc" },
+ { }
+};
+
+U_BOOT_DRIVER(tegra_lcd) = {
+ .name = "tegra_lcd",
+ .id = UCLASS_VIDEO,
+ .of_match = tegra_lcd_ids,
+ .ops = &tegra_lcd_ops,
+ .bind = tegra_lcd_bind,
+ .probe = tegra_lcd_probe,
+ .ofdata_to_platdata = tegra_lcd_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct tegra_lcd_priv),
+};
diff --git a/drivers/video/tegra124/Makefile b/drivers/video/tegra124/Makefile
index 52eedb0..4287b9a 100644
--- a/drivers/video/tegra124/Makefile
+++ b/drivers/video/tegra124/Makefile
@@ -7,4 +7,3 @@
obj-y += display.o
obj-y += dp.o
obj-y += sor.o
-obj-y += tegra124-lcd.o
diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra124/display.c
index 610ffa9..2f1f0df 100644
--- a/drivers/video/tegra124/display.c
+++ b/drivers/video/tegra124/display.c
@@ -14,11 +14,13 @@
#include <edid.h>
#include <fdtdec.h>
#include <lcd.h>
+#include <video.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/pwm.h>
#include <asm/arch-tegra/dc.h>
+#include <dm/uclass-internal.h>
#include "displayport.h"
DECLARE_GLOBAL_DATA_PTR;
@@ -333,73 +335,46 @@ static int display_update_config_from_edid(struct udevice *dp_dev,
return 0;
}
-/* Somewhat torturous method */
-static int get_backlight_info(const void *blob, struct gpio_desc *vdd,
- struct gpio_desc *enable, int *pwmp)
-{
- int sor, panel, backlight, power;
- const u32 *prop;
- int len;
- int ret;
-
- *pwmp = 0;
- sor = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR);
- if (sor < 0)
- return -ENOENT;
- panel = fdtdec_lookup_phandle(blob, sor, "nvidia,panel");
- if (panel < 0)
- return -ENOENT;
- backlight = fdtdec_lookup_phandle(blob, panel, "backlight");
- if (backlight < 0)
- return -ENOENT;
- ret = gpio_request_by_name_nodev(blob, backlight, "enable-gpios", 0,
- enable, GPIOD_IS_OUT);
- if (ret)
- return ret;
- prop = fdt_getprop(blob, backlight, "pwms", &len);
- if (!prop || len != 3 * sizeof(u32))
- return -EINVAL;
- *pwmp = fdt32_to_cpu(prop[1]);
-
- power = fdtdec_lookup_phandle(blob, backlight, "power-supply");
- if (power < 0)
- return -ENOENT;
- ret = gpio_request_by_name_nodev(blob, power, "gpio", 0, vdd,
- GPIOD_IS_OUT);
- if (ret)
- goto err;
-
- return 0;
-
-err:
- dm_gpio_free(NULL, enable);
- return ret;
-}
-
-int display_init(void *lcdbase, int fb_bits_per_pixel,
- struct display_timing *timing)
+static int display_init(struct udevice *dev, void *lcdbase,
+ int fb_bits_per_pixel, struct display_timing *timing)
{
+ struct display_plat *disp_uc_plat;
struct dc_ctlr *dc_ctlr;
const void *blob = gd->fdt_blob;
struct udevice *dp_dev;
const int href_to_sync = 1, vref_to_sync = 1;
int panel_bpp = 18; /* default 18 bits per pixel */
u32 plld_rate;
- struct gpio_desc vdd_gpio, enable_gpio;
- int pwm;
- int node;
int ret;
+ /*
+ * Before we probe the display device (eDP), tell it that this device
+ * is are the source of the display data.
+ */
+ ret = uclass_find_first_device(UCLASS_DISPLAY, &dp_dev);
+ if (ret) {
+ debug("%s: device '%s' display not found (ret=%d)\n", __func__,
+ dev->name, ret);
+ return ret;
+ }
+
+ disp_uc_plat = dev_get_uclass_platdata(dp_dev);
+ debug("Found device '%s', disp_uc_priv=%p\n", dp_dev->name,
+ disp_uc_plat);
+ disp_uc_plat->src_dev = dev;
+
ret = uclass_get_device(UCLASS_DISPLAY, 0, &dp_dev);
- if (ret)
+ if (ret) {
+ debug("%s: Failed to probe eDP, ret=%d\n", __func__, ret);
return ret;
+ }
- node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
- if (node < 0)
- return -ENOENT;
- dc_ctlr = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
- if (fdtdec_decode_display_timing(blob, node, 0, timing))
+ dc_ctlr = (struct dc_ctlr *)fdtdec_get_addr(blob, dev->of_offset,
+ "reg");
+ if (fdtdec_decode_display_timing(blob, dev->of_offset, 0, timing)) {
+ debug("%s: Failed to decode display timing\n", __func__);
return -EINVAL;
+ }
ret = display_update_config_from_edid(dp_dev, &panel_bpp, timing);
if (ret) {
@@ -407,12 +382,6 @@ int display_init(void *lcdbase, int fb_bits_per_pixel,
dump_config(panel_bpp, timing);
}
- if (!get_backlight_info(blob, &vdd_gpio, &enable_gpio, &pwm)) {
- dm_gpio_set_value(&vdd_gpio, 1);
- debug("%s: backlight vdd setting gpio %08x to %d\n",
- __func__, gpio_get_number(&vdd_gpio), 1);
- }
-
/*
* The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER
* and PIXEL_CLK_DIVIDER are zero (divide by 1). See the
@@ -443,22 +412,99 @@ int display_init(void *lcdbase, int fb_bits_per_pixel,
/* Enable dp */
ret = display_enable(dp_dev, panel_bpp, timing);
- if (ret)
+ if (ret) {
+ debug("dc: failed to enable display: ret=%d\n", ret);
return ret;
+ }
ret = update_window(dc_ctlr, (ulong)lcdbase, fb_bits_per_pixel, timing);
+ if (ret) {
+ debug("dc: failed to update window\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+enum {
+ /* Maximum LCD size we support */
+ LCD_MAX_WIDTH = 1920,
+ LCD_MAX_HEIGHT = 1200,
+ LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
+};
+
+static int tegra124_lcd_init(struct udevice *dev, void *lcdbase,
+ enum video_log2_bpp l2bpp)
+{
+ struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct display_timing timing;
+ int ret;
+
+ clock_set_up_plldp();
+ clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, 408000000);
+
+ clock_enable(PERIPH_ID_HOST1X);
+ clock_enable(PERIPH_ID_DISP1);
+ clock_enable(PERIPH_ID_PWM);
+ clock_enable(PERIPH_ID_DPAUX);
+ clock_enable(PERIPH_ID_SOR0);
+ udelay(2);
+
+ reset_set_enable(PERIPH_ID_HOST1X, 0);
+ reset_set_enable(PERIPH_ID_DISP1, 0);
+ reset_set_enable(PERIPH_ID_PWM, 0);
+ reset_set_enable(PERIPH_ID_DPAUX, 0);
+ reset_set_enable(PERIPH_ID_SOR0, 0);
+
+ ret = display_init(dev, lcdbase, 1 << l2bpp, &timing);
if (ret)
return ret;
- /* Set up Tegra PWM to drive the panel backlight */
- pwm_enable(pwm, 0, 220, 0x2e);
- udelay(10 * 1000);
+ uc_priv->xsize = roundup(timing.hactive.typ, 16);
+ uc_priv->ysize = timing.vactive.typ;
+ uc_priv->bpix = l2bpp;
- if (dm_gpio_is_valid(&enable_gpio)) {
- dm_gpio_set_value(&enable_gpio, 1);
- debug("%s: backlight enable setting gpio %08x to %d\n",
- __func__, gpio_get_number(&enable_gpio), 1);
- }
+ video_set_flush_dcache(dev, 1);
+ debug("%s: done\n", __func__);
return 0;
}
+
+static int tegra124_lcd_probe(struct udevice *dev)
+{
+ struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+ ulong start;
+ int ret;
+
+ start = get_timer(0);
+ ret = tegra124_lcd_init(dev, (void *)plat->base, VIDEO_BPP16);
+ debug("LCD init took %lu ms\n", get_timer(start));
+ if (ret)
+ printf("%s: Error %d\n", __func__, ret);
+
+ return 0;
+}
+
+static int tegra124_lcd_bind(struct udevice *dev)
+{
+ struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+
+ uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
+ (1 << VIDEO_BPP16) / 8;
+ debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
+
+ return 0;
+}
+
+static const struct udevice_id tegra124_lcd_ids[] = {
+ { .compatible = "nvidia,tegra124-dc" },
+ { }
+};
+
+U_BOOT_DRIVER(tegra124_dc) = {
+ .name = "tegra124-dc",
+ .id = UCLASS_VIDEO,
+ .of_match = tegra124_lcd_ids,
+ .bind = tegra124_lcd_bind,
+ .probe = tegra124_lcd_probe,
+};
diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra124/dp.c
index bb1805a..5bf8524 100644
--- a/drivers/video/tegra124/dp.c
+++ b/drivers/video/tegra124/dp.c
@@ -11,6 +11,7 @@
#include <div64.h>
#include <errno.h>
#include <fdtdec.h>
+#include <video_bridge.h>
#include <asm/io.h>
#include <asm/arch-tegra/dc.h>
#include "display.h"
@@ -26,9 +27,15 @@ struct tegra_dp_plat {
ulong base;
};
+/**
+ * struct tegra_dp_priv - private displayport driver info
+ *
+ * @dc_dev: Display controller device that is sending the video feed
+ */
struct tegra_dp_priv {
+ struct udevice *sor;
+ struct udevice *dc_dev;
struct dpaux_ctlr *regs;
- struct tegra_dc_sor_data *sor;
u8 revision;
int enabled;
};
@@ -710,8 +717,8 @@ static int tegra_dc_dp_init_max_link_cfg(
return 0;
}
-static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp,
- struct tegra_dc_sor_data *sor, int ena)
+static int tegra_dc_dp_set_assr(struct tegra_dp_priv *priv,
+ struct udevice *sor, int ena)
{
int ret;
@@ -719,7 +726,7 @@ static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp,
DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE :
DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE;
- ret = tegra_dc_dp_dpcd_write(dp, DP_EDP_CONFIGURATION_SET,
+ ret = tegra_dc_dp_dpcd_write(priv, DP_EDP_CONFIGURATION_SET,
dpcd_data);
if (ret)
return ret;
@@ -730,7 +737,7 @@ static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp,
}
static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp,
- struct tegra_dc_sor_data *sor,
+ struct udevice *sor,
u8 link_bw)
{
tegra_dc_sor_set_link_bandwidth(sor, link_bw);
@@ -741,7 +748,7 @@ static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp,
static int tegra_dp_set_lane_count(struct tegra_dp_priv *dp,
const struct tegra_dp_link_config *link_cfg,
- struct tegra_dc_sor_data *sor)
+ struct udevice *sor)
{
u8 dpcd_data;
int ret;
@@ -1002,7 +1009,7 @@ fail:
static int tegra_dp_lt_config(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4],
u32 pc[4], const struct tegra_dp_link_config *cfg)
{
- struct tegra_dc_sor_data *sor = dp->sor;
+ struct udevice *sor = dp->sor;
u32 n_lanes = cfg->lane_count;
u8 pc_supported = cfg->tps3_supported;
u32 cnt;
@@ -1186,7 +1193,7 @@ static int tegra_dc_dp_full_link_training(struct tegra_dp_priv *dp,
const struct display_timing *timing,
struct tegra_dp_link_config *cfg)
{
- struct tegra_dc_sor_data *sor = dp->sor;
+ struct udevice *sor = dp->sor;
int err;
u32 pe[4], vs[4], pc[4];
@@ -1229,7 +1236,7 @@ fail:
*/
static int tegra_dc_dp_fast_link_training(struct tegra_dp_priv *dp,
const struct tegra_dp_link_config *link_cfg,
- struct tegra_dc_sor_data *sor)
+ struct udevice *sor)
{
u8 link_bw;
u8 lane_count;
@@ -1301,7 +1308,7 @@ static int tegra_dc_dp_fast_link_training(struct tegra_dp_priv *dp,
static int tegra_dp_do_link_training(struct tegra_dp_priv *dp,
struct tegra_dp_link_config *link_cfg,
const struct display_timing *timing,
- struct tegra_dc_sor_data *sor)
+ struct udevice *sor)
{
u8 link_bw;
u8 lane_count;
@@ -1344,7 +1351,7 @@ static int tegra_dp_do_link_training(struct tegra_dp_priv *dp,
static int tegra_dc_dp_explore_link_cfg(struct tegra_dp_priv *dp,
struct tegra_dp_link_config *link_cfg,
- struct tegra_dc_sor_data *sor,
+ struct udevice *sor,
const struct display_timing *timing)
{
struct tegra_dp_link_config temp_cfg;
@@ -1444,7 +1451,7 @@ static int tegra_dc_dp_check_sink(struct tegra_dp_priv *dp,
printf("DP: Out of sync after %d retries\n", max_retry);
return -EIO;
}
- ret = tegra_dc_sor_detach(dp->sor);
+ ret = tegra_dc_sor_detach(dp->dc_dev, dp->sor);
if (ret)
return ret;
if (tegra_dc_dp_explore_link_cfg(dp, link_cfg, dp->sor,
@@ -1454,7 +1461,7 @@ static int tegra_dc_dp_check_sink(struct tegra_dp_priv *dp,
}
tegra_dc_sor_set_power_state(dp->sor, 1);
- tegra_dc_sor_attach(dp->sor, link_cfg, timing);
+ tegra_dc_sor_attach(dp->dc_dev, dp->sor, link_cfg, timing);
/* Increase delay_frame for next try in case the sink is
skipping more frames */
@@ -1467,7 +1474,7 @@ int tegra_dp_enable(struct udevice *dev, int panel_bpp,
{
struct tegra_dp_priv *priv = dev_get_priv(dev);
struct tegra_dp_link_config slink_cfg, *link_cfg = &slink_cfg;
- struct tegra_dc_sor_data *sor;
+ struct udevice *sor;
int data;
int retry;
int ret;
@@ -1489,9 +1496,11 @@ int tegra_dp_enable(struct udevice *dev, int panel_bpp,
return -ENOLINK;
}
- ret = tegra_dc_sor_init(&sor);
- if (ret)
+ ret = uclass_first_device(UCLASS_VIDEO_BRIDGE, &sor);
+ if (ret || !sor) {
+ debug("dp: failed to find SOR device: ret=%d\n", ret);
return ret;
+ }
priv->sor = sor;
ret = tegra_dc_sor_enable_dp(sor, link_cfg);
if (ret)
@@ -1531,7 +1540,7 @@ int tegra_dp_enable(struct udevice *dev, int panel_bpp,
}
tegra_dc_sor_set_power_state(sor, 1);
- ret = tegra_dc_sor_attach(sor, link_cfg, timing);
+ ret = tegra_dc_sor_attach(priv->dc_dev, sor, link_cfg, timing);
if (ret && ret != -EEXIST)
return ret;
@@ -1548,6 +1557,12 @@ int tegra_dp_enable(struct udevice *dev, int panel_bpp,
/* Power down the unused lanes to save power - a few hundred mW */
tegra_dc_sor_power_down_unused_lanes(sor, link_cfg);
+ ret = video_bridge_set_backlight(sor, 80);
+ if (ret) {
+ debug("dp: failed to set backlight\n");
+ return ret;
+ }
+
priv->enabled = true;
error_enable:
return 0;
@@ -1583,10 +1598,14 @@ static int dp_tegra_probe(struct udevice *dev)
{
struct tegra_dp_plat *plat = dev_get_platdata(dev);
struct tegra_dp_priv *priv = dev_get_priv(dev);
+ struct display_plat *disp_uc_plat = dev_get_uclass_platdata(dev);
priv->regs = (struct dpaux_ctlr *)plat->base;
priv->enabled = false;
+ /* Remember the display controller that is sending us video */
+ priv->dc_dev = disp_uc_plat->src_dev;
+
return 0;
}
diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra124/sor.c
index aa3d80c..e5cea51 100644
--- a/drivers/video/tegra124/sor.c
+++ b/drivers/video/tegra124/sor.c
@@ -5,9 +5,12 @@
*/
#include <common.h>
+#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
+#include <panel.h>
+#include <video_bridge.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch-tegra/dc.h>
@@ -37,6 +40,14 @@ DECLARE_GLOBAL_DATA_PTR;
#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF (0 << 25)
#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON (1 << 25)
+struct tegra_dc_sor_data {
+ void *base;
+ void *pmc_base;
+ u8 portnum; /* 0 or 1 */
+ int power_is_up;
+ struct udevice *panel;
+};
+
static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg)
{
return readl((u32 *)sor->base + reg);
@@ -57,15 +68,19 @@ static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor,
tegra_sor_writel(sor, reg, reg_val);
}
-void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor)
+void tegra_dp_disable_tx_pu(struct udevice *dev)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE);
}
-void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask, u32 pe_reg,
+void tegra_dp_set_pe_vs_pc(struct udevice *dev, u32 mask, u32 pe_reg,
u32 vs_reg, u32 pc_reg, u8 pc_supported)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg);
tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg);
if (pc_supported) {
@@ -95,8 +110,9 @@ static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg,
return -ETIMEDOUT;
}
-int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd)
+int tegra_dc_sor_set_power_state(struct udevice *dev, int pu_pd)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 reg_val;
u32 orig_val;
@@ -123,10 +139,11 @@ int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd)
return 0;
}
-void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
+void tegra_dc_sor_set_dp_linkctl(struct udevice *dev, int ena,
u8 training_pattern,
const struct tegra_dp_link_config *link_cfg)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 reg_val;
reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
@@ -194,9 +211,10 @@ static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor,
return 0;
}
-static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor,
+static int tegra_dc_sor_power_dplanes(struct udevice *dev,
u32 lane_count, int pu)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 reg_val;
reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
@@ -218,15 +236,15 @@ static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor,
}
tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
- tegra_dc_sor_set_lane_count(sor, lane_count);
+ tegra_dc_sor_set_lane_count(dev, lane_count);
}
return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0);
}
-void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
- int power_up)
+void tegra_dc_sor_set_panel_power(struct udevice *dev, int power_up)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 reg_val;
reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
@@ -255,14 +273,15 @@ static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div,
}
}
-static void tegra_dc_sor_set_dp_mode(struct tegra_dc_sor_data *sor,
+static void tegra_dc_sor_set_dp_mode(struct udevice *dev,
const struct tegra_dp_link_config *link_cfg)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 reg_val;
- tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
+ tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
- tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg);
+ tegra_dc_sor_set_dp_linkctl(dev, 1, training_pattern_none, link_cfg);
reg_val = tegra_sor_readl(sor, DP_CONFIG(sor->portnum));
reg_val &= ~DP_CONFIG_WATERMARK_MASK;
reg_val |= link_cfg->watermark;
@@ -351,8 +370,9 @@ static int tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up)
return 0;
}
-void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int)
+void tegra_dc_sor_set_internal_panel(struct udevice *dev, int is_int)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 reg_val;
reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum));
@@ -366,9 +386,10 @@ void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int)
tegra_sor_writel(sor, DP_SPARE(sor->portnum), reg_val);
}
-void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
+void tegra_dc_sor_read_link_config(struct udevice *dev, u8 *link_bw,
u8 *lane_count)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 reg_val;
reg_val = tegra_sor_readl(sor, CLK_CNTRL);
@@ -395,15 +416,18 @@ void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
}
}
-void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw)
+void tegra_dc_sor_set_link_bandwidth(struct udevice *dev, u8 link_bw)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
tegra_sor_write_field(sor, CLK_CNTRL,
CLK_CNTRL_DP_LINK_SPEED_MASK,
link_bw << CLK_CNTRL_DP_LINK_SPEED_SHIFT);
}
-void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count)
+void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 reg_val;
reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
@@ -439,15 +463,16 @@ void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count)
* 4 1 0 0 0 0 0 1
* 5 0 0 0 0 0 0 1
*/
-static int tegra_dc_sor_power_up(struct tegra_dc_sor_data *sor, int is_lvds)
+static int tegra_dc_sor_power_up(struct udevice *dev, int is_lvds)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
int ret;
if (sor->power_is_up)
return 0;
/* Set link bw */
- tegra_dc_sor_set_link_bandwidth(sor, is_lvds ?
+ tegra_dc_sor_set_link_bandwidth(dev, is_lvds ?
CLK_CNTRL_DP_LINK_SPEED_LVDS :
CLK_CNTRL_DP_LINK_SPEED_G1_62);
@@ -655,9 +680,10 @@ static void tegra_dc_sor_enable_dc(struct dc_ctlr *disp_ctrl)
writel(reg_val, &disp_ctrl->cmd.state_access);
}
-int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_enable_dp(struct udevice *dev,
const struct tegra_dp_link_config *link_cfg)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
int ret;
tegra_sor_write_field(sor, CLK_CNTRL,
@@ -701,7 +727,7 @@ int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
PLL2_AUX2_OVERRIDE_POWERDOWN |
PLL2_AUX7_PORT_POWERDOWN_DISABLE);
- ret = tegra_dc_sor_power_up(sor, 0);
+ ret = tegra_dc_sor_power_up(dev, 0);
if (ret) {
debug("DP failed to power up\n");
return ret;
@@ -711,18 +737,19 @@ int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
clock_sor_enable_edp_clock();
/* Power up lanes */
- tegra_dc_sor_power_dplanes(sor, link_cfg->lane_count, 1);
+ tegra_dc_sor_power_dplanes(dev, link_cfg->lane_count, 1);
- tegra_dc_sor_set_dp_mode(sor, link_cfg);
+ tegra_dc_sor_set_dp_mode(dev, link_cfg);
debug("%s ret\n", __func__);
return 0;
}
-int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_attach(struct udevice *dc_dev, struct udevice *dev,
const struct tegra_dp_link_config *link_cfg,
const struct display_timing *timing)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
struct dc_ctlr *disp_ctrl;
u32 reg_val;
@@ -730,9 +757,7 @@ int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
/* Use the first display controller */
debug("%s\n", __func__);
- node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
- if (node < 0)
- return -ENOENT;
+ node = dc_dev->of_offset;
disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
tegra_dc_sor_enable_dc(disp_ctrl);
@@ -798,9 +823,11 @@ int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
return 0;
}
-void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
+void tegra_dc_sor_set_lane_parm(struct udevice *dev,
const struct tegra_dp_link_config *link_cfg)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
link_cfg->drive_current);
tegra_sor_writel(sor, PR(sor->portnum),
@@ -809,8 +836,8 @@ void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
link_cfg->postcursor);
tegra_sor_writel(sor, LVDS, 0);
- tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
- tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count);
+ tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
+ tegra_dc_sor_set_lane_count(dev, link_cfg->lane_count);
tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
DP_PADCTL_TX_PU_ENABLE |
@@ -825,9 +852,10 @@ void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0);
}
-int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_set_voltage_swing(struct udevice *dev,
const struct tegra_dp_link_config *link_cfg)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 drive_current = 0;
u32 pre_emphasis = 0;
@@ -851,9 +879,10 @@ int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
return 0;
}
-void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
+void tegra_dc_sor_power_down_unused_lanes(struct udevice *dev,
const struct tegra_dp_link_config *link_cfg)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 pad_ctrl = 0;
int err = 0;
@@ -891,9 +920,10 @@ void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
}
}
-int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+int tegra_sor_precharge_lanes(struct udevice *dev,
const struct tegra_dp_link_config *cfg)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
u32 val = 0;
switch (cfg->lane_count) {
@@ -931,8 +961,9 @@ static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable)
writel(reg_val, &disp_ctrl->disp.disp_win_opt);
}
-int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor)
+int tegra_dc_sor_detach(struct udevice *dc_dev, struct udevice *dev)
{
+ struct tegra_dc_sor_data *sor = dev_get_priv(dev);
int dc_reg_ctx[DC_REG_SAVE_SPACE];
const void *blob = gd->fdt_blob;
struct dc_ctlr *disp_ctrl;
@@ -942,11 +973,7 @@ int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor)
debug("%s\n", __func__);
/* Use the first display controller */
- node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
- if (node < 0) {
- ret = -ENOENT;
- goto err;
- }
+ node = dc_dev->of_offset;
disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
/* Sleep mode */
@@ -997,28 +1024,61 @@ err:
return ret;
}
-int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp)
+static int tegra_sor_set_backlight(struct udevice *dev, int percent)
{
+ struct tegra_dc_sor_data *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = panel_enable_backlight(priv->panel);
+ if (ret) {
+ debug("sor: Cannot enable panel backlight\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tegra_sor_ofdata_to_platdata(struct udevice *dev)
+{
+ struct tegra_dc_sor_data *priv = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
- struct tegra_dc_sor_data *sor;
int node;
+ int ret;
- node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR);
- if (node < 0)
- return -ENOENT;
- sor = calloc(1, sizeof(*sor));
- if (!sor)
- return -ENOMEM;
- sor->base = (void *)fdtdec_get_addr(blob, node, "reg");
+ priv->base = (void *)fdtdec_get_addr(blob, dev->of_offset, "reg");
node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_PMC);
- if (node < 0)
+ if (node < 0) {
+ debug("%s: Cannot find PMC\n", __func__);
return -ENOENT;
- sor->pmc_base = (void *)fdtdec_get_addr(blob, node, "reg");
+ }
+ priv->pmc_base = (void *)fdtdec_get_addr(blob, node, "reg");
- sor->power_is_up = 0;
- sor->portnum = 0;
- *sorp = sor;
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "nvidia,panel",
+ &priv->panel);
+ if (ret) {
+ debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
+ dev->name, ret);
+ return ret;
+ }
return 0;
}
+
+static const struct video_bridge_ops tegra_sor_ops = {
+ .set_backlight = tegra_sor_set_backlight,
+};
+
+static const struct udevice_id tegra_sor_ids[] = {
+ { .compatible = "nvidia,tegra124-sor" },
+ { }
+};
+
+U_BOOT_DRIVER(sor_tegra) = {
+ .name = "sor_tegra",
+ .id = UCLASS_VIDEO_BRIDGE,
+ .of_match = tegra_sor_ids,
+ .ofdata_to_platdata = tegra_sor_ofdata_to_platdata,
+ .ops = &tegra_sor_ops,
+ .priv_auto_alloc_size = sizeof(struct tegra_dc_sor_data),
+};
diff --git a/drivers/video/tegra124/sor.h b/drivers/video/tegra124/sor.h
index dc8fd03..e854bef 100644
--- a/drivers/video/tegra124/sor.h
+++ b/drivers/video/tegra124/sor.h
@@ -873,44 +873,37 @@ struct tegra_dp_link_config {
u8 tps3_supported;
};
-struct tegra_dc_sor_data {
- void *base;
- void *pmc_base;
- u8 portnum; /* 0 or 1 */
- int power_is_up;
-};
-
#define TEGRA_SOR_TIMEOUT_MS 1000
#define TEGRA_SOR_ATTACH_TIMEOUT_MS 1000
-int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_enable_dp(struct udevice *sor,
const struct tegra_dp_link_config *link_cfg);
-int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd);
-void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
+int tegra_dc_sor_set_power_state(struct udevice *sor, int pu_pd);
+void tegra_dc_sor_set_dp_linkctl(struct udevice *dev, int ena,
u8 training_pattern, const struct tegra_dp_link_config *link_cfg);
-void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw);
-void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count);
-void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
+void tegra_dc_sor_set_link_bandwidth(struct udevice *dev, u8 link_bw);
+void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count);
+void tegra_dc_sor_set_panel_power(struct udevice *sor,
int power_up);
-void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int);
-void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
+void tegra_dc_sor_set_internal_panel(struct udevice *dev, int is_int);
+void tegra_dc_sor_read_link_config(struct udevice *dev, u8 *link_bw,
u8 *lane_count);
-void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
- const struct tegra_dp_link_config *link_cfg);
-void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
+void tegra_dc_sor_set_lane_parm(struct udevice *dev,
+ const struct tegra_dp_link_config *link_cfg);
+void tegra_dc_sor_power_down_unused_lanes(struct udevice *sor,
const struct tegra_dp_link_config *link_cfg);
-int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_set_voltage_swing(struct udevice *sor,
const struct tegra_dp_link_config *link_cfg);
-int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+int tegra_sor_precharge_lanes(struct udevice *dev,
const struct tegra_dp_link_config *cfg);
-void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor);
-void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask,
- u32 pe_reg, u32 vs_reg, u32 pc_reg, u8 pc_supported);
+void tegra_dp_disable_tx_pu(struct udevice *sor);
+void tegra_dp_set_pe_vs_pc(struct udevice *dev, u32 mask, u32 pe_reg,
+ u32 vs_reg, u32 pc_reg, u8 pc_supported);
-int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_attach(struct udevice *dc_dev, struct udevice *sor,
const struct tegra_dp_link_config *link_cfg,
const struct display_timing *timing);
-int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor);
+int tegra_dc_sor_detach(struct udevice *dc_dev, struct udevice *sor);
void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl,
int *dc_reg_ctx);
@@ -918,5 +911,5 @@ int tegra_dc_sor_general_act(struct dc_ctlr *disp_ctrl);
void tegra_dc_sor_restore_win_and_raster(struct dc_ctlr *disp_ctrl,
int *dc_reg_ctx);
-int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp);
+int tegra_dc_sor_init(struct udevice **sorp);
#endif
diff --git a/drivers/video/tegra124/tegra124-lcd.c b/drivers/video/tegra124/tegra124-lcd.c
deleted file mode 100644
index cfdc77f..0000000
--- a/drivers/video/tegra124/tegra124-lcd.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- */
-
-#include <common.h>
-#include <errno.h>
-#include <fdtdec.h>
-#include <lcd.h>
-#include <asm/gpio.h>
-#include <asm/arch-tegra/clk_rst.h>
-#include <asm/arch/clock.h>
-#include <asm/arch-tegra/dc.h>
-#include <asm/io.h>
-
-DECLARE_GLOBAL_DATA_PTR;
-
-enum {
- /* Maximum LCD size we support */
- LCD_MAX_WIDTH = 1920,
- LCD_MAX_HEIGHT = 1200,
- LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
-};
-
-vidinfo_t panel_info = {
- /* Insert a value here so that we don't end up in the BSS */
- .vl_col = -1,
-};
-
-int tegra_lcd_check_next_stage(const void *blob, int wait)
-{
- return 0;
-}
-
-void tegra_lcd_early_init(const void *blob)
-{
- /*
- * Go with the maximum size for now. We will fix this up after
- * relocation. These values are only used for memory alocation.
- */
- panel_info.vl_col = LCD_MAX_WIDTH;
- panel_info.vl_row = LCD_MAX_HEIGHT;
- panel_info.vl_bpix = LCD_MAX_LOG2_BPP;
-}
-
-static int tegra124_lcd_init(void *lcdbase)
-{
- struct display_timing timing;
- int ret;
-
- clock_set_up_plldp();
- clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, 408000000);
-
- clock_enable(PERIPH_ID_HOST1X);
- clock_enable(PERIPH_ID_DISP1);
- clock_enable(PERIPH_ID_PWM);
- clock_enable(PERIPH_ID_DPAUX);
- clock_enable(PERIPH_ID_SOR0);
- udelay(2);
-
- reset_set_enable(PERIPH_ID_HOST1X, 0);
- reset_set_enable(PERIPH_ID_DISP1, 0);
- reset_set_enable(PERIPH_ID_PWM, 0);
- reset_set_enable(PERIPH_ID_DPAUX, 0);
- reset_set_enable(PERIPH_ID_SOR0, 0);
-
- ret = display_init(lcdbase, 1 << LCD_BPP, &timing);
- if (ret)
- return ret;
-
- panel_info.vl_col = roundup(timing.hactive.typ, 16);
- panel_info.vl_row = timing.vactive.typ;
-
- lcd_set_flush_dcache(1);
-
- return 0;
-}
-
-void lcd_ctrl_init(void *lcdbase)
-{
- ulong start;
- int ret;
-
- start = get_timer(0);
- ret = tegra124_lcd_init(lcdbase);
- debug("LCD init took %lu ms\n", get_timer(start));
- if (ret)
- printf("%s: Error %d\n", __func__, ret);
-}
-
-void lcd_enable(void)
-{
-}
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index f6326b6..832e90a 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -170,6 +170,7 @@ static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
while (*s)
vidconsole_put_char(dev, *s++);
+ video_sync(dev->parent);
}
/* Set up the number of rows and colours (rotated drivers override this) */