jcs's openbsd hax
openbsd
at jcs 928 lines 27 kB view raw
1/* $OpenBSD: dwhdmi.c,v 1.6 2025/03/23 22:39:27 jmatthew Exp $ */ 2/* $NetBSD: dw_hdmi.c,v 1.7 2019/12/22 23:23:32 thorpej Exp $ */ 3 4/*- 5 * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/param.h> 31#include <sys/device.h> 32#include <sys/systm.h> 33#include <sys/kernel.h> 34 35#include <dev/ic/dwhdmi.h> 36 37#include <dev/i2c/i2cvar.h> 38#include <linux/i2c.h> 39 40#ifdef notyet 41#include <dev/audio/audio_dai.h> 42#endif 43 44#include <drm/drm_atomic.h> 45#include <drm/drm_atomic_helper.h> 46#include <drm/drm_crtc.h> 47#include <drm/drm_crtc_helper.h> 48#include <drm/drm_edid.h> 49#include <drm/drm_probe_helper.h> 50 51#define DDC_SEGMENT_ADDR 0x30 52 53#define HDMI_DESIGN_ID 0x0000 54#define HDMI_REVISION_ID 0x0001 55#define HDMI_CONFIG0_ID 0x0004 56#define HDMI_CONFIG0_ID_AUDI2S (1 << 4) 57#define HDMI_CONFIG2_ID 0x0006 58 59#define HDMI_IH_I2CM_STAT0 0x0105 60#define HDMI_IH_I2CM_STAT0_DONE (1 << 1) 61#define HDMI_IH_I2CM_STAT0_ERROR (1 << 0) 62#define HDMI_IH_MUTE 0x01ff 63#define HDMI_IH_MUTE_WAKEUP_INTERRUPT (1 << 1) 64#define HDMI_IH_MUTE_ALL_INTERRUPT (1 << 0) 65 66#define HDMI_TX_INVID0 0x0200 67#define HDMI_TX_INVID0_VIDEO_MAPPING (0x1f << 0) 68#define HDMI_TX_INVID0_VIDEO_MAPPING_DEFAULT (1 << 0) 69#define HDMI_TX_INSTUFFING 0x0201 70#define HDMI_TX_INSTUFFING_BCBDATA_STUFFING (1 << 2) 71#define HDMI_TX_INSTUFFING_RCRDATA_STUFFING (1 << 1) 72#define HDMI_TX_INSTUFFING_GYDATA_STUFFING (1 << 0) 73#define HDMI_TX_GYDATA0 0x0202 74#define HDMI_TX_GYDATA1 0x0203 75#define HDMI_TX_RCRDATA0 0x0204 76#define HDMI_TX_RCRDATA1 0x0205 77#define HDMI_TX_BCBDATA0 0x0206 78#define HDMI_TX_BCBDATA1 0x0207 79 80#define HDMI_VP_STATUS 0x0800 81#define HDMI_VP_PR_CD 0x0801 82#define HDMI_VP_PR_CD_COLOR_DEPTH (0xf << 4) 83#define HDMI_VP_PR_CD_COLOR_DEPTH_24 0 84#define HDMI_VP_PR_CD_DESIRED_PR_FACTOR (0xf << 0) 85#define HDMI_VP_PR_CD_DESIRED_PR_FACTOR_NONE 0 86#define HDMI_VP_STUFF 0x0802 87#define HDMI_VP_STUFF_IDEFAULT_PHASE (1 << 5) 88#define HDMI_VP_STUFF_YCC422_STUFFING (1 << 2) 89#define HDMI_VP_STUFF_PP_STUFFING (1 << 1) 90#define HDMI_VP_STUFF_PR_STUFFING (1 << 0) 91#define HDMI_VP_REMAP 0x0803 92#define HDMI_VP_REMAP_YCC422_SIZE (0x3 << 0) 93#define HDMI_VP_REMAP_YCC422_SIZE_16 0 94#define HDMI_VP_CONF 0x0804 95#define HDMI_VP_CONF_BYPASS_EN (1 << 6) 96#define HDMI_VP_CONF_BYPASS_SELECT (1 << 2) 97#define HDMI_VP_CONF_OUTPUT_SELECT (0x3 << 0) 98#define HDMI_VP_CONF_OUTPUT_SELECT_BYPASS (2 << 0) 99#define HDMI_VP_STAT 0x0805 100#define HDMI_VP_INT 0x0806 101#define HDMI_VP_MASK 0x0807 102#define HDMI_VP_POL 0x0808 103 104#define HDMI_FC_INVIDCONF 0x1000 105#define HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY (1 << 6) 106#define HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY (1 << 5) 107#define HDMI_FC_INVIDCONF_DE_IN_POLARITY (1 << 4) 108#define HDMI_FC_INVIDCONF_DVI_MODE (1 << 3) 109#define HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC (1 << 1) 110#define HDMI_FC_INVIDCONF_IN_I_P (1 << 0) 111#define HDMI_FC_INHACTIV0 0x1001 112#define HDMI_FC_INHACTIV1 0x1002 113#define HDMI_FC_INHBLANK0 0x1003 114#define HDMI_FC_INHBLANK1 0x1004 115#define HDMI_FC_INVACTIV0 0x1005 116#define HDMI_FC_INVACTIV1 0x1006 117#define HDMI_FC_INVBLANK 0x1007 118#define HDMI_FC_HSYNCINDELAY0 0x1008 119#define HDMI_FC_HSYNCINDELAY1 0x1009 120#define HDMI_FC_HSYNCINWIDTH0 0x100a 121#define HDMI_FC_HSYNCINWIDTH1 0x100b 122#define HDMI_FC_VSYNCINDELAY 0x100c 123#define HDMI_FC_VSYNCINWIDTH 0x100d 124#define HDMI_FC_CTRLDUR 0x1011 125#define HDMI_FC_CTRLDUR_DEFAULT 12 126#define HDMI_FC_EXCTRLDUR 0x1012 127#define HDMI_FC_EXCTRLDUR_DEFAULT 32 128#define HDMI_FC_EXCTRLSPAC 0x1013 129#define HDMI_FC_EXCTRLSPAC_DEFAULT 1 130#define HDMI_FC_CH0PREAM 0x1014 131#define HDMI_FC_CH0PREAM_DEFAULT 0x0b 132#define HDMI_FC_CH1PREAM 0x1015 133#define HDMI_FC_CH1PREAM_DEFAULT 0x16 134#define HDMI_FC_CH2PREAM 0x1016 135#define HDMI_FC_CH2PREAM_DEFAULT 0x21 136#define HDMI_FC_AUDCONF0 0x1025 137#define HDMI_FC_AUDCONF1 0x1026 138#define HDMI_FC_AUDCONF2 0x1027 139#define HDMI_FC_AUDCONF3 0x1028 140 141#define HDMI_PHY_CONF0 0x3000 142#define HDMI_PHY_CONF0_PDZ (1 << 7) 143#define HDMI_PHY_CONF0_ENTMDS (1 << 6) 144#define HDMI_PHY_CONF0_SVSRET (1 << 5) 145#define HDMI_PHY_CONF0_PDDQ (1 << 4) 146#define HDMI_PHY_CONF0_TXPWRON (1 << 3) 147#define HDMI_PHY_CONF0_ENHPDRXSENSE (1 << 2) 148#define HDMI_PHY_CONF0_SELDATAENPOL (1 << 1) 149#define HDMI_PHY_CONF0_SELDIPIF (1 << 0) 150#define HDMI_PHY_STAT0 0x3004 151#define HDMI_PHY_STAT0_RX_SENSE_3 (1 << 7) 152#define HDMI_PHY_STAT0_RX_SENSE_2 (1 << 6) 153#define HDMI_PHY_STAT0_RX_SENSE_1 (1 << 5) 154#define HDMI_PHY_STAT0_RX_SENSE_0 (1 << 4) 155#define HDMI_PHY_STAT0_HPD (1 << 1) 156#define HDMI_PHY_STAT0_TX_PHY_LOCK (1 << 0) 157 158#define HDMI_AUD_CONF0 0x3100 159#define HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST (1 << 7) 160#define HDMI_AUD_CONF0_I2S_SELECT (1 << 5) 161#define HDMI_AUD_CONF0_I2S_IN_EN (0xf << 0) 162#define HDMI_AUD_CONF1 0x3101 163#define HDMI_AUD_CONF1_I2S_WIDTH (0x1f << 0) 164#define HDMI_AUD_INT 0x3102 165#define HDMI_AUD_CONF2 0x3103 166#define HDMI_AUD_CONF2_INSERT_PCUV (1 << 2) 167#define HDMI_AUD_CONF2_NLPCM (1 << 1) 168#define HDMI_AUD_CONF2_HBR (1 << 0) 169#define HDMI_AUD_INT1 0x3104 170 171#define HDMI_AUD_N1 0x3200 172#define HDMI_AUD_N2 0x3201 173#define HDMI_AUD_N3 0x3202 174#define HDMI_AUD_CTS1 0x3203 175#define HDMI_AUD_CTS2 0x3204 176#define HDMI_AUD_CTS3 0x3205 177#define HDMI_AUD_INPUTCLKFS 0x3206 178#define HDMI_AUD_INPUTCLKFS_IFSFACTOR (0x7 << 0) 179 180#define HDMI_MC_CLKDIS 0x4001 181#define HDMI_MC_CLKDIS_HDCPCLK_DISABLE (1 << 6) 182#define HDMI_MC_CLKDIS_CECCLK_DISABLE (1 << 5) 183#define HDMI_MC_CLKDIS_CSCCLK_DISABLE (1 << 4) 184#define HDMI_MC_CLKDIS_AUDCLK_DISABLE (1 << 3) 185#define HDMI_MC_CLKDIS_PREPCLK_DISABLE (1 << 2) 186#define HDMI_MC_CLKDIS_TMDSCLK_DISABLE (1 << 1) 187#define HDMI_MC_CLKDIS_PIXELCLK_DISABLE (1 << 0) 188#define HDMI_MC_SWRSTZREQ 0x4002 189#define HDMI_MC_SWRSTZREQ_CECSWRST_REQ __BIT(6) 190#define HDMI_MC_SWRSTZREQ_PREPSWRST_REQ (1 << 2) 191#define HDMI_MC_SWRSTZREQ_TMDSSWRST_REQ (1 << 1) 192#define HDMI_MC_SWRSTZREQ_PIXELSWRST_REQ (1 << 0) 193#define HDMI_MC_FLOWCTRL 0x4004 194#define HDMI_MC_PHYRSTZ 0x4005 195#define HDMI_MC_PHYRSTZ_ASSERT (1 << 0) 196#define HDMI_MC_PHYRSTZ_DEASSERT 0 197#define HDMI_MC_LOCKONCLOCK 0x4006 198#define HDMI_MC_HEACPHY_RST 0x4007 199 200#define HDMI_I2CM_SLAVE 0x7e00 201#define HDMI_I2CM_ADDRESS 0x7e01 202#define HDMI_I2CM_DATAO 0x7e02 203#define HDMI_I2CM_DATAI 0x7e03 204#define HDMI_I2CM_OPERATION 0x7e04 205#define HDMI_I2CM_OPERATION_WR (1 << 4) 206#define HDMI_I2CM_OPERATION_RD_EXT (1 << 1) 207#define HDMI_I2CM_OPERATION_RD (1 << 0) 208#define HDMI_I2CM_INT 0x7e05 209#define HDMI_I2CM_INT_DONE_POL (1 << 3) 210#define HDMI_I2CM_INT_DONE_MASK (1 << 2) 211#define HDMI_I2CM_INT_DONE_INTERRUPT (1 << 1) 212#define HDMI_I2CM_INT_DONE_STATUS (1 << 0) 213#define HDMI_I2CM_INT_DEFAULT \ 214 (HDMI_I2CM_INT_DONE_POL| \ 215 HDMI_I2CM_INT_DONE_INTERRUPT| \ 216 HDMI_I2CM_INT_DONE_STATUS) 217#define HDMI_I2CM_CTLINT 0x7e06 218#define HDMI_I2CM_CTLINT_NACK_POL (1 << 7) 219#define HDMI_I2CM_CTLINT_NACK_MASK (1 << 6) 220#define HDMI_I2CM_CTLINT_NACK_INTERRUPT (1 << 5) 221#define HDMI_I2CM_CTLINT_NACK_STATUS (1 << 4) 222#define HDMI_I2CM_CTLINT_ARB_POL (1 << 3) 223#define HDMI_I2CM_CTLINT_ARB_MASK (1 << 2) 224#define HDMI_I2CM_CTLINT_ARB_INTERRUPT (1 << 1) 225#define HDMI_I2CM_CTLINT_ARB_STATUS (1 << 0) 226#define HDMI_I2CM_CTLINT_DEFAULT \ 227 (HDMI_I2CM_CTLINT_NACK_POL| \ 228 HDMI_I2CM_CTLINT_NACK_INTERRUPT| \ 229 HDMI_I2CM_CTLINT_NACK_STATUS| \ 230 HDMI_I2CM_CTLINT_ARB_POL| \ 231 HDMI_I2CM_CTLINT_ARB_INTERRUPT| \ 232 HDMI_I2CM_CTLINT_ARB_STATUS) 233#define HDMI_I2CM_DIV 0x7e07 234#define HDMI_I2CM_DIV_FAST_STD_MODE (1 << 3) 235#define HDMI_I2CM_SEGADDR 0x7e08 236#define HDMI_I2CM_SEGADDR_SEGADDR (0x7f << 0) 237#define HDMI_I2CM_SOFTRSTZ 0x7e09 238#define HDMI_I2CM_SOFTRSTZ_I2C_SOFTRST (1 << 0) 239#define HDMI_I2CM_SEGPTR 0x7e0a 240#define HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x730c 241#define HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0x730e 242 243enum dwhdmi_dai_mixer_ctrl { 244 DWHDMI_DAI_OUTPUT_CLASS, 245 DWHDMI_DAI_INPUT_CLASS, 246 247 DWHDMI_DAI_OUTPUT_MASTER_VOLUME, 248 DWHDMI_DAI_INPUT_DAC_VOLUME, 249 250 DWHDMI_DAI_MIXER_CTRL_LAST 251}; 252 253int 254dwhdmi_ddc_acquire_bus(void *priv, int flags) 255{ 256 return 0; 257} 258 259void 260dwhdmi_ddc_release_bus(void *priv, int flags) 261{ 262} 263 264int 265dwhdmi_ddc_exec(void *priv, i2c_op_t op, i2c_addr_t addr, 266 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 267{ 268 struct dwhdmi_softc * const sc = priv; 269 uint8_t block, operation, val; 270 uint8_t *pbuf = buf; 271 int off, n, retry; 272 273 if (addr != DDC_ADDR || op != I2C_OP_READ_WITH_STOP || cmdlen == 0 || buf == NULL) { 274 printf("%s: bad args addr=%#x op=%#x cmdlen=%d buf=%p\n", 275 __func__, addr, op, (int)cmdlen, buf); 276 return ENXIO; 277 } 278 if (len > 256) { 279 printf("dwhdmi_ddc_exec: bad len %d\n", (int)len); 280 return ERANGE; 281 } 282 283 dwhdmi_write(sc, HDMI_I2CM_SOFTRSTZ, 0); 284 dwhdmi_write(sc, HDMI_IH_I2CM_STAT0, dwhdmi_read(sc, HDMI_IH_I2CM_STAT0)); 285 if (sc->sc_scl_hcnt) 286 dwhdmi_write(sc, HDMI_I2CM_SS_SCL_HCNT_0_ADDR, sc->sc_scl_hcnt); 287 if (sc->sc_scl_lcnt) 288 dwhdmi_write(sc, HDMI_I2CM_SS_SCL_LCNT_0_ADDR, sc->sc_scl_lcnt); 289 dwhdmi_write(sc, HDMI_I2CM_DIV, 0); 290 dwhdmi_write(sc, HDMI_I2CM_SLAVE, DDC_ADDR); 291 dwhdmi_write(sc, HDMI_I2CM_SEGADDR, DDC_SEGMENT_ADDR); 292 293 block = *(const uint8_t *)cmdbuf; 294 operation = block ? HDMI_I2CM_OPERATION_RD_EXT : HDMI_I2CM_OPERATION_RD; 295 off = (block & 1) ? 128 : 0; 296 297 dwhdmi_write(sc, HDMI_I2CM_SEGPTR, block >> 1); 298 299 for (n = 0; n < len; n++) { 300 dwhdmi_write(sc, HDMI_I2CM_ADDRESS, n + off); 301 dwhdmi_write(sc, HDMI_I2CM_OPERATION, operation); 302 for (retry = 10000; retry > 0; retry--) { 303 val = dwhdmi_read(sc, HDMI_IH_I2CM_STAT0); 304 if (val & HDMI_IH_I2CM_STAT0_ERROR) { 305 return EIO; 306 } 307 if (val & HDMI_IH_I2CM_STAT0_DONE) { 308 dwhdmi_write(sc, HDMI_IH_I2CM_STAT0, val); 309 break; 310 } 311 delay(1); 312 } 313 if (retry == 0) { 314 printf("dwhdmi_ddc_exec: timeout waiting for xfer, stat0=%#x\n", dwhdmi_read(sc, HDMI_IH_I2CM_STAT0)); 315 return ETIMEDOUT; 316 } 317 318 pbuf[n] = dwhdmi_read(sc, HDMI_I2CM_DATAI); 319 } 320 321 return 0; 322} 323 324uint8_t 325dwhdmi_read(struct dwhdmi_softc *sc, bus_size_t reg) 326{ 327 uint8_t val; 328 329 switch (sc->sc_reg_width) { 330 case 1: 331 val = bus_space_read_1(sc->sc_bst, sc->sc_bsh, reg); 332 break; 333 case 4: 334 val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg * 4) & 0xff; 335 break; 336 default: 337 val = 0; 338 break; 339 } 340 341 return val; 342} 343 344void 345dwhdmi_write(struct dwhdmi_softc *sc, bus_size_t reg, uint8_t val) 346{ 347 switch (sc->sc_reg_width) { 348 case 1: 349 bus_space_write_1(sc->sc_bst, sc->sc_bsh, reg, val); 350 break; 351 case 4: 352 bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg * 4, val); 353 break; 354 } 355} 356 357void 358dwhdmi_vp_init(struct dwhdmi_softc *sc) 359{ 360 uint8_t val; 361 362 /* Select 24-bits per pixel video, 8-bit packing mode and disable pixel repetition */ 363 val = HDMI_VP_PR_CD_COLOR_DEPTH_24 << 4 | 364 HDMI_VP_PR_CD_DESIRED_PR_FACTOR_NONE << 0; 365 dwhdmi_write(sc, HDMI_VP_PR_CD, val); 366 367 /* Configure stuffing */ 368 val = HDMI_VP_STUFF_IDEFAULT_PHASE | 369 HDMI_VP_STUFF_YCC422_STUFFING | 370 HDMI_VP_STUFF_PP_STUFFING | 371 HDMI_VP_STUFF_PR_STUFFING; 372 dwhdmi_write(sc, HDMI_VP_STUFF, val); 373 374 /* Set YCC422 remap to 16-bit input video */ 375 val = HDMI_VP_REMAP_YCC422_SIZE_16 << 0; 376 dwhdmi_write(sc, HDMI_VP_REMAP, val); 377 378 /* Configure video packetizer */ 379 val = HDMI_VP_CONF_BYPASS_EN | 380 HDMI_VP_CONF_BYPASS_SELECT | 381 HDMI_VP_CONF_OUTPUT_SELECT_BYPASS; 382 dwhdmi_write(sc, HDMI_VP_CONF, val); 383} 384 385void 386dwhdmi_tx_init(struct dwhdmi_softc *sc) 387{ 388 uint8_t val; 389 390 /* Disable internal data enable generator and set default video mapping */ 391 val = HDMI_TX_INVID0_VIDEO_MAPPING_DEFAULT; 392 dwhdmi_write(sc, HDMI_TX_INVID0, val); 393 394 /* Enable video sampler stuffing */ 395 val = HDMI_TX_INSTUFFING_BCBDATA_STUFFING | 396 HDMI_TX_INSTUFFING_RCRDATA_STUFFING | 397 HDMI_TX_INSTUFFING_GYDATA_STUFFING; 398 dwhdmi_write(sc, HDMI_TX_INSTUFFING, val); 399} 400 401int 402dwhdmi_cea_mode_uses_fractional_vblank(uint8_t vic) 403{ 404 const uint8_t match[] = { 5, 6, 7, 10, 11, 20, 21, 22 }; 405 u_int n; 406 407 for (n = 0; n < nitems(match); n++) 408 if (match[n] == vic) 409 return true; 410 411 return false; 412} 413 414void 415dwhdmi_fc_init(struct dwhdmi_softc *sc, struct drm_display_mode *mode) 416{ 417 struct dwhdmi_connector *dwhdmi_connector = &sc->sc_connector; 418 uint8_t val; 419 420 const uint8_t vic = drm_match_cea_mode(mode); 421 const uint16_t inhactiv = mode->crtc_hdisplay; 422 const uint16_t inhblank = mode->crtc_htotal - mode->crtc_hdisplay; 423 const uint16_t invactiv = mode->crtc_vdisplay; 424 const uint8_t invblank = mode->crtc_vtotal - mode->crtc_vdisplay; 425 const uint16_t hsyncindelay = mode->crtc_hsync_start - mode->crtc_hdisplay; 426 const uint16_t hsyncinwidth = mode->crtc_hsync_end - mode->crtc_hsync_start; 427 const uint8_t vsyncindelay = mode->crtc_vsync_start - mode->crtc_vdisplay; 428 const uint8_t vsyncinwidth = mode->crtc_vsync_end - mode->crtc_vsync_start; 429 430 /* Input video configuration for frame composer */ 431 val = HDMI_FC_INVIDCONF_DE_IN_POLARITY; 432 if ((mode->flags & DRM_MODE_FLAG_PVSYNC) != 0) 433 val |= HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY; 434 if ((mode->flags & DRM_MODE_FLAG_PHSYNC) != 0) 435 val |= HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY; 436 if ((mode->flags & DRM_MODE_FLAG_INTERLACE) != 0) 437 val |= HDMI_FC_INVIDCONF_IN_I_P; 438 if (dwhdmi_connector->hdmi_monitor) 439 val |= HDMI_FC_INVIDCONF_DVI_MODE; 440 if (dwhdmi_cea_mode_uses_fractional_vblank(vic)) 441 val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC; 442 dwhdmi_write(sc, HDMI_FC_INVIDCONF, val); 443 444 /* Input video mode timings */ 445 dwhdmi_write(sc, HDMI_FC_INHACTIV0, inhactiv & 0xff); 446 dwhdmi_write(sc, HDMI_FC_INHACTIV1, inhactiv >> 8); 447 dwhdmi_write(sc, HDMI_FC_INHBLANK0, inhblank & 0xff); 448 dwhdmi_write(sc, HDMI_FC_INHBLANK1, inhblank >> 8); 449 dwhdmi_write(sc, HDMI_FC_INVACTIV0, invactiv & 0xff); 450 dwhdmi_write(sc, HDMI_FC_INVACTIV1, invactiv >> 8); 451 dwhdmi_write(sc, HDMI_FC_INVBLANK, invblank); 452 dwhdmi_write(sc, HDMI_FC_HSYNCINDELAY0, hsyncindelay & 0xff); 453 dwhdmi_write(sc, HDMI_FC_HSYNCINDELAY1, hsyncindelay >> 8); 454 dwhdmi_write(sc, HDMI_FC_HSYNCINWIDTH0, hsyncinwidth & 0xff); 455 dwhdmi_write(sc, HDMI_FC_HSYNCINWIDTH1, hsyncinwidth >> 8); 456 dwhdmi_write(sc, HDMI_FC_VSYNCINDELAY, vsyncindelay); 457 dwhdmi_write(sc, HDMI_FC_VSYNCINWIDTH, vsyncinwidth); 458 459 /* Setup control period minimum durations */ 460 dwhdmi_write(sc, HDMI_FC_CTRLDUR, HDMI_FC_CTRLDUR_DEFAULT); 461 dwhdmi_write(sc, HDMI_FC_EXCTRLDUR, HDMI_FC_EXCTRLDUR_DEFAULT); 462 dwhdmi_write(sc, HDMI_FC_EXCTRLSPAC, HDMI_FC_EXCTRLSPAC_DEFAULT); 463 464 /* Setup channel preamble filters */ 465 dwhdmi_write(sc, HDMI_FC_CH0PREAM, HDMI_FC_CH0PREAM_DEFAULT); 466 dwhdmi_write(sc, HDMI_FC_CH1PREAM, HDMI_FC_CH1PREAM_DEFAULT); 467 dwhdmi_write(sc, HDMI_FC_CH2PREAM, HDMI_FC_CH2PREAM_DEFAULT); 468} 469 470void 471dwhdmi_mc_init(struct dwhdmi_softc *sc) 472{ 473 uint8_t val; 474 u_int n, iter; 475 476 /* Bypass colour space converter */ 477 dwhdmi_write(sc, HDMI_MC_FLOWCTRL, 0); 478 479 /* Enable TMDS, pixel, and (if required) audio sampler clocks */ 480 val = HDMI_MC_CLKDIS_HDCPCLK_DISABLE | 481 HDMI_MC_CLKDIS_CECCLK_DISABLE | 482 HDMI_MC_CLKDIS_CSCCLK_DISABLE | 483 HDMI_MC_CLKDIS_PREPCLK_DISABLE; 484 dwhdmi_write(sc, HDMI_MC_CLKDIS, val); 485 486 /* Soft reset TMDS */ 487 val = 0xff & ~HDMI_MC_SWRSTZREQ_TMDSSWRST_REQ; 488 dwhdmi_write(sc, HDMI_MC_SWRSTZREQ, val); 489 490 iter = sc->sc_version == 0x130a ? 4 : 1; 491 492 val = dwhdmi_read(sc, HDMI_FC_INVIDCONF); 493 for (n = 0; n < iter; n++) 494 dwhdmi_write(sc, HDMI_FC_INVIDCONF, val); 495} 496 497void 498dwhdmi_mc_disable(struct dwhdmi_softc *sc) 499{ 500 /* Disable clocks */ 501 dwhdmi_write(sc, HDMI_MC_CLKDIS, 0xff); 502} 503 504void 505dwhdmi_audio_init(struct dwhdmi_softc *sc) 506{ 507 uint8_t val; 508 u_int n; 509 510 /* The following values are for 48 kHz */ 511 switch (sc->sc_curmode.clock) { 512 case 25170: 513 n = 6864; 514 break; 515 case 74170: 516 n = 11648; 517 break; 518 case 148350: 519 n = 5824; 520 break; 521 default: 522 n = 6144; 523 break; 524 } 525 526 /* Use automatic CTS generation */ 527 dwhdmi_write(sc, HDMI_AUD_CTS1, 0); 528 dwhdmi_write(sc, HDMI_AUD_CTS2, 0); 529 dwhdmi_write(sc, HDMI_AUD_CTS3, 0); 530 531 /* Set N factor for audio clock regeneration */ 532 dwhdmi_write(sc, HDMI_AUD_N1, n & 0xff); 533 dwhdmi_write(sc, HDMI_AUD_N2, (n >> 8) & 0xff); 534 dwhdmi_write(sc, HDMI_AUD_N3, (n >> 16) & 0xff); 535 536 val = dwhdmi_read(sc, HDMI_AUD_CONF0); 537 val |= HDMI_AUD_CONF0_I2S_SELECT; /* XXX i2s mode */ 538 val &= ~HDMI_AUD_CONF0_I2S_IN_EN; 539 val |= (1 << 0); /* XXX 2ch */ 540 dwhdmi_write(sc, HDMI_AUD_CONF0, val); 541 542 val = (16 << 0); 543 dwhdmi_write(sc, HDMI_AUD_CONF1, val); 544 545 dwhdmi_write(sc, HDMI_AUD_INPUTCLKFS, 4); /* XXX 64 FS */ 546 547 dwhdmi_write(sc, HDMI_FC_AUDCONF0, 1 << 4); /* XXX 2ch */ 548 dwhdmi_write(sc, HDMI_FC_AUDCONF1, 0); 549 dwhdmi_write(sc, HDMI_FC_AUDCONF2, 0); 550 dwhdmi_write(sc, HDMI_FC_AUDCONF3, 0); 551 552 val = dwhdmi_read(sc, HDMI_MC_CLKDIS); 553 val &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE; 554 dwhdmi_write(sc, HDMI_MC_CLKDIS, val); 555} 556 557enum drm_connector_status 558dwhdmi_connector_detect(struct drm_connector *connector, bool force) 559{ 560 struct dwhdmi_connector *dwhdmi_connector = to_dwhdmi_connector(connector); 561 struct dwhdmi_softc * const sc = dwhdmi_connector->sc; 562 563 if (sc->sc_detect != NULL) 564 return sc->sc_detect(sc, force); 565 566 return connector_status_connected; 567} 568 569void 570dwhdmi_connector_destroy(struct drm_connector *connector) 571{ 572 drm_connector_unregister(connector); 573 drm_connector_cleanup(connector); 574} 575 576const struct drm_connector_funcs dwhdmi_connector_funcs = { 577 .dpms = drm_helper_connector_dpms, 578 .detect = dwhdmi_connector_detect, 579 .fill_modes = drm_helper_probe_single_connector_modes, 580 .destroy = dwhdmi_connector_destroy, 581 .reset = drm_atomic_helper_connector_reset, 582 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 583 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 584}; 585 586int 587dwhdmi_connector_get_modes(struct drm_connector *connector) 588{ 589 struct dwhdmi_connector *dwhdmi_connector = to_dwhdmi_connector(connector); 590 struct dwhdmi_softc * const sc = dwhdmi_connector->sc; 591 struct i2c_adapter ddc; 592 struct edid *edid; 593 int error = 0; 594 595 memset(&ddc, 0, sizeof(ddc)); 596 ddc.ic = *sc->sc_ic; 597 598 edid = drm_get_edid(connector, &ddc); 599 if (edid) { 600 dwhdmi_connector->hdmi_monitor = drm_detect_hdmi_monitor(edid); 601 dwhdmi_connector->monitor_audio = drm_detect_monitor_audio(edid); 602 drm_connector_update_edid_property(connector, edid); 603 error = drm_add_edid_modes(connector, edid); 604 kfree(edid); 605 } else { 606 dwhdmi_connector->hdmi_monitor = false; 607 dwhdmi_connector->monitor_audio = false; 608 } 609 610 return error; 611} 612 613const struct drm_connector_helper_funcs dwhdmi_connector_helper_funcs = { 614 .get_modes = dwhdmi_connector_get_modes, 615}; 616 617int 618dwhdmi_bridge_attach(struct drm_bridge *bridge, 619 enum drm_bridge_attach_flags flags) 620{ 621 struct dwhdmi_softc * const sc = bridge->driver_private; 622 struct dwhdmi_connector *dwhdmi_connector = &sc->sc_connector; 623 struct drm_connector *connector = &dwhdmi_connector->base; 624 int error; 625 626 dwhdmi_connector->sc = sc; 627 628 connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; 629 connector->interlace_allowed = 0; 630 connector->doublescan_allowed = 0; 631 632 drm_connector_init(bridge->dev, connector, &dwhdmi_connector_funcs, 633 DRM_MODE_CONNECTOR_HDMIA); 634 drm_connector_helper_add(connector, &dwhdmi_connector_helper_funcs); 635 636 error = drm_connector_attach_encoder(connector, bridge->encoder); 637 if (error != 0) 638 return error; 639 640 return drm_connector_register(connector); 641} 642 643void 644dwhdmi_bridge_enable(struct drm_bridge *bridge) 645{ 646 struct dwhdmi_softc * const sc = bridge->driver_private; 647 648 dwhdmi_vp_init(sc); 649 dwhdmi_fc_init(sc, &sc->sc_curmode); 650 651 if (sc->sc_enable) 652 sc->sc_enable(sc); 653 654 dwhdmi_tx_init(sc); 655 dwhdmi_mc_init(sc); 656 657 if (sc->sc_connector.monitor_audio) 658 dwhdmi_audio_init(sc); 659} 660 661void 662dwhdmi_bridge_pre_enable(struct drm_bridge *bridge) 663{ 664} 665 666void 667dwhdmi_bridge_disable(struct drm_bridge *bridge) 668{ 669 struct dwhdmi_softc * const sc = bridge->driver_private; 670 671 if (sc->sc_disable) 672 sc->sc_disable(sc); 673 674 dwhdmi_mc_disable(sc); 675} 676 677void 678dwhdmi_bridge_post_disable(struct drm_bridge *bridge) 679{ 680} 681 682void 683dwhdmi_bridge_mode_set(struct drm_bridge *bridge, 684 const struct drm_display_mode *mode, 685 const struct drm_display_mode *adjusted_mode) 686{ 687 struct dwhdmi_softc *sc = bridge->driver_private; 688 689 if (sc->sc_mode_set) 690 sc->sc_mode_set(sc, mode, adjusted_mode); 691 692 sc->sc_curmode = *adjusted_mode; 693} 694 695enum drm_mode_status 696dwhdmi_bridge_mode_valid(struct drm_bridge *bridge, 697 const struct drm_display_info *info, 698 const struct drm_display_mode *mode) 699{ 700 struct dwhdmi_softc *sc = bridge->driver_private; 701 702 if (mode->flags & DRM_MODE_FLAG_DBLCLK) 703 return MODE_BAD; 704 705 if (sc->sc_mode_valid) 706 return sc->sc_mode_valid(sc, mode); 707 708 return MODE_OK; 709} 710 711const struct drm_bridge_funcs dwhdmi_bridge_funcs = { 712 .attach = dwhdmi_bridge_attach, 713 .enable = dwhdmi_bridge_enable, 714 .pre_enable = dwhdmi_bridge_pre_enable, 715 .disable = dwhdmi_bridge_disable, 716 .post_disable = dwhdmi_bridge_post_disable, 717 .mode_set = dwhdmi_bridge_mode_set, 718 .mode_valid = dwhdmi_bridge_mode_valid, 719}; 720 721#ifdef notyet 722 723static int 724dwhdmi_dai_set_format(audio_dai_tag_t dai, u_int format) 725{ 726 return 0; 727} 728 729static int 730dwhdmi_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux) 731{ 732 /* Not supported */ 733 return 0; 734} 735 736static void 737dwhdmi_audio_swvol_codec(audio_filter_arg_t *arg) 738{ 739 struct dwhdmi_softc * const sc = arg->context; 740 const aint_t *src; 741 aint_t *dst; 742 u_int sample_count; 743 u_int i; 744 745 src = arg->src; 746 dst = arg->dst; 747 sample_count = arg->count * arg->srcfmt->channels; 748 for (i = 0; i < sample_count; i++) { 749 aint2_t v = (aint2_t)(*src++); 750 v = v * sc->sc_swvol / 255; 751 *dst++ = (aint_t)v; 752 } 753} 754 755static int 756dwhdmi_audio_set_format(void *priv, int setmode, 757 const audio_params_t *play, const audio_params_t *rec, 758 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 759{ 760 struct dwhdmi_softc * const sc = priv; 761 762 pfil->codec = dwhdmi_audio_swvol_codec; 763 pfil->context = sc; 764 765 return 0; 766} 767 768static int 769dwhdmi_audio_set_port(void *priv, mixer_ctrl_t *mc) 770{ 771 struct dwhdmi_softc * const sc = priv; 772 773 switch (mc->dev) { 774 case DWHDMI_DAI_OUTPUT_MASTER_VOLUME: 775 case DWHDMI_DAI_INPUT_DAC_VOLUME: 776 sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 777 return 0; 778 default: 779 return ENXIO; 780 } 781} 782 783static int 784dwhdmi_audio_get_port(void *priv, mixer_ctrl_t *mc) 785{ 786 struct dwhdmi_softc * const sc = priv; 787 788 switch (mc->dev) { 789 case DWHDMI_DAI_OUTPUT_MASTER_VOLUME: 790 case DWHDMI_DAI_INPUT_DAC_VOLUME: 791 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_swvol; 792 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->sc_swvol; 793 return 0; 794 default: 795 return ENXIO; 796 } 797} 798 799static int 800dwhdmi_audio_query_devinfo(void *priv, mixer_devinfo_t *di) 801{ 802 switch (di->index) { 803 case DWHDMI_DAI_OUTPUT_CLASS: 804 di->mixer_class = di->index; 805 strcpy(di->label.name, AudioCoutputs); 806 di->type = AUDIO_MIXER_CLASS; 807 di->next = di->prev = AUDIO_MIXER_LAST; 808 return 0; 809 810 case DWHDMI_DAI_INPUT_CLASS: 811 di->mixer_class = di->index; 812 strcpy(di->label.name, AudioCinputs); 813 di->type = AUDIO_MIXER_CLASS; 814 di->next = di->prev = AUDIO_MIXER_LAST; 815 return 0; 816 817 case DWHDMI_DAI_OUTPUT_MASTER_VOLUME: 818 di->mixer_class = DWHDMI_DAI_OUTPUT_CLASS; 819 strcpy(di->label.name, AudioNmaster); 820 di->un.v.delta = 1; 821 di->un.v.num_channels = 2; 822 strcpy(di->un.v.units.name, AudioNvolume); 823 di->type = AUDIO_MIXER_VALUE; 824 di->next = di->prev = AUDIO_MIXER_LAST; 825 return 0; 826 827 case DWHDMI_DAI_INPUT_DAC_VOLUME: 828 di->mixer_class = DWHDMI_DAI_INPUT_CLASS; 829 strcpy(di->label.name, AudioNdac); 830 di->un.v.delta = 1; 831 di->un.v.num_channels = 2; 832 strcpy(di->un.v.units.name, AudioNvolume); 833 di->type = AUDIO_MIXER_VALUE; 834 di->next = di->prev = AUDIO_MIXER_LAST; 835 return 0; 836 837 default: 838 return ENXIO; 839 } 840} 841 842static const struct audio_hw_if dwhdmi_dai_hw_if = { 843 .set_format = dwhdmi_audio_set_format, 844 .set_port = dwhdmi_audio_set_port, 845 .get_port = dwhdmi_audio_get_port, 846 .query_devinfo = dwhdmi_audio_query_devinfo, 847}; 848 849#endif 850 851int 852dwhdmi_attach(struct dwhdmi_softc *sc) 853{ 854 uint8_t val; 855 856 if (sc->sc_reg_width != 1 && sc->sc_reg_width != 4) { 857 printf("%s: unsupported register width %d\n", 858 sc->sc_dev.dv_xname, sc->sc_reg_width); 859 return EINVAL; 860 } 861 862 sc->sc_version = dwhdmi_read(sc, HDMI_DESIGN_ID); 863 sc->sc_version <<= 8; 864 sc->sc_version |= dwhdmi_read(sc, HDMI_REVISION_ID); 865 866 sc->sc_phytype = dwhdmi_read(sc, HDMI_CONFIG2_ID); 867 868 printf("%s: version %x.%03x, phytype 0x%02x\n", sc->sc_dev.dv_xname, 869 sc->sc_version >> 12, sc->sc_version & 0xfff, 870 sc->sc_phytype); 871 872#ifdef notyet 873 sc->sc_swvol = 255; 874#endif 875 876 /* 877 * If a DDC i2c bus tag is provided by the caller, use it. Otherwise, 878 * use the I2C master built-in to DWC HDMI. 879 */ 880 if (sc->sc_ic == NULL) { 881 struct i2c_controller *ic = &sc->sc_ic_builtin; 882 883 memset(ic, 0, sizeof(*ic)); 884 ic->ic_cookie = sc; 885 ic->ic_acquire_bus = dwhdmi_ddc_acquire_bus; 886 ic->ic_release_bus = dwhdmi_ddc_release_bus; 887 ic->ic_exec = dwhdmi_ddc_exec; 888 sc->sc_ic = ic; 889 } 890 891 /* 892 * Enable HPD on internal PHY 893 */ 894 if ((sc->sc_flags & DWHDMI_USE_INTERNAL_PHY) != 0) { 895 val = dwhdmi_read(sc, HDMI_PHY_CONF0); 896 val |= HDMI_PHY_CONF0_ENHPDRXSENSE; 897 dwhdmi_write(sc, HDMI_PHY_CONF0, val); 898 } 899 900#ifdef notyet 901 /* 902 * Initialize audio DAI 903 */ 904 sc->sc_dai.dai_set_format = dwhdmi_dai_set_format; 905 sc->sc_dai.dai_add_device = dwhdmi_dai_add_device; 906 sc->sc_dai.dai_hw_if = &dwhdmi_dai_hw_if; 907 sc->sc_dai.dai_dev = sc->sc_dev; 908 sc->sc_dai.dai_priv = sc; 909#endif 910 911 return 0; 912} 913 914int 915dwhdmi_bind(struct dwhdmi_softc *sc, struct drm_encoder *encoder) 916{ 917 int error; 918 919 sc->sc_bridge.driver_private = sc; 920 sc->sc_bridge.funcs = &dwhdmi_bridge_funcs; 921 sc->sc_bridge.encoder = encoder; 922 923 error = drm_bridge_attach(encoder, &sc->sc_bridge, NULL, 0); 924 if (error != 0) 925 return EIO; 926 927 return 0; 928}