diff --git a/src/soc/nvidia/tegra/dc.h b/src/soc/nvidia/tegra/dc.h index ff36a0b589..0d2d7618a9 100644 --- a/src/soc/nvidia/tegra/dc.h +++ b/src/soc/nvidia/tegra/dc.h @@ -24,6 +24,7 @@ #ifndef __SOC_NVIDIA_TEGRA_DC_H #define __SOC_NVIDIA_TEGRA_DC_H +#include /* Register definitions for the Tegra display controller */ diff --git a/src/soc/nvidia/tegra/types.h b/src/soc/nvidia/tegra/types.h new file mode 100644 index 0000000000..ce0b6bb088 --- /dev/null +++ b/src/soc/nvidia/tegra/types.h @@ -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__ */ diff --git a/src/soc/nvidia/tegra132/Makefile.inc b/src/soc/nvidia/tegra132/Makefile.inc index 117f629b46..a7485fa398 100644 --- a/src/soc/nvidia/tegra132/Makefile.inc +++ b/src/soc/nvidia/tegra132/Makefile.inc @@ -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 diff --git a/src/soc/nvidia/tegra132/chip.h b/src/soc/nvidia/tegra132/chip.h index 23f2cd8f38..35e72d59e6 100644 --- a/src/soc/nvidia/tegra132/chip.h +++ b/src/soc/nvidia/tegra132/chip.h @@ -19,13 +19,9 @@ #ifndef __SOC_NVIDIA_TEGRA132_CHIP_H__ #define __SOC_NVIDIA_TEGRA132_CHIP_H__ -#include #include #include -#define EFAULT 1 -#define EINVAL 2 - struct soc_nvidia_tegra132_config { /* Address to monitor if spintable employed. */ uintptr_t spintable_addr; diff --git a/src/soc/nvidia/tegra132/display.c b/src/soc/nvidia/tegra132/display.c new file mode 100644 index 0000000000..62cea026e5 --- /dev/null +++ b/src/soc/nvidia/tegra132/display.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "chip.h" +#include + +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__); +} + diff --git a/src/soc/nvidia/tegra132/include/soc/addressmap.h b/src/soc/nvidia/tegra132/include/soc/addressmap.h index 79135217a0..36b0233873 100644 --- a/src/soc/nvidia/tegra132/include/soc/addressmap.h +++ b/src/soc/nvidia/tegra132/include/soc/addressmap.h @@ -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, diff --git a/src/soc/nvidia/tegra132/include/soc/display.h b/src/soc/nvidia/tegra132/include/soc/display.h index a6cd1a4d5f..3378a4f2d2 100644 --- a/src/soc/nvidia/tegra132/include/soc/display.h +++ b/src/soc/nvidia/tegra132/include/soc/display.h @@ -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__ */ diff --git a/src/soc/nvidia/tegra132/include/soc/mipi-phy.h b/src/soc/nvidia/tegra132/include/soc/mipi-phy.h new file mode 100644 index 0000000000..2ec3fce113 --- /dev/null +++ b/src/soc/nvidia/tegra132/include/soc/mipi-phy.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 + +/* + * 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 diff --git a/src/soc/nvidia/tegra132/include/soc/mipi_display.h b/src/soc/nvidia/tegra132/include/soc/mipi_display.h new file mode 100644 index 0000000000..99bfa93ea7 --- /dev/null +++ b/src/soc/nvidia/tegra132/include/soc/mipi_display.h @@ -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 + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak + * + * 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 diff --git a/src/soc/nvidia/tegra132/include/soc/mipi_dsi.h b/src/soc/nvidia/tegra132/include/soc/mipi_dsi.h new file mode 100644 index 0000000000..1bca657ac5 --- /dev/null +++ b/src/soc/nvidia/tegra132/include/soc/mipi_dsi.h @@ -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 + * + * 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__ */ diff --git a/src/soc/nvidia/tegra132/include/soc/tegra_dsi.h b/src/soc/nvidia/tegra132/include/soc/tegra_dsi.h new file mode 100644 index 0000000000..830257d7cc --- /dev/null +++ b/src/soc/nvidia/tegra132/include/soc/tegra_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__ */ diff --git a/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.c b/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.c new file mode 100644 index 0000000000..45797b1cc5 --- /dev/null +++ b/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../chip.h" +#include +#include +#include +#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; +} diff --git a/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.h b/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.h new file mode 100644 index 0000000000..a44882d386 --- /dev/null +++ b/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.h @@ -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 diff --git a/src/soc/nvidia/tegra132/mipi-phy.c b/src/soc/nvidia/tegra132/mipi-phy.c new file mode 100644 index 0000000000..35adf6e6ee --- /dev/null +++ b/src/soc/nvidia/tegra132/mipi-phy.c @@ -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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/soc/nvidia/tegra132/mipi.c b/src/soc/nvidia/tegra132/mipi.c new file mode 100644 index 0000000000..abd2230378 --- /dev/null +++ b/src/soc/nvidia/tegra132/mipi.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/src/soc/nvidia/tegra132/mipi_dsi.c b/src/soc/nvidia/tegra132/mipi_dsi.c new file mode 100644 index 0000000000..440bb23dfc --- /dev/null +++ b/src/soc/nvidia/tegra132/mipi_dsi.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/soc/nvidia/tegra132/soc.c b/src/soc/nvidia/tegra132/soc.c index 0014f84b54..d80a388f3c 100644 --- a/src/soc/nvidia/tegra132/soc.c +++ b/src/soc/nvidia/tegra132/soc.c @@ -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; diff --git a/src/soc/nvidia/tegra132/tegra_dsi.c b/src/soc/nvidia/tegra132/tegra_dsi.c new file mode 100644 index 0000000000..3a7949d655 --- /dev/null +++ b/src/soc/nvidia/tegra132/tegra_dsi.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "chip.h" +#include +#include +#include +#include +#include +#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; +}