void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp, struct hdmi_audio_dma *aud_dma) { u32 r; DSSDBG("Enter hdmi_wp_audio_config_dma\n"); r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG2); r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); r = FLD_MOD(r, aud_dma->block_size, 7, 0); hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG2, r); r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CTRL); r = FLD_MOD(r, aud_dma->mode, 9, 9); r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CTRL, r); }
/* HDMI_CORE_VIDEO_CONFIG */ static void hdmi_core_video_config(struct hdmi_core_data *core, struct hdmi_core_video_config *cfg) { u32 r = 0; void __iomem *core_sys_base = core->base; void __iomem *core_av_base = hdmi_av_base(core); /* sys_ctrl1 default configuration not tunable */ r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1); r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_VEN_FOLLOWVSYNC, 5, 5); r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_HEN_FOLLOWHSYNC, 4, 4); r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_BSEL_24BITBUS, 2, 2); r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_EDGE_RISINGEDGE, 1, 1); hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1, r); REG_FLD_MOD(core_sys_base, HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6); /* Vid_Mode */ r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE); /* dither truncation configuration */ if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) { r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6); r = FLD_MOD(r, 1, 5, 5); } else { r = FLD_MOD(r, cfg->op_dither_truc, 7, 6); r = FLD_MOD(r, 0, 5, 5); } hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE, r); /* HDMI_Ctrl */ r = hdmi_read_reg(core_av_base, HDMI_CORE_AV_HDMI_CTRL); r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6); r = FLD_MOD(r, cfg->pkt_mode, 5, 3); r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0); hdmi_write_reg(core_av_base, HDMI_CORE_AV_HDMI_CTRL, r); /* TMDS_CTRL */ REG_FLD_MOD(core_sys_base, HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5); }
int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp, struct hdmi_config *cfg) { u16 r = 0; u32 irqstatus; hdmi_wp_clear_irqenable(wp, 0xffffffff); irqstatus = hdmi_wp_get_irqstatus(wp); hdmi_wp_set_irqstatus(wp, irqstatus); r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); if (r) return r; /* * Read address 0 in order to get the SCP reset done completed * Dummy access performed to make sure reset is done */ hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL); /* * Write to phy address 0 to configure the clock * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field */ REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30); /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); /* Setup max LDO voltage */ REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); /* Write to phy address 3 to change the polarity control */ REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); r = request_threaded_irq(phy->irq, NULL, hdmi_irq_handler, IRQF_ONESHOT, "OMAP HDMI", wp); if (r) { DSSERR("HDMI IRQ request failed\n"); hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); return r; } hdmi_wp_set_irqenable(wp, HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); return 0; }
void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, struct omap_video_timings *timings) { u32 r; bool vsync_pol, hsync_pol; DSSDBG("Enter hdmi_wp_video_config_interface\n"); vsync_pol = timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH; hsync_pol = timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH; r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG); r = FLD_MOD(r, vsync_pol, 7, 7); r = FLD_MOD(r, hsync_pol, 6, 6); r = FLD_MOD(r, timings->interlace, 3, 3); r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r); }
int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, unsigned long lfbitclk) { u8 freqout; /* * Read address 0 in order to get the SCP reset done completed * Dummy access performed to make sure reset is done */ hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL); /* * In OMAP5+, the HFBITCLK must be divided by 2 before issuing the * HDMI_PHYPWRCMD_LDOON command. */ if (phy->features->bist_ctrl) REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11); /* * If the hfbitclk != lfbitclk, it means the lfbitclk was configured * to be used for TMDS. */ if (hfbitclk != lfbitclk) freqout = 0; else if (hfbitclk / 10 < phy->features->max_phy) freqout = 1; else freqout = 2; /* * Write to phy address 0 to configure the clock * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field */ REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30); /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); /* Setup max LDO voltage */ if (phy->features->ldo_voltage) REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); hdmi_phy_configure_lanes(phy); return 0; }
static void hdmi_power_off_full(struct omap_dss_device *dssdev) { struct omap_overlay_manager *mgr = hdmi.output.manager; const struct omap_video_timings *t; unsigned vblank; hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); /* * XXX Seems that on we easily get a flood of sync-lost errors when * disabling the output, and sometimes the DISPC seems to get stuck and * we never get FRAMEDONE. This seems to happen if we disable DISPC * output during HDMI VBLANK. * * To reduce the possibility for sync-lost errors, calculate the time * for the vertical blanking, wait for VBLANK, then wait until VBLANK * ends. */ t = &hdmi.cfg.timings; vblank = t->hfp + t->hsw + t->hbp + t->x_res; vblank *= t->vsw + t->vbp; vblank = (vblank * 1000) / (t->pixelclock / 1000); hdmi_write_reg(hdmi.wp.base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_VSYNC); while (true) { u32 v = hdmi_read_reg(hdmi.wp.base, HDMI_WP_IRQSTATUS_RAW); if (v & HDMI_IRQ_VIDEO_VSYNC) break; usleep_range(500, 1000); } usleep_range(vblank, vblank + 1000); dss_mgr_disable(mgr); hdmi_wp_video_stop(&hdmi.wp); hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); dss_pll_disable(&hdmi.pll.pll); hdmi_power_off_core(dssdev); }
void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, struct videomode *vm) { u32 r; bool vsync_pol, hsync_pol; DSSDBG("Enter hdmi_wp_video_config_interface\n"); vsync_pol = !!(vm->flags & DISPLAY_FLAGS_VSYNC_HIGH); hsync_pol = !!(vm->flags & DISPLAY_FLAGS_HSYNC_HIGH); r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG); r = FLD_MOD(r, vsync_pol, 7, 7); r = FLD_MOD(r, hsync_pol, 6, 6); r = FLD_MOD(r, !!(vm->flags & DISPLAY_FLAGS_INTERLACED), 3, 3); r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r); }
static irqreturn_t hdmi_irq_handler(int irq, void *data) { struct hdmi_wp_data *wp = data; u32 irqstatus; irqstatus = hdmi_wp_get_irqstatus(wp); hdmi_wp_set_irqstatus(wp, irqstatus); if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && irqstatus & HDMI_IRQ_LINK_DISCONNECT) { u32 v; /* * If we get both connect and disconnect interrupts at the same * time, turn off the PHY, clear interrupts, and restart, which * raises connect interrupt if a cable is connected, or nothing * if cable is not connected. */ hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); /* * We always get bogus CONNECT & DISCONNECT interrupts when * setting the PHY to LDOON. To ignore those, we force the RXDET * line to 0 until the PHY power state has been changed. */ v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL); v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */ v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */ hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v); hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15); } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); } return IRQ_HANDLED; }
void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, struct hdmi_audio_format *aud_fmt) { u32 r; DSSDBG("Enter hdmi_wp_audio_config_format\n"); r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG); r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); r = FLD_MOD(r, aud_fmt->type, 4, 4); r = FLD_MOD(r, aud_fmt->justification, 3, 3); r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG, r); }
void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, struct videomode *vm) { u32 r; bool vsync_inv, hsync_inv; DSSDBG("Enter hdmi_wp_video_config_interface\n"); vsync_inv = !!(vm->flags & DISPLAY_FLAGS_VSYNC_LOW); hsync_inv = !!(vm->flags & DISPLAY_FLAGS_HSYNC_LOW); r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG); r = FLD_MOD(r, 1, 7, 7); /* VSYNC_POL to dispc active high */ r = FLD_MOD(r, 1, 6, 6); /* HSYNC_POL to dispc active high */ r = FLD_MOD(r, vsync_inv, 5, 5); /* CORE_VSYNC_INV */ r = FLD_MOD(r, hsync_inv, 4, 4); /* CORE_HSYNC_INV */ r = FLD_MOD(r, !!(vm->flags & DISPLAY_FLAGS_INTERLACED), 3, 3); r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r); }
void hdmi_wp_video_stop(struct hdmi_wp_data *wp) { int i; hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_FRAME_DONE); REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31); for (i = 0; i < 50; ++i) { u32 v; msleep(20); v = hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS_RAW); if (v & HDMI_IRQ_VIDEO_FRAME_DONE) return; } DSSERR("no HDMI FRAMEDONE when disabling output\n"); }
void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus) { hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, irqstatus); /* flush posted write */ hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); }
u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp) { return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); }
int hdmi_core_ddc_edid(u8 *pEDID) { u32 i, j, l; char checksum = 0; u32 sts = HDMI_CORE_DDC_STATUS; u32 ins = HDMI_CORE_SYS; /* Turn on CLK for DDC */ REG_FLD_MOD(HDMI_CORE_AV, HDMI_CORE_AV_DPD, 0x7, 2, 0); /* Wait */ mdelay(10); /* Clk SCL Devices */ REG_FLD_MOD(ins, HDMI_CORE_DDC_CMD, 0xA, 3, 0); /* HDMI_CORE_DDC_STATUS__IN_PROG */ while (FLD_GET(hdmi_read_reg(ins, sts), 4, 4) == 1) /* Clear FIFO */ REG_FLD_MOD(ins, HDMI_CORE_DDC_CMD, 0x9, 3, 0); /* HDMI_CORE_DDC_STATUS__IN_PROG */ while (FLD_GET(hdmi_read_reg(ins, sts), 4, 4) == 1) /* Load Slave Address Register */ REG_FLD_MOD(ins, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1); /* Load Offset Address Register */ REG_FLD_MOD(ins, HDMI_CORE_DDC_OFFSET, 0x0, 7, 0); /* Load Byte Count */ REG_FLD_MOD(ins, HDMI_CORE_DDC_COUNT1, 0x100, 7, 0); REG_FLD_MOD(ins, HDMI_CORE_DDC_COUNT2, 0x100>>8, 1, 0); /* Set DDC_CMD */ REG_FLD_MOD(ins, HDMI_CORE_DDC_CMD, 0x2, 3, 0); /* Yong: do not optimize this part of the code, seems DDC bus needs some time to get stabilized */ l = hdmi_read_reg(ins, sts); /* HDMI_CORE_DDC_STATUS__BUS_LOW */ if (FLD_GET(l, 6, 6) == 1) { printk("I2C Bus Low?\n\r"); return -1; } /* HDMI_CORE_DDC_STATUS__NO_ACK */ if (FLD_GET(l, 5, 5) == 1) { printk("I2C No Ack\n\r"); return -1; } j = 100; while (j--) { l = hdmi_read_reg(ins, sts); /* progress */ if (FLD_GET(l, 4, 4) == 1) { /* HACK: Load Slave Address Register again */ REG_FLD_MOD(ins, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1); REG_FLD_MOD(ins, HDMI_CORE_DDC_CMD, 0x2, 3, 0); break; } mdelay(20); }
static int hdmi_pll_config(struct hdmi_pll_data *pll) { u32 r; struct hdmi_pll_info *fmt = &pll->info; /* PLL start always use manual mode */ REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); r = hdmi_read_reg(pll->base, PLLCTRL_CFG1); r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */ hdmi_write_reg(pll->base, PLLCTRL_CFG1, r); r = hdmi_read_reg(pll->base, PLLCTRL_CFG2); r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */ if (fmt->dcofreq) r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ else r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ hdmi_write_reg(pll->base, PLLCTRL_CFG2, r); REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10); r = hdmi_read_reg(pll->base, PLLCTRL_CFG4); r = FLD_MOD(r, fmt->regm2, 24, 18); r = FLD_MOD(r, fmt->regmf, 17, 0); hdmi_write_reg(pll->base, PLLCTRL_CFG4, r); /* go now */ REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0); /* wait for bit change */ if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO, 0, 0, 0) != 0) { DSSERR("PLL GO bit not clearing\n"); return -ETIMEDOUT; } /* Wait till the lock bit is set in PLL status */ if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { DSSERR("cannot lock PLL\n"); DSSERR("CFG1 0x%x\n", hdmi_read_reg(pll->base, PLLCTRL_CFG1)); DSSERR("CFG2 0x%x\n", hdmi_read_reg(pll->base, PLLCTRL_CFG2)); DSSERR("CFG4 0x%x\n", hdmi_read_reg(pll->base, PLLCTRL_CFG4)); return -ETIMEDOUT; } DSSDBG("PLL locked!\n"); return 0; }
static int hdmi_power_on_full(struct omap_dss_device *dssdev) { int r; struct omap_video_timings *p; struct omap_overlay_manager *mgr = hdmi.output.manager; struct dss_pll_clock_info hdmi_cinfo = { 0 }; r = hdmi_power_on_core(dssdev); if (r) return r; p = &hdmi.cfg.timings; DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo); /* disable and clear irqs */ hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); hdmi_wp_set_irqstatus(&hdmi.wp, hdmi_wp_get_irqstatus(&hdmi.wp)); r = dss_pll_enable(&hdmi.pll.pll); if (r) { DSSERR("Failed to enable PLL\n"); goto err_pll_enable; } r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo); if (r) { DSSERR("Failed to configure PLL\n"); goto err_pll_cfg; } r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco, hdmi_cinfo.clkout[0]); if (r) { DSSDBG("Failed to start PHY\n"); goto err_phy_cfg; } r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON); if (r) goto err_phy_pwr; hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); /* bypass TV gamma table */ dispc_enable_gamma_table(0); /* tv size */ dss_mgr_set_timings(mgr, p); r = hdmi_wp_video_start(&hdmi.wp); if (r) goto err_vid_enable; /* * XXX Seems that on we easily get a flood of sync-lost errors when * enabling the output. This seems to be related to the time between * HDMI VSYNC and enabling the DISPC output. * * Testing shows that the sync-lost errors do not happen if we enable * the DISPC output very soon after HDMI VBLANK. So wait here for * VBLANK to reduce the chances of sync-losts. */ hdmi_write_reg(hdmi.wp.base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_VSYNC); while (true) { u32 v = hdmi_read_reg(hdmi.wp.base, HDMI_WP_IRQSTATUS_RAW); if (v & HDMI_IRQ_VIDEO_VSYNC) break; usleep_range(500, 1000); } r = dss_mgr_enable(mgr); if (r) goto err_mgr_enable; hdmi_wp_set_irqenable(&hdmi.wp, HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); return 0; err_mgr_enable: hdmi_wp_video_stop(&hdmi.wp); err_vid_enable: hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); err_phy_pwr: err_phy_cfg: err_pll_cfg: dss_pll_disable(&hdmi.pll.pll); err_pll_enable: hdmi_power_off_core(dssdev); return -EIO; }