tegra132: Add dsi driver
Add dsi and related dc, panel configuration functions. BRANCH=none BUG=chrome-os-partner:31936 TEST=build and test on ryu Change-Id: I8440b6dfccc7ed7cd280a0df3a98cbc7b7d66070 Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: fb08563f67daf9a616b60609c4523b823d34f8e3 Original-Change-Id: I87b8047e23ebe114af353fcce5924a46621d16d2 Original-Signed-off-by: Jimmy Zhang <jimmzhang@nvidia.com> Original-Reviewed-on: https://chromium-review.googlesource.com/227202 Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org> Original-Commit-Queue: Aaron Durbin <adurbin@chromium.org> Reviewed-on: http://review.coreboot.org/9517 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
51067116eb
commit
e3a938dfdd
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#ifndef __SOC_NVIDIA_TEGRA_DC_H
|
#ifndef __SOC_NVIDIA_TEGRA_DC_H
|
||||||
#define __SOC_NVIDIA_TEGRA_DC_H
|
#define __SOC_NVIDIA_TEGRA_DC_H
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
/* Register definitions for the Tegra display controller */
|
/* Register definitions for the Tegra display controller */
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __TEGRA_MISC_TYPES_H__
|
||||||
|
#define __TEGRA_MISC_TYPES_H__
|
||||||
|
|
||||||
|
#define EFAULT 1
|
||||||
|
#define EINVAL 2
|
||||||
|
#define ETIMEDOUT 3
|
||||||
|
#define ENOSPC 4
|
||||||
|
#define ENOSYS 5
|
||||||
|
#define EPTR 6
|
||||||
|
|
||||||
|
#define IS_ERR_PTR(ptr) \
|
||||||
|
(ptr == (void *)-EPTR)
|
||||||
|
|
||||||
|
#ifndef bool
|
||||||
|
#define bool int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef false
|
||||||
|
#define false 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef true
|
||||||
|
#define true 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef container_of
|
||||||
|
/**
|
||||||
|
* container_of - cast a member of a structure out to the containing structure
|
||||||
|
* @ptr: the pointer to the member.
|
||||||
|
* @type: the type of the container struct this is embedded in.
|
||||||
|
* @member: the name of the member within the struct.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define container_of(ptr, type, member) ({ \
|
||||||
|
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||||
|
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Divide positive or negative dividend by positive divisor and round
|
||||||
|
* to closest integer. Result is undefined for negative divisors and
|
||||||
|
* for negative dividends if the divisor variable type is unsigned.
|
||||||
|
*/
|
||||||
|
#define DIV_ROUND_CLOSEST(x, divisor)( \
|
||||||
|
{ \
|
||||||
|
typeof(x) __x = x; \
|
||||||
|
typeof(divisor) __d = divisor; \
|
||||||
|
(((typeof(x))-1) > 0 || \
|
||||||
|
((typeof(divisor))-1) > 0 || (__x) > 0) ? \
|
||||||
|
(((__x) + ((__d) / 2)) / (__d)) : \
|
||||||
|
(((__x) - ((__d) / 2)) / (__d)); \
|
||||||
|
} \
|
||||||
|
)
|
||||||
|
|
||||||
|
#endif /* __TEGRA_MISC_TYPES_H__ */
|
|
@ -66,6 +66,13 @@ ramstage-y += cbmem.c
|
||||||
ramstage-y += cpu.c
|
ramstage-y += cpu.c
|
||||||
ramstage-y += cpu_lib.S
|
ramstage-y += cpu_lib.S
|
||||||
ramstage-y += clock.c
|
ramstage-y += clock.c
|
||||||
|
ramstage-y += display.c
|
||||||
|
ramstage-y += tegra_dsi.c
|
||||||
|
ramstage-y += mipi_dsi.c
|
||||||
|
ramstage-y += mipi.c
|
||||||
|
ramstage-y += mipi-phy.c
|
||||||
|
ramstage-y += ./jdi_25x18_display/panel-jdi-lpm102a188a.c
|
||||||
|
|
||||||
ramstage-y += soc.c
|
ramstage-y += soc.c
|
||||||
ramstage-y += spi.c
|
ramstage-y += spi.c
|
||||||
ramstage-y += i2c.c
|
ramstage-y += i2c.c
|
||||||
|
|
|
@ -19,13 +19,9 @@
|
||||||
|
|
||||||
#ifndef __SOC_NVIDIA_TEGRA132_CHIP_H__
|
#ifndef __SOC_NVIDIA_TEGRA132_CHIP_H__
|
||||||
#define __SOC_NVIDIA_TEGRA132_CHIP_H__
|
#define __SOC_NVIDIA_TEGRA132_CHIP_H__
|
||||||
#include <arch/cache.h>
|
|
||||||
#include <soc/addressmap.h>
|
#include <soc/addressmap.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define EFAULT 1
|
|
||||||
#define EINVAL 2
|
|
||||||
|
|
||||||
struct soc_nvidia_tegra132_config {
|
struct soc_nvidia_tegra132_config {
|
||||||
/* Address to monitor if spintable employed. */
|
/* Address to monitor if spintable employed. */
|
||||||
uintptr_t spintable_addr;
|
uintptr_t spintable_addr;
|
||||||
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <delay.h>
|
||||||
|
#include <soc/addressmap.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/i2c.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <cpu/cpu.h>
|
||||||
|
#include <boot/tables.h>
|
||||||
|
#include <cbmem.h>
|
||||||
|
#include <soc/clock.h>
|
||||||
|
#include <soc/nvidia/tegra/dc.h>
|
||||||
|
#include <soc/funitcfg.h>
|
||||||
|
#include "chip.h"
|
||||||
|
#include <soc/display.h>
|
||||||
|
|
||||||
|
int dump = 0;
|
||||||
|
unsigned long READL(void * p)
|
||||||
|
{
|
||||||
|
unsigned long value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In case of hard hung on readl(p), we can set dump > 1 to print out
|
||||||
|
* the address accessed.
|
||||||
|
*/
|
||||||
|
if (dump > 1)
|
||||||
|
printk(BIOS_SPEW, "readl %p\n", p);
|
||||||
|
|
||||||
|
value = readl(p);
|
||||||
|
if (dump)
|
||||||
|
printk(BIOS_SPEW, "readl %p %08lx\n", p, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WRITEL(unsigned long value, void * p)
|
||||||
|
{
|
||||||
|
if (dump)
|
||||||
|
printk(BIOS_SPEW, "writel %p %08lx\n", p, value);
|
||||||
|
writel(value, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return in 1000ths of a Hertz */
|
||||||
|
static int tegra_calc_refresh(const struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
int refresh;
|
||||||
|
int h_total = htotal(config);
|
||||||
|
int v_total = vtotal(config);
|
||||||
|
int pclk = config->pixel_clock;
|
||||||
|
|
||||||
|
if (!pclk || !h_total || !v_total)
|
||||||
|
return 0;
|
||||||
|
refresh = pclk / h_total;
|
||||||
|
refresh *= 1000;
|
||||||
|
refresh /= v_total;
|
||||||
|
return refresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_mode(const struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
if (config) {
|
||||||
|
int refresh = tegra_calc_refresh(config);
|
||||||
|
printk(BIOS_ERR,
|
||||||
|
"MODE:%dx%d@%d.%03uHz pclk=%d\n",
|
||||||
|
config->xres, config->yres,
|
||||||
|
refresh / 1000, refresh % 1000,
|
||||||
|
config->pixel_clock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int update_display_mode(struct display_controller *disp_ctrl,
|
||||||
|
struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
print_mode(config);
|
||||||
|
|
||||||
|
printk(BIOS_ERR, "config: xres:yres: %d x %d\n ",
|
||||||
|
config->xres, config->yres);
|
||||||
|
printk(BIOS_ERR, " href_sync:vref_sync: %d x %d\n ",
|
||||||
|
config->href_to_sync, config->vref_to_sync);
|
||||||
|
printk(BIOS_ERR, " hsyn_width:vsyn_width: %d x %d\n ",
|
||||||
|
config->hsync_width, config->vsync_width);
|
||||||
|
printk(BIOS_ERR, " hfnt_porch:vfnt_porch: %d x %d\n ",
|
||||||
|
config->hfront_porch, config->vfront_porch);
|
||||||
|
printk(BIOS_ERR, " hbk_porch:vbk_porch: %d x %d\n ",
|
||||||
|
config->hback_porch, config->vback_porch);
|
||||||
|
|
||||||
|
WRITEL(0x0, &disp_ctrl->disp.disp_timing_opt);
|
||||||
|
WRITEL(0x0, &disp_ctrl->disp.disp_color_ctrl);
|
||||||
|
|
||||||
|
// select DSI
|
||||||
|
WRITEL(DSI_ENABLE, &disp_ctrl->disp.disp_win_opt);
|
||||||
|
|
||||||
|
WRITEL(config->vref_to_sync << 16 | config->href_to_sync,
|
||||||
|
&disp_ctrl->disp.ref_to_sync);
|
||||||
|
|
||||||
|
WRITEL(config->vsync_width << 16 | config->hsync_width,
|
||||||
|
&disp_ctrl->disp.sync_width);
|
||||||
|
|
||||||
|
|
||||||
|
WRITEL((config->vback_porch << 16) | config->hback_porch,
|
||||||
|
&disp_ctrl->disp.back_porch);
|
||||||
|
|
||||||
|
WRITEL((config->vfront_porch << 16) | config->hfront_porch,
|
||||||
|
&disp_ctrl->disp.front_porch);
|
||||||
|
|
||||||
|
WRITEL(config->xres | (config->yres << 16),
|
||||||
|
&disp_ctrl->disp.disp_active);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We want to use PLLD_out0, which is PLLD / 2:
|
||||||
|
* PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv.
|
||||||
|
*
|
||||||
|
* Currently most panels work inside clock range 50MHz~100MHz, and PLLD
|
||||||
|
* has some requirements to have VCO in range 500MHz~1000MHz (see
|
||||||
|
* clock.c for more detail). To simplify calculation, we set
|
||||||
|
* PixelClockDiv to 1 and ShiftClockDiv to 1. In future these values
|
||||||
|
* may be calculated by clock_display, to allow wider frequency range.
|
||||||
|
*
|
||||||
|
* Note ShiftClockDiv is a 7.1 format value.
|
||||||
|
*/
|
||||||
|
const u32 shift_clock_div = 1;
|
||||||
|
WRITEL((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
|
||||||
|
((shift_clock_div - 1) * 2 + 1) << SHIFT_CLK_DIVIDER_SHIFT,
|
||||||
|
&disp_ctrl->disp.disp_clk_ctrl);
|
||||||
|
printk(BIOS_DEBUG, "%s: PixelClock=%u, ShiftClockDiv=%u\n",
|
||||||
|
__func__, config->pixel_clock, shift_clock_div);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* update_window:
|
||||||
|
* set up window registers and activate window except two:
|
||||||
|
* frame buffer base address register (WINBUF_START_ADDR) and
|
||||||
|
* display enable register (_DISP_DISP_WIN_OPTIONS). This is
|
||||||
|
* becasue framebuffer is not available until payload stage.
|
||||||
|
*/
|
||||||
|
static void update_window(const struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
struct display_controller *disp_ctrl =
|
||||||
|
(void *)config->display_controller;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
|
||||||
|
|
||||||
|
WRITEL(((config->yres << 16) | config->xres), &disp_ctrl->win.size);
|
||||||
|
WRITEL(((config->yres << 16) |
|
||||||
|
(config->xres * config->framebuffer_bits_per_pixel / 8)),
|
||||||
|
&disp_ctrl->win.prescaled_size);
|
||||||
|
|
||||||
|
val = ALIGN_UP((config->xres * config->framebuffer_bits_per_pixel / 8),
|
||||||
|
64);
|
||||||
|
WRITEL(val, &disp_ctrl->win.line_stride);
|
||||||
|
|
||||||
|
WRITEL(config->color_depth, &disp_ctrl->win.color_depth);
|
||||||
|
WRITEL(COLOR_BLACK, &disp_ctrl->disp.blend_background_color);
|
||||||
|
WRITEL((V_DDA_INC(0x1000) | H_DDA_INC(0x1000)),
|
||||||
|
&disp_ctrl->win.dda_increment);
|
||||||
|
|
||||||
|
WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd);
|
||||||
|
|
||||||
|
WRITEL(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
|
||||||
|
|
||||||
|
WRITEL(0, &disp_ctrl->win.buffer_addr_mode);
|
||||||
|
|
||||||
|
val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
|
||||||
|
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
|
||||||
|
WRITEL(val, &disp_ctrl->cmd.disp_pow_ctrl);
|
||||||
|
|
||||||
|
val = GENERAL_UPDATE | WIN_A_UPDATE;
|
||||||
|
val |= GENERAL_ACT_REQ | WIN_A_ACT_REQ;
|
||||||
|
WRITEL(val, &disp_ctrl->cmd.state_ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dc_init(struct display_controller *disp_ctrl)
|
||||||
|
{
|
||||||
|
/* do not accept interrupts during initialization */
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->cmd.int_mask);
|
||||||
|
WRITEL(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY,
|
||||||
|
&disp_ctrl->cmd.state_access);
|
||||||
|
WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.win_opt);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.byte_swap);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.buffer_ctrl);
|
||||||
|
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.pos);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.h_initial_dda);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.v_initial_dda);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.dda_increment);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.dv_ctrl);
|
||||||
|
|
||||||
|
WRITEL(0x01000000, &disp_ctrl->win.blend_layer_ctrl);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.blend_match_select);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.blend_nomatch_select);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->win.blend_alpha_1bit);
|
||||||
|
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->winbuf.start_addr_hi);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->winbuf.addr_h_offset);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->winbuf.addr_v_offset);
|
||||||
|
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->com.crc_checksum);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[0]);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[1]);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[2]);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[3]);
|
||||||
|
WRITEL(0x00000000, &disp_ctrl->disp.disp_signal_opt0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_startup(device_t dev)
|
||||||
|
{
|
||||||
|
struct soc_nvidia_tegra132_config *config = dev->chip_info;
|
||||||
|
struct display_controller *disp_ctrl =
|
||||||
|
(void *)config->display_controller;
|
||||||
|
u32 plld_rate;
|
||||||
|
|
||||||
|
u32 framebuffer_size_mb = config->framebuffer_size / MiB;
|
||||||
|
u32 framebuffer_base_mb= config->framebuffer_base / MiB;
|
||||||
|
|
||||||
|
printk(BIOS_INFO, "%s: entry: disp_ctrl: %p.\n",
|
||||||
|
__func__, disp_ctrl);
|
||||||
|
|
||||||
|
if (disp_ctrl == NULL) {
|
||||||
|
printk(BIOS_ERR, "Error: No dc is assigned by dt.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (framebuffer_size_mb == 0){
|
||||||
|
framebuffer_size_mb = ALIGN_UP(config->xres * config->yres *
|
||||||
|
(config->framebuffer_bits_per_pixel / 8), MiB)/MiB;
|
||||||
|
}
|
||||||
|
|
||||||
|
config->framebuffer_size = framebuffer_size_mb * MiB;
|
||||||
|
config->framebuffer_base = framebuffer_base_mb * MiB;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER
|
||||||
|
* and PIXEL_CLK_DIVIDER are zero (divide by 1). See the
|
||||||
|
* update_display_mode() for detail.
|
||||||
|
*/
|
||||||
|
/* set default plld */
|
||||||
|
plld_rate = clock_display(config->pixel_clock * 2);
|
||||||
|
if (plld_rate == 0) {
|
||||||
|
printk(BIOS_ERR, "dc: clock init failed\n");
|
||||||
|
return;
|
||||||
|
} else if (plld_rate != config->pixel_clock * 2) {
|
||||||
|
printk(BIOS_WARNING, "dc: plld rounded to %u\n", plld_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set disp1's clock source to PLLD_OUT0 */
|
||||||
|
clock_configure_source(disp1, PLLD, (plld_rate/KHz)/2);
|
||||||
|
|
||||||
|
/* Init dc */
|
||||||
|
if (tegra_dc_init(disp_ctrl)) {
|
||||||
|
printk(BIOS_ERR, "dc: init failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure dc mode */
|
||||||
|
if (update_display_mode(disp_ctrl, config)) {
|
||||||
|
printk(BIOS_ERR, "dc: failed to configure display mode.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure and enable dsi controller and panel */
|
||||||
|
if (dsi_enable(config)) {
|
||||||
|
printk(BIOS_ERR, "%s: failed to enable dsi controllers.\n",
|
||||||
|
__func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set up window */
|
||||||
|
update_window(config);
|
||||||
|
|
||||||
|
printk(BIOS_INFO, "%s: display init done.\n", __func__);
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ enum {
|
||||||
TEGRA_GICC_BASE = 0x50042000,
|
TEGRA_GICC_BASE = 0x50042000,
|
||||||
TEGRA_ARM_DISPLAYA = 0x54200000,
|
TEGRA_ARM_DISPLAYA = 0x54200000,
|
||||||
TEGRA_ARM_DISPLAYB = 0x54240000,
|
TEGRA_ARM_DISPLAYB = 0x54240000,
|
||||||
|
TEGRA_DSIA_BASE = 0x54300000,
|
||||||
|
TEGRA_DSIB_BASE = 0x54400000,
|
||||||
TEGRA_ARM_SOR = 0x54540000,
|
TEGRA_ARM_SOR = 0x54540000,
|
||||||
TEGRA_ARM_DPAUX = 0x545c0000,
|
TEGRA_ARM_DPAUX = 0x545c0000,
|
||||||
TEGRA_PG_UP_BASE = 0x60000000,
|
TEGRA_PG_UP_BASE = 0x60000000,
|
||||||
|
@ -85,6 +87,7 @@ enum {
|
||||||
TEGRA_SDMMC2_BASE = TEGRA_SDMMC_BASE + 0x0200,
|
TEGRA_SDMMC2_BASE = TEGRA_SDMMC_BASE + 0x0200,
|
||||||
TEGRA_SDMMC3_BASE = TEGRA_SDMMC_BASE + 0x0400,
|
TEGRA_SDMMC3_BASE = TEGRA_SDMMC_BASE + 0x0400,
|
||||||
TEGRA_SDMMC4_BASE = TEGRA_SDMMC_BASE + 0x0600,
|
TEGRA_SDMMC4_BASE = TEGRA_SDMMC_BASE + 0x0600,
|
||||||
|
TEGRA_MIPI_CAL_BASE = 0x700E3000,
|
||||||
TEGRA_SYSCTR0_BASE = 0x700F0000,
|
TEGRA_SYSCTR0_BASE = 0x700F0000,
|
||||||
TEGRA_USBD_BASE = 0x7D000000,
|
TEGRA_USBD_BASE = 0x7D000000,
|
||||||
TEGRA_USB2_BASE = 0x7D004000,
|
TEGRA_USB2_BASE = 0x7D004000,
|
||||||
|
|
|
@ -17,184 +17,25 @@
|
||||||
#ifndef __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__
|
#ifndef __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__
|
||||||
#define __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__
|
#define __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__
|
||||||
|
|
||||||
/* ardisplay.h */
|
|
||||||
#define DC_CMD_DISPLAY_WINDOW_HEADER_0 0x42
|
|
||||||
#define DC_COM_CRC_CONTROL_0 0x300
|
|
||||||
#define DC_COM_CRC_CHECKSUM_0 0x301
|
|
||||||
#define DC_COM_PIN_OUTPUT_ENABLE0_0 0x302
|
|
||||||
#define DC_COM_PIN_OUTPUT_ENABLE1_0 0x303
|
|
||||||
#define DC_COM_PIN_OUTPUT_ENABLE2_0 0x304
|
|
||||||
#define DC_COM_PIN_OUTPUT_ENABLE3_0 0x305
|
|
||||||
#define DC_CMD_STATE_ACCESS_0 0x40
|
|
||||||
#define DC_DISP_DISP_CLOCK_CONTROL_0 0x42e
|
|
||||||
#define DC_DISP_DISP_TIMING_OPTIONS_0 0x405
|
|
||||||
#define DC_DISP_REF_TO_SYNC_0 0x406
|
|
||||||
#define DC_DISP_SYNC_WIDTH_0 0x407
|
|
||||||
#define DC_DISP_BACK_PORCH_0 0x408
|
|
||||||
#define DC_DISP_DISP_ACTIVE_0 0x409
|
|
||||||
#define DC_DISP_FRONT_PORCH_0 0x40a
|
|
||||||
#define DC_DISP_DISP_WIN_OPTIONS_0 0x402
|
|
||||||
#define DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_SHIFT 25
|
|
||||||
#define DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_FIELD (0x1 << DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_SHIFT)
|
|
||||||
#define DC_DISP_DISP_SIGNAL_OPTIONS0_0 0x400
|
|
||||||
#define DC_DISP_BLEND_BACKGROUND_COLOR_0 0x4e4
|
|
||||||
#define DC_CMD_DISPLAY_COMMAND_0 0x32
|
|
||||||
#define DC_CMD_STATE_CONTROL_0 0x41
|
|
||||||
#define DC_CMD_DISPLAY_POWER_CONTROL_0 0x36
|
|
||||||
|
|
||||||
/* ardisplay_a.h */
|
|
||||||
#define DC_WIN_A_WIN_OPTIONS_0 0x700
|
|
||||||
#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_SHIFT 30
|
|
||||||
#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_FIELD (0x1 << DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_SHIFT)
|
|
||||||
#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_ENABLE (1)
|
|
||||||
#define DC_WIN_A_BYTE_SWAP_0 0x701
|
|
||||||
#define DC_WIN_A_BUFFER_CONTROL_0 0x702
|
|
||||||
#define DC_WIN_A_COLOR_DEPTH_0 0x703
|
|
||||||
#define DC_WIN_A_POSITION_0 0x704
|
|
||||||
#define DC_WIN_A_SIZE_0 0x705
|
|
||||||
#define DC_WIN_A_PRESCALED_SIZE_0 0x706
|
|
||||||
#define DC_WIN_A_H_INITIAL_DDA_0 0x707
|
|
||||||
#define DC_WIN_A_V_INITIAL_DDA_0 0x708
|
|
||||||
#define DC_WIN_A_DDA_INCREMENT_0 0x709
|
|
||||||
#define DC_WIN_A_LINE_STRIDE_0 0x70a
|
|
||||||
#define DC_WIN_A_DV_CONTROL_0 0x70e
|
|
||||||
#define DC_WIN_A_BLEND_LAYER_CONTROL_0 0x716
|
|
||||||
#define DC_WIN_A_BLEND_MATCH_SELECT_0 0x717
|
|
||||||
#define DC_WIN_A_BLEND_NOMATCH_SELECT_0 0x718
|
|
||||||
#define DC_WIN_A_BLEND_ALPHA_1BIT_0 0x719
|
|
||||||
#define DC_WINBUF_A_START_ADDR_LO_0 0x800
|
|
||||||
#define DC_WINBUF_A_START_ADDR_HI_0 0x80d
|
|
||||||
#define DC_WINBUF_A_ADDR_H_OFFSET_0 0x806
|
|
||||||
#define DC_WINBUF_A_ADDR_V_OFFSET_0 0x808
|
|
||||||
|
|
||||||
/* ardisplay_bd.h */
|
|
||||||
#define DC_B_WIN_BD_SIZE_0 0xd85
|
|
||||||
#define DC_B_WIN_BD_PRESCALED_SIZE_0 0xd86
|
|
||||||
#define DC_B_WIN_BD_LINE_STRIDE_0 0xd8a
|
|
||||||
#define DC_B_WIN_BD_COLOR_DEPTH_0 0xd83
|
|
||||||
#define DC_B_WINBUF_BD_START_ADDR_0 0xdc0
|
|
||||||
#define DC_B_WIN_BD_DDA_INCREMENT_0 0xd89
|
|
||||||
#define DC_B_WIN_BD_WIN_OPTIONS_0 0xd80
|
|
||||||
#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_SHIFT 30
|
|
||||||
#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_FIELD (0x1 << DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_SHIFT)
|
|
||||||
#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_ENABLE (1)
|
|
||||||
|
|
||||||
/* arsor.h */
|
|
||||||
#define SOR_NV_PDISP_SOR_CLK_CNTRL_0 0x13
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0 0x5c
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL0_0 0x17
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL1_0 0x18
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0 0x19
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL3_0 0x1a
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0_AUX6_SHIFT 22
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0_AUX6_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX6_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL0_0_PWR_SHIFT 0
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL0_0_PWR_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL0_0_PWR_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL0_0_VCOPD_SHIFT 2
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL0_0_VCOPD_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL0_0_VCOPD_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0_AUX8_SHIFT 24
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0_AUX8_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX8_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0_AUX7_SHIFT 23
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0_AUX7_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX7_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0_AUX9_SHIFT 25
|
|
||||||
#define SOR_NV_PDISP_SOR_PLL2_0_AUX9_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX9_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0 0x4e
|
|
||||||
#define SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0 0x52
|
|
||||||
#define SOR_NV_PDISP_SOR_POSTCURSOR0_0 0x56
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0 0x5c
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_SHIFT 8
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_FIELD (0xff << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_SHIFT 22
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_FIELD (0x1 << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_LVDS_0 0x1c
|
|
||||||
#define SOR_NV_PDISP_SOR_CLK_CNTRL_0 0x13
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0 0x4c
|
|
||||||
#define SOR_NV_PDISP_SOR_LANE_SEQ_CTL_0 0x21
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_TPG_0 0x6d
|
|
||||||
#define SOR_NV_PDISP_HEAD_STATE1_0 0x7
|
|
||||||
#define SOR_NV_PDISP_HEAD_STATE2_0 0x9
|
|
||||||
#define SOR_NV_PDISP_HEAD_STATE3_0 0xb
|
|
||||||
#define SOR_NV_PDISP_HEAD_STATE4_0 0xd
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0 0x4
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_SHIFT 12
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_FIELD (0x1 << SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_SHIFT 13
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_FIELD (0x1 << SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_SHIFT 8
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_LVDS_CUSTOM (0)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_DP_A (8)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_DP_B (9)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_CUSTOM (15)
|
|
||||||
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_ACTIVE_RASTER (0)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_COMPLETE_RASTER (1)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_NON_ACTIVE_RASTER (2)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_SHIFT 6
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_FIELD (0x3 << SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SHIFT 4
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_FIELD (0x3 << SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_NONE (0)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SUBHEAD0 (1)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SUBHEAD1 (2)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_BOTH (3)
|
|
||||||
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_SHIFT 0
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_NONE (0)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_HEAD0 (1)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_HEAD1 (2)
|
|
||||||
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0 0x58
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_SHIFT 24
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_FIELD (0x1 << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_SHIFT 16
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_FIELD (0xf << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_SHIFT 8
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_FIELD (0x7f << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_SHIFT 0
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_FIELD (0x3f << SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_SHIFT 2
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_FIELD (0x7f << SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_SHIFT)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT 17
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT)
|
|
||||||
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_DEFAULTVAL (0)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_16_422 (1)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_18_444 (2)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_20_422 (3)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_24_422 (4)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_24_444 (5)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_30_444 (6)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_32_422 (7)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_36_444 (8)
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_48_444 (9)
|
|
||||||
|
|
||||||
#define SOR_NV_PDISP_SOR_CRC_CNTRL_0 0x11
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_AUDIO_VBLANK_SYMBOLS_0 0x64
|
|
||||||
#define SOR_NV_PDISP_SOR_DP_SPARE0_0 0x60
|
|
||||||
#define SOR_NV_PDISP_SOR_PWR_0 0x15
|
|
||||||
#define SOR_NV_PDISP_SOR_STATE0_0 0x3
|
|
||||||
#define SOR_NV_PDISP_SOR_SUPER_STATE1_0 0x2
|
|
||||||
#define SOR_NV_PDISP_SOR_SUPER_STATE0_0 0x1
|
|
||||||
|
|
||||||
/* ardpaux.h */
|
|
||||||
#define DPAUX_DP_AUXDATA_READ_W0 0x19
|
|
||||||
|
|
||||||
#define DP_LVDS_SHIFT 25
|
|
||||||
#define DP_LVDS (1 << DP_LVDS_SHIFT)
|
|
||||||
|
|
||||||
#define SRC_BPP 16
|
|
||||||
#define COLORDEPTH 0x6
|
|
||||||
#define COLOR_WHITE 0xFFFFFF
|
#define COLOR_WHITE 0xFFFFFF
|
||||||
|
#define COLOR_BLACK 0x000000
|
||||||
|
|
||||||
|
#define hsync_start(mode) \
|
||||||
|
(mode->xres + mode->hfront_porch)
|
||||||
|
|
||||||
|
#define hsync_end(mode) \
|
||||||
|
(mode->xres + mode->hfront_porch + mode->hsync_width)
|
||||||
|
|
||||||
|
#define htotal(mode) \
|
||||||
|
(mode->xres + mode->hfront_porch + \
|
||||||
|
mode->hsync_width + mode->hback_porch)
|
||||||
|
|
||||||
|
#define vtotal(mode) \
|
||||||
|
(mode->yres + mode->vfront_porch + \
|
||||||
|
mode->vsync_width + mode->vback_porch)
|
||||||
|
|
||||||
struct soc_nvidia_tegra132_config; /* forward declaration */
|
struct soc_nvidia_tegra132_config; /* forward declaration */
|
||||||
void setup_display(struct soc_nvidia_tegra132_config *config);
|
|
||||||
void init_dca_regs(void);
|
int dsi_enable(struct soc_nvidia_tegra132_config *config);
|
||||||
void dp_io_powerup(void);
|
|
||||||
u32 dp_setup_timing(u32 width, u32 height);
|
|
||||||
void dp_misc_setting(u32 panel_bpp, u32 width, u32 height, u32 winb_addr,
|
|
||||||
u32 lane_count, u32 enhanced_framing, u32 panel_edp,
|
|
||||||
u32 pclkfreq, u32 linkfreq);
|
|
||||||
|
|
||||||
#endif /* __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__ */
|
#endif /* __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__ */
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#ifndef _TEGRA_MIPI_PHY_H
|
||||||
|
#define _TEGRA_MIPI_PHY_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Macros for calculating the phy timings
|
||||||
|
*/
|
||||||
|
/* Period of one bit time in nano seconds */
|
||||||
|
#define DSI_TBIT_Factorized(Freq) (((1000) * (1000))/(Freq))
|
||||||
|
#define DSI_TBIT(Freq) (DSI_TBIT_Factorized(Freq)/(1000))
|
||||||
|
|
||||||
|
//#define NV_MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
/* Period of one byte time in nano seconds */
|
||||||
|
#define DSI_TBYTE(Freq) ((DSI_TBIT_Factorized(Freq)) * (8))
|
||||||
|
#define DSI_PHY_TIMING_DIV(X, Freq) ((X*1000) / (DSI_TBYTE(Freq)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As per Mipi spec (minimum):
|
||||||
|
* (3 + MAX(8 * DSI_TBIT, 60 + 4 * DSI_TBIT) / DSI_TBYTE)
|
||||||
|
*/
|
||||||
|
#define DSI_THSTRAIL_VAL(Freq) \
|
||||||
|
(MAX(((8) * (DSI_TBIT(Freq))), ((60) + ((4) * (DSI_TBIT(Freq))))))
|
||||||
|
|
||||||
|
int mipi_dphy_set_timing(struct tegra_dsi *dsi);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Defines for Mobile Industry Processor Interface (MIPI(R))
|
||||||
|
* Display Working Group standards: DSI, DCS, DBI, DPI
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||||
|
* Copyright (C) 2006 Nokia Corporation
|
||||||
|
* Author: Imre Deak <imre.deak@nokia.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
#ifndef MIPI_DISPLAY_H
|
||||||
|
#define MIPI_DISPLAY_H
|
||||||
|
|
||||||
|
/* MIPI DSI Processor-to-Peripheral transaction types */
|
||||||
|
enum {
|
||||||
|
MIPI_DSI_V_SYNC_START = 0x01,
|
||||||
|
MIPI_DSI_V_SYNC_END = 0x11,
|
||||||
|
MIPI_DSI_H_SYNC_START = 0x21,
|
||||||
|
MIPI_DSI_H_SYNC_END = 0x31,
|
||||||
|
|
||||||
|
MIPI_DSI_COLOR_MODE_OFF = 0x02,
|
||||||
|
MIPI_DSI_COLOR_MODE_ON = 0x12,
|
||||||
|
MIPI_DSI_SHUTDOWN_PERIPHERAL = 0x22,
|
||||||
|
MIPI_DSI_TURN_ON_PERIPHERAL = 0x32,
|
||||||
|
|
||||||
|
MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM = 0x03,
|
||||||
|
MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM = 0x13,
|
||||||
|
MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM = 0x23,
|
||||||
|
|
||||||
|
MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM = 0x04,
|
||||||
|
MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM = 0x14,
|
||||||
|
MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM = 0x24,
|
||||||
|
|
||||||
|
MIPI_DSI_DCS_SHORT_WRITE = 0x05,
|
||||||
|
MIPI_DSI_DCS_SHORT_WRITE_PARAM = 0x15,
|
||||||
|
|
||||||
|
MIPI_DSI_DCS_READ = 0x06,
|
||||||
|
|
||||||
|
MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE = 0x37,
|
||||||
|
|
||||||
|
MIPI_DSI_END_OF_TRANSMISSION = 0x08,
|
||||||
|
|
||||||
|
MIPI_DSI_NULL_PACKET = 0x09,
|
||||||
|
MIPI_DSI_BLANKING_PACKET = 0x19,
|
||||||
|
MIPI_DSI_GENERIC_LONG_WRITE = 0x29,
|
||||||
|
MIPI_DSI_DCS_LONG_WRITE = 0x39,
|
||||||
|
|
||||||
|
MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20 = 0x0c,
|
||||||
|
MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24 = 0x1c,
|
||||||
|
MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16 = 0x2c,
|
||||||
|
|
||||||
|
MIPI_DSI_PACKED_PIXEL_STREAM_30 = 0x0d,
|
||||||
|
MIPI_DSI_PACKED_PIXEL_STREAM_36 = 0x1d,
|
||||||
|
MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12 = 0x3d,
|
||||||
|
|
||||||
|
MIPI_DSI_PACKED_PIXEL_STREAM_16 = 0x0e,
|
||||||
|
MIPI_DSI_PACKED_PIXEL_STREAM_18 = 0x1e,
|
||||||
|
MIPI_DSI_PIXEL_STREAM_3BYTE_18 = 0x2e,
|
||||||
|
MIPI_DSI_PACKED_PIXEL_STREAM_24 = 0x3e,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MIPI DSI Peripheral-to-Processor transaction types */
|
||||||
|
enum {
|
||||||
|
MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT = 0x02,
|
||||||
|
MIPI_DSI_RX_END_OF_TRANSMISSION = 0x08,
|
||||||
|
MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE = 0x11,
|
||||||
|
MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE = 0x12,
|
||||||
|
MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE = 0x1a,
|
||||||
|
MIPI_DSI_RX_DCS_LONG_READ_RESPONSE = 0x1c,
|
||||||
|
MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE = 0x21,
|
||||||
|
MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE = 0x22,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MIPI DCS commands */
|
||||||
|
enum {
|
||||||
|
MIPI_DCS_NOP = 0x00,
|
||||||
|
MIPI_DCS_SOFT_RESET = 0x01,
|
||||||
|
MIPI_DCS_GET_DISPLAY_ID = 0x04,
|
||||||
|
MIPI_DCS_GET_RED_CHANNEL = 0x06,
|
||||||
|
MIPI_DCS_GET_GREEN_CHANNEL = 0x07,
|
||||||
|
MIPI_DCS_GET_BLUE_CHANNEL = 0x08,
|
||||||
|
MIPI_DCS_GET_DISPLAY_STATUS = 0x09,
|
||||||
|
MIPI_DCS_GET_POWER_MODE = 0x0A,
|
||||||
|
MIPI_DCS_GET_ADDRESS_MODE = 0x0B,
|
||||||
|
MIPI_DCS_GET_PIXEL_FORMAT = 0x0C,
|
||||||
|
MIPI_DCS_GET_DISPLAY_MODE = 0x0D,
|
||||||
|
MIPI_DCS_GET_SIGNAL_MODE = 0x0E,
|
||||||
|
MIPI_DCS_GET_DIAGNOSTIC_RESULT = 0x0F,
|
||||||
|
MIPI_DCS_ENTER_SLEEP_MODE = 0x10,
|
||||||
|
MIPI_DCS_EXIT_SLEEP_MODE = 0x11,
|
||||||
|
MIPI_DCS_ENTER_PARTIAL_MODE = 0x12,
|
||||||
|
MIPI_DCS_ENTER_NORMAL_MODE = 0x13,
|
||||||
|
MIPI_DCS_EXIT_INVERT_MODE = 0x20,
|
||||||
|
MIPI_DCS_ENTER_INVERT_MODE = 0x21,
|
||||||
|
MIPI_DCS_SET_GAMMA_CURVE = 0x26,
|
||||||
|
MIPI_DCS_SET_DISPLAY_OFF = 0x28,
|
||||||
|
MIPI_DCS_SET_DISPLAY_ON = 0x29,
|
||||||
|
MIPI_DCS_SET_COLUMN_ADDRESS = 0x2A,
|
||||||
|
MIPI_DCS_SET_PAGE_ADDRESS = 0x2B,
|
||||||
|
MIPI_DCS_WRITE_MEMORY_START = 0x2C,
|
||||||
|
MIPI_DCS_WRITE_LUT = 0x2D,
|
||||||
|
MIPI_DCS_READ_MEMORY_START = 0x2E,
|
||||||
|
MIPI_DCS_SET_PARTIAL_AREA = 0x30,
|
||||||
|
MIPI_DCS_SET_SCROLL_AREA = 0x33,
|
||||||
|
MIPI_DCS_SET_TEAR_OFF = 0x34,
|
||||||
|
MIPI_DCS_SET_TEAR_ON = 0x35,
|
||||||
|
MIPI_DCS_SET_ADDRESS_MODE = 0x36,
|
||||||
|
MIPI_DCS_SET_SCROLL_START = 0x37,
|
||||||
|
MIPI_DCS_EXIT_IDLE_MODE = 0x38,
|
||||||
|
MIPI_DCS_ENTER_IDLE_MODE = 0x39,
|
||||||
|
MIPI_DCS_SET_PIXEL_FORMAT = 0x3A,
|
||||||
|
MIPI_DCS_WRITE_MEMORY_CONTINUE = 0x3C,
|
||||||
|
MIPI_DCS_READ_MEMORY_CONTINUE = 0x3E,
|
||||||
|
MIPI_DCS_SET_TEAR_SCANLINE = 0x44,
|
||||||
|
MIPI_DCS_GET_SCANLINE = 0x45,
|
||||||
|
MIPI_DCS_READ_DDB_START = 0xA1,
|
||||||
|
MIPI_DCS_READ_DDB_CONTINUE = 0xA8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MIPI DCS pixel formats */
|
||||||
|
#define MIPI_DCS_PIXEL_FMT_24BIT 7
|
||||||
|
#define MIPI_DCS_PIXEL_FMT_18BIT 6
|
||||||
|
#define MIPI_DCS_PIXEL_FMT_16BIT 5
|
||||||
|
#define MIPI_DCS_PIXEL_FMT_12BIT 3
|
||||||
|
#define MIPI_DCS_PIXEL_FMT_8BIT 2
|
||||||
|
#define MIPI_DCS_PIXEL_FMT_3BIT 1
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* MIPI DSI Bus
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
|
||||||
|
* Andrzej Hajda <a.hajda@samsung.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __MIPI_DSI_H__
|
||||||
|
#define __MIPI_DSI_H__
|
||||||
|
|
||||||
|
struct mipi_dsi_host;
|
||||||
|
struct mipi_dsi_device;
|
||||||
|
|
||||||
|
/* request ACK from peripheral */
|
||||||
|
#define MIPI_DSI_MSG_REQ_ACK BIT(0)
|
||||||
|
/* use Low Power Mode to transmit message */
|
||||||
|
#define MIPI_DSI_MSG_USE_LPM BIT(1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct mipi_dsi_msg - read/write DSI buffer
|
||||||
|
* @channel: virtual channel id
|
||||||
|
* @type: payload data type
|
||||||
|
* @flags: flags controlling this message transmission
|
||||||
|
* @tx_len: length of @tx_buf
|
||||||
|
* @tx_buf: data to be written
|
||||||
|
* @rx_len: length of @rx_buf
|
||||||
|
* @rx_buf: data to be read, or NULL
|
||||||
|
*/
|
||||||
|
struct mipi_dsi_msg {
|
||||||
|
u8 channel;
|
||||||
|
u8 type;
|
||||||
|
u16 flags;
|
||||||
|
|
||||||
|
size_t tx_len;
|
||||||
|
const void *tx_buf;
|
||||||
|
|
||||||
|
size_t rx_len;
|
||||||
|
void *rx_buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct mipi_dsi_host_ops - DSI bus operations
|
||||||
|
* @attach: attach DSI device to DSI host
|
||||||
|
* @detach: detach DSI device from DSI host
|
||||||
|
* @transfer: transmit a DSI packet
|
||||||
|
*
|
||||||
|
* DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg
|
||||||
|
* structures. This structure contains information about the type of packet
|
||||||
|
* being transmitted as well as the transmit and receive buffers. When an
|
||||||
|
* error is encountered during transmission, this function will return a
|
||||||
|
* negative error code. On success it shall return the number of bytes
|
||||||
|
* transmitted for write packets or the number of bytes received for read
|
||||||
|
* packets.
|
||||||
|
*
|
||||||
|
* Note that typically DSI packet transmission is atomic, so the .transfer()
|
||||||
|
* function will seldomly return anything other than the number of bytes
|
||||||
|
* contained in the transmit buffer on success.
|
||||||
|
*/
|
||||||
|
struct mipi_dsi_host_ops {
|
||||||
|
int (*attach)(struct mipi_dsi_host *host,
|
||||||
|
struct mipi_dsi_device *dsi);
|
||||||
|
int (*detach)(struct mipi_dsi_host *host,
|
||||||
|
struct mipi_dsi_device *dsi);
|
||||||
|
ssize_t (*transfer)(struct mipi_dsi_host *host,
|
||||||
|
const struct mipi_dsi_msg *msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct mipi_dsi_host - DSI host device
|
||||||
|
* @dev: driver model device node for this DSI host
|
||||||
|
* @ops: DSI host operations
|
||||||
|
*/
|
||||||
|
struct mipi_dsi_host {
|
||||||
|
//struct device *dev;
|
||||||
|
void *dev;
|
||||||
|
const struct mipi_dsi_host_ops *ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
int mipi_dsi_host_register(struct mipi_dsi_host *host);
|
||||||
|
|
||||||
|
/* DSI mode flags */
|
||||||
|
|
||||||
|
/* video mode */
|
||||||
|
#define MIPI_DSI_MODE_VIDEO BIT(0)
|
||||||
|
/* video burst mode */
|
||||||
|
#define MIPI_DSI_MODE_VIDEO_BURST BIT(1)
|
||||||
|
/* video pulse mode */
|
||||||
|
#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2)
|
||||||
|
/* enable auto vertical count mode */
|
||||||
|
#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3)
|
||||||
|
/* enable hsync-end packets in vsync-pulse and v-porch area */
|
||||||
|
#define MIPI_DSI_MODE_VIDEO_HSE BIT(4)
|
||||||
|
/* disable hfront-porch area */
|
||||||
|
#define MIPI_DSI_MODE_VIDEO_HFP BIT(5)
|
||||||
|
/* disable hback-porch area */
|
||||||
|
#define MIPI_DSI_MODE_VIDEO_HBP BIT(6)
|
||||||
|
/* disable hsync-active area */
|
||||||
|
#define MIPI_DSI_MODE_VIDEO_HSA BIT(7)
|
||||||
|
/* flush display FIFO on vsync pulse */
|
||||||
|
#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8)
|
||||||
|
/* disable EoT packets in HS mode */
|
||||||
|
#define MIPI_DSI_MODE_EOT_PACKET BIT(9)
|
||||||
|
/* device supports non-continuous clock behavior (DSI spec 5.6.1) */
|
||||||
|
#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10)
|
||||||
|
|
||||||
|
enum mipi_dsi_pixel_format {
|
||||||
|
MIPI_DSI_FMT_RGB888,
|
||||||
|
MIPI_DSI_FMT_RGB666,
|
||||||
|
MIPI_DSI_FMT_RGB666_PACKED,
|
||||||
|
MIPI_DSI_FMT_RGB565,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mipi_dsi_master_ops {
|
||||||
|
int (*enslave)(struct mipi_dsi_device *master,
|
||||||
|
struct mipi_dsi_device *slave);
|
||||||
|
int (*liberate)(struct mipi_dsi_device *master,
|
||||||
|
struct mipi_dsi_device *slave);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct mipi_dsi_device - DSI peripheral device
|
||||||
|
* @host: DSI host for this peripheral
|
||||||
|
* @dev: driver model device node for this peripheral
|
||||||
|
* @channel: virtual channel assigned to the peripheral
|
||||||
|
* @format: pixel format for video mode
|
||||||
|
* @lanes: number of active data lanes
|
||||||
|
* @mode_flags: DSI operation mode related flags
|
||||||
|
* @ops: callbacks for master/slave setup
|
||||||
|
* @master: master interface for dual-channel peripherals
|
||||||
|
* @slave: slave interface for dual-channel peripherals
|
||||||
|
*
|
||||||
|
* For dual-channel interfaces, the master interface can be identified by the
|
||||||
|
* fact that it's .slave field is set to non-NULL. The slave interface will
|
||||||
|
* have the .master field set to non-NULL.
|
||||||
|
*/
|
||||||
|
struct mipi_dsi_device {
|
||||||
|
struct mipi_dsi_host *host;
|
||||||
|
|
||||||
|
unsigned int channel;
|
||||||
|
unsigned int lanes;
|
||||||
|
enum mipi_dsi_pixel_format format;
|
||||||
|
unsigned long mode_flags;
|
||||||
|
|
||||||
|
const struct mipi_dsi_master_ops *ops;
|
||||||
|
struct mipi_dsi_device *master;
|
||||||
|
struct mipi_dsi_device *slave;
|
||||||
|
};
|
||||||
|
|
||||||
|
int mipi_dsi_attach(struct mipi_dsi_device *dsi);
|
||||||
|
int mipi_dsi_detach(struct mipi_dsi_device *dsi);
|
||||||
|
int mipi_dsi_enslave(struct mipi_dsi_device *master,
|
||||||
|
struct mipi_dsi_device *slave);
|
||||||
|
int mipi_dsi_liberate(struct mipi_dsi_device *master,
|
||||||
|
struct mipi_dsi_device *slave);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode
|
||||||
|
* @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking
|
||||||
|
* information only
|
||||||
|
* @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both
|
||||||
|
* V-Blanking and H-Blanking information
|
||||||
|
*/
|
||||||
|
enum mipi_dsi_dcs_tear_mode {
|
||||||
|
MIPI_DSI_DCS_TEAR_MODE_VBLANK,
|
||||||
|
MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MIPI_DSI_DCS_POWER_MODE_DISPLAY (1 << 2)
|
||||||
|
#define MIPI_DSI_DCS_POWER_MODE_NORMAL (1 << 3)
|
||||||
|
#define MIPI_DSI_DCS_POWER_MODE_SLEEP (1 << 4)
|
||||||
|
#define MIPI_DSI_DCS_POWER_MODE_PARTIAL (1 << 5)
|
||||||
|
#define MIPI_DSI_DCS_POWER_MODE_IDLE (1 << 6)
|
||||||
|
|
||||||
|
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
|
||||||
|
const void *data, size_t len);
|
||||||
|
int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
|
||||||
|
int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
|
||||||
|
int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
|
||||||
|
u16 end);
|
||||||
|
int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
|
||||||
|
u16 end);
|
||||||
|
int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi,
|
||||||
|
bool reverse_page_address,
|
||||||
|
bool reverse_col_address,
|
||||||
|
bool reverse_page_col_address,
|
||||||
|
bool refresh_from_bottom,
|
||||||
|
bool reverse_rgb,
|
||||||
|
bool latch_right_to_left,
|
||||||
|
bool flip_horizontal,
|
||||||
|
bool flip_vertical);
|
||||||
|
int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
|
||||||
|
enum mipi_dsi_dcs_tear_mode mode);
|
||||||
|
int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format);
|
||||||
|
|
||||||
|
#define MIPI_CAL_CTRL 0x00
|
||||||
|
#define MIPI_CAL_CTRL_START (1 << 0)
|
||||||
|
|
||||||
|
#define MIPI_CAL_AUTOCAL_CTRL 0x01
|
||||||
|
|
||||||
|
#define MIPI_CAL_STATUS 0x02
|
||||||
|
#define MIPI_CAL_STATUS_DONE (1 << 16)
|
||||||
|
#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
|
||||||
|
|
||||||
|
#define MIPI_CAL_CONFIG_CSIA 0x05
|
||||||
|
#define MIPI_CAL_CONFIG_CSIB 0x06
|
||||||
|
#define MIPI_CAL_CONFIG_CSIC 0x07
|
||||||
|
#define MIPI_CAL_CONFIG_CSID 0x08
|
||||||
|
#define MIPI_CAL_CONFIG_CSIE 0x09
|
||||||
|
#define MIPI_CAL_CONFIG_DSIA 0x0e
|
||||||
|
#define MIPI_CAL_CONFIG_DSIB 0x0f
|
||||||
|
#define MIPI_CAL_CONFIG_DSIC 0x10
|
||||||
|
#define MIPI_CAL_CONFIG_DSID 0x11
|
||||||
|
|
||||||
|
#define MIPI_CAL_CONFIG_DSIAB_CLK 0x19
|
||||||
|
#define MIPI_CAL_CONFIG_DSICD_CLK 0x1a
|
||||||
|
#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
|
||||||
|
#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
|
||||||
|
#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
|
||||||
|
|
||||||
|
#define MIPI_CAL_CONFIG_SELECT (1 << 21)
|
||||||
|
#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
|
||||||
|
#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
|
||||||
|
#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
|
||||||
|
#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
|
||||||
|
#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
|
||||||
|
|
||||||
|
#define MIPI_CAL_BIAS_PAD_CFG0 0x16
|
||||||
|
#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
|
||||||
|
#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
|
||||||
|
|
||||||
|
#define MIPI_CAL_BIAS_PAD_CFG1 0x17
|
||||||
|
#define MIPI_CAL_BIAS_PAD_CFG1_DEFAULT (0x20000)
|
||||||
|
|
||||||
|
#define MIPI_CAL_BIAS_PAD_CFG2 0x18
|
||||||
|
#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
|
||||||
|
|
||||||
|
struct calibration_regs {
|
||||||
|
unsigned long data;
|
||||||
|
unsigned long clk;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tegra_mipi_config {
|
||||||
|
int calibrate_clk_lane;
|
||||||
|
int num_pads;
|
||||||
|
const struct calibration_regs *regs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tegra_mipi {
|
||||||
|
void *regs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tegra_mipi_device {
|
||||||
|
struct tegra_mipi *mipi;
|
||||||
|
const struct tegra_mipi_config *config;
|
||||||
|
unsigned long pads;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tegra_mipi_device *tegra_mipi_request(struct tegra_mipi_device *device,
|
||||||
|
int device_index);
|
||||||
|
int tegra_mipi_calibrate(struct tegra_mipi_device *device);
|
||||||
|
#endif /* __MIPI_DSI_H__ */
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#ifndef __TEGRA_DSI_H__
|
||||||
|
#define __TEGRA_DSI_H__
|
||||||
|
|
||||||
|
#define DSI_INCR_SYNCPT 0x00
|
||||||
|
#define DSI_INCR_SYNCPT_CONTROL 0x01
|
||||||
|
#define DSI_INCR_SYNCPT_ERROR 0x02
|
||||||
|
#define DSI_CTXSW 0x08
|
||||||
|
#define DSI_RD_DATA 0x09
|
||||||
|
#define DSI_WR_DATA 0x0a
|
||||||
|
#define DSI_POWER_CONTROL 0x0b
|
||||||
|
#define DSI_POWER_CONTROL_ENABLE (1 << 0)
|
||||||
|
#define DSI_INT_ENABLE 0x0c
|
||||||
|
#define DSI_INT_STATUS 0x0d
|
||||||
|
#define DSI_INT_MASK 0x0e
|
||||||
|
#define DSI_HOST_CONTROL 0x0f
|
||||||
|
#define DSI_HOST_CONTROL_FIFO_RESET (1 << 21)
|
||||||
|
#define DSI_HOST_CONTROL_CRC_RESET (1 << 20)
|
||||||
|
#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12)
|
||||||
|
#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12)
|
||||||
|
#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12)
|
||||||
|
#define DSI_HOST_CONTROL_RAW (1 << 6)
|
||||||
|
#define DSI_HOST_CONTROL_HS (1 << 5)
|
||||||
|
#define DSI_HOST_CONTROL_FIFO_SEL (1 << 4)
|
||||||
|
#define DSI_HOST_CONTROL_IMM_BTA (1 << 3)
|
||||||
|
#define DSI_HOST_CONTROL_PKT_BTA (1 << 2)
|
||||||
|
#define DSI_HOST_CONTROL_CS (1 << 1)
|
||||||
|
#define DSI_HOST_CONTROL_ECC (1 << 0)
|
||||||
|
#define DSI_CONTROL 0x10
|
||||||
|
#define DSI_CONTROL_HS_CLK_CTRL (1 << 20)
|
||||||
|
#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16)
|
||||||
|
#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12)
|
||||||
|
#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8)
|
||||||
|
#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4)
|
||||||
|
#define DSI_CONTROL_DCS_ENABLE (1 << 3)
|
||||||
|
#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2)
|
||||||
|
#define DSI_CONTROL_VIDEO_ENABLE (1 << 1)
|
||||||
|
#define DSI_CONTROL_HOST_ENABLE (1 << 0)
|
||||||
|
#define DSI_SOL_DELAY 0x11
|
||||||
|
#define DSI_MAX_THRESHOLD 0x12
|
||||||
|
#define DSI_TRIGGER 0x13
|
||||||
|
#define DSI_TRIGGER_HOST (1 << 1)
|
||||||
|
#define DSI_TRIGGER_VIDEO (1 << 0)
|
||||||
|
#define DSI_TX_CRC 0x14
|
||||||
|
#define DSI_STATUS 0x15
|
||||||
|
#define DSI_STATUS_IDLE (1 << 10)
|
||||||
|
#define DSI_STATUS_UNDERFLOW (1 << 9)
|
||||||
|
#define DSI_STATUS_OVERFLOW (1 << 8)
|
||||||
|
#define DSI_INIT_SEQ_CONTROL 0x1a
|
||||||
|
#define DSI_INIT_SEQ_DATA_0 0x1b
|
||||||
|
#define DSI_INIT_SEQ_DATA_1 0x1c
|
||||||
|
#define DSI_INIT_SEQ_DATA_2 0x1d
|
||||||
|
#define DSI_INIT_SEQ_DATA_3 0x1e
|
||||||
|
#define DSI_INIT_SEQ_DATA_4 0x1f
|
||||||
|
#define DSI_INIT_SEQ_DATA_5 0x20
|
||||||
|
#define DSI_INIT_SEQ_DATA_6 0x21
|
||||||
|
#define DSI_INIT_SEQ_DATA_7 0x22
|
||||||
|
#define DSI_PKT_SEQ_0_LO 0x23
|
||||||
|
#define DSI_PKT_SEQ_0_HI 0x24
|
||||||
|
#define DSI_PKT_SEQ_1_LO 0x25
|
||||||
|
#define DSI_PKT_SEQ_1_HI 0x26
|
||||||
|
#define DSI_PKT_SEQ_2_LO 0x27
|
||||||
|
#define DSI_PKT_SEQ_2_HI 0x28
|
||||||
|
#define DSI_PKT_SEQ_3_LO 0x29
|
||||||
|
#define DSI_PKT_SEQ_3_HI 0x2a
|
||||||
|
#define DSI_PKT_SEQ_4_LO 0x2b
|
||||||
|
#define DSI_PKT_SEQ_4_HI 0x2c
|
||||||
|
#define DSI_PKT_SEQ_5_LO 0x2d
|
||||||
|
#define DSI_PKT_SEQ_5_HI 0x2e
|
||||||
|
#define DSI_DCS_CMDS 0x33
|
||||||
|
#define DSI_PKT_LEN_0_1 0x34
|
||||||
|
#define DSI_PKT_LEN_2_3 0x35
|
||||||
|
#define DSI_PKT_LEN_4_5 0x36
|
||||||
|
#define DSI_PKT_LEN_6_7 0x37
|
||||||
|
#define DSI_PHY_TIMING_0 0x3c
|
||||||
|
#define DSI_PHY_TIMING_1 0x3d
|
||||||
|
#define DSI_PHY_TIMING_2 0x3e
|
||||||
|
#define DSI_BTA_TIMING 0x3f
|
||||||
|
|
||||||
|
#define DSI_TIMING_FIELD(value, period, hwinc) \
|
||||||
|
((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff)
|
||||||
|
|
||||||
|
#define DSI_TIMEOUT_0 0x44
|
||||||
|
#define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16)
|
||||||
|
#define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0)
|
||||||
|
#define DSI_TIMEOUT_1 0x45
|
||||||
|
#define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16)
|
||||||
|
#define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0)
|
||||||
|
#define DSI_TO_TALLY 0x46
|
||||||
|
#define DSI_TALLY_TA(x) (((x) & 0xff) << 16)
|
||||||
|
#define DSI_TALLY_LRX(x) (((x) & 0xff) << 8)
|
||||||
|
#define DSI_TALLY_HTX(x) (((x) & 0xff) << 0)
|
||||||
|
#define DSI_PAD_CONTROL_0 0x4b
|
||||||
|
#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0)
|
||||||
|
#define DSI_PAD_CONTROL_VS1_PDIO_CLK (1 << 8)
|
||||||
|
#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16)
|
||||||
|
#define DSI_PAD_CONTROL_VS1_PULLDN_CLK (1 << 24)
|
||||||
|
#define DSI_PAD_CONTROL_CD 0x4c
|
||||||
|
#define DSI_PAD_CD_STATUS 0x4d
|
||||||
|
#define DSI_VIDEO_MODE_CONTROL 0x4e
|
||||||
|
#define DSI_PAD_CONTROL_1 0x4f
|
||||||
|
#define DSI_PAD_CONTROL_2 0x50
|
||||||
|
#define DSI_PAD_OUT_CLK(x) (((x) & 0x7) << 0)
|
||||||
|
#define DSI_PAD_LP_DN(x) (((x) & 0x7) << 4)
|
||||||
|
#define DSI_PAD_LP_UP(x) (((x) & 0x7) << 8)
|
||||||
|
#define DSI_PAD_SLEW_DN(x) (((x) & 0x7) << 12)
|
||||||
|
#define DSI_PAD_SLEW_UP(x) (((x) & 0x7) << 16)
|
||||||
|
#define DSI_PAD_CONTROL_3 0x51
|
||||||
|
#define DSI_PAD_CONTROL_4 0x52
|
||||||
|
#define DSI_GANGED_MODE_CONTROL 0x53
|
||||||
|
#define DSI_GANGED_MODE_CONTROL_ENABLE (1 << 0)
|
||||||
|
#define DSI_GANGED_MODE_START 0x54
|
||||||
|
#define DSI_GANGED_MODE_SIZE 0x55
|
||||||
|
#define DSI_RAW_DATA_BYTE_COUNT 0x56
|
||||||
|
#define DSI_ULTRA_LOW_POWER_CONTROL 0x57
|
||||||
|
#define DSI_INIT_SEQ_DATA_8 0x58
|
||||||
|
#define DSI_INIT_SEQ_DATA_9 0x59
|
||||||
|
#define DSI_INIT_SEQ_DATA_10 0x5a
|
||||||
|
#define DSI_INIT_SEQ_DATA_11 0x5b
|
||||||
|
#define DSI_INIT_SEQ_DATA_12 0x5c
|
||||||
|
#define DSI_INIT_SEQ_DATA_13 0x5d
|
||||||
|
#define DSI_INIT_SEQ_DATA_14 0x5e
|
||||||
|
#define DSI_INIT_SEQ_DATA_15 0x5f
|
||||||
|
|
||||||
|
#define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9))
|
||||||
|
#define PKT_LEN0(len) (((len) & 0x07) << 0)
|
||||||
|
#define PKT_ID1(id) ((((id) & 0x3f) << 13) | (1 << 19))
|
||||||
|
#define PKT_LEN1(len) (((len) & 0x07) << 10)
|
||||||
|
#define PKT_ID2(id) ((((id) & 0x3f) << 23) | (1 << 29))
|
||||||
|
#define PKT_LEN2(len) (((len) & 0x07) << 20)
|
||||||
|
|
||||||
|
#define PKT_LP (1 << 30)
|
||||||
|
#define NUM_PKT_SEQ 12
|
||||||
|
|
||||||
|
#define APB_MISC_GP_MIPI_PAD_CTRL_0 (TEGRA_APB_MISC_GP_BASE + 0x20)
|
||||||
|
#define DSIB_MODE_SHIFT 1
|
||||||
|
#define DSIB_MODE_CSI (0 << DSIB_MODE_SHIFT)
|
||||||
|
#define DSIB_MODE_DSI (1 << DSIB_MODE_SHIFT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pixel format as used in the DSI_CONTROL_FORMAT field
|
||||||
|
*/
|
||||||
|
enum tegra_dsi_format {
|
||||||
|
TEGRA_DSI_FORMAT_16P,
|
||||||
|
TEGRA_DSI_FORMAT_18NP,
|
||||||
|
TEGRA_DSI_FORMAT_18P,
|
||||||
|
TEGRA_DSI_FORMAT_24P,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dsi_dev {
|
||||||
|
DSI_A = 0,
|
||||||
|
DSI_B,
|
||||||
|
NUM_DSI,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct panel_jdi;
|
||||||
|
struct tegra_mipi_device;
|
||||||
|
struct mipi_dsi_host;
|
||||||
|
struct mipi_dsi_msg;
|
||||||
|
|
||||||
|
#define MAX_DSI_VIDEO_FIFO_DEPTH 96
|
||||||
|
#define MAX_DSI_HOST_FIFO_DEPTH 64
|
||||||
|
|
||||||
|
struct tegra_dsi {
|
||||||
|
struct panel_jdi *panel;
|
||||||
|
//struct tegra_output output;
|
||||||
|
void *regs;
|
||||||
|
u8 channel;
|
||||||
|
unsigned long clk_rate;
|
||||||
|
|
||||||
|
unsigned long flags;
|
||||||
|
enum mipi_dsi_pixel_format format;
|
||||||
|
unsigned int lanes;
|
||||||
|
|
||||||
|
struct tegra_mipi_device *mipi;
|
||||||
|
struct mipi_dsi_host host;
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
unsigned int video_fifo_depth;
|
||||||
|
unsigned int host_fifo_depth;
|
||||||
|
|
||||||
|
/* for ganged-mode support */
|
||||||
|
unsigned int ganged_lanes;
|
||||||
|
struct tegra_dsi *slave;
|
||||||
|
int ganged_mode;
|
||||||
|
|
||||||
|
struct tegra_dsi *master;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned long tegra_dsi_readl(struct tegra_dsi *dsi,
|
||||||
|
unsigned long reg)
|
||||||
|
{
|
||||||
|
return readl(dsi->regs + (reg << 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void tegra_dsi_writel(struct tegra_dsi *dsi, unsigned long value,
|
||||||
|
unsigned long reg)
|
||||||
|
{
|
||||||
|
writel(value, dsi->regs + (reg << 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __TEGRA_DSI_H__ */
|
|
@ -0,0 +1,213 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <delay.h>
|
||||||
|
#include <soc/addressmap.h>
|
||||||
|
#include <soc/clock.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <soc/nvidia/tegra/types.h>
|
||||||
|
#include "../chip.h"
|
||||||
|
#include <soc/display.h>
|
||||||
|
#include <soc/mipi_dsi.h>
|
||||||
|
#include <soc/tegra_dsi.h>
|
||||||
|
#include "panel-jdi-lpm102a188a.h"
|
||||||
|
|
||||||
|
struct panel_jdi jdi_data[NUM_DSI];
|
||||||
|
|
||||||
|
int panel_jdi_prepare(struct panel_jdi *jdi)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 data;
|
||||||
|
|
||||||
|
if (jdi->enabled)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_column_address(jdi->dsi, 0,
|
||||||
|
jdi->mode->xres / 2 - 1); // 2560/2
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set column address: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_column_address(jdi->dsi->slave, 0,
|
||||||
|
jdi->mode->xres / 2 - 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set column address: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_page_address(jdi->dsi, 0,
|
||||||
|
jdi->mode->yres - 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set page address: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_page_address(jdi->dsi->slave, 0,
|
||||||
|
jdi->mode->yres - 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set page address: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_exit_sleep_mode(jdi->dsi);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to exit sleep mode: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_exit_sleep_mode(jdi->dsi->slave);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to exit sleep mode: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_tear_on(jdi->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set tear on: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_tear_on(jdi->dsi->slave,
|
||||||
|
MIPI_DSI_DCS_TEAR_MODE_VBLANK);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set tear on: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_address_mode(jdi->dsi, false, false, false,
|
||||||
|
false, false, false, false, false);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set address mode: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_address_mode(jdi->dsi->slave, false, false,
|
||||||
|
false, false, false, false, false, false);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set address mode: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_pixel_format(jdi->dsi, 0x77);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set pixel format: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_pixel_format(jdi->dsi->slave, 0x77);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set pixel format: %d\n", ret);
|
||||||
|
|
||||||
|
data = 0xFF;
|
||||||
|
ret = mipi_dsi_dcs_write(jdi->dsi, 0x51, &data, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set 0x51: %d\n", ret);
|
||||||
|
|
||||||
|
data = 0xFF;
|
||||||
|
ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x51, &data, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set 0x51: %d\n", ret);
|
||||||
|
|
||||||
|
data = 0x24;
|
||||||
|
ret = mipi_dsi_dcs_write(jdi->dsi, 0x53, &data, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set 0x53: %d\n", ret);
|
||||||
|
|
||||||
|
data = 0x24;
|
||||||
|
ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x53, &data, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set 0x53: %d\n", ret);
|
||||||
|
|
||||||
|
data = 0x00;
|
||||||
|
ret = mipi_dsi_dcs_write(jdi->dsi, 0x55, &data, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set 0x55: %d\n", ret);
|
||||||
|
|
||||||
|
data = 0x00;
|
||||||
|
ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x55, &data, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set 0x55: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_display_on(jdi->dsi);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set display on: %d\n", ret);
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_set_display_on(jdi->dsi->slave);
|
||||||
|
if (ret < 0)
|
||||||
|
printk(BIOS_ERR, "failed to set display on: %d\n", ret);
|
||||||
|
|
||||||
|
jdi->enabled = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int panel_jdi_enslave(struct mipi_dsi_device *master,
|
||||||
|
struct mipi_dsi_device *slave)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mipi_dsi_attach(master);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int panel_jdi_liberate(struct mipi_dsi_device *master,
|
||||||
|
struct mipi_dsi_device *slave)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mipi_dsi_detach(master);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mipi_dsi_master_ops panel_jdi_master_ops = {
|
||||||
|
.enslave = panel_jdi_enslave,
|
||||||
|
.liberate = panel_jdi_liberate,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct panel_jdi *panel_jdi_dsi_probe(struct mipi_dsi_device *dsi)
|
||||||
|
{
|
||||||
|
static int index = 0;
|
||||||
|
struct panel_jdi *jdi;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (index >= NUM_DSI)
|
||||||
|
return (void *)-EPTR;
|
||||||
|
|
||||||
|
jdi = &jdi_data[index++];
|
||||||
|
|
||||||
|
jdi->dsi = dsi;
|
||||||
|
|
||||||
|
dsi->lanes = 4;
|
||||||
|
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||||
|
dsi->mode_flags = 0;
|
||||||
|
|
||||||
|
if (dsi->master) {
|
||||||
|
ret = mipi_dsi_attach(dsi);
|
||||||
|
if (ret < 0) {
|
||||||
|
printk(BIOS_ERR, "mipi_dsi_attach() failed: %d\n", ret);
|
||||||
|
return (void *)-EPTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mipi_dsi_enslave(dsi->master, dsi);
|
||||||
|
if (ret < 0) {
|
||||||
|
printk(BIOS_ERR, "mipi_dsi_enslave() failed: %d\n",
|
||||||
|
ret);
|
||||||
|
return (void *)-EPTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jdi;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsi->ops = &panel_jdi_master_ops;
|
||||||
|
|
||||||
|
jdi->enabled = 0;
|
||||||
|
jdi->width_mm = 211;
|
||||||
|
jdi->height_mm = 148;
|
||||||
|
|
||||||
|
return jdi;
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#ifndef _PANEL_JDI_LPM102A188A_H_
|
||||||
|
#define _PANEL_JDI_LPM102A188A_H_
|
||||||
|
|
||||||
|
#define LP8557_MAX_BRIGHTNESS 0xFFF;
|
||||||
|
|
||||||
|
#define LP8557_COMMAND 0x00
|
||||||
|
#define LP8557_COMMAND_ON (1 << 0)
|
||||||
|
|
||||||
|
#define LP8557_BRIGHTNESS_LOW 0x03
|
||||||
|
#define LP8557_BRIGHTNESS_LOW_MASK(x) (((x) & 0xF) << 4)
|
||||||
|
|
||||||
|
#define LP8557_BRIGHTNESS_HIGH 0x04
|
||||||
|
#define LP8557_BRIGHTNESS_HIGH_MASK(x) (((x) & 0xFF0) >> 4)
|
||||||
|
|
||||||
|
enum lp8557_config_brightness_mode {
|
||||||
|
LP8557_CONFIG_BRTMODE_PWM = 0x0,
|
||||||
|
LP8557_CONFIG_BRTMODE_REG,
|
||||||
|
LP8557_CONFIG_BRTMODE_PWM_REG_SHAPE_PWM,
|
||||||
|
LP8557_CONFIG_BRTMODE_PWM_REG_SHAPE_BRIGHTNESS,
|
||||||
|
LP8557_CONFIG_BRTMODE_MAX,
|
||||||
|
};
|
||||||
|
#define LP8557_CONFIG 0x10
|
||||||
|
#define LP8557_CONFIG_BRTMODE(x) (((x) & 0x3) << 0)
|
||||||
|
#define LP8557_CONFIG_AUTO_DETECT_LED (1 << 2)
|
||||||
|
#define LP8557_CONFIG_PWM_STANDBY (1 << 7)
|
||||||
|
|
||||||
|
enum lp8557_current {
|
||||||
|
LP8557_CURRENT_5_MA = 0x0,
|
||||||
|
LP8557_CURRENT_10_MA,
|
||||||
|
LP8557_CURRENT_13_MA,
|
||||||
|
LP8557_CURRENT_15_MA,
|
||||||
|
LP8557_CURRENT_18_MA,
|
||||||
|
LP8557_CURRENT_20_MA,
|
||||||
|
LP8557_CURRENT_23_MA,
|
||||||
|
LP8557_CURRENT_25_MA,
|
||||||
|
LP8557_CURRENT_MAX,
|
||||||
|
};
|
||||||
|
#define LP8557_CURRENT 0x11
|
||||||
|
#define LP8557_CURRENT_MAXCURR(x) (((x) & 0x7) << 0)
|
||||||
|
#define LP8557_CURRENT_ISET (1 << 7)
|
||||||
|
|
||||||
|
enum lp8557_pgen_frequency {
|
||||||
|
LP8557_PGEN_FREQ_4_9_KHZ = 0x0,
|
||||||
|
LP8557_PGEN_FREQ_9_8_KHZ,
|
||||||
|
LP8557_PGEN_FREQ_14_6_KHZ,
|
||||||
|
LP8557_PGEN_FREQ_19_5_KHZ,
|
||||||
|
LP8557_PGEN_FREQ_24_4_KHZ,
|
||||||
|
LP8557_PGEN_FREQ_29_3_KHZ,
|
||||||
|
LP8557_PGEN_FREQ_34_2_KHZ,
|
||||||
|
LP8557_PGEN_FREQ_39_1_KHZ,
|
||||||
|
LP8557_PGEN_FREQ_MAX,
|
||||||
|
};
|
||||||
|
#define LP8557_PGEN 0x12
|
||||||
|
#define LP8557_PGEN_FREQ(x) (((x) & 0x7) << 0)
|
||||||
|
#define LP8557_PGEN_MAGIC (5 << 3)
|
||||||
|
#define LP8557_PGEN_FSET (1 << 7)
|
||||||
|
|
||||||
|
enum lp8557_boost_freq {
|
||||||
|
LP8557_BOOST_FREQ_500_KHZ = 0x0,
|
||||||
|
LP8557_BOOST_FREQ_1_MHZ,
|
||||||
|
LP8557_BOOST_FREQ_MAX,
|
||||||
|
};
|
||||||
|
enum lp8557_boost_bcomp {
|
||||||
|
LP8557_BOOST_BCOMP_OPTION_0 = 0x0,
|
||||||
|
LP8557_BOOST_BCOMP_OPTION_1,
|
||||||
|
LP8557_BOOST_BCOMP_MAX,
|
||||||
|
};
|
||||||
|
#define LP8557_BOOST 0x13
|
||||||
|
#define LP8557_BOOST_FREQ(x) (((x) & 0x1) << 0)
|
||||||
|
#define LP8557_BOOST_BCOMP(x) (((x) & 0x1) << 1)
|
||||||
|
#define LP8557_BOOST_BCSET (1 << 6)
|
||||||
|
#define LP8557_BOOST_BFSET (1 << 7)
|
||||||
|
|
||||||
|
#define LP8557_LED_ENABLE 0x14
|
||||||
|
#define LP8557_LED_ENABLE_SINKS(x) (((x) & 0x3F) << 0)
|
||||||
|
#define LP8557_LED_ENABLE_MAGIC (2 << 6)
|
||||||
|
|
||||||
|
enum lp8557_step_ramp {
|
||||||
|
LP8557_STEP_RAMP_0_MS = 0x0,
|
||||||
|
LP8557_STEP_RAMP_50_MS,
|
||||||
|
LP8557_STEP_RAMP_100_MS,
|
||||||
|
LP8557_STEP_RAMP_200_MS,
|
||||||
|
LP8557_STEP_RAMP_MAX,
|
||||||
|
};
|
||||||
|
enum lp8557_step_smoothing {
|
||||||
|
LP8557_STEP_SMOOTHING_NONE = 0x0,
|
||||||
|
LP8557_STEP_SMOOTHING_LIGHT,
|
||||||
|
LP8557_STEP_SMOOTHING_MEDIUM,
|
||||||
|
LP8557_STEP_SMOOTHING_HEAVY,
|
||||||
|
LP8557_STEP_SMOOTHING_MAX,
|
||||||
|
};
|
||||||
|
#define LP8557_STEP 0x15
|
||||||
|
#define LP8557_STEP_RAMP(x) (((x) & 0x3) << 0)
|
||||||
|
#define LP8557_STEP_SMOOTHING(x) (((x) & 0x3) << 6)
|
||||||
|
|
||||||
|
struct mipi_dsi_device;
|
||||||
|
struct soc_nvidia_tegra132_config;
|
||||||
|
|
||||||
|
struct panel_jdi {
|
||||||
|
struct mipi_dsi_device *dsi;
|
||||||
|
const struct soc_nvidia_tegra132_config *mode;
|
||||||
|
|
||||||
|
/* Physical size */
|
||||||
|
unsigned int width_mm;
|
||||||
|
unsigned int height_mm;
|
||||||
|
|
||||||
|
int enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct panel_jdi *panel_jdi_dsi_probe(struct mipi_dsi_device *dsi);
|
||||||
|
int panel_jdi_prepare(struct panel_jdi *jdi);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <soc/addressmap.h>
|
||||||
|
#include <soc/clock.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <soc/nvidia/tegra/types.h>
|
||||||
|
#include <soc/display.h>
|
||||||
|
#include <soc/mipi_dsi.h>
|
||||||
|
#include <soc/mipi_display.h>
|
||||||
|
#include <soc/tegra_dsi.h>
|
||||||
|
#include <soc/mipi-phy.h>
|
||||||
|
|
||||||
|
int mipi_dphy_set_timing(struct tegra_dsi *dsi)
|
||||||
|
{
|
||||||
|
|
||||||
|
u32 freq = (dsi->clk_rate * 2) / 1000000;
|
||||||
|
|
||||||
|
u32 thsdexit = (DSI_PHY_TIMING_DIV(120, (freq)));
|
||||||
|
u32 thstrial = (((3) + (DSI_PHY_TIMING_DIV((DSI_THSTRAIL_VAL(freq)),
|
||||||
|
freq))));
|
||||||
|
u32 tdatzero = DSI_PHY_TIMING_DIV(((145) + (5 * (DSI_TBIT(freq)))),
|
||||||
|
(freq));
|
||||||
|
u32 thsprepare = DSI_PHY_TIMING_DIV((65 + (5*(DSI_TBIT(freq)))), freq);
|
||||||
|
u32 tclktrial = (DSI_PHY_TIMING_DIV(80, freq));
|
||||||
|
u32 tclkpost = ((DSI_PHY_TIMING_DIV(((70) + ((52) * (DSI_TBIT(freq)))),
|
||||||
|
freq)));
|
||||||
|
u32 tclkzero = (DSI_PHY_TIMING_DIV(260, freq));
|
||||||
|
u32 ttlpx = (DSI_PHY_TIMING_DIV(60, freq)) ;
|
||||||
|
u32 tclkprepare = (DSI_PHY_TIMING_DIV(60, freq));
|
||||||
|
u32 tclkpre = 1; //min = 8*UI per mipi spec, tclk_pre=0 should be ok, but using 1 value
|
||||||
|
u32 twakeup = 0x7F; //min = 1ms
|
||||||
|
|
||||||
|
u32 ttaget;
|
||||||
|
u32 ttassure;
|
||||||
|
u32 ttago;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
if (!ttlpx) {
|
||||||
|
ttaget = 5;
|
||||||
|
ttassure = 2;
|
||||||
|
ttago = 4;
|
||||||
|
} else {
|
||||||
|
ttaget = 5 * ttlpx;
|
||||||
|
ttassure = 2 * ttlpx;
|
||||||
|
ttago = 4 * ttlpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = (thsdexit << 24) |
|
||||||
|
(thstrial << 16) |
|
||||||
|
(tdatzero << 8) |
|
||||||
|
(thsprepare << 0);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0);
|
||||||
|
|
||||||
|
value = (tclktrial << 24) |
|
||||||
|
(tclkpost << 16) |
|
||||||
|
(tclkzero << 8) |
|
||||||
|
(ttlpx << 0);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1);
|
||||||
|
|
||||||
|
value = (tclkprepare << 16) |
|
||||||
|
(tclkpre << 8) |
|
||||||
|
(twakeup << 0);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2);
|
||||||
|
|
||||||
|
value = (ttaget << 16) |
|
||||||
|
(ttassure << 8) |
|
||||||
|
(ttago << 0),
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_BTA_TIMING);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <delay.h>
|
||||||
|
#include <soc/addressmap.h>
|
||||||
|
#include <soc/clock.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <soc/nvidia/tegra/types.h>
|
||||||
|
#include <soc/display.h>
|
||||||
|
#include <soc/mipi_dsi.h>
|
||||||
|
#include <soc/tegra_dsi.h>
|
||||||
|
#include "jdi_25x18_display/panel-jdi-lpm102a188a.h"
|
||||||
|
|
||||||
|
static unsigned long dsi_pads[] = {
|
||||||
|
0x060, /* DSIA & DSIB pads */
|
||||||
|
0x180, /* DSIC & DSID pads */
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct tegra_mipi mipi_data = {
|
||||||
|
.regs = (void *)TEGRA_MIPI_CAL_BASE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned long tegra_mipi_readl(struct tegra_mipi *mipi,
|
||||||
|
unsigned long reg)
|
||||||
|
{
|
||||||
|
return readl(mipi->regs + (reg << 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void tegra_mipi_writel(struct tegra_mipi *mipi,
|
||||||
|
unsigned long value, unsigned long reg)
|
||||||
|
{
|
||||||
|
writel(value, mipi->regs + (reg << 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct calibration_regs tegra124_mipi_calibration_regs[] = {
|
||||||
|
{ .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
|
||||||
|
{ .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
|
||||||
|
{ .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
|
||||||
|
{ .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
|
||||||
|
{ .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
|
||||||
|
{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIAB_CLK },
|
||||||
|
{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIAB_CLK },
|
||||||
|
};
|
||||||
|
static const struct tegra_mipi_config tegra124_mipi_config = {
|
||||||
|
.calibrate_clk_lane = 1,
|
||||||
|
.regs = tegra124_mipi_calibration_regs,
|
||||||
|
.num_pads = ARRAY_SIZE(tegra124_mipi_calibration_regs),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tegra_mipi_device *tegra_mipi_request(struct tegra_mipi_device *device,
|
||||||
|
int device_index)
|
||||||
|
{
|
||||||
|
device->mipi = &mipi_data;
|
||||||
|
device->config = &tegra124_mipi_config;
|
||||||
|
device->pads = dsi_pads[device_index];
|
||||||
|
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_mipi_wait(struct tegra_mipi *mipi)
|
||||||
|
{
|
||||||
|
u32 poll_interval_us = 1000;
|
||||||
|
u32 timeout_us = 250 * 1000;
|
||||||
|
unsigned long value;
|
||||||
|
|
||||||
|
do {
|
||||||
|
value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
|
||||||
|
if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 &&
|
||||||
|
(value & MIPI_CAL_STATUS_DONE) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (timeout_us > poll_interval_us)
|
||||||
|
timeout_us -= poll_interval_us;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
udelay(poll_interval_us);
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
printk(BIOS_ERR, "%s: ERROR: timeout\n", __func__);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tegra_mipi_calibrate(struct tegra_mipi_device *device)
|
||||||
|
{
|
||||||
|
const struct tegra_mipi_config *cfg = device->config;
|
||||||
|
unsigned long value, clk_value;
|
||||||
|
unsigned int i;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0);
|
||||||
|
value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
|
||||||
|
value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
|
||||||
|
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
|
||||||
|
|
||||||
|
tegra_mipi_writel(device->mipi, MIPI_CAL_BIAS_PAD_CFG1_DEFAULT,
|
||||||
|
MIPI_CAL_BIAS_PAD_CFG1);
|
||||||
|
|
||||||
|
value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
|
||||||
|
value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
|
||||||
|
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
|
||||||
|
|
||||||
|
for (i = 0; i < cfg->num_pads; i++) {
|
||||||
|
if (device->pads & BIT(i)) {
|
||||||
|
value = MIPI_CAL_CONFIG_SELECT |
|
||||||
|
MIPI_CAL_CONFIG_HSPDOS(0) |
|
||||||
|
MIPI_CAL_CONFIG_HSPUOS(4) |
|
||||||
|
MIPI_CAL_CONFIG_TERMOS(5);
|
||||||
|
clk_value = MIPI_CAL_CONFIG_SELECT |
|
||||||
|
MIPI_CAL_CONFIG_HSCLKPDOSD(0) |
|
||||||
|
MIPI_CAL_CONFIG_HSCLKPUOSD(4);
|
||||||
|
} else {
|
||||||
|
value = 0;
|
||||||
|
clk_value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tegra_mipi_writel(device->mipi, value, cfg->regs[i].data);
|
||||||
|
|
||||||
|
if (cfg->calibrate_clk_lane)
|
||||||
|
tegra_mipi_writel(device->mipi, clk_value,
|
||||||
|
cfg->regs[i].clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
|
||||||
|
value |= MIPI_CAL_CTRL_START;
|
||||||
|
tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
|
||||||
|
|
||||||
|
err = tegra_mipi_wait(device->mipi);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
|
@ -0,0 +1,431 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* MIPI DSI Bus
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
|
||||||
|
* Andrzej Hajda <a.hajda@samsung.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sub license, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the
|
||||||
|
* next paragraph) shall be included in all copies or substantial portions
|
||||||
|
* of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <delay.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <soc/addressmap.h>
|
||||||
|
#include <soc/clock.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <soc/nvidia/tegra/types.h>
|
||||||
|
#include <soc/display.h>
|
||||||
|
#include <soc/mipi_dsi.h>
|
||||||
|
#include <soc/mipi_display.h>
|
||||||
|
#include <soc/tegra_dsi.h>
|
||||||
|
|
||||||
|
struct mipi_dsi_device mipi_dsi_device_data[NUM_DSI] = {
|
||||||
|
{
|
||||||
|
.master = NULL,
|
||||||
|
.slave = &mipi_dsi_device_data[DSI_B],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.master = &mipi_dsi_device_data[DSI_A],
|
||||||
|
.slave = NULL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mipi_dsi_device *
|
||||||
|
mipi_dsi_device_alloc(struct mipi_dsi_host *host)
|
||||||
|
{
|
||||||
|
static int index = 0;
|
||||||
|
struct mipi_dsi_device *dsi;
|
||||||
|
|
||||||
|
if (index >= NUM_DSI)
|
||||||
|
return (void *)-EPTR;
|
||||||
|
|
||||||
|
dsi = &mipi_dsi_device_data[index++];
|
||||||
|
dsi->host = host;
|
||||||
|
return dsi;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mipi_dsi_device *
|
||||||
|
of_mipi_dsi_device_add(struct mipi_dsi_host *host)
|
||||||
|
{
|
||||||
|
struct mipi_dsi_device *dsi;
|
||||||
|
u32 reg = 0;
|
||||||
|
|
||||||
|
dsi = mipi_dsi_device_alloc(host);
|
||||||
|
if (IS_ERR_PTR(dsi)) {
|
||||||
|
printk(BIOS_ERR, "failed to allocate DSI device\n");
|
||||||
|
return dsi;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsi->channel = reg;
|
||||||
|
host->dev = (void *)dsi;
|
||||||
|
|
||||||
|
return dsi;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mipi_dsi_host_register(struct mipi_dsi_host *host)
|
||||||
|
{
|
||||||
|
of_mipi_dsi_device_add(host);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_attach - attach a DSI device to its DSI host
|
||||||
|
* @dsi: DSI peripheral
|
||||||
|
*/
|
||||||
|
int mipi_dsi_attach(struct mipi_dsi_device *dsi)
|
||||||
|
{
|
||||||
|
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
|
||||||
|
|
||||||
|
if (!ops || !ops->attach)
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
|
return ops->attach(dsi->host, dsi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_detach - detach a DSI device from its DSI host
|
||||||
|
* @dsi: DSI peripheral
|
||||||
|
*/
|
||||||
|
int mipi_dsi_detach(struct mipi_dsi_device *dsi)
|
||||||
|
{
|
||||||
|
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
|
||||||
|
|
||||||
|
if (!ops || !ops->detach)
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
|
return ops->detach(dsi->host, dsi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_enslave() - use a MIPI DSI peripheral as slave for dual-channel
|
||||||
|
* operation
|
||||||
|
* @master: master DSI peripheral device
|
||||||
|
* @slave: slave DSI peripheral device
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure.
|
||||||
|
*/
|
||||||
|
int mipi_dsi_enslave(struct mipi_dsi_device *master,
|
||||||
|
struct mipi_dsi_device *slave)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
slave->master = master;
|
||||||
|
master->slave = slave;
|
||||||
|
|
||||||
|
if (master->ops && master->ops->enslave)
|
||||||
|
err = master->ops->enslave(master, slave);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_liberate() - stop using a MIPI DSI peripheral as slave for dual-
|
||||||
|
* channel operation
|
||||||
|
* @master: master DSI peripheral device
|
||||||
|
* @slave: slave DSI peripheral device
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure.
|
||||||
|
*/
|
||||||
|
int mipi_dsi_liberate(struct mipi_dsi_device *master,
|
||||||
|
struct mipi_dsi_device *slave)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (master->ops && master->ops->liberate)
|
||||||
|
err = master->ops->liberate(master, slave);
|
||||||
|
|
||||||
|
master->slave = NULL;
|
||||||
|
slave->master = NULL;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_dcs_write() - send DCS write command
|
||||||
|
* @dsi: DSI peripheral device
|
||||||
|
* @cmd: DCS command
|
||||||
|
* @data: buffer containing the command payload
|
||||||
|
* @len: command payload length
|
||||||
|
*
|
||||||
|
* This function will automatically choose the right data type depending on
|
||||||
|
* the command payload length.
|
||||||
|
*
|
||||||
|
* Return: The number of bytes successfully transmitted or a negative error
|
||||||
|
* code on failure.
|
||||||
|
*/
|
||||||
|
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
|
||||||
|
const void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct mipi_dsi_msg msg;
|
||||||
|
ssize_t err;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
u8 buffer[MAX_DSI_HOST_FIFO_DEPTH + 4];
|
||||||
|
u8 *tx = buffer;
|
||||||
|
|
||||||
|
if (len > MAX_DSI_HOST_FIFO_DEPTH) {
|
||||||
|
printk(BIOS_ERR, "%s: Error: too large payload length: %zu\n",
|
||||||
|
__func__, len);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
unsigned int offset = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DCS long write packets contain the word count in the header
|
||||||
|
* bytes 1 and 2 and have a payload containing the DCS command
|
||||||
|
* byte folowed by word count minus one bytes.
|
||||||
|
*
|
||||||
|
* DCS short write packets encode the DCS command and up to
|
||||||
|
* one parameter in header bytes 1 and 2.
|
||||||
|
*/
|
||||||
|
if (len > 1)
|
||||||
|
size = 3 + len;
|
||||||
|
else
|
||||||
|
size = 1 + len;
|
||||||
|
|
||||||
|
/* write word count to header for DCS long write packets */
|
||||||
|
if (len > 1) {
|
||||||
|
tx[offset++] = ((1 + len) >> 0) & 0xff;
|
||||||
|
tx[offset++] = ((1 + len) >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write the DCS command byte followed by the payload */
|
||||||
|
tx[offset++] = cmd;
|
||||||
|
memcpy(tx + offset, data, len);
|
||||||
|
} else {
|
||||||
|
tx = &cmd;
|
||||||
|
size = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&msg, 0, sizeof(msg));
|
||||||
|
msg.flags = MIPI_DSI_MSG_USE_LPM;
|
||||||
|
msg.channel = dsi->channel;
|
||||||
|
msg.tx_len = size;
|
||||||
|
msg.tx_buf = tx;
|
||||||
|
|
||||||
|
switch (len) {
|
||||||
|
case 0:
|
||||||
|
msg.type = MIPI_DSI_DCS_SHORT_WRITE;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
msg.type = MIPI_DSI_DCS_LONG_WRITE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dsi->host->ops->transfer(dsi->host, &msg);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
|
||||||
|
* module
|
||||||
|
* @dsi: DSI peripheral device
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure.
|
||||||
|
*/
|
||||||
|
int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
|
||||||
|
{
|
||||||
|
ssize_t err;
|
||||||
|
|
||||||
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_dcs_set_display_on() - start displaying the image data on the
|
||||||
|
* display device
|
||||||
|
* @dsi: DSI peripheral device
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure
|
||||||
|
*/
|
||||||
|
int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
|
||||||
|
{
|
||||||
|
ssize_t err;
|
||||||
|
|
||||||
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_dcs_set_column_address() - define the column extent of the frame
|
||||||
|
* memory accessed by the host processor
|
||||||
|
* @dsi: DSI peripheral device
|
||||||
|
* @start: first column of frame memory
|
||||||
|
* @end: last column of frame memory
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure.
|
||||||
|
*/
|
||||||
|
int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
|
||||||
|
u16 end)
|
||||||
|
{
|
||||||
|
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
|
||||||
|
ssize_t err;
|
||||||
|
|
||||||
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
|
||||||
|
sizeof(payload));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_dcs_set_page_address() - define the page extent of the frame
|
||||||
|
* memory accessed by the host processor
|
||||||
|
* @dsi: DSI peripheral device
|
||||||
|
* @start: first page of frame memory
|
||||||
|
* @end: last page of frame memory
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure.
|
||||||
|
*/
|
||||||
|
int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
|
||||||
|
u16 end)
|
||||||
|
{
|
||||||
|
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
|
||||||
|
ssize_t err;
|
||||||
|
|
||||||
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
|
||||||
|
sizeof(payload));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
|
||||||
|
* output signal on the TE signal line.
|
||||||
|
* @dsi: DSI peripheral device
|
||||||
|
* @mode: the Tearing Effect Output Line mode
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure
|
||||||
|
*/
|
||||||
|
int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
|
||||||
|
enum mipi_dsi_dcs_tear_mode mode)
|
||||||
|
{
|
||||||
|
u8 value = mode;
|
||||||
|
ssize_t err;
|
||||||
|
|
||||||
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
|
||||||
|
sizeof(value));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
|
||||||
|
* data used by the interface
|
||||||
|
* @dsi: DSI peripheral device
|
||||||
|
* @format: pixel format
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure.
|
||||||
|
*/
|
||||||
|
int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
|
||||||
|
{
|
||||||
|
ssize_t err;
|
||||||
|
|
||||||
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
|
||||||
|
sizeof(format));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mipi_dsi_dcs_set_address_mode() - sets the data order for forward transfers
|
||||||
|
* from the host to the peripheral
|
||||||
|
* @dsi: DSI peripheral device
|
||||||
|
* @reverse_page_address: reverses the page addressing to bottom->top
|
||||||
|
* @reverse_col_address: reverses the column addressing to right->left
|
||||||
|
* @reverse_page_col_address: reverses the page/column addressing order
|
||||||
|
* @refresh_from_bottom: refresh the display bottom to top
|
||||||
|
* @reverse_rgb: send pixel data bgr instead of rgb
|
||||||
|
* @latch_right_to_left: latch the incoming display data right to left
|
||||||
|
* @flip_horizontal: flip the image horizontally, left to right
|
||||||
|
* @flip_vertical: flip the image vertically, top to bottom
|
||||||
|
*
|
||||||
|
* Return: 0 on success or a negative error code on failure.
|
||||||
|
*/
|
||||||
|
int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi,
|
||||||
|
bool reverse_page_address,
|
||||||
|
bool reverse_col_address,
|
||||||
|
bool reverse_page_col_address,
|
||||||
|
bool refresh_from_bottom,
|
||||||
|
bool reverse_rgb,
|
||||||
|
bool latch_right_to_left,
|
||||||
|
bool flip_horizontal,
|
||||||
|
bool flip_vertical)
|
||||||
|
{
|
||||||
|
ssize_t err;
|
||||||
|
u8 data;
|
||||||
|
|
||||||
|
data = ((flip_vertical ? 1 : 0) << 0) |
|
||||||
|
((flip_horizontal ? 1 : 0) << 1) |
|
||||||
|
((latch_right_to_left ? 1 : 0) << 2) |
|
||||||
|
((reverse_rgb ? 1 : 0) << 3) |
|
||||||
|
((refresh_from_bottom ? 1 : 0) << 4) |
|
||||||
|
((reverse_page_col_address ? 1 : 0) << 5) |
|
||||||
|
((reverse_col_address ? 1 : 0) << 6) |
|
||||||
|
((reverse_page_address ? 1 : 0) << 7);
|
||||||
|
|
||||||
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &data, 1);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -76,11 +76,6 @@ static struct cpu_control_ops cntrl_ops = {
|
||||||
.start_cpu = cntrl_start_cpu,
|
.start_cpu = cntrl_start_cpu,
|
||||||
};
|
};
|
||||||
|
|
||||||
void display_startup(device_t dev)
|
|
||||||
{
|
|
||||||
printk(BIOS_INFO, "Entering %s()\n", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void soc_init(device_t dev)
|
static void soc_init(device_t dev)
|
||||||
{
|
{
|
||||||
struct soc_nvidia_tegra132_config *cfg;
|
struct soc_nvidia_tegra132_config *cfg;
|
||||||
|
|
|
@ -0,0 +1,874 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <delay.h>
|
||||||
|
#include <timer.h>
|
||||||
|
#include <soc/addressmap.h>
|
||||||
|
#include <soc/clock.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <soc/nvidia/tegra/types.h>
|
||||||
|
#include "chip.h"
|
||||||
|
#include <soc/display.h>
|
||||||
|
#include <soc/mipi_dsi.h>
|
||||||
|
#include <soc/mipi_display.h>
|
||||||
|
#include <soc/tegra_dsi.h>
|
||||||
|
#include <soc/mipi-phy.h>
|
||||||
|
#include "jdi_25x18_display/panel-jdi-lpm102a188a.h"
|
||||||
|
|
||||||
|
struct tegra_mipi_device mipi_device_data[NUM_DSI];
|
||||||
|
|
||||||
|
struct tegra_dsi dsi_data[NUM_DSI] = {
|
||||||
|
{
|
||||||
|
.regs = (void *)TEGRA_DSIA_BASE,
|
||||||
|
.channel = 0,
|
||||||
|
.slave = &dsi_data[DSI_B],
|
||||||
|
.master = NULL,
|
||||||
|
.video_fifo_depth = MAX_DSI_VIDEO_FIFO_DEPTH,
|
||||||
|
.host_fifo_depth = MAX_DSI_HOST_FIFO_DEPTH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.regs = (void *)TEGRA_DSIB_BASE,
|
||||||
|
.channel = 0,
|
||||||
|
.slave = NULL,
|
||||||
|
.master = &dsi_data[DSI_A],
|
||||||
|
.video_fifo_depth = MAX_DSI_VIDEO_FIFO_DEPTH,
|
||||||
|
.host_fifo_depth = MAX_DSI_HOST_FIFO_DEPTH,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct tegra_dsi *host_to_tegra(struct mipi_dsi_host *host)
|
||||||
|
{
|
||||||
|
return container_of(host, struct tegra_dsi, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* non-burst mode with sync pulses
|
||||||
|
*/
|
||||||
|
static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = {
|
||||||
|
[ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
|
||||||
|
PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
|
||||||
|
PKT_LP,
|
||||||
|
[ 1] = 0,
|
||||||
|
[ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
|
||||||
|
PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
|
||||||
|
PKT_LP,
|
||||||
|
[ 3] = 0,
|
||||||
|
[ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
|
||||||
|
PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
|
||||||
|
PKT_LP,
|
||||||
|
[ 5] = 0,
|
||||||
|
[ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
|
||||||
|
PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0),
|
||||||
|
[ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) |
|
||||||
|
PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) |
|
||||||
|
PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
|
||||||
|
[ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
|
||||||
|
PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
|
||||||
|
PKT_LP,
|
||||||
|
[ 9] = 0,
|
||||||
|
[10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
|
||||||
|
PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0),
|
||||||
|
[11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) |
|
||||||
|
PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) |
|
||||||
|
PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* non-burst mode with sync events
|
||||||
|
*/
|
||||||
|
static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {
|
||||||
|
[ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
|
||||||
|
PKT_LP,
|
||||||
|
[ 1] = 0,
|
||||||
|
[ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
|
||||||
|
PKT_LP,
|
||||||
|
[ 3] = 0,
|
||||||
|
[ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
|
||||||
|
PKT_LP,
|
||||||
|
[ 5] = 0,
|
||||||
|
|
||||||
|
[ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
|
||||||
|
PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
|
||||||
|
|
||||||
|
[ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
|
||||||
|
[ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
|
||||||
|
PKT_LP,
|
||||||
|
[ 9] = 0,
|
||||||
|
|
||||||
|
[10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
|
||||||
|
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
|
||||||
|
PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
|
||||||
|
|
||||||
|
[11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = {
|
||||||
|
[ 0] = 0,
|
||||||
|
[ 1] = 0,
|
||||||
|
[ 2] = 0,
|
||||||
|
[ 3] = 0,
|
||||||
|
[ 4] = 0,
|
||||||
|
[ 5] = 0,
|
||||||
|
[ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP,
|
||||||
|
[ 7] = 0,
|
||||||
|
[ 8] = 0,
|
||||||
|
[ 9] = 0,
|
||||||
|
[10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP,
|
||||||
|
[11] = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = mipi_dphy_set_timing(dsi);
|
||||||
|
if (err < 0) {
|
||||||
|
printk(BIOS_ERR, "failed to set D-PHY timing: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dsi->slave)
|
||||||
|
tegra_dsi_set_phy_timing(dsi->slave);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
|
||||||
|
unsigned int *mulp, unsigned int *divp)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case MIPI_DSI_FMT_RGB666_PACKED:
|
||||||
|
case MIPI_DSI_FMT_RGB888:
|
||||||
|
*mulp = 3;
|
||||||
|
*divp = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_FMT_RGB565:
|
||||||
|
*mulp = 2;
|
||||||
|
*divp = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_FMT_RGB666:
|
||||||
|
*mulp = 9;
|
||||||
|
*divp = 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format,
|
||||||
|
enum tegra_dsi_format *fmt)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case MIPI_DSI_FMT_RGB888:
|
||||||
|
*fmt = TEGRA_DSI_FORMAT_24P;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_FMT_RGB666:
|
||||||
|
*fmt = TEGRA_DSI_FORMAT_18NP;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_FMT_RGB666_PACKED:
|
||||||
|
*fmt = TEGRA_DSI_FORMAT_18P;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_FMT_RGB565:
|
||||||
|
*fmt = TEGRA_DSI_FORMAT_16P;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tegra_dsi_ganged_enable(struct tegra_dsi *dsi, unsigned int start,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, start, DSI_GANGED_MODE_START);
|
||||||
|
tegra_dsi_writel(dsi, size << 16 | size, DSI_GANGED_MODE_SIZE);
|
||||||
|
|
||||||
|
value = DSI_GANGED_MODE_CONTROL_ENABLE;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_GANGED_MODE_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tegra_dsi_enable(struct tegra_dsi *dsi)
|
||||||
|
{
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
|
||||||
|
value |= DSI_POWER_CONTROL_ENABLE;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
|
||||||
|
|
||||||
|
if (dsi->slave)
|
||||||
|
tegra_dsi_enable(dsi->slave);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
|
||||||
|
const struct soc_nvidia_tegra132_config *mode)
|
||||||
|
{
|
||||||
|
unsigned int hact, hsw, hbp, hfp, i, mul, div;
|
||||||
|
enum tegra_dsi_format format;
|
||||||
|
const u32 *pkt_seq;
|
||||||
|
u32 value;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
|
||||||
|
printk(BIOS_SPEW, "Non-burst video mode with sync pulses\n");
|
||||||
|
pkt_seq = pkt_seq_video_non_burst_sync_pulses;
|
||||||
|
} else if (dsi->flags & MIPI_DSI_MODE_VIDEO) {
|
||||||
|
printk(BIOS_SPEW, "Non-burst video mode with sync events\n");
|
||||||
|
pkt_seq = pkt_seq_video_non_burst_sync_events;
|
||||||
|
} else {
|
||||||
|
printk(BIOS_SPEW, "Command mode\n");
|
||||||
|
pkt_seq = pkt_seq_command_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = tegra_dsi_get_format(dsi->format, &format);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |
|
||||||
|
DSI_CONTROL_LANES(dsi->lanes - 1) |
|
||||||
|
DSI_CONTROL_SOURCE(pipe);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_CONTROL);
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, dsi->video_fifo_depth, DSI_MAX_THRESHOLD);
|
||||||
|
|
||||||
|
value = DSI_HOST_CONTROL_HS;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
|
||||||
|
|
||||||
|
value = tegra_dsi_readl(dsi, DSI_CONTROL);
|
||||||
|
|
||||||
|
if (dsi->flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
|
||||||
|
value |= DSI_CONTROL_HS_CLK_CTRL;
|
||||||
|
|
||||||
|
value &= ~DSI_CONTROL_TX_TRIG(3);
|
||||||
|
|
||||||
|
/* enable DCS commands for command mode */
|
||||||
|
if (dsi->flags & MIPI_DSI_MODE_VIDEO)
|
||||||
|
value &= ~DSI_CONTROL_DCS_ENABLE;
|
||||||
|
else
|
||||||
|
value |= DSI_CONTROL_DCS_ENABLE;
|
||||||
|
|
||||||
|
value |= DSI_CONTROL_VIDEO_ENABLE;
|
||||||
|
value &= ~DSI_CONTROL_HOST_ENABLE;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_CONTROL);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_PKT_SEQ; i++)
|
||||||
|
tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i);
|
||||||
|
|
||||||
|
if (dsi->flags & MIPI_DSI_MODE_VIDEO) {
|
||||||
|
/* horizontal active pixels */
|
||||||
|
hact = mode->xres * mul / div;
|
||||||
|
|
||||||
|
/* horizontal sync width */
|
||||||
|
hsw = (hsync_end(mode) - hsync_start(mode)) * mul / div;
|
||||||
|
hsw -= 10;
|
||||||
|
|
||||||
|
/* horizontal back porch */
|
||||||
|
hbp = (htotal(mode) - hsync_end(mode)) * mul / div;
|
||||||
|
hbp -= 14;
|
||||||
|
|
||||||
|
/* horizontal front porch */
|
||||||
|
hfp = (hsync_start(mode) - mode->xres) * mul / div;
|
||||||
|
hfp -= 8;
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1);
|
||||||
|
tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3);
|
||||||
|
tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5);
|
||||||
|
tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7);
|
||||||
|
|
||||||
|
/* set SOL delay (for non-burst mode only) */
|
||||||
|
tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY);
|
||||||
|
|
||||||
|
/* TODO: implement ganged mode */
|
||||||
|
} else {
|
||||||
|
u16 bytes;
|
||||||
|
if (dsi->ganged_mode) {
|
||||||
|
/*
|
||||||
|
* For ganged mode, assume symmetric left-right mode.
|
||||||
|
*/
|
||||||
|
bytes = 1 + (mode->xres / 2) * mul / div;
|
||||||
|
} else {
|
||||||
|
/* 1 byte (DCS command) + pixel data */
|
||||||
|
bytes = 1 + mode->xres * mul / div;
|
||||||
|
}
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1);
|
||||||
|
tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_2_3);
|
||||||
|
tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_4_5);
|
||||||
|
tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_6_7);
|
||||||
|
|
||||||
|
value = MIPI_DCS_WRITE_MEMORY_START << 8 |
|
||||||
|
MIPI_DCS_WRITE_MEMORY_CONTINUE;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_DCS_CMDS);
|
||||||
|
|
||||||
|
/* set SOL delay */
|
||||||
|
if (dsi->ganged_mode) {
|
||||||
|
unsigned long delay, bclk, bclk_ganged;
|
||||||
|
unsigned int lanes = dsi->ganged_lanes;
|
||||||
|
|
||||||
|
/* SOL to valid, valid to FIFO and FIFO write delay */
|
||||||
|
delay = 4 + 4 + 2;
|
||||||
|
delay = DIV_ROUND_UP(delay * mul, div * lanes);
|
||||||
|
/* FIFO read delay */
|
||||||
|
delay = delay + 6;
|
||||||
|
|
||||||
|
bclk = DIV_ROUND_UP(htotal(mode) * mul, div * lanes);
|
||||||
|
bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes);
|
||||||
|
value = bclk - bclk_ganged + delay + 20;
|
||||||
|
} else {
|
||||||
|
/* TODO: revisit for non-ganged mode */
|
||||||
|
value = 8 * mul / div;
|
||||||
|
}
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_SOL_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dsi->slave) {
|
||||||
|
err = tegra_dsi_configure(dsi->slave, pipe, mode);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* enable ganged mode
|
||||||
|
*/
|
||||||
|
if (dsi->ganged_mode) {
|
||||||
|
tegra_dsi_ganged_enable(dsi, mode->xres / 2,
|
||||||
|
mode->xres / 2);
|
||||||
|
tegra_dsi_ganged_enable(dsi->slave, 0, mode->xres / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_output_dsi_enable(struct tegra_dsi *dsi,
|
||||||
|
const struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (dsi->enabled)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err = tegra_dsi_configure(dsi, 0, config);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* enable DSI controller */
|
||||||
|
tegra_dsi_enable(dsi);
|
||||||
|
|
||||||
|
dsi->enabled = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk,
|
||||||
|
unsigned int vrefresh)
|
||||||
|
{
|
||||||
|
unsigned int timeout;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
/* one frame high-speed transmission timeout */
|
||||||
|
timeout = (bclk / vrefresh) / 512;
|
||||||
|
value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
|
||||||
|
|
||||||
|
/* 2 ms peripheral timeout for panel */
|
||||||
|
timeout = 2 * bclk / 512 * 1000;
|
||||||
|
value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
|
||||||
|
|
||||||
|
value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
|
||||||
|
|
||||||
|
if (dsi->slave)
|
||||||
|
tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_output_dsi_setup_clock(struct tegra_dsi *dsi,
|
||||||
|
const struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
unsigned int mul, div, num_lanes; // , vrefresh, num_lanes;
|
||||||
|
unsigned long bclk;
|
||||||
|
unsigned long pclk = config->pixel_clock;
|
||||||
|
int plld;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In ganged mode, account for the total number of lanes across both
|
||||||
|
* DSI channels so that the bit clock is properly computed.
|
||||||
|
*/
|
||||||
|
if (dsi->ganged_mode)
|
||||||
|
num_lanes = dsi->ganged_lanes;
|
||||||
|
else
|
||||||
|
num_lanes = dsi->lanes;
|
||||||
|
|
||||||
|
/* compute byte clock */
|
||||||
|
bclk = (pclk * mul) / (div * num_lanes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute bit clock and round up to the next MHz.
|
||||||
|
*/
|
||||||
|
plld = DIV_ROUND_UP(bclk * 8, USECS_PER_SEC) * USECS_PER_SEC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the actual rate on PLLD_OUT0 is 1/2 plld
|
||||||
|
*/
|
||||||
|
dsi->clk_rate = plld / 2;
|
||||||
|
if (dsi->slave)
|
||||||
|
dsi->slave->clk_rate = dsi->clk_rate;
|
||||||
|
|
||||||
|
/* set up plld */
|
||||||
|
plld = clock_display(plld);
|
||||||
|
if (plld == 0) {
|
||||||
|
printk(BIOS_ERR, "%s: clock init failed\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tegra_dsi_set_timeout(dsi, bclk, config->refresh);
|
||||||
|
return plld/1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
|
||||||
|
{
|
||||||
|
unsigned long value;
|
||||||
|
|
||||||
|
value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi)
|
||||||
|
{
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
|
||||||
|
tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
|
||||||
|
tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
|
||||||
|
tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
|
||||||
|
tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
|
||||||
|
|
||||||
|
/* start calibration */
|
||||||
|
tegra_dsi_pad_enable(dsi);
|
||||||
|
|
||||||
|
value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
|
||||||
|
DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
|
||||||
|
DSI_PAD_OUT_CLK(0x0);
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2);
|
||||||
|
|
||||||
|
return tegra_mipi_calibrate(dsi->mipi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * const error_report[16] = {
|
||||||
|
"SoT Error",
|
||||||
|
"SoT Sync Error",
|
||||||
|
"EoT Sync Error",
|
||||||
|
"Escape Mode Entry Command Error",
|
||||||
|
"Low-Power Transmit Sync Error",
|
||||||
|
"Peripheral Timeout Error",
|
||||||
|
"False Control Error",
|
||||||
|
"Contention Detected",
|
||||||
|
"ECC Error, single-bit",
|
||||||
|
"ECC Error, multi-bit",
|
||||||
|
"Checksum Error",
|
||||||
|
"DSI Data Type Not Recognized",
|
||||||
|
"DSI VC ID Invalid",
|
||||||
|
"Invalid Transmission Length",
|
||||||
|
"Reserved",
|
||||||
|
"DSI Protocol Violation",
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tegra_dsi_read_response(struct tegra_dsi *dsi,
|
||||||
|
const struct mipi_dsi_msg *msg,
|
||||||
|
unsigned int count)
|
||||||
|
{
|
||||||
|
u8 *rx = msg->rx_buf;
|
||||||
|
unsigned int i, j, k;
|
||||||
|
size_t size = 0;
|
||||||
|
u16 errors;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
/* read and parse packet header */
|
||||||
|
value = tegra_dsi_readl(dsi, DSI_RD_DATA);
|
||||||
|
|
||||||
|
switch (value & 0x3f) {
|
||||||
|
case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
|
||||||
|
errors = (value >> 8) & 0xffff;
|
||||||
|
printk(BIOS_ERR, "Acknowledge and error report: %04x\n",
|
||||||
|
errors);
|
||||||
|
for (i = 0; i < ARRAY_SIZE(error_report); i++)
|
||||||
|
if (errors & BIT(i))
|
||||||
|
printk(BIOS_INFO, " %2u: %s\n", i,
|
||||||
|
error_report[i]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
|
||||||
|
rx[0] = (value >> 8) & 0xff;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
|
||||||
|
rx[0] = (value >> 8) & 0xff;
|
||||||
|
rx[1] = (value >> 16) & 0xff;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
|
||||||
|
size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
|
||||||
|
size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printk(BIOS_ERR, "unhandled response type: %02x\n",
|
||||||
|
value & 0x3f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = MIN(size, msg->rx_len);
|
||||||
|
|
||||||
|
if (msg->rx_buf && size > 0) {
|
||||||
|
for (i = 0, j = 0; i < count - 1; i++, j += 4) {
|
||||||
|
value = tegra_dsi_readl(dsi, DSI_RD_DATA);
|
||||||
|
|
||||||
|
for (k = 0; k < 4 && (j + k) < msg->rx_len; k++)
|
||||||
|
rx[j + k] = (value >> (k << 3)) & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dsi_transmit(struct tegra_dsi *dsi, unsigned long timeout_ms)
|
||||||
|
{
|
||||||
|
u32 poll_interval_us = 2000;
|
||||||
|
u32 timeout_us = timeout_ms * 1000;
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, DSI_TRIGGER_HOST, DSI_TRIGGER);
|
||||||
|
udelay(poll_interval_us);
|
||||||
|
|
||||||
|
do {
|
||||||
|
u32 value = tegra_dsi_readl(dsi, DSI_TRIGGER);
|
||||||
|
if ((value & DSI_TRIGGER_HOST) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
//usleep_range(1000, 2000);
|
||||||
|
if (timeout_us > poll_interval_us)
|
||||||
|
timeout_us -= poll_interval_us;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
udelay(poll_interval_us);
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
printk(BIOS_ERR, "%s: ERROR: timeout waiting for transmission"
|
||||||
|
" to complete\n", __func__);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dsi_wait_for_response(struct tegra_dsi *dsi,
|
||||||
|
unsigned long timeout_ms)
|
||||||
|
{
|
||||||
|
u32 poll_interval_us = 2000;
|
||||||
|
u32 timeout_us = timeout_ms * 1000;
|
||||||
|
|
||||||
|
do {
|
||||||
|
u32 value = tegra_dsi_readl(dsi, DSI_STATUS);
|
||||||
|
u8 count = value & 0x1f;
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
return count;
|
||||||
|
|
||||||
|
if (timeout_us > poll_interval_us)
|
||||||
|
timeout_us -= poll_interval_us;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
udelay(poll_interval_us);
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
printk(BIOS_ERR, "%s: ERROR: timeout\n", __func__);
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host,
|
||||||
|
const struct mipi_dsi_msg *msg)
|
||||||
|
{
|
||||||
|
struct tegra_dsi *dsi = host_to_tegra(host);
|
||||||
|
const u8 *tx = msg->tx_buf;
|
||||||
|
unsigned int count, i, j;
|
||||||
|
u32 value;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (msg->tx_len > dsi->video_fifo_depth * 4)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
/* reset underflow/overflow flags */
|
||||||
|
value = tegra_dsi_readl(dsi, DSI_STATUS);
|
||||||
|
if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) {
|
||||||
|
value = DSI_HOST_CONTROL_FIFO_RESET;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
|
||||||
|
udelay(20); // usleep_range(10, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
|
||||||
|
value |= DSI_POWER_CONTROL_ENABLE;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
|
||||||
|
|
||||||
|
udelay(7000); //usleep_range(5000, 10000);
|
||||||
|
|
||||||
|
value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST |
|
||||||
|
DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC;
|
||||||
|
|
||||||
|
if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0)
|
||||||
|
value |= DSI_HOST_CONTROL_HS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The host FIFO has a maximum of 64 words, so larger transmissions
|
||||||
|
* need to use the video FIFO.
|
||||||
|
*/
|
||||||
|
if (msg->tx_len > dsi->host_fifo_depth * 4)
|
||||||
|
value |= DSI_HOST_CONTROL_FIFO_SEL;
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For reads and messages with explicitly requested ACK, generate a
|
||||||
|
* BTA sequence after the transmission of the packet.
|
||||||
|
*/
|
||||||
|
if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) ||
|
||||||
|
(msg->rx_buf && msg->rx_len > 0)) {
|
||||||
|
value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
|
||||||
|
value |= DSI_HOST_CONTROL_PKT_BTA;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE;
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_CONTROL);
|
||||||
|
|
||||||
|
/* write packet header */
|
||||||
|
value = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
|
||||||
|
|
||||||
|
if (tx && msg->tx_len > 0)
|
||||||
|
value |= tx[0] << 8;
|
||||||
|
|
||||||
|
if (tx && msg->tx_len > 1)
|
||||||
|
value |= tx[1] << 16;
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_WR_DATA);
|
||||||
|
|
||||||
|
/* write payload (if any) */
|
||||||
|
if (msg->tx_len > 2) {
|
||||||
|
for (j = 2; j < msg->tx_len; j += 4) {
|
||||||
|
value = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 4 && j + i < msg->tx_len; i++)
|
||||||
|
value |= tx[j + i] << (i << 3);
|
||||||
|
|
||||||
|
tegra_dsi_writel(dsi, value, DSI_WR_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tegra_dsi_transmit(dsi, 250);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) ||
|
||||||
|
(msg->rx_buf && msg->rx_len > 0)) {
|
||||||
|
err = tegra_dsi_wait_for_response(dsi, 250);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
count = err;
|
||||||
|
|
||||||
|
value = tegra_dsi_readl(dsi, DSI_RD_DATA);
|
||||||
|
switch (value) {
|
||||||
|
case 0x84:
|
||||||
|
/*
|
||||||
|
dev_dbg(dsi->dev, "ACK\n");
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x87:
|
||||||
|
/*
|
||||||
|
dev_dbg(dsi->dev, "ESCAPE\n");
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printk(BIOS_INFO, "unknown status: %08x\n", value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 1) {
|
||||||
|
err = tegra_dsi_read_response(dsi, msg, count);
|
||||||
|
if (err < 0)
|
||||||
|
printk(BIOS_INFO,
|
||||||
|
"failed to parse response: %d\n",
|
||||||
|
err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dsi_ganged_setup(struct tegra_dsi *dsi,
|
||||||
|
struct tegra_dsi *slave)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The number of ganged lanes is the sum of lanes of all peripherals
|
||||||
|
* in the gang.
|
||||||
|
*/
|
||||||
|
dsi->slave->ganged_lanes = dsi->lanes + dsi->slave->lanes;
|
||||||
|
dsi->slave->ganged_mode = 1;
|
||||||
|
|
||||||
|
dsi->ganged_lanes = dsi->lanes + dsi->slave->lanes;
|
||||||
|
dsi->ganged_mode = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
|
||||||
|
struct mipi_dsi_device *device)
|
||||||
|
{
|
||||||
|
struct tegra_dsi *dsi = host_to_tegra(host);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
dsi->flags = device->mode_flags;
|
||||||
|
dsi->format = device->format;
|
||||||
|
dsi->lanes = device->lanes;
|
||||||
|
|
||||||
|
if (dsi->master) {
|
||||||
|
err = tegra_dsi_ganged_setup(dsi->master, dsi);
|
||||||
|
if (err < 0) {
|
||||||
|
printk(BIOS_ERR, "failed to set up ganged mode: %d\n",
|
||||||
|
err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mipi_dsi_host_ops tegra_dsi_host_ops = {
|
||||||
|
.attach = tegra_dsi_host_attach,
|
||||||
|
.transfer = tegra_dsi_host_transfer,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dsi_probe_if(int dsi_index,
|
||||||
|
struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
struct tegra_dsi *dsi = &dsi_data[dsi_index];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set default value. Will be taken from attached device once detected
|
||||||
|
*/
|
||||||
|
dsi->flags = 0;
|
||||||
|
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||||
|
dsi->lanes = 4;
|
||||||
|
|
||||||
|
/* get tegra_mipi_device */
|
||||||
|
dsi->mipi = tegra_mipi_request(&mipi_device_data[dsi_index], dsi_index);
|
||||||
|
|
||||||
|
/* calibrate */
|
||||||
|
err = tegra_dsi_pad_calibrate(dsi);
|
||||||
|
if (err < 0) {
|
||||||
|
printk(BIOS_ERR, "MIPI calibration failed: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsi->host.ops = &tegra_dsi_host_ops;
|
||||||
|
err = mipi_dsi_host_register(&dsi->host);
|
||||||
|
if (err < 0) {
|
||||||
|
printk(BIOS_ERR, "failed to register DSI host: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get panel */
|
||||||
|
dsi->panel = panel_jdi_dsi_probe((struct mipi_dsi_device *)dsi->host.dev);
|
||||||
|
if (IS_ERR_PTR(dsi->panel)) {
|
||||||
|
printk(BIOS_ERR, "failed to get dsi panel\n");
|
||||||
|
return -EPTR;
|
||||||
|
}
|
||||||
|
dsi->panel->mode = config;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsi_probe(struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
dsi_probe_if(DSI_A, config);
|
||||||
|
dsi_probe_if(DSI_B, config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dsi_enable(struct soc_nvidia_tegra132_config *config)
|
||||||
|
{
|
||||||
|
struct tegra_dsi *dsi_a = &dsi_data[DSI_A];
|
||||||
|
|
||||||
|
dsi_probe(config);
|
||||||
|
|
||||||
|
/* set up clock and TimeOutRegisters */
|
||||||
|
tegra_output_dsi_setup_clock(dsi_a, config);
|
||||||
|
|
||||||
|
/* configure APB_MISC_GP_MIPI_PAD_CTRL_0 */
|
||||||
|
write32(DSIB_MODE_DSI, (unsigned int *)APB_MISC_GP_MIPI_PAD_CTRL_0);
|
||||||
|
|
||||||
|
/* configure phy interface timing registers */
|
||||||
|
tegra_dsi_set_phy_timing(dsi_a);
|
||||||
|
|
||||||
|
/* prepare panel */
|
||||||
|
panel_jdi_prepare(dsi_a->panel);
|
||||||
|
|
||||||
|
/* enable dsi */
|
||||||
|
if (tegra_output_dsi_enable(dsi_a, config)) {
|
||||||
|
printk(BIOS_ERR,"%s: Error: failed to enable dsi output.\n",
|
||||||
|
__func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue