1/*- 2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28/* 29 * Allwinner A10/A20 HDMI TX 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/bus.h> 38#include <sys/rman.h> 39#include <sys/condvar.h> 40#include <sys/eventhandler.h> 41#include <sys/kernel.h> 42#include <sys/module.h> 43 44#include <machine/bus.h> 45 46#include <dev/ofw/ofw_bus.h> 47#include <dev/ofw/ofw_bus_subr.h> 48 49#include <dev/videomode/videomode.h> 50#include <dev/videomode/edidvar.h> 51 52#include <dev/extres/clk/clk.h> 53 54#include "hdmi_if.h" 55 56#define HDMI_CTRL 0x004 57#define CTRL_MODULE_EN (1 << 31) 58#define HDMI_INT_STATUS 0x008 59#define HDMI_HPD 0x00c 60#define HPD_DET (1 << 0) 61#define HDMI_VID_CTRL 0x010 62#define VID_CTRL_VIDEO_EN (1 << 31) 63#define VID_CTRL_HDMI_MODE (1 << 30) 64#define VID_CTRL_INTERLACE (1 << 4) 65#define VID_CTRL_REPEATER_2X (1 << 0) 66#define HDMI_VID_TIMING0 0x014 67#define VID_ACT_V(v) (((v) - 1) << 16) 68#define VID_ACT_H(h) (((h) - 1) << 0) 69#define HDMI_VID_TIMING1 0x018 70#define VID_VBP(vbp) (((vbp) - 1) << 16) 71#define VID_HBP(hbp) (((hbp) - 1) << 0) 72#define HDMI_VID_TIMING2 0x01c 73#define VID_VFP(vfp) (((vfp) - 1) << 16) 74#define VID_HFP(hfp) (((hfp) - 1) << 0) 75#define HDMI_VID_TIMING3 0x020 76#define VID_VSPW(vspw) (((vspw) - 1) << 16) 77#define VID_HSPW(hspw) (((hspw) - 1) << 0) 78#define HDMI_VID_TIMING4 0x024 79#define TX_CLOCK_NORMAL 0x03e00000 80#define VID_VSYNC_ACTSEL (1 << 1) 81#define VID_HSYNC_ACTSEL (1 << 0) 82#define HDMI_AUD_CTRL 0x040 83#define AUD_CTRL_EN (1 << 31) 84#define AUD_CTRL_RST (1 << 30) 85#define HDMI_ADMA_CTRL 0x044 86#define HDMI_ADMA_MODE (1 << 31) 87#define HDMI_ADMA_MODE_DDMA (0 << 31) 88#define HDMI_ADMA_MODE_NDMA (1 << 31) 89#define HDMI_AUD_FMT 0x048 90#define AUD_FMT_CH(n) ((n) - 1) 91#define HDMI_PCM_CTRL 0x04c 92#define HDMI_AUD_CTS 0x050 93#define HDMI_AUD_N 0x054 94#define HDMI_AUD_CH_STATUS0 0x058 95#define CH_STATUS0_FS_FREQ (0xf << 24) 96#define CH_STATUS0_FS_FREQ_48 (2 << 24) 97#define HDMI_AUD_CH_STATUS1 0x05c 98#define CH_STATUS1_WORD_LEN (0x7 << 1) 99#define CH_STATUS1_WORD_LEN_16 (1 << 1) 100#define HDMI_AUDIO_RESET_RETRY 1000 101#define HDMI_AUDIO_CHANNELS 2 102#define HDMI_AUDIO_CHANNELMAP 0x76543210 103#define HDMI_AUDIO_N 6144 /* 48 kHz */ 104#define HDMI_AUDIO_CTS(r, n) ((((r) * 10) * ((n) / 128)) / 480) 105#define HDMI_PADCTRL0 0x200 106#define PADCTRL0_BIASEN (1 << 31) 107#define PADCTRL0_LDOCEN (1 << 30) 108#define PADCTRL0_LDODEN (1 << 29) 109#define PADCTRL0_PWENC (1 << 28) 110#define PADCTRL0_PWEND (1 << 27) 111#define PADCTRL0_PWENG (1 << 26) 112#define PADCTRL0_CKEN (1 << 25) 113#define PADCTRL0_SEN (1 << 24) 114#define PADCTRL0_TXEN (1 << 23) 115#define HDMI_PADCTRL1 0x204 116#define PADCTRL1_AMP_OPT (1 << 23) 117#define PADCTRL1_AMPCK_OPT (1 << 22) 118#define PADCTRL1_DMP_OPT (1 << 21) 119#define PADCTRL1_EMP_OPT (1 << 20) 120#define PADCTRL1_EMPCK_OPT (1 << 19) 121#define PADCTRL1_PWSCK (1 << 18) 122#define PADCTRL1_PWSDT (1 << 17) 123#define PADCTRL1_REG_CSMPS (1 << 16) 124#define PADCTRL1_REG_DEN (1 << 15) 125#define PADCTRL1_REG_DENCK (1 << 14) 126#define PADCTRL1_REG_PLRCK (1 << 13) 127#define PADCTRL1_REG_EMP (0x7 << 10) 128#define PADCTRL1_REG_EMP_EN (0x2 << 10) 129#define PADCTRL1_REG_CD (0x3 << 8) 130#define PADCTRL1_REG_CKSS (0x3 << 6) 131#define PADCTRL1_REG_CKSS_1X (0x1 << 6) 132#define PADCTRL1_REG_CKSS_2X (0x0 << 6) 133#define PADCTRL1_REG_AMP (0x7 << 3) 134#define PADCTRL1_REG_AMP_EN (0x6 << 3) 135#define PADCTRL1_REG_PLR (0x7 << 0) 136#define HDMI_PLLCTRL0 0x208 137#define PLLCTRL0_PLL_EN (1 << 31) 138#define PLLCTRL0_BWS (1 << 30) 139#define PLLCTRL0_HV_IS_33 (1 << 29) 140#define PLLCTRL0_LDO1_EN (1 << 28) 141#define PLLCTRL0_LDO2_EN (1 << 27) 142#define PLLCTRL0_SDIV2 (1 << 25) 143#define PLLCTRL0_VCO_GAIN (0x1 << 22) 144#define PLLCTRL0_S (0x7 << 17) 145#define PLLCTRL0_CP_S (0xf << 12) 146#define PLLCTRL0_CS (0x7 << 8) 147#define PLLCTRL0_PREDIV(x) ((x) << 4) 148#define PLLCTRL0_VCO_S (0x8 << 0) 149#define HDMI_PLLDBG0 0x20c 150#define PLLDBG0_CKIN_SEL (1 << 21) 151#define PLLDBG0_CKIN_SEL_PLL3 (0 << 21) 152#define PLLDBG0_CKIN_SEL_PLL7 (1 << 21) 153#define HDMI_PKTCTRL0 0x2f0 154#define HDMI_PKTCTRL1 0x2f4 155#define PKTCTRL_PACKET(n,t) ((t) << ((n) << 2)) 156#define PKT_NULL 0 157#define PKT_GC 1 158#define PKT_AVI 2 159#define PKT_AI 3 160#define PKT_SPD 5 161#define PKT_END 15 162#define DDC_CTRL 0x500 163#define CTRL_DDC_EN (1 << 31) 164#define CTRL_DDC_ACMD_START (1 << 30) 165#define CTRL_DDC_FIFO_DIR (1 << 8) 166#define CTRL_DDC_FIFO_DIR_READ (0 << 8) 167#define CTRL_DDC_FIFO_DIR_WRITE (1 << 8) 168#define CTRL_DDC_SWRST (1 << 0) 169#define DDC_SLAVE_ADDR 0x504 170#define SLAVE_ADDR_SEG_SHIFT 24 171#define SLAVE_ADDR_EDDC_SHIFT 16 172#define SLAVE_ADDR_OFFSET_SHIFT 8 173#define SLAVE_ADDR_SHIFT 0 174#define DDC_INT_STATUS 0x50c 175#define INT_STATUS_XFER_DONE (1 << 0) 176#define DDC_FIFO_CTRL 0x510 177#define FIFO_CTRL_CLEAR (1 << 31) 178#define DDC_BYTE_COUNTER 0x51c 179#define DDC_COMMAND 0x520 180#define COMMAND_EOREAD (4 << 0) 181#define DDC_CLOCK 0x528 182#define DDC_CLOCK_M (1 << 3) 183#define DDC_CLOCK_N (5 << 0) 184#define DDC_FIFO 0x518 185#define SWRST_DELAY 1000 186#define DDC_DELAY 1000 187#define DDC_RETRY 1000 188#define DDC_BLKLEN 16 189#define DDC_ADDR 0x50 190#define EDDC_ADDR 0x60 191#define EDID_LENGTH 128 192#define DDC_CTRL_LINE 0x540 193#define DDC_LINE_SCL_ENABLE (1 << 8) 194#define DDC_LINE_SDA_ENABLE (1 << 9) 195#define HDMI_ENABLE_DELAY 50000 196#define DDC_READ_RETRY 4 197#define EXT_TAG 0x00 198#define CEA_TAG_ID 0x02 199#define CEA_DTD 0x03 200#define DTD_BASIC_AUDIO (1 << 6) 201#define CEA_REV 0x02 202#define CEA_DATA_OFF 0x03 203#define CEA_DATA_START 4 204#define BLOCK_TAG(x) (((x) >> 5) & 0x7) 205#define BLOCK_TAG_VSDB 3 206#define BLOCK_LEN(x) ((x) & 0x1f) 207#define HDMI_VSDB_MINLEN 5 208#define HDMI_OUI "\x03\x0c\x00" 209#define HDMI_OUI_LEN 3 210#define HDMI_DEFAULT_FREQ 297000000 211 212struct a10hdmi_softc { 213 struct resource *res; 214 215 struct intr_config_hook mode_hook; 216 217 uint8_t edid[EDID_LENGTH]; 218 219 int has_hdmi; 220 int has_audio; 221 222 clk_t clk_ahb; 223 clk_t clk_hdmi; 224 clk_t clk_lcd; 225}; 226 227static struct resource_spec a10hdmi_spec[] = { 228 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 229 { -1, 0 } 230}; 231 232#define HDMI_READ(sc, reg) bus_read_4((sc)->res, (reg)) 233#define HDMI_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) 234 235static void 236a10hdmi_init(struct a10hdmi_softc *sc) 237{ 238 /* Enable the HDMI module */ 239 HDMI_WRITE(sc, HDMI_CTRL, CTRL_MODULE_EN); 240 241 /* Configure PLL/DRV settings */ 242 HDMI_WRITE(sc, HDMI_PADCTRL0, PADCTRL0_BIASEN | PADCTRL0_LDOCEN | 243 PADCTRL0_LDODEN | PADCTRL0_PWENC | PADCTRL0_PWEND | 244 PADCTRL0_PWENG | PADCTRL0_CKEN | PADCTRL0_TXEN); 245 HDMI_WRITE(sc, HDMI_PADCTRL1, PADCTRL1_AMP_OPT | PADCTRL1_AMPCK_OPT | 246 PADCTRL1_EMP_OPT | PADCTRL1_EMPCK_OPT | PADCTRL1_REG_DEN | 247 PADCTRL1_REG_DENCK | PADCTRL1_REG_EMP_EN | PADCTRL1_REG_AMP_EN); 248 249 /* Select PLL3 as input clock */ 250 HDMI_WRITE(sc, HDMI_PLLDBG0, PLLDBG0_CKIN_SEL_PLL3); 251 252 DELAY(HDMI_ENABLE_DELAY); 253} 254 255static void 256a10hdmi_hpd(void *arg) 257{ 258 struct a10hdmi_softc *sc; 259 device_t dev; 260 uint32_t hpd; 261 262 dev = arg; 263 sc = device_get_softc(dev); 264 265 hpd = HDMI_READ(sc, HDMI_HPD); 266 if ((hpd & HPD_DET) == HPD_DET) 267 EVENTHANDLER_INVOKE(hdmi_event, dev, HDMI_EVENT_CONNECTED); 268 269 config_intrhook_disestablish(&sc->mode_hook); 270} 271 272static int 273a10hdmi_probe(device_t dev) 274{ 275 if (!ofw_bus_status_okay(dev)) 276 return (ENXIO); 277 278 if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmi")) 279 return (ENXIO); 280 281 device_set_desc(dev, "Allwinner HDMI TX"); 282 return (BUS_PROBE_DEFAULT); 283} 284 285static int 286a10hdmi_attach(device_t dev) 287{ 288 struct a10hdmi_softc *sc; 289 int error; 290 291 sc = device_get_softc(dev); 292 293 if (bus_alloc_resources(dev, a10hdmi_spec, &sc->res)) { 294 device_printf(dev, "cannot allocate resources for device\n"); 295 return (ENXIO); 296 } 297 298 /* Setup clocks */ 299 error = clk_get_by_ofw_name(dev, 0, "ahb", &sc->clk_ahb); 300 if (error != 0) { 301 device_printf(dev, "cannot find ahb clock\n"); 302 return (error); 303 } 304 error = clk_get_by_ofw_name(dev, 0, "hdmi", &sc->clk_hdmi); 305 if (error != 0) { 306 device_printf(dev, "cannot find hdmi clock\n"); 307 return (error); 308 } 309 error = clk_get_by_ofw_name(dev, 0, "lcd", &sc->clk_lcd); 310 if (error != 0) { 311 device_printf(dev, "cannot find lcd clock\n"); 312 } 313 /* Enable HDMI clock */ 314 error = clk_enable(sc->clk_hdmi); 315 if (error != 0) { 316 device_printf(dev, "cannot enable hdmi clock\n"); 317 return (error); 318 } 319 /* Gating AHB clock for HDMI */ 320 error = clk_enable(sc->clk_ahb); 321 if (error != 0) { 322 device_printf(dev, "cannot enable ahb gate\n"); 323 return (error); 324 } 325 326 a10hdmi_init(sc); 327 328 sc->mode_hook.ich_func = a10hdmi_hpd; 329 sc->mode_hook.ich_arg = dev; 330 331 error = config_intrhook_establish(&sc->mode_hook); 332 if (error != 0) 333 return (error); 334 335 return (0); 336} 337 338static int 339a10hdmi_ddc_xfer(struct a10hdmi_softc *sc, uint16_t addr, uint8_t seg, 340 uint8_t off, int len) 341{ 342 uint32_t val; 343 int retry; 344 345 /* Set FIFO direction to read */ 346 val = HDMI_READ(sc, DDC_CTRL); 347 val &= ~CTRL_DDC_FIFO_DIR; 348 val |= CTRL_DDC_FIFO_DIR_READ; 349 HDMI_WRITE(sc, DDC_CTRL, val); 350 351 /* Setup DDC slave address */ 352 val = (addr << SLAVE_ADDR_SHIFT) | (seg << SLAVE_ADDR_SEG_SHIFT) | 353 (EDDC_ADDR << SLAVE_ADDR_EDDC_SHIFT) | 354 (off << SLAVE_ADDR_OFFSET_SHIFT); 355 HDMI_WRITE(sc, DDC_SLAVE_ADDR, val); 356 357 /* Clear FIFO */ 358 val = HDMI_READ(sc, DDC_FIFO_CTRL); 359 val |= FIFO_CTRL_CLEAR; 360 HDMI_WRITE(sc, DDC_FIFO_CTRL, val); 361 362 /* Set transfer length */ 363 HDMI_WRITE(sc, DDC_BYTE_COUNTER, len); 364 365 /* Set command to "Explicit Offset Address Read" */ 366 HDMI_WRITE(sc, DDC_COMMAND, COMMAND_EOREAD); 367 368 /* Start transfer */ 369 val = HDMI_READ(sc, DDC_CTRL); 370 val |= CTRL_DDC_ACMD_START; 371 HDMI_WRITE(sc, DDC_CTRL, val); 372 373 /* Wait for command to start */ 374 retry = DDC_RETRY; 375 while (--retry > 0) { 376 val = HDMI_READ(sc, DDC_CTRL); 377 if ((val & CTRL_DDC_ACMD_START) == 0) 378 break; 379 DELAY(DDC_DELAY); 380 } 381 if (retry == 0) 382 return (ETIMEDOUT); 383 384 /* Ensure that the transfer completed */ 385 val = HDMI_READ(sc, DDC_INT_STATUS); 386 if ((val & INT_STATUS_XFER_DONE) == 0) 387 return (EIO); 388 389 return (0); 390} 391 392static int 393a10hdmi_ddc_read(struct a10hdmi_softc *sc, int block, uint8_t *edid) 394{ 395 int resid, off, len, error; 396 uint8_t *pbuf; 397 398 pbuf = edid; 399 resid = EDID_LENGTH; 400 off = (block & 1) ? EDID_LENGTH : 0; 401 402 while (resid > 0) { 403 len = min(resid, DDC_BLKLEN); 404 error = a10hdmi_ddc_xfer(sc, DDC_ADDR, block >> 1, off, len); 405 if (error != 0) 406 return (error); 407 408 bus_read_multi_1(sc->res, DDC_FIFO, pbuf, len); 409 410 pbuf += len; 411 off += len; 412 resid -= len; 413 } 414 415 return (0); 416} 417 418static int 419a10hdmi_detect_hdmi_vsdb(uint8_t *edid) 420{ 421 int off, p, btag, blen; 422 423 if (edid[EXT_TAG] != CEA_TAG_ID) 424 return (0); 425 426 off = edid[CEA_DATA_OFF]; 427 428 /* CEA data block collection starts at byte 4 */ 429 if (off <= CEA_DATA_START) 430 return (0); 431 432 /* Parse the CEA data blocks */ 433 for (p = CEA_DATA_START; p < off;) { 434 btag = BLOCK_TAG(edid[p]); 435 blen = BLOCK_LEN(edid[p]); 436 437 /* Make sure the length is sane */ 438 if (p + blen + 1 > off) 439 break; 440 441 /* Look for a VSDB with the HDMI 24-bit IEEE registration ID */ 442 if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN && 443 memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0) 444 return (1); 445 446 /* Next data block */ 447 p += (1 + blen); 448 } 449 450 return (0); 451} 452 453static void 454a10hdmi_detect_hdmi(struct a10hdmi_softc *sc, int *phdmi, int *paudio) 455{ 456 struct edid_info ei; 457 uint8_t edid[EDID_LENGTH]; 458 int block; 459 460 *phdmi = *paudio = 0; 461 462 if (edid_parse(sc->edid, &ei) != 0) 463 return; 464 465 /* Scan through extension blocks, looking for a CEA-861 block. */ 466 for (block = 1; block <= ei.edid_ext_block_count; block++) { 467 if (a10hdmi_ddc_read(sc, block, edid) != 0) 468 return; 469 470 if (a10hdmi_detect_hdmi_vsdb(edid) != 0) { 471 *phdmi = 1; 472 *paudio = ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0); 473 return; 474 } 475 } 476} 477 478static int 479a10hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len) 480{ 481 struct a10hdmi_softc *sc; 482 int error, retry; 483 484 sc = device_get_softc(dev); 485 retry = DDC_READ_RETRY; 486 487 while (--retry > 0) { 488 /* I2C software reset */ 489 HDMI_WRITE(sc, DDC_FIFO_CTRL, 0); 490 HDMI_WRITE(sc, DDC_CTRL, CTRL_DDC_EN | CTRL_DDC_SWRST); 491 DELAY(SWRST_DELAY); 492 if (HDMI_READ(sc, DDC_CTRL) & CTRL_DDC_SWRST) { 493 device_printf(dev, "DDC software reset failed\n"); 494 return (ENXIO); 495 } 496 497 /* Configure DDC clock */ 498 HDMI_WRITE(sc, DDC_CLOCK, DDC_CLOCK_M | DDC_CLOCK_N); 499 500 /* Enable SDA/SCL */ 501 HDMI_WRITE(sc, DDC_CTRL_LINE, 502 DDC_LINE_SCL_ENABLE | DDC_LINE_SDA_ENABLE); 503 504 /* Read EDID block */ 505 error = a10hdmi_ddc_read(sc, 0, sc->edid); 506 if (error == 0) { 507 *edid = sc->edid; 508 *edid_len = sizeof(sc->edid); 509 break; 510 } 511 } 512 513 if (error == 0) 514 a10hdmi_detect_hdmi(sc, &sc->has_hdmi, &sc->has_audio); 515 else 516 sc->has_hdmi = sc->has_audio = 0; 517 518 return (error); 519} 520 521static void 522a10hdmi_set_audiomode(device_t dev, const struct videomode *mode) 523{ 524 struct a10hdmi_softc *sc; 525 uint32_t val; 526 int retry; 527 528 sc = device_get_softc(dev); 529 530 /* Disable and reset audio module and wait for reset bit to clear */ 531 HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_RST); 532 for (retry = HDMI_AUDIO_RESET_RETRY; retry > 0; retry--) { 533 val = HDMI_READ(sc, HDMI_AUD_CTRL); 534 if ((val & AUD_CTRL_RST) == 0) 535 break; 536 } 537 if (retry == 0) { 538 device_printf(dev, "timeout waiting for audio module\n"); 539 return; 540 } 541 542 if (!sc->has_audio) 543 return; 544 545 /* DMA and FIFO control */ 546 HDMI_WRITE(sc, HDMI_ADMA_CTRL, HDMI_ADMA_MODE_DDMA); 547 548 /* Audio format control (LPCM, S16LE, stereo) */ 549 HDMI_WRITE(sc, HDMI_AUD_FMT, AUD_FMT_CH(HDMI_AUDIO_CHANNELS)); 550 551 /* Channel mappings */ 552 HDMI_WRITE(sc, HDMI_PCM_CTRL, HDMI_AUDIO_CHANNELMAP); 553 554 /* Clocks */ 555 HDMI_WRITE(sc, HDMI_AUD_CTS, 556 HDMI_AUDIO_CTS(mode->dot_clock, HDMI_AUDIO_N)); 557 HDMI_WRITE(sc, HDMI_AUD_N, HDMI_AUDIO_N); 558 559 /* Set sampling frequency to 48 kHz, word length to 16-bit */ 560 HDMI_WRITE(sc, HDMI_AUD_CH_STATUS0, CH_STATUS0_FS_FREQ_48); 561 HDMI_WRITE(sc, HDMI_AUD_CH_STATUS1, CH_STATUS1_WORD_LEN_16); 562 563 /* Enable */ 564 HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_EN); 565} 566 567static int 568a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl) 569{ 570 uint64_t lcd_fin, lcd_fout; 571 clk_t clk_lcd_parent; 572 const char *pname; 573 int error; 574 575 error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent); 576 if (error != 0) 577 return (error); 578 579 /* Get the LCD CH1 special clock 2 divider */ 580 error = clk_get_freq(sc->clk_lcd, &lcd_fout); 581 if (error != 0) 582 return (error); 583 error = clk_get_freq(clk_lcd_parent, &lcd_fin); 584 if (error != 0) 585 return (error); 586 *div = lcd_fin / lcd_fout; 587 588 /* Detect LCD CH1 special clock using a 1X or 2X source */ 589 /* XXX */ 590 pname = clk_get_name(clk_lcd_parent); 591 if (strcmp(pname, "pll3") == 0 || strcmp(pname, "pll7") == 0) 592 *dbl = 0; 593 else 594 *dbl = 1; 595 596 return (0); 597} 598 599static int 600a10hdmi_set_videomode(device_t dev, const struct videomode *mode) 601{ 602 struct a10hdmi_softc *sc; 603 int error, clk_div, clk_dbl; 604 int dblscan, hfp, hspw, hbp, vfp, vspw, vbp; 605 uint32_t val; 606 607 sc = device_get_softc(dev); 608 dblscan = !!(mode->flags & VID_DBLSCAN); 609 hfp = mode->hsync_start - mode->hdisplay; 610 hspw = mode->hsync_end - mode->hsync_start; 611 hbp = mode->htotal - mode->hsync_start; 612 vfp = mode->vsync_start - mode->vdisplay; 613 vspw = mode->vsync_end - mode->vsync_start; 614 vbp = mode->vtotal - mode->vsync_start; 615 616 error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl); 617 if (error != 0) { 618 device_printf(dev, "couldn't get tcon config: %d\n", error); 619 return (error); 620 } 621 622 /* Clear interrupt status */ 623 HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS)); 624 625 /* Clock setup */ 626 val = HDMI_READ(sc, HDMI_PADCTRL1); 627 val &= ~PADCTRL1_REG_CKSS; 628 val |= (clk_dbl ? PADCTRL1_REG_CKSS_2X : PADCTRL1_REG_CKSS_1X); 629 HDMI_WRITE(sc, HDMI_PADCTRL1, val); 630 HDMI_WRITE(sc, HDMI_PLLCTRL0, PLLCTRL0_PLL_EN | PLLCTRL0_BWS | 631 PLLCTRL0_HV_IS_33 | PLLCTRL0_LDO1_EN | PLLCTRL0_LDO2_EN | 632 PLLCTRL0_SDIV2 | PLLCTRL0_VCO_GAIN | PLLCTRL0_S | 633 PLLCTRL0_CP_S | PLLCTRL0_CS | PLLCTRL0_PREDIV(clk_div) | 634 PLLCTRL0_VCO_S); 635 636 /* Setup display settings */ 637 if (bootverbose) 638 device_printf(dev, "HDMI: %s, Audio: %s\n", 639 sc->has_hdmi ? "yes" : "no", sc->has_audio ? "yes" : "no"); 640 val = 0; 641 if (sc->has_hdmi) 642 val |= VID_CTRL_HDMI_MODE; 643 if (mode->flags & VID_INTERLACE) 644 val |= VID_CTRL_INTERLACE; 645 if (mode->flags & VID_DBLSCAN) 646 val |= VID_CTRL_REPEATER_2X; 647 HDMI_WRITE(sc, HDMI_VID_CTRL, val); 648 649 /* Setup display timings */ 650 HDMI_WRITE(sc, HDMI_VID_TIMING0, 651 VID_ACT_V(mode->vdisplay) | VID_ACT_H(mode->hdisplay << dblscan)); 652 HDMI_WRITE(sc, HDMI_VID_TIMING1, 653 VID_VBP(vbp) | VID_HBP(hbp << dblscan)); 654 HDMI_WRITE(sc, HDMI_VID_TIMING2, 655 VID_VFP(vfp) | VID_HFP(hfp << dblscan)); 656 HDMI_WRITE(sc, HDMI_VID_TIMING3, 657 VID_VSPW(vspw) | VID_HSPW(hspw << dblscan)); 658 val = TX_CLOCK_NORMAL; 659 if (mode->flags & VID_PVSYNC) 660 val |= VID_VSYNC_ACTSEL; 661 if (mode->flags & VID_PHSYNC) 662 val |= VID_HSYNC_ACTSEL; 663 HDMI_WRITE(sc, HDMI_VID_TIMING4, val); 664 665 /* This is an ordered list of infoframe packets that the HDMI 666 * transmitter will send. Transmit packets in the following order: 667 * 1. General control packet 668 * 2. AVI infoframe 669 * 3. Audio infoframe 670 * There are 2 registers with 4 slots each. The list is terminated 671 * with the special PKT_END marker. 672 */ 673 HDMI_WRITE(sc, HDMI_PKTCTRL0, 674 PKTCTRL_PACKET(0, PKT_GC) | PKTCTRL_PACKET(1, PKT_AVI) | 675 PKTCTRL_PACKET(2, PKT_AI) | PKTCTRL_PACKET(3, PKT_END)); 676 HDMI_WRITE(sc, HDMI_PKTCTRL1, 0); 677 678 /* Setup audio */ 679 a10hdmi_set_audiomode(dev, mode); 680 681 return (0); 682} 683 684static int 685a10hdmi_enable(device_t dev, int onoff) 686{ 687 struct a10hdmi_softc *sc; 688 uint32_t val; 689 690 sc = device_get_softc(dev); 691 692 /* Enable or disable video output */ 693 val = HDMI_READ(sc, HDMI_VID_CTRL); 694 if (onoff) 695 val |= VID_CTRL_VIDEO_EN; 696 else 697 val &= ~VID_CTRL_VIDEO_EN; 698 HDMI_WRITE(sc, HDMI_VID_CTRL, val); 699 700 return (0); 701} 702 703static device_method_t a10hdmi_methods[] = { 704 /* Device interface */ 705 DEVMETHOD(device_probe, a10hdmi_probe), 706 DEVMETHOD(device_attach, a10hdmi_attach), 707 708 /* HDMI interface */ 709 DEVMETHOD(hdmi_get_edid, a10hdmi_get_edid), 710 DEVMETHOD(hdmi_set_videomode, a10hdmi_set_videomode), 711 DEVMETHOD(hdmi_enable, a10hdmi_enable), 712 713 DEVMETHOD_END 714}; 715 716static driver_t a10hdmi_driver = { 717 "a10hdmi", 718 a10hdmi_methods, 719 sizeof(struct a10hdmi_softc), 720}; 721 722static devclass_t a10hdmi_devclass; 723 724DRIVER_MODULE(a10hdmi, simplebus, a10hdmi_driver, a10hdmi_devclass, 0, 0); 725MODULE_VERSION(a10hdmi, 1); 726