iBoot/drivers/samsung/mipi/dsim.c

508 lines
14 KiB
C

/*
* Copyright (C) 2008-2015 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <drivers/mipi.h>
#include <platform.h>
#include <platform/clocks.h>
#include <platform/soc/hwclocks.h>
#include <target/gpiodef.h>
#include "dsim.h"
#define DISPLAY_CONFIG_PLL_P(x) (((x) >> 26) & 0x03F)
#define DISPLAY_CONFIG_PLL_M(x) (((x) >> 16) & 0x3FF)
#define DISPLAY_CONFIG_PLL_S(x) (((x) >> 12) & 0x007)
#define DISPLAY_CONFIG_PLL_B(x) (((x) >> 8) & 0x00F)
#define DISPLAY_CONFIG_ESC_DIV(x) (((x) >> 4) & 0x00F)
#define DISPLAY_CONFIG_LANES(x) (((x) >> 0) & 0x00F)
#define DSIM_LANE_MASK ((1 << DSIM_LANE_COUNT) - 1)
static u_int32_t lane_count;
static u_int32_t lane_mask;
static uint32_t esc_clock_div;
static bool dsim_enabled;
int mipi_dsim_init(struct display_timing *timing, enum colorspace color)
{
bool video_mode = true;
u_int32_t tmp, pll_config, pix_format, afc_code = 0;
u_int32_t config = rDSIM_CONFIG_HfpMode;
bool afc_enable = false;
uint32_t display_config;
dprintf(DEBUG_CRITICAL, "mipi_dsim_init()\n");
memcpy(&display_config, timing->display_config, sizeof(display_config));
// Extract Lane configuration
lane_count = DISPLAY_CONFIG_LANES(display_config);
lane_mask = (1 << lane_count) - 1;
// Extract PLL configuration
pll_config = (rDSIM_PLLCTRL_P(DISPLAY_CONFIG_PLL_P(display_config)) |
rDSIM_PLLCTRL_M(DISPLAY_CONFIG_PLL_M(display_config)) |
rDSIM_PLLCTRL_S(DISPLAY_CONFIG_PLL_S(display_config)));
// Extract Pixel Format
if (timing->display_depth <= 18) pix_format = 5;
else pix_format = 7;
//Extract escape clock divisor
esc_clock_div = DISPLAY_CONFIG_ESC_DIV(display_config);
clock_gate(CLK_MIPI, true);
// Configure clock selections
rDSIM_CLKCTRL = (rDSIM_CLKCTRL_EscClkEn |
((pll_config == 0) ? rDSIM_CLKCTRL_PllBypass : 0) |
rDSIM_CLKCTRL_ByteClkSrc((pll_config == 0) ? 1 : 0) | // SOC or DSIM PLL
rDSIM_CLKCTRL_EscPrescalar(DISPLAY_CONFIG_ESC_DIV(display_config)));
// Select frequency band
rDSIM_PLLCTRL = rDSIM_PLLCTRL_FreqBand(DISPLAY_CONFIG_PLL_B(display_config));
#ifdef TARGET_DSIM_THS_PREPARE
//Adjust tHS-PREPARE on the phy
rDSIM_PLLCTRL |= rDSIM_PLLCTRL_PreprCtl(TARGET_DSIM_THS_PREPARE);
#endif //TARGET_DSIM_THS_PREPARE
// Configure PLL if needed
if (pll_config != 0) {
#if DSIM_VERSION == 1
u_int32_t fin_pll;
// Assume that AFC will be used
afc_enable = true;
// Set the AFC code in PHYACCHR
fin_pll = clock_get_frequency(CLK_NCLK) / (1000000 * DISPLAY_CONFIG_PLL_P(display_config));
// These code values are only for the 45nm PHY (i.e. DSIM_VERSION == 1)
switch (fin_pll) {
case 6: afc_code = 1; break;
case 7: afc_code = 0; break;
case 8: afc_code = 3; break;
case 9: afc_code = 2; break;
case 10: afc_code = 5; break;
case 11: afc_code = 4; break;
case 12: afc_code = 4; break;
default : afc_enable = false; break;
}
#endif
// If needed, enable AFC
if (afc_enable) {
rDSIM_PHYACCHR = rDSIM_PHYACCHR_AFC(afc_code) | rDSIM_PHYACCHR_AFC_ENABLE;
}
rDSIM_PLLTMR = 300000; // 1ms - HACK: assuming 300MHz hperf2
rDSIM_PLLCTRL |= pll_config;
rDSIM_PLLCTRL |= rDSIM_PLLCTRL_PllEn;
while ((rDSIM_STATUS & rDSIM_STATUS_PllStable) == 0);
}
// Issue software reset
rDSIM_SWRST = rDSIM_SWRST_SwRst;
// If needed, enable AFC again
if (afc_enable) {
// PLL must be off to change AFC setting
rDSIM_PLLCTRL &= ~rDSIM_PLLCTRL_PllEn;
rDSIM_PHYACCHR = rDSIM_PHYACCHR_AFC(afc_code) | rDSIM_PHYACCHR_AFC_ENABLE;
// Turn PLL back on
rDSIM_PLLCTRL |= rDSIM_PLLCTRL_PllEn;
}
// Wait for software reset to complete
while ((rDSIM_STATUS & rDSIM_STATUS_SwRstRls) == 0);
#ifdef TARGET_DSIM_DPHYCTL
rDSIM_PHYACCHR |= rDSIM_PHYACCHR_DPHYCTL(TARGET_DSIM_DPHYCTL);
#endif // TARGET_DSIM_DPHYCTL
#if TARGET_DSIM_DOWN_CODE
rDSIM_PHYACCHR |= rDSIM_PHYACCHR_DOWN_CODE(TARGET_DSIM_DOWN_CODE);
rDSIM_PHYACCHR2 |= rDSIM_PHYACCHR2_DOWN_CODE(TARGET_DSIM_DOWN_CODE);
#endif // TARGET_DSIM_DOWN_CODE
#ifdef TARGET_DSIM_UP_CODE
rDSIM_PHYACCHR |= rDSIM_PHYACCHR_UP_CODE(TARGET_DSIM_UP_CODE);
rDSIM_PHYACCHR2 |= rDSIM_PHYACCHR2_UP_CODE(TARGET_DSIM_UP_CODE);
#endif // TARGET_DSIM_UP_CODE
#ifdef TARGET_DSIM_SUPPRESS
rDSIM_SUPPRESS |= TARGET_DSIM_SUPPRESS;
#endif // TARGET_DSIM_SUPPRESS
// Use the same resolution as CLCD
rDSIM_MDRESOL = (rDSIM_MDRESOL_MainVResol(timing->v_active) |
rDSIM_MDRESOL_MainHResol(timing->h_active));
#ifdef TARGET_DISP_VIDEO_MODE
video_mode = TARGET_DISP_VIDEO_MODE;
#endif //TARGET_DISP_VIDEO_MODE
if (video_mode) {
// The vertical blanking settings are the same as the CLCD settings
rDSIM_MVPORCH = (rDSIM_MVPORCH_CmdAllow(13) |
rDSIM_MVPORCH_StableVfp(timing->v_front_porch) |
rDSIM_MVPORCH_MainVbp(timing->v_back_porch));
rDSIM_MSYNC = (timing->v_pulse_width << 22);
// The horizontal blanking settings are constants which are
// sufficent for all lane counts, frequencies and CLCD settings
// Hfp = 15
// Hbp = 14
// Hsa = 1
rDSIM_MHPORCH = (15 << 16) | (14 << 0);
rDSIM_MSYNC |= (1 << 0);
}
#if DSIM_VERSION == 3
else {
rDSIM_I80_LPVALID_ST = (timing->h_active * 3) + 100;
}
#endif // DSIM_VERSION == 3
rDSIM_SSCNT = 0xa;
#ifdef TARGET_DSIM_CONFIG
config = TARGET_DSIM_CONFIG;
#endif
rDSIM_CONFIG = (rDSIM_CONFIG_BurstMode |
rDSIM_CONFIG_AutoMode |
config |
rDSIM_CONFIG_MainPixFormat(pix_format) |
rDSIM_CONFIG_NumOfDatLane(DSIM_LANE_COUNT - 1) |
rDSIM_CONFIG_LaneEn(DSIM_LANE_MASK) |
rDSIM_CONFIG_LaneClkEn);
if (video_mode) {
rDSIM_CONFIG |= rDSIM_CONFIG_VideoMode;
}
#ifdef TARGET_NO_BURST_MODE
if (TARGET_NO_BURST_MODE) {
rDSIM_CONFIG &= ~rDSIM_CONFIG_BurstMode;
}
#endif
rDSIM_CLKCTRL |= (rDSIM_CLKCTRL_ByteClkEn |
rDSIM_CLKCTRL_LaneEscClkEn(DSIM_LANE_MASK) |
rDSIM_CLKCTRL_LaneEscClkEnClk);
rDSIM_FIFOCTRL = 0x1F;
rDSIM_FIFOTHLD = 0x1FF;
// Force Stop state on all lanes
rDSIM_ESCMODE = (rDSIM_ESCMODE_CmdLpdt | rDSIM_ESCMODE_ForceStopstate);
spin(1 * 1000);
rDSIM_ESCMODE &= ~rDSIM_ESCMODE_ForceStopstate;
// Wait for Stop state on all lanes
tmp = rDSIM_STATUS_StopstateClk | rDSIM_STATUS_StopstateDat(DSIM_LANE_MASK);
while ((rDSIM_STATUS & tmp) != tmp);
// Request ULPS mode on all lanes
rDSIM_ESCMODE = (rDSIM_ESCMODE_CmdLpdt | rDSIM_ESCMODE_TxUlpsClk | rDSIM_ESCMODE_TxUlpsDat);
// Wait for ULPS mode on all lanes
tmp = rDSIM_STATUS_UlpsClk | rDSIM_STATUS_UlpsDat(DSIM_LANE_MASK);
while ((rDSIM_STATUS & tmp) != tmp);
// Set the lane configuration
rDSIM_CONFIG &= ~(rDSIM_CONFIG_NumOfDatLaneMsk | rDSIM_CONFIG_LaneEnMsk);
rDSIM_CONFIG |= rDSIM_CONFIG_NumOfDatLane(lane_count - 1) | rDSIM_CONFIG_LaneEn(lane_mask);
// Enable the configured lanes
rDSIM_CLKCTRL &= ~rDSIM_CLKCTRL_LaneEscClkEnMsk;
rDSIM_CLKCTRL |= rDSIM_CLKCTRL_LaneEscClkEn(lane_mask);
// Request exit ULPS mode on configured lanes
rDSIM_ESCMODE |= (rDSIM_ESCMODE_TxUlpsClkExit | rDSIM_ESCMODE_TxUlpsExit);
// Wait for exit ULPS mode on configured lanes
tmp = rDSIM_STATUS_UlpsClk | rDSIM_STATUS_UlpsDat(lane_mask);
while ((rDSIM_STATUS & tmp) != 0);
// Clear exit ULPS mode request and ULPS mode request
rDSIM_ESCMODE &= ~(rDSIM_ESCMODE_TxUlpsClkExit | rDSIM_ESCMODE_TxUlpsExit);
spin(1 * 1000);
rDSIM_ESCMODE &= ~(rDSIM_ESCMODE_TxUlpsClk | rDSIM_ESCMODE_TxUlpsDat);
// Wait for Stop state on configured lanes
tmp = rDSIM_STATUS_StopstateClk | rDSIM_STATUS_StopstateDat(lane_mask);
while ((rDSIM_STATUS & tmp) != tmp);
#if DSIM_VERSION == 3
if (!video_mode) {
rDSIM_ULPSIN = TARGET_DSI_ULPS_IN_DELAY;
rDSIM_ULPSEND = TARGET_DSI_ULPS_END_DELAY;
rDSIM_ULPSOUT = TARGET_DSI_ULPS_OUT_DELAY;
//<rdar://problem/18037708> SkiHillVail12S5356c: panic(cpu 0 caller 0x855223df): "_waitUntilPayloadFifoIsEmpty: Timeout Waiting for payload fifo to empty"
rDSIM_ESCMODE |= rDSIM_ESCMODE_Auto_Ulps_Clk;
}
#endif //DSIM_VERSION == 3
dsim_enabled = true;
return 0;
}
int mipi_dsim_quiesce(void)
{
u_int32_t tmp, pllctrl, phyacchr;
dprintf(DEBUG_CRITICAL, "mipi_dsim_quiesce()\n");
if (!dsim_enabled) return 0;
// Save the PLLCTRL and PHYACCHR settings before software reset
pllctrl = rDSIM_PLLCTRL;
phyacchr = rDSIM_PHYACCHR;
// Issue software reset
rDSIM_SWRST = rDSIM_SWRST_SwRst;
// Turn off the PLL if it was on
rDSIM_PLLCTRL = pllctrl & ~rDSIM_PLLCTRL_PllEn;
// Restore PHYACCHR setting
rDSIM_PHYACCHR = phyacchr;
// If the PLL was on, turn it back on
rDSIM_PLLCTRL = pllctrl;
// Wait for reset to complete
while ((rDSIM_STATUS & rDSIM_STATUS_SwRstRls) == 0);
// Set the lane configuration
rDSIM_CONFIG &= ~(rDSIM_CONFIG_NumOfDatLaneMsk | rDSIM_CONFIG_LaneEnMsk);
rDSIM_CONFIG |= (rDSIM_CONFIG_NumOfDatLane(DSIM_LANE_COUNT - 1) |
rDSIM_CONFIG_LaneEn(DSIM_LANE_MASK) |
rDSIM_CONFIG_LaneClkEn);
// Enable the configured lanes
rDSIM_CLKCTRL &= ~(rDSIM_CLKCTRL_TxRequestHsClk | rDSIM_CLKCTRL_LaneEscClkEnMsk);
rDSIM_CLKCTRL |= rDSIM_CLKCTRL_LaneEscClkEn(DSIM_LANE_MASK);
// Force Stop state on all lanes
rDSIM_ESCMODE = (rDSIM_ESCMODE_CmdLpdt | rDSIM_ESCMODE_ForceStopstate);
spin(1 * 1000);
rDSIM_ESCMODE &= ~rDSIM_ESCMODE_ForceStopstate;
// Wait for Stop state on all lanes
tmp = rDSIM_STATUS_StopstateClk | rDSIM_STATUS_StopstateDat(DSIM_LANE_MASK);
while ((rDSIM_STATUS & tmp) != tmp);
// Request ULPS mode on all lanes
rDSIM_ESCMODE = (rDSIM_ESCMODE_CmdLpdt | rDSIM_ESCMODE_TxUlpsClk | rDSIM_ESCMODE_TxUlpsDat);
// Wait for ULPS mode on all lanes
tmp = rDSIM_STATUS_UlpsClk | rDSIM_STATUS_UlpsDat(DSIM_LANE_MASK);
while ((rDSIM_STATUS & tmp) != tmp);
// Turn off internal clocks
rDSIM_CLKCTRL = 0x00000000;
rDSIM_CONFIG = rDSIM_CONFIG_VideoMode;
rDSIM_ESCMODE = 0x00000000;
rDSIM_PLLCTRL = 0x00000000;
clock_gate(CLK_MIPI, false);
dsim_enabled = false;
return 0;
}
void mipi_dsim_enable_high_speed(bool enable)
{
if (enable) {
rDSIM_CLKCTRL |= rDSIM_CLKCTRL_TxRequestHsClk;
while ((rDSIM_STATUS & rDSIM_STATUS_TxReadyHsClk) == 0);
} else {
rDSIM_CLKCTRL &= ~rDSIM_CLKCTRL_TxRequestHsClk;
while ((rDSIM_STATUS & rDSIM_STATUS_TxReadyHsClk) != 0);
}
}
void mipi_dsim_enable_video(bool enable)
{
if (enable) {
rDSIM_MDRESOL |= rDSIM_MDRESOL_MainStandby;
} else {
rDSIM_MDRESOL &= ~rDSIM_MDRESOL_MainStandby;
}
}
int mipi_dsim_send_short_command(u_int8_t cmd, u_int8_t data0, u_int8_t data1)
{
uint32_t escape_clock_period_us;
uint32_t mipi_byte_clock_frequency;
mipi_byte_clock_frequency = clock_get_frequency(CLK_MIPI)/ 8;
escape_clock_period_us = (100000000 * esc_clock_div) /mipi_byte_clock_frequency;
escape_clock_period_us = (escape_clock_period_us/100) < 1 ? 1 : (escape_clock_period_us/100);
rDSIM_PKTHDR = (cmd & 0x3f) | (((u_int32_t)data0) << 8) | (((u_int32_t)data1) << 16);
//10 ESC clocks before checking the status bit.
spin (10 * escape_clock_period_us);
//check for command header to have been sent
while ((rDSIM_FIFOCTRL & rDSIM_FIFOCTRL_EmptyHSfr) == 0);
//check for command payload to have been sent
while ((rDSIM_FIFOCTRL & rDSIM_FIFOCTRL_EmptyLSfr) == 0);
spin(22 + ((343 * 1000000 * esc_clock_div)/mipi_byte_clock_frequency));
return 0;
}
int mipi_dsim_send_long_command(u_int8_t cmd, const u_int8_t *data, u_int32_t length)
{
u_int32_t cnt, payload = 0;
for (cnt = 0; cnt < length; cnt++) {
payload = (payload >> 8) | ((u_int32_t)data[cnt] << 24);
if ((cnt & 3) == 3) {
rDSIM_PAYLOAD = payload;
payload = 0;
}
}
if (payload) {
payload >>= 32 - ((length & 3) * 8);
rDSIM_PAYLOAD = payload;
}
for (cnt = 0; cnt < 5; cnt++) {
rDSIM_PAYLOAD = 0;
}
length += 20;
return mipi_dsim_send_short_command(cmd, length & 0xff, (length >> 8) & 0xff);
}
int mipi_dsim_read_short_command(u_int8_t cmd, u_int8_t *data)
{
u_int32_t tmp, i = 0;
// Clear pending interrupts
rDSIM_INTSRC = 0xffffffff;
mipi_dsim_send_short_command(cmd, data[0], data[1]);
while (1) {
tmp = rDSIM_INTSRC;
if (tmp & rDSIM_INTSRC_RxAck) return -1;
if (tmp & rDSIM_INTSRC_LpdrTout) return -1;
// Ignore errors for now (not all drivers do ECC)
// if (tmp & rDSIM_INTSRC_AnyError) return -1;
if (tmp & rDSIM_INTSRC_RxDatDone) break;
// Hardware timeout depends on having a receiver, and
// completing BTA. This isn't guaranteed for dev boards.
if (++i == 5) {
dprintf(DEBUG_CRITICAL, "mipi: timing out\n");
return -1;
}
spin(50 * 1000);
}
while (rDSIM_FIFOCTRL & rDSIM_FIFOCTRL_EmptyRx);
tmp = rDSIM_RXFIFO;
switch (tmp & 0x3f) {
case DSIM_RSP_READ_1B:
case DSIM_RSP_DSC_READ_1B:
data[0] = (tmp >> 8) & 0xff;
data[1] = 0;
break;
case DSIM_RSP_READ_2B:
case DSIM_RSP_DSC_READ_2B:
data[0] = (tmp >> 8) & 0xff;
data[1] = (tmp >> 16) & 0xff;
break;
default:
return -1;
}
return 0;
}
int mipi_dsim_read_long_command(u_int8_t cmd, u_int8_t *data, u_int32_t *length)
{
u_int32_t tmp, len, maxlen, i = 0;
maxlen = *length;
// Clear pending interrupts
rDSIM_INTSRC = 0xffffffff;
mipi_dsim_send_short_command(cmd, data[0], data[1]);
while (1) {
tmp = rDSIM_INTSRC;
if (tmp & rDSIM_INTSRC_RxAck) return -1;
if (tmp & rDSIM_INTSRC_LpdrTout) return -1;
// Ignore errors for now (not all drivers do ECC)
// if (tmp & rDSIM_INTSRC_AnyError) return -1;
if (tmp & rDSIM_INTSRC_RxDatDone) break;
// Hardware timeout depends on having a receiver, and
// completing BTA. This isn't guaranteed for dev boards.
if (++i == 5) {
dprintf(DEBUG_CRITICAL, "mipi: timing out\n");
return -1;
}
spin(50 * 1000);
}
while (rDSIM_FIFOCTRL & rDSIM_FIFOCTRL_EmptyRx);
tmp = rDSIM_RXFIFO;
switch (tmp & 0x3f) {
case DSIM_RSP_LONG_READ:
case DSIM_RSP_DSC_LONG_READ:
break;
default:
dprintf(DEBUG_CRITICAL, "mipi: weird response %08x\n", tmp);
return -1;
}
len = (tmp >> 8) & 0xffff;
if (len < maxlen)
*length = len;
for (i = 0; i < len; i++) {
if ((i % 4) == 0) {
while (rDSIM_FIFOCTRL & rDSIM_FIFOCTRL_EmptyRx);
tmp = rDSIM_RXFIFO;
}
if (i < maxlen)
data[i] = tmp & 0xff;
tmp >>= 8;
}
return 0;
}