/* Clocks */ #define DP0_VIDMNGEN0 0x0610 /* DP0 Video Force M Value Register */ #define DP0_VIDMNGEN1 0x0614 /* DP0 Video Force N Value Register */ #define DP0_VMNGENSTATUS 0x0618 /* DP0 Video Current M Value Register */ #define DP0_AUDMNGEN0 0x0628 /* DP0 Audio Force M Value Register */ #define DP0_AUDMNGEN1 0x062c /* DP0 Audio Force N Value Register */ #define DP0_AMNGENSTATUS 0x0630 /* DP0 Audio Current M Value Register */
switch (request) { case DP_AUX_NATIVE_READ: case DP_AUX_I2C_READ: break; case DP_AUX_NATIVE_WRITE: case DP_AUX_I2C_WRITE: if (size) {
ret = tc_aux_write_data(tc, msg->buffer, size); if (ret < 0) return ret;
} break; default: return -EINVAL;
}
/* Store address */
ret = regmap_write(tc->regmap, DP0_AUXADDR, msg->address); if (ret) return ret; /* Start transfer */
ret = regmap_write(tc->regmap, DP0_AUXCFG0, tc_auxcfg0(msg, size)); if (ret) return ret;
ret = tc_aux_wait_busy(tc); if (ret) return ret;
ret = regmap_read(tc->regmap, DP0_AUXSTATUS, &auxstatus); if (ret) return ret;
if (auxstatus & AUX_TIMEOUT) return -ETIMEDOUT; /* * For some reason address-only DP_AUX_I2C_WRITE (MOT), still * reports 1 byte transferred in its status. To deal we that * we ignore aux_bytes field if we know that this was an * address-only transfer
*/ if (size)
size = FIELD_GET(AUX_BYTES, auxstatus);
msg->reply = FIELD_GET(AUX_STATUS, auxstatus);
switch (request) { case DP_AUX_NATIVE_READ: case DP_AUX_I2C_READ: if (size) return tc_aux_read_data(tc, msg->buffer, size); break;
}
static u32 tc_srcctrl(struct tc_data *tc)
{ /* * No training pattern, skew lane 1 data by two LSCLK cycles with * respect to lane 0 data, AutoCorrect Mode = 0
*/
u32 reg = DP0_SRCCTRL_NOTP | DP0_SRCCTRL_LANESKEW | DP0_SRCCTRL_EN810B;
if (tc->link.scrambler_dis)
reg |= DP0_SRCCTRL_SCRMBLDIS; /* Scrambler Disabled */ if (tc->link.spread)
reg |= DP0_SRCCTRL_SSCG; /* Spread Spectrum Enable */ if (tc->link.num_lanes == 2)
reg |= DP0_SRCCTRL_LANES_2; /* Two Main Channel Lanes */ if (tc->link.rate != 162000)
reg |= DP0_SRCCTRL_BW27; /* 2.7 Gbps link */ return reg;
}
staticint tc_pllupdate(struct tc_data *tc, unsignedint pllctrl)
{ int ret;
ret = regmap_write(tc->regmap, pllctrl, PLLUPDATE | PLLEN); if (ret) return ret;
/* Wait for PLL to lock: up to 7.5 ms, depending on refclk */
usleep_range(15000, 20000);
return 0;
}
staticint tc_pxl_pll_calc(struct tc_data *tc, u32 refclk, u32 pixelclock, int *out_best_pixelclock, u32 *out_pxl_pllparam)
{ int i_pre, best_pre = 1; int i_post, best_post = 1; int div, best_div = 1; int mul, best_mul = 1; int delta, best_delta; int ext_div[] = {1, 2, 3, 5, 7}; int clk_min, clk_max; int best_pixelclock = 0; int vco_hi = 0;
u32 pxl_pllparam;
/* * refclk * mul / (ext_pre_div * pre_div) should be in range: * - DPI ..... 0 to 100 MHz * - (e)DP ... 150 to 650 MHz
*/ if (tc->bridge.type == DRM_MODE_CONNECTOR_DPI) {
clk_min = 0;
clk_max = 100000000;
} else {
clk_min = 150000000;
clk_max = 650000000;
}
dev_dbg(tc->dev, "PLL: requested %d pixelclock, ref %d\n", pixelclock,
refclk);
best_delta = pixelclock; /* Loop over all possible ext_divs, skipping invalid configurations */ for (i_pre = 0; i_pre < ARRAY_SIZE(ext_div); i_pre++) { /* * refclk / ext_pre_div should be in the 1 to 200 MHz range. * We don't allow any refclk > 200 MHz, only check lower bounds.
*/ if (refclk / ext_div[i_pre] < 1000000) continue; for (i_post = 0; i_post < ARRAY_SIZE(ext_div); i_post++) { for (div = 1; div <= 16; div++) {
u32 clk, iclk;
u64 tmp;
ret = tc_pxl_pll_calc(tc, refclk, pixelclock, NULL, &pxl_pllparam); if (ret) return ret;
/* Power up PLL and switch to bypass */
ret = regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP | PLLEN); if (ret) return ret;
ret = regmap_write(tc->regmap, PXL_PLLPARAM, pxl_pllparam); if (ret) return ret;
/* Force PLL parameter update and disable bypass */ return tc_pllupdate(tc, PXL_PLLCTRL);
}
staticint tc_pxl_pll_dis(struct tc_data *tc)
{ /* Enable PLL bypass, power down PLL */ return regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP);
}
staticint tc_stream_clock_calc(struct tc_data *tc)
{ /* * If the Stream clock and Link Symbol clock are * asynchronous with each other, the value of M changes over * time. This way of generating link clock and stream * clock is called Asynchronous Clock mode. The value M * must change while the value N stays constant. The * value of N in this Asynchronous Clock mode must be set * to 2^15 or 32,768. * * LSCLK = 1/10 of high speed link clock * * f_STRMCLK = M/N * f_LSCLK * M/N = f_STRMCLK / f_LSCLK *
*/ return regmap_write(tc->regmap, DP0_VIDMNGEN1, 32768);
}
/* * LCD Ctl Frame Size * datasheet is not clear of vsdelay in case of DPI * assume we do not need any delay when DPI is a source of * sync signals
*/
ret = regmap_write(tc->regmap, VPCTRL0,
FIELD_PREP(VSDELAY, right_margin + 10) |
OPXLFMT_RGB888 | FRMSYNC_ENABLED | MSF_DISABLED); if (ret) return ret;
ret = regmap_write(tc->regmap, HTIM01,
FIELD_PREP(HBPR, ALIGN(left_margin, 2)) |
FIELD_PREP(HPW, ALIGN(hsync_len, 2))); if (ret) return ret;
ret = regmap_write(tc->regmap, HTIM02,
FIELD_PREP(HDISPR, ALIGN(mode->hdisplay, 2)) |
FIELD_PREP(HFPR, ALIGN(right_margin, 2))); if (ret) return ret;
ret = regmap_write(tc->regmap, VTIM01,
FIELD_PREP(VBPR, upper_margin) |
FIELD_PREP(VSPR, vsync_len)); if (ret) return ret;
ret = regmap_write(tc->regmap, VTIM02,
FIELD_PREP(VFPR, lower_margin) |
FIELD_PREP(VDISPR, mode->vdisplay)); if (ret) return ret;
ret = regmap_write(tc->regmap, VFUEN0, VFUEN); /* update settings */ if (ret) return ret;
if (tc->mode.flags & DRM_MODE_FLAG_NHSYNC)
value |= POCTRL_HS_POL;
if (tc->mode.flags & DRM_MODE_FLAG_NVSYNC)
value |= POCTRL_VS_POL;
return regmap_write(tc->regmap, POCTRL, value);
}
staticint tc_set_edp_video_mode(struct tc_data *tc, conststruct drm_display_mode *mode)
{ int ret; int vid_sync_dly; int max_tu_symbol;
int left_margin = mode->htotal - mode->hsync_end; int hsync_len = mode->hsync_end - mode->hsync_start; int upper_margin = mode->vtotal - mode->vsync_end; int vsync_len = mode->vsync_end - mode->vsync_start;
u32 dp0_syncval;
u32 bits_per_pixel = 24;
u32 in_bw, out_bw;
u32 dpipxlfmt;
/* * Recommended maximum number of symbols transferred in a transfer unit: * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size, * (output active video bandwidth in bytes)) * Must be less than tu_size.
*/
ret = regmap_read(tc->regmap, DP0CTL, &value); if (ret) return ret;
if (WARN_ON(value & DP_EN)) {
ret = regmap_write(tc->regmap, DP0CTL, 0); if (ret) return ret;
}
ret = regmap_write(tc->regmap, DP0_SRCCTRL,
tc_srcctrl(tc) |
FIELD_PREP(DP0_SRCCTRL_PRE0, tc->pre_emphasis[0]) |
FIELD_PREP(DP0_SRCCTRL_PRE1, tc->pre_emphasis[1])); if (ret) return ret; /* SSCG and BW27 on DP1 must be set to the same as on DP0 */
ret = regmap_write(tc->regmap, DP1_SRCCTRL,
(tc->link.spread ? DP0_SRCCTRL_SSCG : 0) |
((tc->link.rate != 162000) ? DP0_SRCCTRL_BW27 : 0) |
FIELD_PREP(DP1_SRCCTRL_PRE, tc->pre_emphasis[1])); if (ret) return ret;
ret = tc_set_syspllparam(tc); if (ret) return ret;
/* Setup Main Link */
dp_phy_ctrl = BGREN | PWR_SW_EN | PHY_A0_EN | PHY_M0_EN; if (tc->link.num_lanes == 2)
dp_phy_ctrl |= PHY_2LANE;
ret = regmap_write(tc->regmap, DP_PHY_CTRL, dp_phy_ctrl); if (ret) return ret;
/* PLL setup */
ret = tc_pllupdate(tc, DP0_PLLCTRL); if (ret) return ret;
ret = tc_pllupdate(tc, DP1_PLLCTRL); if (ret) return ret;
ret = tc_poll_timeout(tc, DP_PHY_CTRL, PHY_RDY, PHY_RDY, 500, 100000); if (ret) {
dev_err(dev, "timeout waiting for phy become ready"); return ret;
}
/* Set misc: 8 bits per color */
ret = regmap_update_bits(tc->regmap, DP0_MISC, BPC_8, BPC_8); if (ret) return ret;
/* * ASSR mode * on TC358767 side ASSR configured through strap pin * seems there is no way to change this setting from SW * * check is tc configured for same mode
*/ if (tc->assr != tc->link.assr) {
dev_dbg(dev, "Trying to set display to ASSR: %d\n",
tc->assr); /* try to set ASSR on display side */
tmp[0] = tc->assr;
ret = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, tmp[0]); if (ret < 0) goto err_dpcd_read; /* read back */
ret = drm_dp_dpcd_readb(aux, DP_EDP_CONFIGURATION_SET, tmp); if (ret < 0) goto err_dpcd_read;
if (tmp[0] != tc->assr) {
dev_dbg(dev, "Failed to switch display ASSR to %d, falling back to unscrambled mode\n",
tc->assr); /* trying with disabled scrambler */
tc->link.scrambler_dis = true;
}
}
/* Setup Link & DPRx Config for Training */
tmp[0] = drm_dp_link_rate_to_bw_code(tc->link.rate);
tmp[1] = tc->link.num_lanes;
if (drm_dp_enhanced_frame_cap(tc->link.dpcd))
tmp[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
ret = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, tmp, 2); if (ret < 0) goto err_dpcd_write;
/* Set DPCD 0x102 for Training Pattern 1 */
ret = regmap_write(tc->regmap, DP0_SNKLTCTRL,
DP_LINK_SCRAMBLING_DISABLE |
DP_TRAINING_PATTERN_1); if (ret) return ret;
/* Enable DP0 to start Link Training */
ret = regmap_write(tc->regmap, DP0CTL,
(drm_dp_enhanced_frame_cap(tc->link.dpcd) ?
EF_EN : 0) | DP_EN); if (ret) return ret;
/* wait */
ret = tc_wait_link_training(tc); if (ret < 0) return ret;
if (ret) {
dev_err(tc->dev, "Link training phase 1 failed: %s\n",
training_pattern1_errors[ret]); return -ENODEV;
}
/* Channel Equalization */
/* Set DPCD 0x102 for Training Pattern 2 */
ret = regmap_write(tc->regmap, DP0_SNKLTCTRL,
DP_LINK_SCRAMBLING_DISABLE |
DP_TRAINING_PATTERN_2); if (ret) return ret;
/* wait */
ret = tc_wait_link_training(tc); if (ret < 0) return ret;
if (ret) {
dev_err(tc->dev, "Link training phase 2 failed: %s\n",
training_pattern2_errors[ret]); return -ENODEV;
}
/* * Toshiba's documentation suggests to first clear DPCD 0x102, then * clear the training pattern bit in DP0_SRCCTRL. Testing shows * that the link sometimes drops if those steps are done in that order, * but if the steps are done in reverse order, the link stays up. * * So we do the steps differently than documented here.
*/
/* Clear Training Pattern, set AutoCorrect Mode = 1 */
ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc) |
DP0_SRCCTRL_AUTOCORRECT |
FIELD_PREP(DP0_SRCCTRL_PRE0, tc->pre_emphasis[0]) |
FIELD_PREP(DP0_SRCCTRL_PRE1, tc->pre_emphasis[1])); if (ret) return ret;
/* Clear DPCD 0x102 */ /* Note: Can Not use DP0_SNKLTCTRL (0x06E4) short cut */
tmp[0] = tc->link.scrambler_dis ? DP_LINK_SCRAMBLING_DISABLE : 0x00;
ret = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, tmp[0]); if (ret < 0) goto err_dpcd_write;
/* Check link status */
ret = drm_dp_dpcd_read_link_status(aux, tmp); if (ret < 0) goto err_dpcd_read;
ret = 0;
value = tmp[0] & DP_CHANNEL_EQ_BITS;
if (value != DP_CHANNEL_EQ_BITS) {
dev_err(tc->dev, "Lane 0 failed: %x\n", value);
ret = -ENODEV;
}
if (tc->link.num_lanes == 2) {
value = (tmp[0] >> 4) & DP_CHANNEL_EQ_BITS;
if (value != DP_CHANNEL_EQ_BITS) {
dev_err(tc->dev, "Lane 1 failed: %x\n", value);
ret = -ENODEV;
}
if (!(tmp[2] & DP_INTERLANE_ALIGN_DONE)) {
dev_err(tc->dev, "Interlane align failed\n");
ret = -ENODEV;
}
}
/* Set input interface */
value = DP0_AUDSRC_NO_INPUT; if (tc_test_pattern)
value |= DP0_VIDSRC_COLOR_BAR; else
value |= DP0_VIDSRC_DSI_RX;
ret = regmap_write(tc->regmap, SYSCTRL, value); if (ret) return ret;
/* Set input interface */
value = DP0_AUDSRC_NO_INPUT; if (tc_test_pattern)
value |= DP0_VIDSRC_COLOR_BAR; else
value |= DP0_VIDSRC_DPI_RX; return regmap_write(tc->regmap, SYSCTRL, value);
}
staticint tc_dpi_stream_enable(struct tc_data *tc)
{ int ret;
dev_dbg(tc->dev, "enable video stream\n");
/* Setup PLL */
ret = tc_set_syspllparam(tc); if (ret) return ret;
/* * Initially PLLs are in bypass. Force PLL parameter update, * disable PLL bypass, enable PLL
*/
ret = tc_pllupdate(tc, DP0_PLLCTRL); if (ret) return ret;
ret = tc_pllupdate(tc, DP1_PLLCTRL); if (ret) return ret;
/* Pixel PLL must always be enabled for DPI mode */
ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk),
1000 * tc->mode.clock); if (ret) return ret;
ret = tc_set_common_video_mode(tc, &tc->mode); if (ret) return ret;
ret = tc_set_dpi_video_mode(tc, &tc->mode); if (ret) return ret;
return tc_dsi_rx_enable(tc);
}
staticint tc_dpi_stream_disable(struct tc_data *tc)
{
dev_dbg(tc->dev, "disable video stream\n");
tc_pxl_pll_dis(tc);
return 0;
}
staticint tc_edp_stream_enable(struct tc_data *tc)
{ int ret;
u32 value;
dev_dbg(tc->dev, "enable video stream\n");
/* * Pixel PLL must be enabled for DSI input mode and test pattern. * * Per TC9595XBG datasheet Revision 0.1 2018-12-27 Figure 4.18 * "Clock Mode Selection and Clock Sources", either Pixel PLL * or DPI_PCLK supplies StrmClk. DPI_PCLK is only available in * case valid Pixel Clock are supplied to the chip DPI input. * In case built-in test pattern is desired OR DSI input mode * is used, DPI_PCLK is not available and thus Pixel PLL must * be used instead.
*/ if (tc->input_connector_dsi || tc_test_pattern) {
ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk),
1000 * tc->mode.clock); if (ret) return ret;
}
ret = tc_set_common_video_mode(tc, &tc->mode); if (ret) return ret;
ret = tc_set_edp_video_mode(tc, &tc->mode); if (ret) return ret;
/* Set M/N */
ret = tc_stream_clock_calc(tc); if (ret) return ret;
value = VID_MN_GEN | DP_EN; if (drm_dp_enhanced_frame_cap(tc->link.dpcd))
value |= EF_EN;
ret = regmap_write(tc->regmap, DP0CTL, value); if (ret) return ret; /* * VID_EN assertion should be delayed by at least N * LSCLK * cycles from the time VID_MN_GEN is enabled in order to * generate stable values for VID_M. LSCLK is 270 MHz or * 162 MHz, VID_N is set to 32768 in tc_stream_clock_calc(), * so a delay of at least 203 us should suffice.
*/
usleep_range(500, 1000);
value |= VID_EN;
ret = regmap_write(tc->regmap, DP0CTL, value); if (ret) return ret;
/* Set input interface */ if (tc->input_connector_dsi) return tc_dsi_rx_enable(tc); else return tc_dpi_rx_enable(tc);
}
staticint tc_edp_stream_disable(struct tc_data *tc)
{ int ret;
dev_dbg(tc->dev, "disable video stream\n");
ret = regmap_update_bits(tc->regmap, DP0CTL, VID_EN, 0); if (ret) return ret;
if (tc->panel_bridge) { /* If a connector is required then this driver shall create it */
ret = drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge,
&tc->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret) return ret;
}
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) return 0;
tc->aux.drm_dev = drm;
ret = drm_dp_aux_register(&tc->aux); if (ret < 0) return ret;
/* Create DP/eDP connector */
drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, tc->bridge.type); if (ret) goto aux_unregister;
/* Don't poll if don't have HPD connected */ if (tc->hpd_pin >= 0) { if (tc->have_irq)
tc->connector.polled = DRM_CONNECTOR_POLL_HPD; else
tc->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
}
staticbool tc_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { /* DSI D-PHY Layer */ case 0x004: case 0x020: case 0x024: case 0x028: case 0x02c: case 0x030: case 0x038: case 0x040: case 0x044: case 0x048: case 0x04c: case 0x050: case 0x054: /* DSI PPI Layer */ case PPI_STARTPPI: case 0x108: case 0x110: case PPI_LPTXTIMECNT: case PPI_LANEENABLE: case PPI_TX_RX_TA: case 0x140: case PPI_D0S_ATMR: case PPI_D1S_ATMR: case 0x14c: case 0x150: case PPI_D0S_CLRSIPOCOUNT: case PPI_D1S_CLRSIPOCOUNT: case PPI_D2S_CLRSIPOCOUNT: case PPI_D3S_CLRSIPOCOUNT: case 0x180: case 0x184: case 0x188: case 0x18c: case 0x190: case 0x1a0: case 0x1a4: case 0x1a8: case 0x1ac: case 0x1b0: case 0x1c0: case 0x1c4: case 0x1c8: case 0x1cc: case 0x1d0: case 0x1e0: case 0x1e4: case 0x1f0: case 0x1f4: /* DSI Protocol Layer */ case DSI_STARTDSI: case DSI_BUSYDSI: case DSI_LANEENABLE: case DSI_LANESTATUS0: case DSI_LANESTATUS1: case DSI_INTSTATUS: case 0x224: case 0x228: case 0x230: /* DSI General */ case DSIERRCNT: /* DSI Application Layer */ case 0x400: case 0x404: /* DPI */ case DPIPXLFMT: /* Parallel Output */ case POCTRL: /* Video Path0 Configuration */ case VPCTRL0: case HTIM01: case HTIM02: case VTIM01: case VTIM02: case VFUEN0: /* System */ case TC_IDREG: case 0x504: case SYSSTAT: case SYSRSTENB: case SYSCTRL: /* I2C */ case 0x520: /* GPIO */ case GPIOM: case GPIOC: case GPIOO: case GPIOI: /* Interrupt */ case INTCTL_G: case INTSTS_G: case 0x570: case 0x574: case INT_GP0_LCNT: case INT_GP1_LCNT: /* DisplayPort Control */ case DP0CTL: /* DisplayPort Clock */ case DP0_VIDMNGEN0: case DP0_VIDMNGEN1: case DP0_VMNGENSTATUS: case 0x628: case 0x62c: case 0x630: /* DisplayPort Main Channel */ case DP0_SECSAMPLE: case DP0_VIDSYNCDELAY: case DP0_TOTALVAL: case DP0_STARTVAL: case DP0_ACTIVEVAL: case DP0_SYNCVAL: case DP0_MISC: /* DisplayPort Aux Channel */ case DP0_AUXCFG0: case DP0_AUXCFG1: case DP0_AUXADDR: case 0x66c: case 0x670: case 0x674: case 0x678: case 0x67c: case 0x680: case 0x684: case 0x688: case DP0_AUXSTATUS: case DP0_AUXI2CADR: /* DisplayPort Link Training */ case DP0_SRCCTRL: case DP0_LTSTAT: case DP0_SNKLTCHGREQ: case DP0_LTLOOPCTRL: case DP0_SNKLTCTRL: case 0x6e8: case 0x6ec: case 0x6f0: case 0x6f4: /* DisplayPort Audio */ case 0x700: case 0x704: case 0x708: case 0x70c: case 0x710: case 0x714: case 0x718: case 0x71c: case 0x720: /* DisplayPort Source Control */ case DP1_SRCCTRL: /* DisplayPort PHY */ case DP_PHY_CTRL: case 0x810: case 0x814: case 0x820: case 0x840: /* I2S */ case 0x880: case 0x888: case 0x88c: case 0x890: case 0x894: case 0x898: case 0x89c: case 0x8a0: case 0x8a4: case 0x8a8: case 0x8ac: case 0x8b0: case 0x8b4: /* PLL */ case DP0_PLLCTRL: case DP1_PLLCTRL: case PXL_PLLCTRL: case PXL_PLLPARAM: case SYS_PLLPARAM: /* HDCP */ case 0x980: case 0x984: case 0x988: case 0x98c: case 0x990: case 0x994: case 0x998: case 0x99c: case 0x9a0: case 0x9a4: case 0x9a8: case 0x9ac: /* Debug */ case TSTCTL: case PLL_DBG: returntrue;
} returnfalse;
}
staticbool tc_writeable_reg(struct device *dev, unsignedint reg)
{ /* RO reg */ switch (reg) { case PPI_BUSYPPI: case DSI_BUSYDSI: case DSI_LANESTATUS0: case DSI_LANESTATUS1: case DSI_INTSTATUS: case TC_IDREG: case SYSBOOT: case SYSSTAT: case GPIOI: case DP0_LTSTAT: case DP0_SNKLTCHGREQ: returnfalse;
} /* WO reg */ switch (reg) { case DSI_STARTDSI: case DSI_INTCLR: returntrue;
} return tc_readable_reg(dev, reg);
}
r = regmap_read(tc->regmap, INTSTS_G, &val); if (r) return IRQ_NONE;
if (!val) return IRQ_NONE;
if (val & INT_SYSERR) {
u32 stat = 0;
regmap_read(tc->regmap, SYSSTAT, &stat);
dev_err(tc->dev, "syserr %x\n", stat);
}
if (tc->hpd_pin >= 0 && tc->bridge.dev && tc->aux.drm_dev) { /* * H is triggered when the GPIO goes high. * * LC is triggered when the GPIO goes low and stays low for * the duration of LCNT
*/ bool h = val & INT_GPIO_H(tc->hpd_pin); bool lc = val & INT_GPIO_LC(tc->hpd_pin);
/* port@1 is the DPI input/output port */
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge); if (ret && ret != -ENODEV) return dev_err_probe(dev, ret, "Could not find DPI panel or bridge\n");
if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel); if (IS_ERR(bridge)) return PTR_ERR(bridge);
}
if (bridge) {
tc->panel_bridge = bridge;
tc->bridge.type = DRM_MODE_CONNECTOR_DPI;
/* port@2 is the output port */
ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL); if (ret && ret != -ENODEV) return dev_err_probe(dev, ret, "Could not find DSI panel or bridge\n");
if (panel) { struct drm_bridge *panel_bridge;
panel_bridge = devm_drm_panel_bridge_add(dev, panel); if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge);
ret = tc_probe_bridge_endpoint(tc, mode); if (ret) return ret;
tc->refclk = devm_clk_get_enabled(dev, "ref"); if (IS_ERR(tc->refclk)) return dev_err_probe(dev, PTR_ERR(tc->refclk), "Failed to get and enable the ref clk\n");
/* tRSTW = 100 cycles , at 13 MHz that is ~7.69 us */
usleep_range(10, 15);
/* Shut down GPIO is optional */
tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(tc->sd_gpio)) return PTR_ERR(tc->sd_gpio);
if (tc->sd_gpio) {
gpiod_set_value_cansleep(tc->sd_gpio, 0);
usleep_range(5000, 10000);
}
/* Reset GPIO is optional */
tc->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(tc->reset_gpio)) return PTR_ERR(tc->reset_gpio);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.