# HG changeset patch # User Paul Boddie # Date 1590266057 -7200 # Node ID 5401b7ef73afacc71905ed9a8234ee790ac67086 # Parent 992d837eb2447b4606829125e0e58bdde0433ab8 Added support for detecting HDMI connector hotplug events. diff -r 992d837eb244 -r 5401b7ef73af pkg/devices/lib/hdmi/include/hdmi-jz4780.h --- a/pkg/devices/lib/hdmi/include/hdmi-jz4780.h Sat May 23 16:05:22 2020 +0200 +++ b/pkg/devices/lib/hdmi/include/hdmi-jz4780.h Sat May 23 22:34:17 2020 +0200 @@ -51,11 +51,13 @@ protected: void get_identification(); - void int_init(); + void irq_init(); void i2c_init(); long i2c_wait(); + void hotplug_init(); + public: Hdmi_jz4780_chip(l4_addr_t start, l4_addr_t end, l4_cap_idx_t irq); @@ -65,6 +67,9 @@ void i2c_set_address(uint8_t address); void i2c_set_segment(uint8_t segment); void i2c_set_register(uint8_t device_register); + + bool connected(); + long wait_for_connection(); }; #endif /* __cplusplus */ @@ -87,4 +92,8 @@ void jz4780_hdmi_i2c_set_register(void *hdmi, uint8_t device_register); +int jz4780_hdmi_connected(void *hdmi); + +long jz4780_hdmi_wait_for_connection(void *hdmi); + EXTERN_C_END diff -r 992d837eb244 -r 5401b7ef73af pkg/devices/lib/hdmi/src/jz4780.cc --- a/pkg/devices/lib/hdmi/src/jz4780.cc Sat May 23 16:05:22 2020 +0200 +++ b/pkg/devices/lib/hdmi/src/jz4780.cc Sat May 23 22:34:17 2020 +0200 @@ -96,17 +96,28 @@ I2c_phy_int_config0 = 0x3027, // PHY_I2CM_INT_ADDR I2c_phy_int_config1 = 0x3028, // PHY_I2CM_CTLINT_ADDR + + // PHY registers. + + Phy_config = 0x3000, // PHY_CONF0 + Phy_test0 = 0x3001, // PHY_TST0 + Phy_test1 = 0x3002, // PHY_TST1 + Phy_test2 = 0x3003, // PHY_TST2 + Phy_status = 0x3004, // PHY_STAT0 + Phy_int_config = 0x3005, // PHY_INT0 + Phy_mask = 0x3006, // PHY_MASK0 + Phy_polarity = 0x3007, // PHY_POL0 }; // Identification values. enum Product_id_values : unsigned { - Product_id0_transmitter = 0xa0, // PRODUCT_ID0_HDMI_TX + Product_id0_transmitter = 0xa0, // PRODUCT_ID0_HDMI_TX - Product_id1_hdcp = 0xc0, // PRODUCT_ID1_HDCP - Product_id1_receiver = 0x02, // PRODUCT_ID1_HDMI_RX - Product_id1_transmitter = 0x01, // PRODUCT_ID1_HDMI_TX + Product_id1_hdcp = 0xc0, // PRODUCT_ID1_HDCP + Product_id1_receiver = 0x02, // PRODUCT_ID1_HDMI_RX + Product_id1_transmitter = 0x01, // PRODUCT_ID1_HDMI_TX }; // Configuration values. @@ -134,39 +145,86 @@ enum Int_mask_bits : unsigned { - Int_mask_wakeup = 0x2, - Int_mask_all = 0x1, + Int_mask_wakeup = 0x02, + Int_mask_all = 0x01, }; enum I2c_int_status_bits : unsigned { - I2c_int_status_done = 0x2, - I2c_int_status_error = 0x1, + I2c_int_status_done = 0x02, + I2c_int_status_error = 0x01, }; // I2C operation bits. enum I2c_operation_bits : unsigned { - I2c_operation_write = 0x10, - I2c_operation_segment_read = 0x2, - I2c_operation_read = 0x1, + I2c_operation_write = 0x10, + I2c_operation_segment_read = 0x02, + I2c_operation_read = 0x01, }; // Interrupt configuration bits. enum I2c_int_config0_bits : unsigned { - I2c_int_config_done_polarity = 0x8, - I2c_int_config_done_mask = 0x4, + I2c_int_config0_done_polarity = 0x08, + I2c_int_config0_done_mask = 0x04, }; enum I2c_int_config1_bits : unsigned { - I2c_int_config_nack_polarity = 0x80, - I2c_int_config_nack_mask = 0x40, - I2c_int_config_arb_polarity = 0x8, - I2c_int_config_arb_mask = 0x4, + I2c_int_config1_nack_polarity = 0x80, + I2c_int_config1_nack_mask = 0x40, + I2c_int_config1_arb_polarity = 0x08, + I2c_int_config1_arb_mask = 0x04, +}; + +// PHY configuration values. + +enum Phy_config_bits : unsigned +{ + Phy_config_pdz_mask = 0x80, // PHY_CONF0_PDZ_MASK + Phy_config_enable_tmds_mask = 0x40, // PHY_CONF0_ENTMDS_MASK + Phy_config_svsret_mask = 0x20, // PHY_CONF0_SVSRET_MASK + Phy_config_gen2_pddq_mask = 0x10, // PHY_CONF0_GEN2_PDDQ_MASK + Phy_config_gen2_tx_power_on_mask = 0x08, // PHY_CONF0_GEN2_TXPWRON_MASK + Phy_config_gen2_enable_hotplug_detect_rx_sense_mask = 0x04, // PHY_CONF0_GEN2_ENHPDRXSENSE_MASK + Phy_config_select_data_enable_polarity_mask = 0x02, // PHY_CONF0_SELDATAENPOL_MASK + Phy_config_select_interface_control_mask = 0x01, // PHY_CONF0_SELDIPIF_MASK +}; + +enum Phy_test_bits : unsigned +{ + Phy_test0_clear_mask = 0x20, // PHY_TST0_TSTCLR_MASK + Phy_test0_enable_mask = 0x10, // PHY_TST0_TSTEN_MASK + Phy_test0_clock_mask = 0x01, // PHY_TST0_TSTCLK_MASK +}; + +// PHY status and mask values. + +enum Phy_status_bits : unsigned +{ + Phy_status_rx_sense_all = 0xf0, + Phy_status_rx_sense3 = 0x80, // PHY_RX_SENSE3 + Phy_status_rx_sense2 = 0x40, // PHY_RX_SENSE2 + Phy_status_rx_sense1 = 0x20, // PHY_RX_SENSE1 + Phy_status_rx_sense0 = 0x10, // PHY_RX_SENSE0 + Phy_status_hotplug_detect = 0x02, // PHY_HPD + Phy_status_tx_phy_lock = 0x01, // PHY_TX_PHY_LOCK +}; + +// PHY interrupt status and mask bits. + +enum Phy_int_status_bits : unsigned +{ + Phy_int_status_rx_sense_all = 0x3c, + Phy_int_status_rx_sense3 = 0x20, // IH_PHY_STAT0_RX_SENSE3 + Phy_int_status_rx_sense2 = 0x10, // IH_PHY_STAT0_RX_SENSE2 + Phy_int_status_rx_sense1 = 0x08, // IH_PHY_STAT0_RX_SENSE1 + Phy_int_status_rx_sense0 = 0x04, // IH_PHY_STAT0_RX_SENSE0 + Phy_int_status_tx_phy_lock = 0x02, // IH_PHY_STAT0_TX_PHY_LOCK + Phy_int_status_hotplug_detect = 0x01, // IH_PHY_STAT0_HPD }; @@ -185,8 +243,9 @@ _device_register = 0; get_identification(); - int_init(); + irq_init(); i2c_init(); + hotplug_init(); } void Hdmi_jz4780_chip::get_identification() @@ -200,7 +259,7 @@ *minor = _version & 0xfff; } -void Hdmi_jz4780_chip::int_init() +void Hdmi_jz4780_chip::irq_init() { // Disable interrupts. @@ -226,11 +285,11 @@ void Hdmi_jz4780_chip::i2c_init() { - // Set PHY interrupt priorities. + // Set PHY interrupt polarities. - _regs[I2c_phy_int_config0] = I2c_int_config_done_polarity; - _regs[I2c_phy_int_config1] = I2c_int_config_nack_polarity | - I2c_int_config_arb_polarity; + _regs[I2c_phy_int_config0] = I2c_int_config0_done_polarity; + _regs[I2c_phy_int_config1] = I2c_int_config1_nack_polarity | + I2c_int_config1_arb_polarity; // Software reset. @@ -242,9 +301,9 @@ // Set interrupt polarities. - _regs[I2c_int_config0] = I2c_int_config_done_polarity; - _regs[I2c_int_config1] = I2c_int_config_nack_polarity | - I2c_int_config_arb_polarity; + _regs[I2c_int_config0] = I2c_int_config0_done_polarity; + _regs[I2c_int_config1] = I2c_int_config1_nack_polarity | + I2c_int_config1_arb_polarity; // Clear and mask/mute interrupts. @@ -255,21 +314,29 @@ long Hdmi_jz4780_chip::i2c_wait() { long err; + uint8_t int_status; + l4_msgtag_t tag; - // Wait for around 1s. + do + { + tag = l4_irq_receive(_irq, L4_IPC_NEVER); - l4_msgtag_t tag = l4_irq_receive(_irq, L4_IPC_NEVER); + err = l4_ipc_error(tag, l4_utcb()); + if (err) + return err; - err = l4_ipc_error(tag, l4_utcb()); - if (err) - return err; + int_status = _regs[I2c_int_status]; + + // Test for an error condition. - // Test for an error condition. + if (int_status & I2c_int_status_error) + return -L4_EIO; - if (_regs[I2c_int_status] & I2c_int_status_error) - return -L4_EIO; + // Acknowledge the interrupt. - _regs[I2c_int_status] = _regs[I2c_int_status] | I2c_int_status_done; + _regs[I2c_int_status] = int_status; + + } while (!(int_status & I2c_int_status_done)); return L4_EOK; } @@ -279,7 +346,7 @@ unsigned int i; long err; - // Clear interrupts. + // Unmask interrupts. _regs[I2c_int_mask] = 0; @@ -327,6 +394,61 @@ _device_register = device_register; } +void Hdmi_jz4780_chip::hotplug_init() +{ + // Set PHY interrupt polarities. + + _regs[Phy_polarity] = Phy_status_hotplug_detect | Phy_status_rx_sense_all; + + // Enable/unmask second-level interrupts. + + _regs[Phy_mask] = _regs[Phy_mask] & ~(Phy_status_hotplug_detect | Phy_status_rx_sense_all); + + // Clear pending interrupts. + + _regs[Phy_int_status] = Phy_int_status_hotplug_detect | Phy_int_status_rx_sense_all; + + // Enable/unmask interrupts. + + _regs[Phy_int_mask] = _regs[Phy_int_mask] & ~(Phy_int_status_hotplug_detect | Phy_int_status_rx_sense_all); +} + +bool Hdmi_jz4780_chip::connected() +{ + return (_regs[Phy_status] & Phy_status_hotplug_detect) != 0; +} + +long Hdmi_jz4780_chip::wait_for_connection() +{ + long err; + uint8_t int_status, polarity; + l4_msgtag_t tag; + + do + { + tag = l4_irq_receive(_irq, L4_IPC_NEVER); + + err = l4_ipc_error(tag, l4_utcb()); + if (err) + return err; + + // Obtain the details. + + int_status = _regs[Phy_int_status]; + polarity = _regs[Phy_polarity]; + + // Acknowledge the interrupt. + + _regs[Phy_int_status] = int_status; + + // Continue without a hotplug event indicating connection. + + } while (!((int_status & Phy_int_status_hotplug_detect) && + (polarity & Phy_status_hotplug_detect))); + + return L4_EOK; +} + // C language interface functions. @@ -360,3 +482,13 @@ { static_cast(hdmi)->i2c_set_register(device_register); } + +int jz4780_hdmi_connected(void *hdmi) +{ + return (int) static_cast(hdmi)->connected(); +} + +long jz4780_hdmi_wait_for_connection(void *hdmi) +{ + return static_cast(hdmi)->wait_for_connection(); +} diff -r 992d837eb244 -r 5401b7ef73af pkg/landfall-examples/ci20_hdmi_i2c/ci20_hdmi_i2c.c --- a/pkg/landfall-examples/ci20_hdmi_i2c/ci20_hdmi_i2c.c Sat May 23 16:05:22 2020 +0200 +++ b/pkg/landfall-examples/ci20_hdmi_i2c/ci20_hdmi_i2c.c Sat May 23 22:34:17 2020 +0200 @@ -1,14 +1,22 @@ -/* - * (c) 2008-2009 Adam Lackorzynski - * economic rights: Technische Universität Dresden (Germany) - * Copyright (C) 2017, 2018, 2019, 2020 Paul Boddie - * - * This file is part of TUD:OS and distributed under the terms of the - * GNU General Public License 2. - * Please see the COPYING-GPL-2 file for details. - */ /* * Access the HDMI I2C peripheral on the MIPS Creator CI20 board. + * + * Copyright (C) 2020 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA */ #include @@ -265,6 +273,11 @@ printf("HDMI version is %x.%03x\n", hdmi_major, hdmi_minor); + printf("Connected: %s\n", jz4780_hdmi_connected(hdmi) ? "yes" : "no"); + + while (!jz4780_hdmi_connected(hdmi)) + jz4780_hdmi_wait_for_connection(hdmi); + printf("Read EDID...\n"); jz4780_hdmi_i2c_set_address(hdmi, 0x50);