a10_hdmi.c revision 302408
1/*- 2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 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 ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 * 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 * $FreeBSD: stable/11/sys/arm/allwinner/a10_hdmi.c 297627 2016-04-06 23:11:03Z jmcneill $ 27 */ 28 29/* 30 * Allwinner A10/A20 HDMI TX 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_hdmi.c 297627 2016-04-06 23:11:03Z jmcneill $"); 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/bus.h> 39#include <sys/rman.h> 40#include <sys/condvar.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 HDMI_ENABLE_DELAY 50000 193#define DDC_READ_RETRY 4 194#define EXT_TAG 0x00 195#define CEA_TAG_ID 0x02 196#define CEA_DTD 0x03 197#define DTD_BASIC_AUDIO (1 << 6) 198#define CEA_REV 0x02 199#define CEA_DATA_OFF 0x03 200#define CEA_DATA_START 4 201#define BLOCK_TAG(x) (((x) >> 5) & 0x7) 202#define BLOCK_TAG_VSDB 3 203#define BLOCK_LEN(x) ((x) & 0x1f) 204#define HDMI_VSDB_MINLEN 5 205#define HDMI_OUI "\x03\x0c\x00" 206#define HDMI_OUI_LEN 3 207#define HDMI_DEFAULT_FREQ 297000000 208 209struct a10hdmi_softc { 210 struct resource *res; 211 212 struct intr_config_hook mode_hook; 213 214 uint8_t edid[EDID_LENGTH]; 215 216 int has_hdmi; 217 int has_audio; 218 219 clk_t clk_ahb; 220 clk_t clk_hdmi; 221 clk_t clk_lcd; 222}; 223 224static struct resource_spec a10hdmi_spec[] = { 225 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 226 { -1, 0 } 227}; 228 229#define HDMI_READ(sc, reg) bus_read_4((sc)->res, (reg)) 230#define HDMI_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) 231 232static void 233a10hdmi_init(struct a10hdmi_softc *sc) 234{ 235 /* Enable the HDMI module */ 236 HDMI_WRITE(sc, HDMI_CTRL, CTRL_MODULE_EN); 237 238 /* Configure PLL/DRV settings */ 239 HDMI_WRITE(sc, HDMI_PADCTRL0, PADCTRL0_BIASEN | PADCTRL0_LDOCEN | 240 PADCTRL0_LDODEN | PADCTRL0_PWENC | PADCTRL0_PWEND | 241 PADCTRL0_PWENG | PADCTRL0_CKEN | PADCTRL0_TXEN); 242 HDMI_WRITE(sc, HDMI_PADCTRL1, PADCTRL1_AMP_OPT | PADCTRL1_AMPCK_OPT | 243 PADCTRL1_EMP_OPT | PADCTRL1_EMPCK_OPT | PADCTRL1_REG_DEN | 244 PADCTRL1_REG_DENCK | PADCTRL1_REG_EMP_EN | PADCTRL1_REG_AMP_EN); 245 246 /* Select PLL3 as input clock */ 247 HDMI_WRITE(sc, HDMI_PLLDBG0, PLLDBG0_CKIN_SEL_PLL3); 248 249 DELAY(HDMI_ENABLE_DELAY); 250} 251 252static void 253a10hdmi_hpd(void *arg) 254{ 255 struct a10hdmi_softc *sc; 256 device_t dev; 257 uint32_t hpd; 258 259 dev = arg; 260 sc = device_get_softc(dev); 261 262 hpd = HDMI_READ(sc, HDMI_HPD); 263 if ((hpd & HPD_DET) == HPD_DET) 264 EVENTHANDLER_INVOKE(hdmi_event, dev, HDMI_EVENT_CONNECTED); 265 266 config_intrhook_disestablish(&sc->mode_hook); 267} 268 269static int 270a10hdmi_probe(device_t dev) 271{ 272 if (!ofw_bus_status_okay(dev)) 273 return (ENXIO); 274 275 if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmi")) 276 return (ENXIO); 277 278 device_set_desc(dev, "Allwinner HDMI TX"); 279 return (BUS_PROBE_DEFAULT); 280} 281 282static int 283a10hdmi_attach(device_t dev) 284{ 285 struct a10hdmi_softc *sc; 286 int error; 287 288 sc = device_get_softc(dev); 289 290 if (bus_alloc_resources(dev, a10hdmi_spec, &sc->res)) { 291 device_printf(dev, "cannot allocate resources for device\n"); 292 return (ENXIO); 293 } 294 295 /* Setup clocks */ 296 error = clk_get_by_ofw_name(dev, "ahb", &sc->clk_ahb); 297 if (error != 0) { 298 device_printf(dev, "cannot find ahb clock\n"); 299 return (error); 300 } 301 error = clk_get_by_ofw_name(dev, "hdmi", &sc->clk_hdmi); 302 if (error != 0) { 303 device_printf(dev, "cannot find hdmi clock\n"); 304 return (error); 305 } 306 error = clk_get_by_ofw_name(dev, "lcd", &sc->clk_lcd); 307 if (error != 0) { 308 device_printf(dev, "cannot find lcd clock\n"); 309 } 310 /* Enable HDMI clock */ 311 error = clk_enable(sc->clk_hdmi); 312 if (error != 0) { 313 device_printf(dev, "cannot enable hdmi clock\n"); 314 return (error); 315 } 316 /* Gating AHB clock for HDMI */ 317 error = clk_enable(sc->clk_ahb); 318 if (error != 0) { 319 device_printf(dev, "cannot enable ahb gate\n"); 320 return (error); 321 } 322 323 a10hdmi_init(sc); 324 325 sc->mode_hook.ich_func = a10hdmi_hpd; 326 sc->mode_hook.ich_arg = dev; 327 328 error = config_intrhook_establish(&sc->mode_hook); 329 if (error != 0) 330 return (error); 331 332 return (0); 333} 334 335static int 336a10hdmi_ddc_xfer(struct a10hdmi_softc *sc, uint16_t addr, uint8_t seg, 337 uint8_t off, int len) 338{ 339 uint32_t val; 340 int retry; 341 342 /* Set FIFO direction to read */ 343 val = HDMI_READ(sc, DDC_CTRL); 344 val &= ~CTRL_DDC_FIFO_DIR; 345 val |= CTRL_DDC_FIFO_DIR_READ; 346 HDMI_WRITE(sc, DDC_CTRL, val); 347 348 /* Setup DDC slave address */ 349 val = (addr << SLAVE_ADDR_SHIFT) | (seg << SLAVE_ADDR_SEG_SHIFT) | 350 (EDDC_ADDR << SLAVE_ADDR_EDDC_SHIFT) | 351 (off << SLAVE_ADDR_OFFSET_SHIFT); 352 HDMI_WRITE(sc, DDC_SLAVE_ADDR, val); 353 354 /* Clear FIFO */ 355 val = HDMI_READ(sc, DDC_FIFO_CTRL); 356 val |= FIFO_CTRL_CLEAR; 357 HDMI_WRITE(sc, DDC_FIFO_CTRL, val); 358 359 /* Set transfer length */ 360 HDMI_WRITE(sc, DDC_BYTE_COUNTER, len); 361 362 /* Set command to "Explicit Offset Address Read" */ 363 HDMI_WRITE(sc, DDC_COMMAND, COMMAND_EOREAD); 364 365 /* Start transfer */ 366 val = HDMI_READ(sc, DDC_CTRL); 367 val |= CTRL_DDC_ACMD_START; 368 HDMI_WRITE(sc, DDC_CTRL, val); 369 370 /* Wait for command to start */ 371 retry = DDC_RETRY; 372 while (--retry > 0) { 373 val = HDMI_READ(sc, DDC_CTRL); 374 if ((val & CTRL_DDC_ACMD_START) == 0) 375 break; 376 DELAY(DDC_DELAY); 377 } 378 if (retry == 0) 379 return (ETIMEDOUT); 380 381 /* Ensure that the transfer completed */ 382 val = HDMI_READ(sc, DDC_INT_STATUS); 383 if ((val & INT_STATUS_XFER_DONE) == 0) 384 return (EIO); 385 386 return (0); 387} 388 389static int 390a10hdmi_ddc_read(struct a10hdmi_softc *sc, int block, uint8_t *edid) 391{ 392 int resid, off, len, error; 393 uint8_t *pbuf; 394 395 pbuf = edid; 396 resid = EDID_LENGTH; 397 off = (block & 1) ? EDID_LENGTH : 0; 398 399 while (resid > 0) { 400 len = min(resid, DDC_BLKLEN); 401 error = a10hdmi_ddc_xfer(sc, DDC_ADDR, block >> 1, off, len); 402 if (error != 0) 403 return (error); 404 405 bus_read_multi_1(sc->res, DDC_FIFO, pbuf, len); 406 407 pbuf += len; 408 off += len; 409 resid -= len; 410 } 411 412 return (0); 413} 414 415static int 416a10hdmi_detect_hdmi_vsdb(uint8_t *edid) 417{ 418 int off, p, btag, blen; 419 420 if (edid[EXT_TAG] != CEA_TAG_ID) 421 return (0); 422 423 off = edid[CEA_DATA_OFF]; 424 425 /* CEA data block collection starts at byte 4 */ 426 if (off <= CEA_DATA_START) 427 return (0); 428 429 /* Parse the CEA data blocks */ 430 for (p = CEA_DATA_START; p < off;) { 431 btag = BLOCK_TAG(edid[p]); 432 blen = BLOCK_LEN(edid[p]); 433 434 /* Make sure the length is sane */ 435 if (p + blen + 1 > off) 436 break; 437 438 /* Look for a VSDB with the HDMI 24-bit IEEE registration ID */ 439 if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN && 440 memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0) 441 return (1); 442 443 /* Next data block */ 444 p += (1 + blen); 445 } 446 447 return (0); 448} 449 450static void 451a10hdmi_detect_hdmi(struct a10hdmi_softc *sc, int *phdmi, int *paudio) 452{ 453 struct edid_info ei; 454 uint8_t edid[EDID_LENGTH]; 455 int block; 456 457 *phdmi = *paudio = 0; 458 459 if (edid_parse(sc->edid, &ei) != 0) 460 return; 461 462 /* Scan through extension blocks, looking for a CEA-861 block. */ 463 for (block = 1; block <= ei.edid_ext_block_count; block++) { 464 if (a10hdmi_ddc_read(sc, block, edid) != 0) 465 return; 466 467 if (a10hdmi_detect_hdmi_vsdb(edid) != 0) { 468 *phdmi = 1; 469 *paudio = ((edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0); 470 return; 471 } 472 } 473} 474 475static int 476a10hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len) 477{ 478 struct a10hdmi_softc *sc; 479 int error, retry; 480 481 sc = device_get_softc(dev); 482 retry = DDC_READ_RETRY; 483 484 while (--retry > 0) { 485 /* I2C software reset */ 486 HDMI_WRITE(sc, DDC_FIFO_CTRL, 0); 487 HDMI_WRITE(sc, DDC_CTRL, CTRL_DDC_EN | CTRL_DDC_SWRST); 488 DELAY(SWRST_DELAY); 489 if (HDMI_READ(sc, DDC_CTRL) & CTRL_DDC_SWRST) { 490 device_printf(dev, "DDC software reset failed\n"); 491 return (ENXIO); 492 } 493 494 /* Configure DDC clock */ 495 HDMI_WRITE(sc, DDC_CLOCK, DDC_CLOCK_M | DDC_CLOCK_N); 496 497 /* Read EDID block */ 498 error = a10hdmi_ddc_read(sc, 0, sc->edid); 499 if (error == 0) { 500 *edid = sc->edid; 501 *edid_len = sizeof(sc->edid); 502 break; 503 } 504 } 505 506 if (error == 0) 507 a10hdmi_detect_hdmi(sc, &sc->has_hdmi, &sc->has_audio); 508 else 509 sc->has_hdmi = sc->has_audio = 0; 510 511 return (error); 512} 513 514static void 515a10hdmi_set_audiomode(device_t dev, const struct videomode *mode) 516{ 517 struct a10hdmi_softc *sc; 518 uint32_t val; 519 int retry; 520 521 sc = device_get_softc(dev); 522 523 /* Disable and reset audio module and wait for reset bit to clear */ 524 HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_RST); 525 for (retry = HDMI_AUDIO_RESET_RETRY; retry > 0; retry--) { 526 val = HDMI_READ(sc, HDMI_AUD_CTRL); 527 if ((val & AUD_CTRL_RST) == 0) 528 break; 529 } 530 if (retry == 0) { 531 device_printf(dev, "timeout waiting for audio module\n"); 532 return; 533 } 534 535 if (!sc->has_audio) 536 return; 537 538 /* DMA and FIFO control */ 539 HDMI_WRITE(sc, HDMI_ADMA_CTRL, HDMI_ADMA_MODE_DDMA); 540 541 /* Audio format control (LPCM, S16LE, stereo) */ 542 HDMI_WRITE(sc, HDMI_AUD_FMT, AUD_FMT_CH(HDMI_AUDIO_CHANNELS)); 543 544 /* Channel mappings */ 545 HDMI_WRITE(sc, HDMI_PCM_CTRL, HDMI_AUDIO_CHANNELMAP); 546 547 /* Clocks */ 548 HDMI_WRITE(sc, HDMI_AUD_CTS, 549 HDMI_AUDIO_CTS(mode->dot_clock, HDMI_AUDIO_N)); 550 HDMI_WRITE(sc, HDMI_AUD_N, HDMI_AUDIO_N); 551 552 /* Set sampling frequency to 48 kHz, word length to 16-bit */ 553 HDMI_WRITE(sc, HDMI_AUD_CH_STATUS0, CH_STATUS0_FS_FREQ_48); 554 HDMI_WRITE(sc, HDMI_AUD_CH_STATUS1, CH_STATUS1_WORD_LEN_16); 555 556 /* Enable */ 557 HDMI_WRITE(sc, HDMI_AUD_CTRL, AUD_CTRL_EN); 558} 559 560static int 561a10hdmi_get_tcon_config(struct a10hdmi_softc *sc, int *div, int *dbl) 562{ 563 uint64_t lcd_fin, lcd_fout; 564 clk_t clk_lcd_parent; 565 const char *pname; 566 int error; 567 568 error = clk_get_parent(sc->clk_lcd, &clk_lcd_parent); 569 if (error != 0) 570 return (error); 571 572 /* Get the LCD CH1 special clock 2 divider */ 573 error = clk_get_freq(sc->clk_lcd, &lcd_fout); 574 if (error != 0) 575 return (error); 576 error = clk_get_freq(clk_lcd_parent, &lcd_fin); 577 if (error != 0) 578 return (error); 579 *div = lcd_fin / lcd_fout; 580 581 /* Detect LCD CH1 special clock using a 1X or 2X source */ 582 /* XXX */ 583 pname = clk_get_name(clk_lcd_parent); 584 if (strcmp(pname, "pll3-1x") == 0 || strcmp(pname, "pll7-1x") == 0) 585 *dbl = 0; 586 else 587 *dbl = 1; 588 589 return (0); 590} 591 592static int 593a10hdmi_set_videomode(device_t dev, const struct videomode *mode) 594{ 595 struct a10hdmi_softc *sc; 596 int error, clk_div, clk_dbl; 597 int dblscan, hfp, hspw, hbp, vfp, vspw, vbp; 598 uint32_t val; 599 600 sc = device_get_softc(dev); 601 dblscan = !!(mode->flags & VID_DBLSCAN); 602 hfp = mode->hsync_start - mode->hdisplay; 603 hspw = mode->hsync_end - mode->hsync_start; 604 hbp = mode->htotal - mode->hsync_start; 605 vfp = mode->vsync_start - mode->vdisplay; 606 vspw = mode->vsync_end - mode->vsync_start; 607 vbp = mode->vtotal - mode->vsync_start; 608 609 error = a10hdmi_get_tcon_config(sc, &clk_div, &clk_dbl); 610 if (error != 0) { 611 device_printf(dev, "couldn't get tcon config: %d\n", error); 612 return (error); 613 } 614 615 /* Clear interrupt status */ 616 HDMI_WRITE(sc, HDMI_INT_STATUS, HDMI_READ(sc, HDMI_INT_STATUS)); 617 618 /* Clock setup */ 619 val = HDMI_READ(sc, HDMI_PADCTRL1); 620 val &= ~PADCTRL1_REG_CKSS; 621 val |= (clk_dbl ? PADCTRL1_REG_CKSS_2X : PADCTRL1_REG_CKSS_1X); 622 HDMI_WRITE(sc, HDMI_PADCTRL1, val); 623 HDMI_WRITE(sc, HDMI_PLLCTRL0, PLLCTRL0_PLL_EN | PLLCTRL0_BWS | 624 PLLCTRL0_HV_IS_33 | PLLCTRL0_LDO1_EN | PLLCTRL0_LDO2_EN | 625 PLLCTRL0_SDIV2 | PLLCTRL0_VCO_GAIN | PLLCTRL0_S | 626 PLLCTRL0_CP_S | PLLCTRL0_CS | PLLCTRL0_PREDIV(clk_div) | 627 PLLCTRL0_VCO_S); 628 629 /* Setup display settings */ 630 if (bootverbose) 631 device_printf(dev, "HDMI: %s, Audio: %s\n", 632 sc->has_hdmi ? "yes" : "no", sc->has_audio ? "yes" : "no"); 633 val = 0; 634 if (sc->has_hdmi) 635 val |= VID_CTRL_HDMI_MODE; 636 if (mode->flags & VID_INTERLACE) 637 val |= VID_CTRL_INTERLACE; 638 if (mode->flags & VID_DBLSCAN) 639 val |= VID_CTRL_REPEATER_2X; 640 HDMI_WRITE(sc, HDMI_VID_CTRL, val); 641 642 /* Setup display timings */ 643 HDMI_WRITE(sc, HDMI_VID_TIMING0, 644 VID_ACT_V(mode->vdisplay) | VID_ACT_H(mode->hdisplay << dblscan)); 645 HDMI_WRITE(sc, HDMI_VID_TIMING1, 646 VID_VBP(vbp) | VID_HBP(hbp << dblscan)); 647 HDMI_WRITE(sc, HDMI_VID_TIMING2, 648 VID_VFP(vfp) | VID_HFP(hfp << dblscan)); 649 HDMI_WRITE(sc, HDMI_VID_TIMING3, 650 VID_VSPW(vspw) | VID_HSPW(hspw << dblscan)); 651 val = TX_CLOCK_NORMAL; 652 if (mode->flags & VID_PVSYNC) 653 val |= VID_VSYNC_ACTSEL; 654 if (mode->flags & VID_PHSYNC) 655 val |= VID_HSYNC_ACTSEL; 656 HDMI_WRITE(sc, HDMI_VID_TIMING4, val); 657 658 /* This is an ordered list of infoframe packets that the HDMI 659 * transmitter will send. Transmit packets in the following order: 660 * 1. General control packet 661 * 2. AVI infoframe 662 * 3. Audio infoframe 663 * There are 2 registers with 4 slots each. The list is terminated 664 * with the special PKT_END marker. 665 */ 666 HDMI_WRITE(sc, HDMI_PKTCTRL0, 667 PKTCTRL_PACKET(0, PKT_GC) | PKTCTRL_PACKET(1, PKT_AVI) | 668 PKTCTRL_PACKET(2, PKT_AI) | PKTCTRL_PACKET(3, PKT_END)); 669 HDMI_WRITE(sc, HDMI_PKTCTRL1, 0); 670 671 /* Setup audio */ 672 a10hdmi_set_audiomode(dev, mode); 673 674 return (0); 675} 676 677static int 678a10hdmi_enable(device_t dev, int onoff) 679{ 680 struct a10hdmi_softc *sc; 681 uint32_t val; 682 683 sc = device_get_softc(dev); 684 685 /* Enable or disable video output */ 686 val = HDMI_READ(sc, HDMI_VID_CTRL); 687 if (onoff) 688 val |= VID_CTRL_VIDEO_EN; 689 else 690 val &= ~VID_CTRL_VIDEO_EN; 691 HDMI_WRITE(sc, HDMI_VID_CTRL, val); 692 693 return (0); 694} 695 696static device_method_t a10hdmi_methods[] = { 697 /* Device interface */ 698 DEVMETHOD(device_probe, a10hdmi_probe), 699 DEVMETHOD(device_attach, a10hdmi_attach), 700 701 /* HDMI interface */ 702 DEVMETHOD(hdmi_get_edid, a10hdmi_get_edid), 703 DEVMETHOD(hdmi_set_videomode, a10hdmi_set_videomode), 704 DEVMETHOD(hdmi_enable, a10hdmi_enable), 705 706 DEVMETHOD_END 707}; 708 709static driver_t a10hdmi_driver = { 710 "a10hdmi", 711 a10hdmi_methods, 712 sizeof(struct a10hdmi_softc), 713}; 714 715static devclass_t a10hdmi_devclass; 716 717DRIVER_MODULE(a10hdmi, simplebus, a10hdmi_driver, a10hdmi_devclass, 0, 0); 718MODULE_VERSION(a10hdmi, 1); 719