The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/hdmi/dwc_hdmi.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 /*
   31  * HDMI core module
   32  */
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/eventhandler.h>
   37 #include <sys/kernel.h>
   38 #include <sys/module.h>
   39 #include <sys/malloc.h>
   40 #include <sys/bus.h>
   41 #include <sys/rman.h>
   42 
   43 #include <machine/bus.h>
   44 
   45 #include <dev/videomode/videomode.h>
   46 #include <dev/videomode/edidvar.h>
   47 
   48 #include <dev/iicbus/iicbus.h>
   49 #include <dev/iicbus/iiconf.h>
   50 
   51 #include <dev/hdmi/dwc_hdmi.h>
   52 #include <dev/hdmi/dwc_hdmireg.h>
   53 
   54 #include "hdmi_if.h"
   55 
   56 #define I2C_DDC_ADDR    (0x50 << 1)
   57 #define I2C_DDC_SEGADDR (0x30 << 1)
   58 #define EDID_LENGTH     0x80
   59 
   60 #define EXT_TAG                 0x00
   61 #define CEA_TAG_ID              0x02
   62 #define CEA_DTD                 0x03
   63 #define DTD_BASIC_AUDIO         (1 << 6)
   64 #define CEA_REV                 0x02
   65 #define CEA_DATA_OFF            0x03
   66 #define CEA_DATA_START          4
   67 #define BLOCK_TAG(x)            (((x) >> 5) & 0x7)
   68 #define BLOCK_TAG_VSDB          3
   69 #define BLOCK_LEN(x)            ((x) & 0x1f)
   70 #define HDMI_VSDB_MINLEN        5
   71 #define HDMI_OUI                "\x03\x0c\x00"
   72 #define HDMI_OUI_LEN            3
   73 
   74 static void
   75 dwc_hdmi_phy_wait_i2c_done(struct dwc_hdmi_softc *sc, int msec)
   76 {
   77         uint8_t val;
   78 
   79         val = RD1(sc, HDMI_IH_I2CMPHY_STAT0) &
   80             (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
   81         while (val == 0) {
   82                 pause("HDMI_PHY", hz/100);
   83                 msec -= 10;
   84                 if (msec <= 0)
   85                         return;
   86                 val = RD1(sc, HDMI_IH_I2CMPHY_STAT0) &
   87                     (HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
   88         }
   89 }
   90 
   91 static void
   92 dwc_hdmi_phy_i2c_write(struct dwc_hdmi_softc *sc, unsigned short data,
   93     unsigned char addr)
   94 {
   95 
   96         /* clear DONE and ERROR flags */
   97         WR1(sc, HDMI_IH_I2CMPHY_STAT0,
   98             HDMI_IH_I2CMPHY_STAT0_DONE | HDMI_IH_I2CMPHY_STAT0_ERROR);
   99         WR1(sc, HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
  100         WR1(sc, HDMI_PHY_I2CM_DATAO_1_ADDR, ((data >> 8) & 0xff));
  101         WR1(sc, HDMI_PHY_I2CM_DATAO_0_ADDR, ((data >> 0) & 0xff));
  102         WR1(sc, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
  103         dwc_hdmi_phy_wait_i2c_done(sc, 1000);
  104 }
  105 
  106 static void
  107 dwc_hdmi_disable_overflow_interrupts(struct dwc_hdmi_softc *sc)
  108 {
  109         WR1(sc, HDMI_IH_MUTE_FC_STAT2, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK);
  110         WR1(sc, HDMI_FC_MASK2,
  111             HDMI_FC_MASK2_LOW_PRI | HDMI_FC_MASK2_HIGH_PRI);
  112 }
  113 
  114 static void
  115 dwc_hdmi_av_composer(struct dwc_hdmi_softc *sc)
  116 {
  117         uint8_t inv_val;
  118         int is_dvi;
  119         int hblank, vblank, hsync_len, hfp, vfp;
  120 
  121         /* Set up HDMI_FC_INVIDCONF */
  122         inv_val = ((sc->sc_mode.flags & VID_PVSYNC) ?
  123                 HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
  124                 HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW);
  125 
  126         inv_val |= ((sc->sc_mode.flags & VID_PHSYNC) ?
  127                 HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
  128                 HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW);
  129 
  130         inv_val |= HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH;
  131 
  132         inv_val |= ((sc->sc_mode.flags & VID_INTERLACE) ?
  133                         HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
  134                         HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW);
  135 
  136         inv_val |= ((sc->sc_mode.flags & VID_INTERLACE) ?
  137                 HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
  138                 HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE);
  139 
  140         /* TODO: implement HDMI part */
  141         is_dvi = sc->sc_has_audio == 0;
  142         inv_val |= (is_dvi ?
  143                 HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
  144                 HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
  145 
  146         WR1(sc, HDMI_FC_INVIDCONF, inv_val);
  147 
  148         /* Set up horizontal active pixel region width */
  149         WR1(sc, HDMI_FC_INHACTV1, sc->sc_mode.hdisplay >> 8);
  150         WR1(sc, HDMI_FC_INHACTV0, sc->sc_mode.hdisplay);
  151 
  152         /* Set up vertical blanking pixel region width */
  153         WR1(sc, HDMI_FC_INVACTV1, sc->sc_mode.vdisplay >> 8);
  154         WR1(sc, HDMI_FC_INVACTV0, sc->sc_mode.vdisplay);
  155 
  156         /* Set up horizontal blanking pixel region width */
  157         hblank = sc->sc_mode.htotal - sc->sc_mode.hdisplay;
  158         WR1(sc, HDMI_FC_INHBLANK1, hblank >> 8);
  159         WR1(sc, HDMI_FC_INHBLANK0, hblank);
  160 
  161         /* Set up vertical blanking pixel region width */
  162         vblank = sc->sc_mode.vtotal - sc->sc_mode.vdisplay;
  163         WR1(sc, HDMI_FC_INVBLANK, vblank);
  164 
  165         /* Set up HSYNC active edge delay width (in pixel clks) */
  166         hfp = sc->sc_mode.hsync_start - sc->sc_mode.hdisplay;
  167         WR1(sc, HDMI_FC_HSYNCINDELAY1, hfp >> 8);
  168         WR1(sc, HDMI_FC_HSYNCINDELAY0, hfp);
  169 
  170         /* Set up VSYNC active edge delay (in pixel clks) */
  171         vfp = sc->sc_mode.vsync_start - sc->sc_mode.vdisplay;
  172         WR1(sc, HDMI_FC_VSYNCINDELAY, vfp);
  173 
  174         hsync_len = (sc->sc_mode.hsync_end - sc->sc_mode.hsync_start);
  175         /* Set up HSYNC active pulse width (in pixel clks) */
  176         WR1(sc, HDMI_FC_HSYNCINWIDTH1, hsync_len >> 8);
  177         WR1(sc, HDMI_FC_HSYNCINWIDTH0, hsync_len);
  178 
  179         /* Set up VSYNC active edge delay (in pixel clks) */
  180         WR1(sc, HDMI_FC_VSYNCINWIDTH, (sc->sc_mode.vsync_end - sc->sc_mode.vsync_start));
  181 }
  182 
  183 static void
  184 dwc_hdmi_phy_enable_power(struct dwc_hdmi_softc *sc, uint8_t enable)
  185 {
  186         uint8_t reg;
  187 
  188         reg = RD1(sc, HDMI_PHY_CONF0);
  189         reg &= ~HDMI_PHY_CONF0_PDZ_MASK;
  190         reg |= (enable << HDMI_PHY_CONF0_PDZ_OFFSET);
  191         WR1(sc, HDMI_PHY_CONF0, reg);
  192 }
  193 
  194 static void
  195 dwc_hdmi_phy_enable_tmds(struct dwc_hdmi_softc *sc, uint8_t enable)
  196 {
  197         uint8_t reg;
  198 
  199         reg = RD1(sc, HDMI_PHY_CONF0);
  200         reg &= ~HDMI_PHY_CONF0_ENTMDS_MASK;
  201         reg |= (enable << HDMI_PHY_CONF0_ENTMDS_OFFSET);
  202         WR1(sc, HDMI_PHY_CONF0, reg);
  203 }
  204 
  205 static void
  206 dwc_hdmi_phy_gen2_pddq(struct dwc_hdmi_softc *sc, uint8_t enable)
  207 {
  208         uint8_t reg;
  209 
  210         reg = RD1(sc, HDMI_PHY_CONF0);
  211         reg &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK;
  212         reg |= (enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET);
  213         WR1(sc, HDMI_PHY_CONF0, reg);
  214 }
  215 
  216 static void
  217 dwc_hdmi_phy_gen2_txpwron(struct dwc_hdmi_softc *sc, uint8_t enable)
  218 {
  219         uint8_t reg;
  220 
  221         reg = RD1(sc, HDMI_PHY_CONF0);
  222         reg &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK;
  223         reg |= (enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET);
  224         WR1(sc, HDMI_PHY_CONF0, reg);
  225 }
  226 
  227 static void
  228 dwc_hdmi_phy_sel_data_en_pol(struct dwc_hdmi_softc *sc, uint8_t enable)
  229 {
  230         uint8_t reg;
  231 
  232         reg = RD1(sc, HDMI_PHY_CONF0);
  233         reg &= ~HDMI_PHY_CONF0_SELDATAENPOL_MASK;
  234         reg |= (enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET);
  235         WR1(sc, HDMI_PHY_CONF0, reg);
  236 }
  237 
  238 static void
  239 dwc_hdmi_phy_sel_interface_control(struct dwc_hdmi_softc *sc, uint8_t enable)
  240 {
  241         uint8_t reg;
  242 
  243         reg = RD1(sc, HDMI_PHY_CONF0);
  244         reg &= ~HDMI_PHY_CONF0_SELDIPIF_MASK;
  245         reg |= (enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET);
  246         WR1(sc, HDMI_PHY_CONF0, reg);
  247 }
  248 
  249 static inline void
  250 dwc_hdmi_phy_test_clear(struct dwc_hdmi_softc *sc, unsigned char bit)
  251 {
  252         uint8_t val;
  253 
  254         val = RD1(sc, HDMI_PHY_TST0);
  255         val &= ~HDMI_PHY_TST0_TSTCLR_MASK;
  256         val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) &
  257                 HDMI_PHY_TST0_TSTCLR_MASK;
  258         WR1(sc, HDMI_PHY_TST0, val);
  259 }
  260 
  261 static void
  262 dwc_hdmi_clear_overflow(struct dwc_hdmi_softc *sc)
  263 {
  264         int count;
  265         uint8_t val;
  266 
  267         /* TMDS software reset */
  268         WR1(sc, HDMI_MC_SWRSTZ, (uint8_t)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ);
  269 
  270         val = RD1(sc, HDMI_FC_INVIDCONF);
  271 
  272         for (count = 0 ; count < 4 ; count++)
  273                 WR1(sc, HDMI_FC_INVIDCONF, val);
  274 }
  275 
  276 static int
  277 dwc_hdmi_phy_configure(struct dwc_hdmi_softc *sc)
  278 {
  279         uint8_t val;
  280         uint8_t msec;
  281 
  282         WR1(sc, HDMI_MC_FLOWCTRL, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS);
  283 
  284         /* gen2 tx power off */
  285         dwc_hdmi_phy_gen2_txpwron(sc, 0);
  286 
  287         /* gen2 pddq */
  288         dwc_hdmi_phy_gen2_pddq(sc, 1);
  289 
  290         /* PHY reset */
  291         WR1(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_DEASSERT);
  292         WR1(sc, HDMI_MC_PHYRSTZ, HDMI_MC_PHYRSTZ_ASSERT);
  293 
  294         WR1(sc, HDMI_MC_HEACPHY_RST, HDMI_MC_HEACPHY_RST_ASSERT);
  295 
  296         dwc_hdmi_phy_test_clear(sc, 1);
  297         WR1(sc, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
  298         dwc_hdmi_phy_test_clear(sc, 0);
  299 
  300         /*
  301          * Following initialization are for 8bit per color case
  302          */
  303 
  304         /*
  305          * PLL/MPLL config, see section 24.7.22 in TRM
  306          *  config, see section 24.7.22
  307          */
  308         if (sc->sc_mode.dot_clock*1000 <= 45250000) {
  309                 dwc_hdmi_phy_i2c_write(sc, CPCE_CTRL_45_25, HDMI_PHY_I2C_CPCE_CTRL);
  310                 dwc_hdmi_phy_i2c_write(sc, GMPCTRL_45_25, HDMI_PHY_I2C_GMPCTRL);
  311         } else if (sc->sc_mode.dot_clock*1000 <= 92500000) {
  312                 dwc_hdmi_phy_i2c_write(sc, CPCE_CTRL_92_50, HDMI_PHY_I2C_CPCE_CTRL);
  313                 dwc_hdmi_phy_i2c_write(sc, GMPCTRL_92_50, HDMI_PHY_I2C_GMPCTRL);
  314         } else if (sc->sc_mode.dot_clock*1000 <= 185000000) {
  315                 dwc_hdmi_phy_i2c_write(sc, CPCE_CTRL_185, HDMI_PHY_I2C_CPCE_CTRL);
  316                 dwc_hdmi_phy_i2c_write(sc, GMPCTRL_185, HDMI_PHY_I2C_GMPCTRL);
  317         } else {
  318                 dwc_hdmi_phy_i2c_write(sc, CPCE_CTRL_370, HDMI_PHY_I2C_CPCE_CTRL);
  319                 dwc_hdmi_phy_i2c_write(sc, GMPCTRL_370, HDMI_PHY_I2C_GMPCTRL);
  320         }
  321 
  322         /*
  323          * Values described in TRM section 34.9.2 PLL/MPLL Generic
  324          *    Configuration Settings. Table 34-23.
  325          */
  326         if (sc->sc_mode.dot_clock*1000 <= 54000000) {
  327                 dwc_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
  328         } else if (sc->sc_mode.dot_clock*1000 <= 58400000) {
  329                 dwc_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
  330         } else if (sc->sc_mode.dot_clock*1000 <= 72000000) {
  331                 dwc_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
  332         } else if (sc->sc_mode.dot_clock*1000 <= 74250000) {
  333                 dwc_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
  334         } else if (sc->sc_mode.dot_clock*1000 <= 118800000) {
  335                 dwc_hdmi_phy_i2c_write(sc, 0x091c, HDMI_PHY_I2C_CURRCTRL);
  336         } else if (sc->sc_mode.dot_clock*1000 <= 216000000) {
  337                 dwc_hdmi_phy_i2c_write(sc, 0x06dc, HDMI_PHY_I2C_CURRCTRL);
  338         } else {
  339                 panic("Unsupported mode\n");
  340         }
  341 
  342         dwc_hdmi_phy_i2c_write(sc, 0x0000, HDMI_PHY_I2C_PLLPHBYCTRL);
  343         dwc_hdmi_phy_i2c_write(sc, MSM_CTRL_FB_CLK, HDMI_PHY_I2C_MSM_CTRL);
  344         /* RESISTANCE TERM 133 Ohm */
  345         dwc_hdmi_phy_i2c_write(sc, TXTERM_133, HDMI_PHY_I2C_TXTERM);
  346 
  347         /* REMOVE CLK TERM */
  348         dwc_hdmi_phy_i2c_write(sc, CKCALCTRL_OVERRIDE, HDMI_PHY_I2C_CKCALCTRL);
  349 
  350         if (sc->sc_mode.dot_clock*1000 > 148500000) {
  351                 dwc_hdmi_phy_i2c_write(sc,CKSYMTXCTRL_OVERRIDE | CKSYMTXCTRL_TX_SYMON |
  352                     CKSYMTXCTRL_TX_TRBON | CKSYMTXCTRL_TX_CK_SYMON, HDMI_PHY_I2C_CKSYMTXCTRL); 
  353                 dwc_hdmi_phy_i2c_write(sc, VLEVCTRL_TX_LVL(9) | VLEVCTRL_CK_LVL(9),
  354                     HDMI_PHY_I2C_VLEVCTRL);
  355         } else {
  356                 dwc_hdmi_phy_i2c_write(sc,CKSYMTXCTRL_OVERRIDE | CKSYMTXCTRL_TX_SYMON |
  357                     CKSYMTXCTRL_TX_TRAON | CKSYMTXCTRL_TX_CK_SYMON, HDMI_PHY_I2C_CKSYMTXCTRL); 
  358                 dwc_hdmi_phy_i2c_write(sc, VLEVCTRL_TX_LVL(13) | VLEVCTRL_CK_LVL(13),
  359                     HDMI_PHY_I2C_VLEVCTRL);
  360         }
  361 
  362         dwc_hdmi_phy_enable_power(sc, 1);
  363 
  364         /* toggle TMDS enable */
  365         dwc_hdmi_phy_enable_tmds(sc, 0);
  366         dwc_hdmi_phy_enable_tmds(sc, 1);
  367 
  368         /* gen2 tx power on */
  369         dwc_hdmi_phy_gen2_txpwron(sc, 1);
  370         dwc_hdmi_phy_gen2_pddq(sc, 0);
  371 
  372         /*Wait for PHY PLL lock */
  373         msec = 4;
  374         val = RD1(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
  375         while (val == 0) {
  376                 DELAY(1000);
  377                 if (msec-- == 0) {
  378                         device_printf(sc->sc_dev, "PHY PLL not locked\n");
  379                         return (-1);
  380                 }
  381                 val = RD1(sc, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
  382         }
  383 
  384         return true;
  385 }
  386 
  387 static void
  388 dwc_hdmi_phy_init(struct dwc_hdmi_softc *sc)
  389 {
  390         int i;
  391 
  392         /* HDMI Phy spec says to do the phy initialization sequence twice */
  393         for (i = 0 ; i < 2 ; i++) {
  394                 dwc_hdmi_phy_sel_data_en_pol(sc, 1);
  395                 dwc_hdmi_phy_sel_interface_control(sc, 0);
  396                 dwc_hdmi_phy_enable_tmds(sc, 0);
  397                 dwc_hdmi_phy_enable_power(sc, 0);
  398 
  399                 /* Enable CSC */
  400                 dwc_hdmi_phy_configure(sc);
  401         }
  402 }
  403 
  404 static void
  405 dwc_hdmi_enable_video_path(struct dwc_hdmi_softc *sc)
  406 {
  407         uint8_t clkdis;
  408 
  409         /*
  410          * Control period timing
  411          * Values are minimal according to HDMI spec 1.4a
  412          */
  413         WR1(sc, HDMI_FC_CTRLDUR, 12);
  414         WR1(sc, HDMI_FC_EXCTRLDUR, 32);
  415         WR1(sc, HDMI_FC_EXCTRLSPAC, 1);
  416 
  417         /*
  418          * Bits to fill data lines not used to transmit preamble
  419          * for channels 0, 1, and 2 respectively
  420          */
  421         WR1(sc, HDMI_FC_CH0PREAM, 0x0B);
  422         WR1(sc, HDMI_FC_CH1PREAM, 0x16);
  423         WR1(sc, HDMI_FC_CH2PREAM, 0x21);
  424 
  425         /* Save CEC clock */
  426         clkdis = RD1(sc, HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_CECCLK_DISABLE;
  427         clkdis |= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
  428 
  429         /* Enable pixel clock and tmds data path */
  430         clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
  431         WR1(sc, HDMI_MC_CLKDIS, clkdis);
  432 
  433         clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
  434         WR1(sc, HDMI_MC_CLKDIS, clkdis);
  435 }
  436 
  437 static void
  438 dwc_hdmi_configure_audio(struct dwc_hdmi_softc *sc)
  439 {
  440         unsigned int n;
  441         uint8_t val;
  442 
  443         if (sc->sc_has_audio == 0)
  444                 return;
  445 
  446         /* The following values are for 48 kHz */
  447         switch (sc->sc_mode.dot_clock) {
  448         case 25170:
  449                 n = 6864;
  450                 break;
  451         case 27020:
  452                 n = 6144;
  453                 break;
  454         case 74170:
  455                 n = 11648;
  456                 break;
  457         case 148350:
  458                 n = 5824;
  459                 break;
  460         default:
  461                 n = 6144;
  462                 break;
  463         }
  464 
  465         WR1(sc, HDMI_AUD_N1, (n >> 0) & 0xff);
  466         WR1(sc, HDMI_AUD_N2, (n >> 8) & 0xff);
  467         WR1(sc, HDMI_AUD_N3, (n >> 16) & 0xff);
  468 
  469         val = RD1(sc, HDMI_AUD_CTS3);
  470         val &= ~(HDMI_AUD_CTS3_N_SHIFT_MASK | HDMI_AUD_CTS3_CTS_MANUAL);
  471         WR1(sc, HDMI_AUD_CTS3, val);
  472 
  473         val = RD1(sc, HDMI_AUD_CONF0);
  474         val &= ~HDMI_AUD_CONF0_INTERFACE_MASK;
  475         val |= HDMI_AUD_CONF0_INTERFACE_IIS;
  476         val &= ~HDMI_AUD_CONF0_I2SINEN_MASK;
  477         val |= HDMI_AUD_CONF0_I2SINEN_CH2;
  478         WR1(sc, HDMI_AUD_CONF0, val);
  479 
  480         val = RD1(sc, HDMI_AUD_CONF1);
  481         val &= ~HDMI_AUD_CONF1_DATAMODE_MASK;
  482         val |= HDMI_AUD_CONF1_DATAMODE_IIS;
  483         val &= ~HDMI_AUD_CONF1_DATWIDTH_MASK;
  484         val |= HDMI_AUD_CONF1_DATWIDTH_16BIT;
  485         WR1(sc, HDMI_AUD_CONF1, val);
  486 
  487         WR1(sc, HDMI_AUD_INPUTCLKFS, HDMI_AUD_INPUTCLKFS_64);
  488 
  489         WR1(sc, HDMI_FC_AUDICONF0, 1 << 4);     /* CC=1 */
  490         WR1(sc, HDMI_FC_AUDICONF1, 0);
  491         WR1(sc, HDMI_FC_AUDICONF2, 0);          /* CA=0 */
  492         WR1(sc, HDMI_FC_AUDICONF3, 0);
  493         WR1(sc, HDMI_FC_AUDSV, 0xee);           /* channels valid */
  494 
  495         /* Enable audio clock */
  496         val = RD1(sc, HDMI_MC_CLKDIS);
  497         val &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
  498         WR1(sc, HDMI_MC_CLKDIS, val);
  499 }
  500 
  501 static void
  502 dwc_hdmi_video_packetize(struct dwc_hdmi_softc *sc)
  503 {
  504         unsigned int color_depth = 0;
  505         unsigned int remap_size = HDMI_VP_REMAP_YCC422_16BIT;
  506         unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
  507         uint8_t val;
  508 
  509         output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
  510         color_depth = 4;
  511 
  512         /* set the packetizer registers */
  513         val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
  514                 HDMI_VP_PR_CD_COLOR_DEPTH_MASK);
  515         WR1(sc, HDMI_VP_PR_CD, val);
  516 
  517         val = RD1(sc, HDMI_VP_STUFF);
  518         val &= ~HDMI_VP_STUFF_PR_STUFFING_MASK;
  519         val |= HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE;
  520         WR1(sc, HDMI_VP_STUFF, val);
  521 
  522         val = RD1(sc, HDMI_VP_CONF);
  523         val &= ~(HDMI_VP_CONF_PR_EN_MASK |
  524                 HDMI_VP_CONF_BYPASS_SELECT_MASK);
  525         val |= HDMI_VP_CONF_PR_EN_DISABLE |
  526                 HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
  527         WR1(sc, HDMI_VP_CONF, val);
  528 
  529         val = RD1(sc, HDMI_VP_STUFF);
  530         val &= ~HDMI_VP_STUFF_IDEFAULT_PHASE_MASK;
  531         val |= 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET;
  532         WR1(sc, HDMI_VP_STUFF, val);
  533 
  534         WR1(sc, HDMI_VP_REMAP, remap_size);
  535 
  536         if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
  537                 val = RD1(sc, HDMI_VP_CONF);
  538                 val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
  539                         HDMI_VP_CONF_PP_EN_ENMASK |
  540                         HDMI_VP_CONF_YCC422_EN_MASK);
  541                 val |= HDMI_VP_CONF_BYPASS_EN_DISABLE |
  542                         HDMI_VP_CONF_PP_EN_ENABLE |
  543                         HDMI_VP_CONF_YCC422_EN_DISABLE;
  544                 WR1(sc, HDMI_VP_CONF, val);
  545         } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
  546                 val = RD1(sc, HDMI_VP_CONF);
  547                 val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
  548                         HDMI_VP_CONF_PP_EN_ENMASK |
  549                         HDMI_VP_CONF_YCC422_EN_MASK);
  550                 val |= HDMI_VP_CONF_BYPASS_EN_DISABLE |
  551                         HDMI_VP_CONF_PP_EN_DISABLE |
  552                         HDMI_VP_CONF_YCC422_EN_ENABLE;
  553                 WR1(sc, HDMI_VP_CONF, val);
  554         } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
  555                 val = RD1(sc, HDMI_VP_CONF);
  556                 val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
  557                         HDMI_VP_CONF_PP_EN_ENMASK |
  558                         HDMI_VP_CONF_YCC422_EN_MASK);
  559                 val |= HDMI_VP_CONF_BYPASS_EN_ENABLE |
  560                         HDMI_VP_CONF_PP_EN_DISABLE |
  561                         HDMI_VP_CONF_YCC422_EN_DISABLE;
  562                 WR1(sc, HDMI_VP_CONF, val);
  563         } else {
  564                 return;
  565         }
  566 
  567         val = RD1(sc, HDMI_VP_STUFF);
  568         val &= ~(HDMI_VP_STUFF_PP_STUFFING_MASK |
  569                 HDMI_VP_STUFF_YCC422_STUFFING_MASK);
  570         val |= HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
  571                 HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE;
  572         WR1(sc, HDMI_VP_STUFF, val);
  573 
  574         val = RD1(sc, HDMI_VP_CONF);
  575         val &= ~HDMI_VP_CONF_OUTPUT_SELECTOR_MASK;
  576         val |= output_select;
  577         WR1(sc, HDMI_VP_CONF, val);
  578 }
  579 
  580 static void
  581 dwc_hdmi_video_sample(struct dwc_hdmi_softc *sc)
  582 {
  583         int color_format;
  584         uint8_t val;
  585 
  586         color_format = 0x01;
  587         val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
  588                 ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
  589                 HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
  590         WR1(sc, HDMI_TX_INVID0, val);
  591 
  592         /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
  593         val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
  594                 HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
  595                 HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
  596         WR1(sc, HDMI_TX_INSTUFFING, val);
  597         WR1(sc, HDMI_TX_GYDATA0, 0x0);
  598         WR1(sc, HDMI_TX_GYDATA1, 0x0);
  599         WR1(sc, HDMI_TX_RCRDATA0, 0x0);
  600         WR1(sc, HDMI_TX_RCRDATA1, 0x0);
  601         WR1(sc, HDMI_TX_BCBDATA0, 0x0);
  602         WR1(sc, HDMI_TX_BCBDATA1, 0x0);
  603 }
  604 
  605 static void
  606 dwc_hdmi_tx_hdcp_config(struct dwc_hdmi_softc *sc)
  607 {
  608         uint8_t de, val;
  609 
  610         de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
  611 
  612         /* Disable RX detect */
  613         val = RD1(sc, HDMI_A_HDCPCFG0);
  614         val &= ~HDMI_A_HDCPCFG0_RXDETECT_MASK;
  615         val |= HDMI_A_HDCPCFG0_RXDETECT_DISABLE;
  616         WR1(sc, HDMI_A_HDCPCFG0, val);
  617 
  618         /* Set polarity */
  619         val = RD1(sc, HDMI_A_VIDPOLCFG);
  620         val &= ~HDMI_A_VIDPOLCFG_DATAENPOL_MASK;
  621         val |= de;
  622         WR1(sc, HDMI_A_VIDPOLCFG, val);
  623 
  624         /* Disable encryption */
  625         val = RD1(sc, HDMI_A_HDCPCFG1);
  626         val &= ~HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK;
  627         val |= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE;
  628         WR1(sc, HDMI_A_HDCPCFG1, val);
  629 }
  630 
  631 static int
  632 dwc_hdmi_set_mode(struct dwc_hdmi_softc *sc)
  633 {
  634 
  635         /* XXX */
  636         sc->sc_has_audio = 1;
  637 
  638         dwc_hdmi_disable_overflow_interrupts(sc);
  639         dwc_hdmi_av_composer(sc);
  640         dwc_hdmi_phy_init(sc);
  641         dwc_hdmi_enable_video_path(sc);
  642         dwc_hdmi_configure_audio(sc);
  643         /* TODO:  dwc_hdmi_config_avi(sc); */
  644         dwc_hdmi_video_packetize(sc);
  645         /* TODO:  dwc_hdmi_video_csc(sc); */
  646         dwc_hdmi_video_sample(sc);
  647         dwc_hdmi_tx_hdcp_config(sc);
  648         dwc_hdmi_clear_overflow(sc);
  649 
  650         return (0);
  651 }
  652 
  653 static int
  654 hdmi_edid_read(struct dwc_hdmi_softc *sc, int block, uint8_t **edid,
  655     uint32_t *edid_len)
  656 {
  657         device_t i2c_dev;
  658         int result;
  659         uint8_t addr = block & 1 ? EDID_LENGTH : 0;
  660         uint8_t segment = block >> 1;
  661         /*
  662          * Some devices do not support E-DDC so attempt
  663          * writing segment address only if it's neccessary
  664          */
  665         unsigned char xfers = segment ? 3 : 2;
  666         struct iic_msg msg[] = {
  667                 { I2C_DDC_SEGADDR, IIC_M_WR, 1, &segment },
  668                 { I2C_DDC_ADDR, IIC_M_WR, 1, &addr },
  669                 { I2C_DDC_ADDR, IIC_M_RD, EDID_LENGTH, sc->sc_edid }
  670         };
  671 
  672         *edid = NULL;
  673         *edid_len = 0;
  674         i2c_dev = NULL;
  675 
  676         if (sc->sc_get_i2c_dev != NULL)
  677                 i2c_dev = sc->sc_get_i2c_dev(sc->sc_dev);
  678         if (!i2c_dev) {
  679                 device_printf(sc->sc_dev, "no DDC device found\n");
  680                 return (ENXIO);
  681         }
  682 
  683         if (bootverbose)
  684                 device_printf(sc->sc_dev,
  685                     "reading EDID from %s, block %d, addr %02x\n",
  686                     device_get_nameunit(i2c_dev), block, I2C_DDC_ADDR/2);
  687 
  688         result = iicbus_request_bus(i2c_dev, sc->sc_dev, IIC_INTRWAIT);
  689 
  690         if (result) {
  691                 device_printf(sc->sc_dev, "failed to request i2c bus: %d\n", result);
  692                 return (result);
  693         }
  694 
  695         result = iicbus_transfer(i2c_dev, &msg[3 - xfers], xfers);
  696         iicbus_release_bus(i2c_dev, sc->sc_dev);
  697 
  698         if (result) {
  699                 device_printf(sc->sc_dev, "i2c transfer failed: %d\n", result);
  700                 return (result);
  701         } else {
  702                 *edid_len = sc->sc_edid_len;
  703                 *edid = sc->sc_edid;
  704         }
  705 
  706         return (result);
  707 }
  708 
  709 static void
  710 dwc_hdmi_detect_cable(void *arg)
  711 {
  712         struct dwc_hdmi_softc *sc;
  713         uint32_t stat;
  714 
  715         sc = arg;
  716 
  717         stat = RD1(sc, HDMI_IH_PHY_STAT0);
  718         if ((stat & HDMI_IH_PHY_STAT0_HPD) != 0) {
  719                 EVENTHANDLER_INVOKE(hdmi_event, sc->sc_dev,
  720                     HDMI_EVENT_CONNECTED);
  721         }
  722 
  723         /* Finished with the interrupt hook */
  724         config_intrhook_disestablish(&sc->sc_mode_hook);
  725 }
  726 
  727 int
  728 dwc_hdmi_init(device_t dev)
  729 {
  730         struct dwc_hdmi_softc *sc;
  731         int err;
  732 
  733         sc = device_get_softc(dev);
  734         err = 0;
  735 
  736         sc->sc_edid = malloc(EDID_LENGTH, M_DEVBUF, M_WAITOK | M_ZERO);
  737         sc->sc_edid_len = EDID_LENGTH;
  738 
  739         device_printf(sc->sc_dev, "HDMI controller %02x:%02x:%02x:%02x\n",
  740             RD1(sc, HDMI_DESIGN_ID), RD1(sc, HDMI_REVISION_ID),
  741             RD1(sc, HDMI_PRODUCT_ID0), RD1(sc, HDMI_PRODUCT_ID1));
  742 
  743         WR1(sc, HDMI_PHY_POL0, HDMI_PHY_POL0_HPD);
  744         WR1(sc, HDMI_IH_PHY_STAT0, HDMI_IH_PHY_STAT0_HPD);
  745 
  746         sc->sc_mode_hook.ich_func = dwc_hdmi_detect_cable;
  747         sc->sc_mode_hook.ich_arg = sc;
  748         if (config_intrhook_establish(&sc->sc_mode_hook) != 0) {
  749                 err = ENOMEM;
  750                 goto out;
  751         }
  752 
  753 out:
  754 
  755         if (err != 0) {
  756                 free(sc->sc_edid, M_DEVBUF);
  757                 sc->sc_edid = NULL;
  758         }
  759 
  760         return (err);
  761 }
  762 
  763 static int
  764 dwc_hdmi_detect_hdmi_vsdb(uint8_t *edid)
  765 {
  766         int off, p, btag, blen;
  767 
  768         if (edid[EXT_TAG] != CEA_TAG_ID)
  769                 return (0);
  770 
  771         off = edid[CEA_DATA_OFF];
  772 
  773         /* CEA data block collection starts at byte 4 */
  774         if (off <= CEA_DATA_START)
  775                 return (0);
  776 
  777         /* Parse the CEA data blocks */
  778         for (p = CEA_DATA_START; p < off;) {
  779                 btag = BLOCK_TAG(edid[p]);
  780                 blen = BLOCK_LEN(edid[p]);
  781 
  782                 /* Make sure the length is sane */
  783                 if (p + blen + 1 > off)
  784                         break;
  785 
  786                 /* Look for a VSDB with the HDMI 24-bit IEEE registration ID */
  787                 if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN &&
  788                     memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0)
  789                         return (1);
  790 
  791                 /* Next data block */
  792                 p += (1 + blen);
  793         }
  794 
  795         /* Not found */
  796         return (0);
  797 }
  798 
  799 static void
  800 dwc_hdmi_detect_hdmi(struct dwc_hdmi_softc *sc)
  801 {
  802         uint8_t *edid;
  803         uint32_t edid_len;
  804         int block;
  805 
  806         sc->sc_has_audio = 0;
  807 
  808         /* Scan through extension blocks, looking for a CEA-861 block */
  809         for (block = 1; block <= sc->sc_edid_info.edid_ext_block_count;
  810             block++) {
  811                 if (hdmi_edid_read(sc, block, &edid, &edid_len) != 0)
  812                         return;
  813                 if (dwc_hdmi_detect_hdmi_vsdb(edid) != 0) {
  814                         if (bootverbose)
  815                                 device_printf(sc->sc_dev,
  816                                     "enabling audio support\n");
  817                         sc->sc_has_audio =
  818                             (edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0;
  819                         return;
  820                 }
  821         }
  822 }
  823 
  824 int
  825 dwc_hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len)
  826 {
  827         struct dwc_hdmi_softc *sc;
  828         int error;
  829 
  830         sc = device_get_softc(dev);
  831 
  832         memset(&sc->sc_edid_info, 0, sizeof(sc->sc_edid_info));
  833 
  834         error = hdmi_edid_read(sc, 0, edid, edid_len);
  835         if (error != 0)
  836                 return (error);
  837 
  838         edid_parse(*edid, &sc->sc_edid_info);
  839 
  840         return (0);
  841 }
  842 
  843 int
  844 dwc_hdmi_set_videomode(device_t dev, const struct videomode *mode)
  845 {
  846         struct dwc_hdmi_softc *sc;
  847 
  848         sc = device_get_softc(dev);
  849         memcpy(&sc->sc_mode, mode, sizeof(*mode));
  850 
  851         dwc_hdmi_detect_hdmi(sc);
  852 
  853         dwc_hdmi_set_mode(sc);
  854 
  855         return (0);
  856 }

Cache object: 4a00e12e3f528145b04a84be06d2c2b2


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.