/** * Description: programs dpll clocks, enables dpll and waits * till it locks with DSI PLL * * @dev: hdmi_device_t * @dclk: refresh rate dot clock in kHz of current mode * * Returns: OTM_HDMI_SUCCESS on success * OTM_HDMI_ERR_INVAL on NULL input arguments */ otm_hdmi_ret_t ips_hdmi_crtc_mode_set_program_dpll(hdmi_device_t *dev, unsigned long dclk) { otm_hdmi_ret_t rc = OTM_HDMI_SUCCESS; u32 dpll_adj, fp; u32 dpll; int timeout = 0; /* NULL checks */ if (dev == NULL) { pr_debug("\ninvalid argument\n"); return OTM_HDMI_ERR_INVAL; } rc = __ips_hdmi_get_adjusted_clk(dclk, &dpll_adj, &fp, &dev->clock_khz); dpll = hdmi_read32(IPIL_DPLL_B); if (dpll & IPIL_DPLL_VCO_ENABLE) { dpll &= ~IPIL_DPLL_VCO_ENABLE; hdmi_write32(IPIL_DPLL_B, dpll); hdmi_read32(IPIL_DPLL_B); /* reset M1, N1 & P1 */ hdmi_write32(IPIL_DPLL_DIV0, 0); dpll &= ~IPIL_P1_MASK; hdmi_write32(IPIL_DPLL_B, dpll); } /* * When ungating power of DPLL, needs to wait 0.5us * before enable the VCO */ if (dpll & IPIL_PWR_GATE_EN) { dpll &= ~IPIL_PWR_GATE_EN; hdmi_write32(IPIL_DPLL_B, dpll); udelay(1); } dpll = dpll_adj; hdmi_write32(IPIL_DPLL_DIV0, fp); hdmi_write32(IPIL_DPLL_B, dpll); udelay(1); dpll |= IPIL_DPLL_VCO_ENABLE; hdmi_write32(IPIL_DPLL_B, dpll); hdmi_read32(IPIL_DPLL_B); /* wait for DSI PLL to lock */ while ((timeout < 20000) && !(hdmi_read32(IPIL_PIPEBCONF) & IPIL_PIPECONF_PLL_LOCK)) { udelay(150); timeout++; } return OTM_HDMI_SUCCESS; }
/** * Description: Write to DPLL register via IOSF * * @ep_id: IOSF endpoint ID (0x13 for DPLL) * @reg: address of register * @val: value to write to register * * Returns: none */ void gunit_iosf_write32(u32 ep_id, u32 reg, u32 val) { u32 ret; int retry = 0; u32 sb_pkt = (1 << 16) | (ep_id << 8) | 0xf0; /* Write value to side band register */ hdmi_write32(0x2108, reg); hdmi_write32(0x2104, val); hdmi_write32(0x2100, sb_pkt); /* Check if transaction is complete */ ret = hdmi_read32(0x210C); while ((retry++ < 0x1000) && (ret != 0x2)) { usleep_range(500, 1000); ret = hdmi_read32(0x210C); } if (ret != 2) pr_err("%s: failed to program DPLL\n", __func__); }
/** * Description: Read DPLL register via IOSF * * @ep_id: IOSF endpoint ID (0x13 for DPLL) * @reg: address of register * * Returns: value of register */ u32 gunit_iosf_read32(u32 ep_id, u32 reg) { u32 ret; int retry = 0; u32 sb_pkt = (0 << 16) | (ep_id << 8) | 0xf0; /* Read side band register */ hdmi_write32(0x2108, reg); hdmi_write32(0x2100, sb_pkt); /* Check if transaction is complete */ ret = hdmi_read32(0x210C); while ((retry++ < 0x1000) && (ret != 2)) { usleep_range(500, 1000); ret = hdmi_read32(0x210C); } if (ret != 2) pr_err("%s: Failed to read\n", __func__); else ret = hdmi_read32(0x2104); return ret; }
/** * Description: programs hdmi pipe src and size of the input. * * @dev: hdmi_device_t * @scalingtype: scaling type (FULL_SCREEN, CENTER, NO_SCALE etc.) * @mode: mode requested * @adjusted_mode: adjusted mode * @fb_width, fb_height:allocated frame buffer dimensions * * Returns: OTM_HDMI_SUCCESS on success * OTM_HDMI_ERR_INVAL on NULL input arguments */ otm_hdmi_ret_t ipil_hdmi_crtc_mode_set_program_dspregs(hdmi_device_t *dev, int scalingtype, ipil_timings_t *mode, ipil_timings_t *adjusted_mode, int fb_width, int fb_height) { int sprite_pos_x = 0, sprite_pos_y = 0; int sprite_width = 0, sprite_height = 0; int src_image_hor = 0, src_image_vert = 0; int wa; pr_debug("Enter %s\n", __func__); /* NULL checks */ if (dev == NULL || mode == NULL || adjusted_mode == NULL) { pr_debug("\ninvalid argument\n"); return OTM_HDMI_ERR_INVAL; } /* * Frame buffer size may beyond active region in case of * panning mode. */ sprite_width = min_t(int, fb_width, adjusted_mode->width); sprite_height = min_t(int, fb_height, adjusted_mode->height); switch (scalingtype) { case OTM_HDMI_SCALE_NONE: case OTM_HDMI_SCALE_CENTER: /* * This mode is used to support centering the screen * by setting reg in DISPLAY controller */ src_image_hor = adjusted_mode->width; src_image_vert = adjusted_mode->height; sprite_pos_x = (src_image_hor - sprite_width) / 2; sprite_pos_y = (src_image_vert - sprite_height) / 2; hdmi_write32(IPIL_PFIT_CONTROL, hdmi_read32(IPIL_PFIT_CONTROL) & ~IPIL_PFIT_ENABLE); break; case OTM_HDMI_SCALE_FULLSCREEN: src_image_hor = sprite_width; src_image_vert = sprite_height; sprite_pos_x = 0; sprite_pos_y = 0; if ((adjusted_mode->width > sprite_width) || (adjusted_mode->height > sprite_height)) hdmi_write32(IPIL_PFIT_CONTROL, IPIL_PFIT_ENABLE | IPIL_PFIT_PIPE_SELECT_B | IPIL_PFIT_SCALING_AUTO); break; case OTM_HDMI_SCALE_ASPECT: sprite_pos_x = 0; sprite_pos_y = 0; sprite_height = fb_height; sprite_width = fb_width; src_image_hor = fb_width; src_image_vert = fb_height; /* Use panel fitting when the display does not match * with the framebuffer size */ if ((adjusted_mode->width != fb_width) || (adjusted_mode->height != fb_height)) { if (fb_width > fb_height) { /* Landscape mode */ pr_debug("Landscape mode...\n"); /* Landscape mode: program ratios is * used because 480p does not work with * auto */ if (adjusted_mode->height == 480) pfit_landscape(sprite_width, sprite_height, adjusted_mode->width, adjusted_mode->height); else hdmi_write32(IPIL_PFIT_CONTROL, IPIL_PFIT_ENABLE | IPIL_PFIT_PIPE_SELECT_B | IPIL_PFIT_SCALING_AUTO); } else { /* Portrait mode */ pr_debug("Portrait mode...\n"); /* Panel fitter HW has some limitations/bugs * which forces us to tweak the way we use * PILLARBOX mode. */ wa = pfit_pillarbox_wa(fb_height, adjusted_mode->height); pr_debug("wa = %d\n", wa); src_image_hor = max_t(int, fb_width, adjusted_mode->width) + wa; src_image_vert = max_t(int, fb_height, adjusted_mode->height); sprite_pos_x = (src_image_hor - fb_width) / 2; hdmi_write32(IPIL_PFIT_CONTROL, IPIL_PFIT_ENABLE | IPIL_PFIT_PIPE_SELECT_B | IPIL_PFIT_SCALING_PILLARBOX); } } else hdmi_write32(IPIL_PFIT_CONTROL, IPIL_PFIT_ENABLE | IPIL_PFIT_PIPE_SELECT_B | IPIL_PFIT_SCALING_AUTO); break; default: /* The defined sprite rectangle must always be completely contained within the displayable area of the screen image (frame buffer). */ sprite_pos_x = 0; sprite_pos_y = 0; sprite_height = fb_height; sprite_width = fb_width; src_image_hor = fb_width; src_image_vert = fb_height; if ((adjusted_mode->width != fb_width) || (adjusted_mode->height != fb_height)) hdmi_write32(IPIL_PFIT_CONTROL, IPIL_PFIT_ENABLE | IPIL_PFIT_PIPE_SELECT_B); break; }
/** * Description: programs dpll clocks, enables dpll and waits * till it locks with DSI PLL * * @m1, m2: DPLL m values * @n: DPLL n value * @p1, p2: DPLL p values * * Returns: none */ static void __ips_hdmi_set_program_dpll(int n, int p1, int p2, int m1, int m2) { u32 ret, tmp; int retry = 0; u32 div = (0x11 << 24) | (p1 << 21) | (p2 << 16) | (n << 12) | (0x1 << 11) | (m1 << 8) | (m2); pr_debug("enter %s\n", __func__); /* Common reset */ hdmi_write32(IPS_DPLL_B, 0x70006800); /* Program DPLL registers via IOSF (TNG display HAS) */ /* Process monitor to 19.2MHz */ gunit_iosf_write32(DPLL_IOSF_EP, REF_DWORD22, 0x19080000); /* LRC clock to 19.2MHz */ gunit_iosf_write32(DPLL_IOSF_EP, DPLL_LRC_CLK, 0x00000F10); /* Disable periodic GRC IREF update for DPLL */ tmp = gunit_iosf_read32(DPLL_IOSF_EP, PLLB_DWORD8); gunit_iosf_write32(DPLL_IOSF_EP, PLLB_DWORD8, tmp & 0x00FFFFFF); /* Enable Tx for periodic GRC update*/ gunit_iosf_write32(DPLL_IOSF_EP, DPLL_Tx_GRC, 0x0100000F); /* GRC cal clock set to 19.2MHZ */ gunit_iosf_write32(DPLL_IOSF_EP, REF_DWORD18, 0x30002400); /* Set lock time to 53us. * Disable fast lock. */ gunit_iosf_write32(DPLL_IOSF_EP, CMN_DWORD8, 0x0); /* Set divisors*/ gunit_iosf_write32(DPLL_IOSF_EP, PLLA_DWORD3_1, div); gunit_iosf_write32(DPLL_IOSF_EP, PLLA_DWORD3_2, div); /* Set up LCPLL in digital mode */ gunit_iosf_write32(DPLL_IOSF_EP, PLLA_DWORD5_1, 0x0DF44300); gunit_iosf_write32(DPLL_IOSF_EP, PLLA_DWORD5_2, 0x0DF44300); /* LPF co-efficients for LCPLL in digital mode */ gunit_iosf_write32(DPLL_IOSF_EP, PLLB_DWORD10_1, 0x005F0021); gunit_iosf_write32(DPLL_IOSF_EP, PLLB_DWORD10_2, 0x005F0021); /* Disable unused TLine clocks on right side */ gunit_iosf_write32(DPLL_IOSF_EP, CMN_DWORD3, 0x14540000); /* Enable DPLL */ tmp = hdmi_read32(IPS_DPLL_B); hdmi_write32(IPS_DPLL_B, tmp | IPIL_DPLL_VCO_ENABLE); /* Enable DCLP to core */ tmp = gunit_iosf_read32(DPLL_IOSF_EP, PLLA_DWORD7_1); gunit_iosf_write32(DPLL_IOSF_EP, PLLA_DWORD7_1, tmp | (1 << 24)); tmp = gunit_iosf_read32(DPLL_IOSF_EP, PLLA_DWORD7_2); gunit_iosf_write32(DPLL_IOSF_EP, PLLA_DWORD7_2, tmp | (1 << 24)); /* Set HDMI lane CML clock */ gunit_iosf_write32(DPLL_IOSF_EP, DPLL_CML_CLK1, 0x07760018); gunit_iosf_write32(DPLL_IOSF_EP, DPLL_CML_CLK2, 0x00400888); /* Swing settings */ gunit_iosf_write32(DPLL_IOSF_EP, TX_SWINGS_1, 0x00000000); gunit_iosf_write32(DPLL_IOSF_EP, TX_SWINGS_2, 0x2B407055); gunit_iosf_write32(DPLL_IOSF_EP, TX_SWINGS_3, 0x55A0983A); gunit_iosf_write32(DPLL_IOSF_EP, TX_SWINGS_4, 0x0C782040); gunit_iosf_write32(DPLL_IOSF_EP, TX_SWINGS_5, 0x2B247878); gunit_iosf_write32(DPLL_IOSF_EP, TX_SWINGS_6, 0x00030000); gunit_iosf_write32(DPLL_IOSF_EP, TX_SWINGS_7, 0x00004000); gunit_iosf_write32(DPLL_IOSF_EP, TX_SWINGS_1, 0x80000000); /* Stagger Programming */ gunit_iosf_write32(DPLL_IOSF_EP, PCS_DWORD12_1, 0x00401F00); gunit_iosf_write32(DPLL_IOSF_EP, PCS_DWORD12_2, 0x00451F00); /* Wait until DPLL is locked */ ret = hdmi_read32(IPS_DPLL_B); ret &= 0x8000; while ((retry++ < 1000) && (ret != 0x8000)) { usleep_range(500, 1000); ret = hdmi_read32(IPS_DPLL_B); ret &= 0x8000; } if (ret != 0x8000) { pr_err("%s: DPLL failed to lock, exit...\n", __func__); return; } }