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:
Jimmy Zhang 2014-09-15 16:50:36 -07:00 committed by Patrick Georgi
parent 51067116eb
commit e3a938dfdd
18 changed files with 2989 additions and 185 deletions

View File

@ -24,6 +24,7 @@
#ifndef __SOC_NVIDIA_TEGRA_DC_H
#define __SOC_NVIDIA_TEGRA_DC_H
#include <stddef.h>
/* Register definitions for the Tegra display controller */

View File

@ -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__ */

View File

@ -66,6 +66,13 @@ ramstage-y += cbmem.c
ramstage-y += cpu.c
ramstage-y += cpu_lib.S
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 += spi.c
ramstage-y += i2c.c

View File

@ -19,13 +19,9 @@
#ifndef __SOC_NVIDIA_TEGRA132_CHIP_H__
#define __SOC_NVIDIA_TEGRA132_CHIP_H__
#include <arch/cache.h>
#include <soc/addressmap.h>
#include <stdint.h>
#define EFAULT 1
#define EINVAL 2
struct soc_nvidia_tegra132_config {
/* Address to monitor if spintable employed. */
uintptr_t spintable_addr;

View File

@ -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__);
}

View File

@ -35,6 +35,8 @@ enum {
TEGRA_GICC_BASE = 0x50042000,
TEGRA_ARM_DISPLAYA = 0x54200000,
TEGRA_ARM_DISPLAYB = 0x54240000,
TEGRA_DSIA_BASE = 0x54300000,
TEGRA_DSIB_BASE = 0x54400000,
TEGRA_ARM_SOR = 0x54540000,
TEGRA_ARM_DPAUX = 0x545c0000,
TEGRA_PG_UP_BASE = 0x60000000,
@ -85,6 +87,7 @@ enum {
TEGRA_SDMMC2_BASE = TEGRA_SDMMC_BASE + 0x0200,
TEGRA_SDMMC3_BASE = TEGRA_SDMMC_BASE + 0x0400,
TEGRA_SDMMC4_BASE = TEGRA_SDMMC_BASE + 0x0600,
TEGRA_MIPI_CAL_BASE = 0x700E3000,
TEGRA_SYSCTR0_BASE = 0x700F0000,
TEGRA_USBD_BASE = 0x7D000000,
TEGRA_USB2_BASE = 0x7D004000,

View File

@ -17,184 +17,25 @@
#ifndef __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_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 */
void setup_display(struct soc_nvidia_tegra132_config *config);
void init_dca_regs(void);
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);
int dsi_enable(struct soc_nvidia_tegra132_config *config);
#endif /* __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__ */

View File

@ -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

View File

@ -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

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -76,11 +76,6 @@ static struct cpu_control_ops cntrl_ops = {
.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)
{
struct soc_nvidia_tegra132_config *cfg;

View File

@ -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;
}