paul@62 | 1 | /* |
paul@62 | 2 | * JZ4780 HDMI peripheral support. |
paul@62 | 3 | * |
paul@62 | 4 | * Copyright (C) 2020 Paul Boddie <paul@boddie.org.uk> |
paul@62 | 5 | * |
paul@62 | 6 | * This program is free software; you can redistribute it and/or |
paul@62 | 7 | * modify it under the terms of the GNU General Public License as |
paul@62 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@62 | 9 | * the License, or (at your option) any later version. |
paul@62 | 10 | * |
paul@62 | 11 | * This program is distributed in the hope that it will be useful, |
paul@62 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@62 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@62 | 14 | * GNU General Public License for more details. |
paul@62 | 15 | * |
paul@62 | 16 | * You should have received a copy of the GNU General Public License |
paul@62 | 17 | * along with this program; if not, write to the Free Software |
paul@62 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@62 | 19 | * Boston, MA 02110-1301, USA |
paul@62 | 20 | */ |
paul@62 | 21 | |
paul@62 | 22 | #include <l4/devices/hdmi-jz4780.h> |
paul@62 | 23 | #include <l4/devices/hw_mmio_register_block.h> |
paul@62 | 24 | |
paul@62 | 25 | #include <l4/sys/irq.h> |
paul@62 | 26 | #include <l4/util/util.h> |
paul@62 | 27 | |
paul@62 | 28 | #include <cstdio> |
paul@62 | 29 | |
paul@62 | 30 | /* |
paul@62 | 31 | I2C pins: |
paul@62 | 32 | |
paul@62 | 33 | HDMI: PF25/SMB4_SDA/DDCSDA, PF24/SMB4_SCK/DDCSCK |
paul@62 | 34 | |
paul@62 | 35 | See: http://mipscreator.imgtec.com/CI20/hardware/board/ci20_jz4780_v2.0.pdf |
paul@62 | 36 | */ |
paul@62 | 37 | |
paul@62 | 38 | enum Regs |
paul@62 | 39 | { |
paul@62 | 40 | // Identification. |
paul@62 | 41 | |
paul@62 | 42 | Design_id = 0x000, // DESIGN_ID |
paul@62 | 43 | Revision_id = 0x001, // REVISION_ID |
paul@62 | 44 | Product_id0 = 0x002, // PRODUCT_ID0 |
paul@62 | 45 | Product_id1 = 0x003, // PRODUCT_ID1 |
paul@62 | 46 | Config_id0 = 0x004, // CONFIG_ID0 |
paul@62 | 47 | Config_id1 = 0x005, // CONFIG_ID1 |
paul@62 | 48 | Config_id2 = 0x006, // CONFIG_ID2 |
paul@62 | 49 | Config_id3 = 0x007, // CONFIG_ID3 |
paul@62 | 50 | |
paul@62 | 51 | // Top-level interrupt control. |
paul@62 | 52 | |
paul@62 | 53 | Int_mask = 0x1ff, // MUTE |
paul@62 | 54 | |
paul@62 | 55 | // Interrupt status and mask for various functions. |
paul@62 | 56 | |
paul@62 | 57 | Fc_int_status0 = 0x100, // FC_STAT0 |
paul@62 | 58 | Fc_int_status1 = 0x101, // FC_STAT1 |
paul@62 | 59 | Fc_int_status2 = 0x102, // FC_STAT2 |
paul@62 | 60 | As_int_status = 0x103, // AS_STAT0 |
paul@62 | 61 | Phy_int_status = 0x104, // PHY_STAT0 |
paul@62 | 62 | Cec_int_status = 0x106, // CEC_STAT0 |
paul@62 | 63 | Vp_int_status = 0x107, // VP_STAT0 |
paul@62 | 64 | Ahb_dma_audio_int_status = 0x109, // AHBDMAAUD_STAT0 |
paul@62 | 65 | |
paul@62 | 66 | Fc_int_mask0 = 0x180, // MUTE_FC_STAT0 |
paul@62 | 67 | Fc_int_mask1 = 0x181, // MUTE_FC_STAT1 |
paul@62 | 68 | Fc_int_mask2 = 0x182, // MUTE_FC_STAT2 |
paul@62 | 69 | As_int_mask = 0x183, // MUTE_AS_STAT0 |
paul@62 | 70 | Phy_int_mask = 0x184, // MUTE_PHY_STAT0 |
paul@62 | 71 | Cec_int_mask = 0x186, // MUTE_CEC_STAT0 |
paul@62 | 72 | Vp_int_mask = 0x187, // MUTE_VP_STAT0 |
paul@62 | 73 | Ahb_dma_audio_int_mask = 0x189, // MUTE_AHBDMAAUD_STAT0 |
paul@62 | 74 | |
paul@62 | 75 | // I2C for E-DDC. |
paul@62 | 76 | |
paul@62 | 77 | I2c_int_status = 0x105, // I2CM_STAT0 |
paul@62 | 78 | I2c_int_mask = 0x185, // MUTE_I2CM_STAT0 |
paul@62 | 79 | |
paul@62 | 80 | I2c_device_address = 0x7e00, // I2CM_SLAVE |
paul@62 | 81 | I2c_register = 0x7e01, // I2CM_ADDRESS |
paul@62 | 82 | I2c_data_out = 0x7e02, // I2CM_DATAO |
paul@62 | 83 | I2c_data_in = 0x7e03, // I2CM_DATAI |
paul@62 | 84 | I2c_operation = 0x7e04, // I2CM_OPERATION |
paul@62 | 85 | I2c_int_config0 = 0x7e05, // I2CM_INT |
paul@62 | 86 | I2c_int_config1 = 0x7e06, // I2CM_CTLINT |
paul@62 | 87 | I2c_divider = 0x7e07, // I2CM_DIV |
paul@62 | 88 | I2c_segment_address = 0x7e08, // I2CM_SEGADDR |
paul@62 | 89 | I2c_software_reset = 0x7e09, // I2CM_SOFTRSTZ |
paul@62 | 90 | I2c_segment_pointer = 0x7e0a, // I2CM_SEGPTR |
paul@62 | 91 | |
paul@62 | 92 | // I2C for PHY. |
paul@62 | 93 | |
paul@62 | 94 | I2c_phy_int_status = 0x108, // I2CMPHY_STAT0 |
paul@62 | 95 | I2c_phy_int_mask = 0x188, // MUTE_I2CMPHY_STAT0 |
paul@62 | 96 | |
paul@62 | 97 | I2c_phy_int_config0 = 0x3027, // PHY_I2CM_INT_ADDR |
paul@62 | 98 | I2c_phy_int_config1 = 0x3028, // PHY_I2CM_CTLINT_ADDR |
paul@65 | 99 | |
paul@65 | 100 | // PHY registers. |
paul@65 | 101 | |
paul@65 | 102 | Phy_config = 0x3000, // PHY_CONF0 |
paul@65 | 103 | Phy_test0 = 0x3001, // PHY_TST0 |
paul@65 | 104 | Phy_test1 = 0x3002, // PHY_TST1 |
paul@65 | 105 | Phy_test2 = 0x3003, // PHY_TST2 |
paul@65 | 106 | Phy_status = 0x3004, // PHY_STAT0 |
paul@65 | 107 | Phy_int_config = 0x3005, // PHY_INT0 |
paul@65 | 108 | Phy_mask = 0x3006, // PHY_MASK0 |
paul@65 | 109 | Phy_polarity = 0x3007, // PHY_POL0 |
paul@62 | 110 | }; |
paul@62 | 111 | |
paul@62 | 112 | // Identification values. |
paul@62 | 113 | |
paul@62 | 114 | enum Product_id_values : unsigned |
paul@62 | 115 | { |
paul@65 | 116 | Product_id0_transmitter = 0xa0, // PRODUCT_ID0_HDMI_TX |
paul@62 | 117 | |
paul@65 | 118 | Product_id1_hdcp = 0xc0, // PRODUCT_ID1_HDCP |
paul@65 | 119 | Product_id1_receiver = 0x02, // PRODUCT_ID1_HDMI_RX |
paul@65 | 120 | Product_id1_transmitter = 0x01, // PRODUCT_ID1_HDMI_TX |
paul@62 | 121 | }; |
paul@62 | 122 | |
paul@62 | 123 | // Configuration values. |
paul@62 | 124 | |
paul@62 | 125 | enum Config_id_values : unsigned |
paul@62 | 126 | { |
paul@62 | 127 | Config_id0_i2s = 0x10, // CONFIG0_I2S |
paul@62 | 128 | Config_id0_cec = 0x02, // CONFIG0_CEC |
paul@62 | 129 | |
paul@62 | 130 | Config_id1_ahb = 0x01, // CONFIG1_AHB |
paul@62 | 131 | |
paul@62 | 132 | Config2_dwc_hdmi_tx_phy = 0x00, // DWC_HDMI_TX_PHY |
paul@62 | 133 | Config2_dwc_mhl_phy_heac = 0xb2, // DWC_MHL_PHY_HEAC |
paul@62 | 134 | Config2_dwc_mhl_phy = 0xc2, // DWC_MHL_PHY |
paul@62 | 135 | Config2_dwc_hdmi_3d_tx_phy_heac = 0xe2, // DWC_HDMI_3D_TX_PHY_HEAC |
paul@62 | 136 | Config2_dwc_hdmi_3d_tx_phy = 0xf2, // DWC_HDMI_3D_TX_PHY |
paul@62 | 137 | Config2_dwc_hdmi20_tx_phy = 0xf3, // DWC_HDMI20_TX_PHY |
paul@62 | 138 | Config2_vendor_phy = 0xfe, // VENDOR_PHY |
paul@62 | 139 | |
paul@62 | 140 | Config_id3_ahb_audio_dma = 0x02, // CONFIG3_AHBAUDDMA |
paul@62 | 141 | Config_id3_gp_audio = 0x01, // CONFIG3_GPAUD |
paul@62 | 142 | }; |
paul@62 | 143 | |
paul@62 | 144 | // Status and mask bits. |
paul@62 | 145 | |
paul@62 | 146 | enum Int_mask_bits : unsigned |
paul@62 | 147 | { |
paul@65 | 148 | Int_mask_wakeup = 0x02, |
paul@65 | 149 | Int_mask_all = 0x01, |
paul@62 | 150 | }; |
paul@62 | 151 | |
paul@62 | 152 | enum I2c_int_status_bits : unsigned |
paul@62 | 153 | { |
paul@65 | 154 | I2c_int_status_done = 0x02, |
paul@65 | 155 | I2c_int_status_error = 0x01, |
paul@62 | 156 | }; |
paul@62 | 157 | |
paul@62 | 158 | // I2C operation bits. |
paul@62 | 159 | |
paul@62 | 160 | enum I2c_operation_bits : unsigned |
paul@62 | 161 | { |
paul@65 | 162 | I2c_operation_write = 0x10, |
paul@65 | 163 | I2c_operation_segment_read = 0x02, |
paul@65 | 164 | I2c_operation_read = 0x01, |
paul@62 | 165 | }; |
paul@62 | 166 | |
paul@62 | 167 | // Interrupt configuration bits. |
paul@62 | 168 | |
paul@62 | 169 | enum I2c_int_config0_bits : unsigned |
paul@62 | 170 | { |
paul@65 | 171 | I2c_int_config0_done_polarity = 0x08, |
paul@65 | 172 | I2c_int_config0_done_mask = 0x04, |
paul@62 | 173 | }; |
paul@62 | 174 | |
paul@62 | 175 | enum I2c_int_config1_bits : unsigned |
paul@62 | 176 | { |
paul@65 | 177 | I2c_int_config1_nack_polarity = 0x80, |
paul@65 | 178 | I2c_int_config1_nack_mask = 0x40, |
paul@65 | 179 | I2c_int_config1_arb_polarity = 0x08, |
paul@65 | 180 | I2c_int_config1_arb_mask = 0x04, |
paul@65 | 181 | }; |
paul@65 | 182 | |
paul@65 | 183 | // PHY configuration values. |
paul@65 | 184 | |
paul@65 | 185 | enum Phy_config_bits : unsigned |
paul@65 | 186 | { |
paul@65 | 187 | Phy_config_pdz_mask = 0x80, // PHY_CONF0_PDZ_MASK |
paul@65 | 188 | Phy_config_enable_tmds_mask = 0x40, // PHY_CONF0_ENTMDS_MASK |
paul@65 | 189 | Phy_config_svsret_mask = 0x20, // PHY_CONF0_SVSRET_MASK |
paul@65 | 190 | Phy_config_gen2_pddq_mask = 0x10, // PHY_CONF0_GEN2_PDDQ_MASK |
paul@65 | 191 | Phy_config_gen2_tx_power_on_mask = 0x08, // PHY_CONF0_GEN2_TXPWRON_MASK |
paul@65 | 192 | Phy_config_gen2_enable_hotplug_detect_rx_sense_mask = 0x04, // PHY_CONF0_GEN2_ENHPDRXSENSE_MASK |
paul@65 | 193 | Phy_config_select_data_enable_polarity_mask = 0x02, // PHY_CONF0_SELDATAENPOL_MASK |
paul@65 | 194 | Phy_config_select_interface_control_mask = 0x01, // PHY_CONF0_SELDIPIF_MASK |
paul@65 | 195 | }; |
paul@65 | 196 | |
paul@65 | 197 | enum Phy_test_bits : unsigned |
paul@65 | 198 | { |
paul@65 | 199 | Phy_test0_clear_mask = 0x20, // PHY_TST0_TSTCLR_MASK |
paul@65 | 200 | Phy_test0_enable_mask = 0x10, // PHY_TST0_TSTEN_MASK |
paul@65 | 201 | Phy_test0_clock_mask = 0x01, // PHY_TST0_TSTCLK_MASK |
paul@65 | 202 | }; |
paul@65 | 203 | |
paul@65 | 204 | // PHY status and mask values. |
paul@65 | 205 | |
paul@65 | 206 | enum Phy_status_bits : unsigned |
paul@65 | 207 | { |
paul@65 | 208 | Phy_status_rx_sense_all = 0xf0, |
paul@65 | 209 | Phy_status_rx_sense3 = 0x80, // PHY_RX_SENSE3 |
paul@65 | 210 | Phy_status_rx_sense2 = 0x40, // PHY_RX_SENSE2 |
paul@65 | 211 | Phy_status_rx_sense1 = 0x20, // PHY_RX_SENSE1 |
paul@65 | 212 | Phy_status_rx_sense0 = 0x10, // PHY_RX_SENSE0 |
paul@65 | 213 | Phy_status_hotplug_detect = 0x02, // PHY_HPD |
paul@65 | 214 | Phy_status_tx_phy_lock = 0x01, // PHY_TX_PHY_LOCK |
paul@65 | 215 | }; |
paul@65 | 216 | |
paul@65 | 217 | // PHY interrupt status and mask bits. |
paul@65 | 218 | |
paul@65 | 219 | enum Phy_int_status_bits : unsigned |
paul@65 | 220 | { |
paul@65 | 221 | Phy_int_status_rx_sense_all = 0x3c, |
paul@65 | 222 | Phy_int_status_rx_sense3 = 0x20, // IH_PHY_STAT0_RX_SENSE3 |
paul@65 | 223 | Phy_int_status_rx_sense2 = 0x10, // IH_PHY_STAT0_RX_SENSE2 |
paul@65 | 224 | Phy_int_status_rx_sense1 = 0x08, // IH_PHY_STAT0_RX_SENSE1 |
paul@65 | 225 | Phy_int_status_rx_sense0 = 0x04, // IH_PHY_STAT0_RX_SENSE0 |
paul@65 | 226 | Phy_int_status_tx_phy_lock = 0x02, // IH_PHY_STAT0_TX_PHY_LOCK |
paul@65 | 227 | Phy_int_status_hotplug_detect = 0x01, // IH_PHY_STAT0_HPD |
paul@62 | 228 | }; |
paul@62 | 229 | |
paul@62 | 230 | |
paul@62 | 231 | |
paul@62 | 232 | // Initialise the HDMI peripheral. |
paul@62 | 233 | |
paul@62 | 234 | Hdmi_jz4780_chip::Hdmi_jz4780_chip(l4_addr_t start, l4_addr_t end, |
paul@62 | 235 | l4_cap_idx_t irq) |
paul@62 | 236 | : _start(start), _end(end), _irq(irq) |
paul@62 | 237 | { |
paul@62 | 238 | // 8-bit registers with 2-bit address shifting. |
paul@62 | 239 | |
paul@62 | 240 | _regs = new Hw::Mmio_register_block<8>(start, 2); |
paul@62 | 241 | |
paul@62 | 242 | _segment_read = false; |
paul@62 | 243 | _device_register = 0; |
paul@62 | 244 | |
paul@62 | 245 | get_identification(); |
paul@65 | 246 | irq_init(); |
paul@62 | 247 | i2c_init(); |
paul@65 | 248 | hotplug_init(); |
paul@62 | 249 | } |
paul@62 | 250 | |
paul@62 | 251 | void Hdmi_jz4780_chip::get_identification() |
paul@62 | 252 | { |
paul@62 | 253 | _version = (_regs[Design_id] << 8) | _regs[Revision_id]; |
paul@62 | 254 | } |
paul@62 | 255 | |
paul@62 | 256 | void Hdmi_jz4780_chip::get_version(uint8_t *major, uint16_t *minor) |
paul@62 | 257 | { |
paul@62 | 258 | *major = _version >> 12; |
paul@62 | 259 | *minor = _version & 0xfff; |
paul@62 | 260 | } |
paul@62 | 261 | |
paul@65 | 262 | void Hdmi_jz4780_chip::irq_init() |
paul@62 | 263 | { |
paul@62 | 264 | // Disable interrupts. |
paul@62 | 265 | |
paul@62 | 266 | _regs[Int_mask] = _regs[Int_mask] | (Int_mask_wakeup | Int_mask_all); |
paul@62 | 267 | |
paul@62 | 268 | // Mask all interrupts. |
paul@62 | 269 | |
paul@62 | 270 | _regs[Fc_int_mask0] = 0xff; |
paul@62 | 271 | _regs[Fc_int_mask1] = 0xff; |
paul@62 | 272 | _regs[Fc_int_mask2] = 0xff; |
paul@62 | 273 | _regs[As_int_mask] = 0xff; |
paul@62 | 274 | _regs[Phy_int_mask] = 0xff; |
paul@62 | 275 | _regs[I2c_int_mask] = 0xff; |
paul@62 | 276 | _regs[Cec_int_mask] = 0xff; |
paul@62 | 277 | _regs[Vp_int_mask] = 0xff; |
paul@62 | 278 | _regs[I2c_phy_int_mask] = 0xff; |
paul@62 | 279 | _regs[Ahb_dma_audio_int_mask] = 0xff; |
paul@62 | 280 | |
paul@62 | 281 | // Enable interrupts. |
paul@62 | 282 | |
paul@62 | 283 | _regs[Int_mask] = _regs[Int_mask] & ~(Int_mask_wakeup | Int_mask_all); |
paul@62 | 284 | } |
paul@62 | 285 | |
paul@62 | 286 | void Hdmi_jz4780_chip::i2c_init() |
paul@62 | 287 | { |
paul@65 | 288 | // Set PHY interrupt polarities. |
paul@62 | 289 | |
paul@65 | 290 | _regs[I2c_phy_int_config0] = I2c_int_config0_done_polarity; |
paul@65 | 291 | _regs[I2c_phy_int_config1] = I2c_int_config1_nack_polarity | |
paul@65 | 292 | I2c_int_config1_arb_polarity; |
paul@62 | 293 | |
paul@62 | 294 | // Software reset. |
paul@62 | 295 | |
paul@62 | 296 | _regs[I2c_software_reset] = 0; |
paul@62 | 297 | |
paul@62 | 298 | // Standard mode (100kHz). |
paul@62 | 299 | |
paul@62 | 300 | _regs[I2c_divider] = 0; |
paul@62 | 301 | |
paul@62 | 302 | // Set interrupt polarities. |
paul@62 | 303 | |
paul@65 | 304 | _regs[I2c_int_config0] = I2c_int_config0_done_polarity; |
paul@65 | 305 | _regs[I2c_int_config1] = I2c_int_config1_nack_polarity | |
paul@65 | 306 | I2c_int_config1_arb_polarity; |
paul@62 | 307 | |
paul@62 | 308 | // Clear and mask/mute interrupts. |
paul@62 | 309 | |
paul@62 | 310 | _regs[I2c_int_status] = I2c_int_status_done | I2c_int_status_error; |
paul@62 | 311 | _regs[I2c_int_mask] = I2c_int_status_done | I2c_int_status_error; |
paul@62 | 312 | } |
paul@62 | 313 | |
paul@62 | 314 | long Hdmi_jz4780_chip::i2c_wait() |
paul@62 | 315 | { |
paul@62 | 316 | long err; |
paul@65 | 317 | uint8_t int_status; |
paul@65 | 318 | l4_msgtag_t tag; |
paul@62 | 319 | |
paul@65 | 320 | do |
paul@65 | 321 | { |
paul@65 | 322 | tag = l4_irq_receive(_irq, L4_IPC_NEVER); |
paul@62 | 323 | |
paul@65 | 324 | err = l4_ipc_error(tag, l4_utcb()); |
paul@65 | 325 | if (err) |
paul@65 | 326 | return err; |
paul@62 | 327 | |
paul@65 | 328 | int_status = _regs[I2c_int_status]; |
paul@65 | 329 | |
paul@65 | 330 | // Test for an error condition. |
paul@62 | 331 | |
paul@65 | 332 | if (int_status & I2c_int_status_error) |
paul@65 | 333 | return -L4_EIO; |
paul@62 | 334 | |
paul@65 | 335 | // Acknowledge the interrupt. |
paul@62 | 336 | |
paul@65 | 337 | _regs[I2c_int_status] = int_status; |
paul@65 | 338 | |
paul@65 | 339 | } while (!(int_status & I2c_int_status_done)); |
paul@62 | 340 | |
paul@62 | 341 | return L4_EOK; |
paul@62 | 342 | } |
paul@62 | 343 | |
paul@62 | 344 | int Hdmi_jz4780_chip::i2c_read(uint8_t *buf, unsigned int length) |
paul@62 | 345 | { |
paul@62 | 346 | unsigned int i; |
paul@62 | 347 | long err; |
paul@62 | 348 | |
paul@65 | 349 | // Unmask interrupts. |
paul@62 | 350 | |
paul@62 | 351 | _regs[I2c_int_mask] = 0; |
paul@62 | 352 | |
paul@62 | 353 | for (i = 0; i < length; i++) |
paul@62 | 354 | { |
paul@62 | 355 | // Increment the device register. |
paul@62 | 356 | |
paul@62 | 357 | _regs[I2c_register] = _device_register++; |
paul@62 | 358 | _regs[I2c_operation] = _segment_read ? I2c_operation_segment_read |
paul@62 | 359 | : I2c_operation_read; |
paul@62 | 360 | |
paul@62 | 361 | // Wait and then read. |
paul@62 | 362 | |
paul@62 | 363 | err = i2c_wait(); |
paul@62 | 364 | if (err) |
paul@62 | 365 | break; |
paul@62 | 366 | |
paul@62 | 367 | buf[i] = _regs[I2c_data_in]; |
paul@62 | 368 | } |
paul@62 | 369 | |
paul@62 | 370 | // Mask interrupts again. |
paul@62 | 371 | |
paul@62 | 372 | _regs[I2c_int_mask] = I2c_int_status_done | I2c_int_status_error; |
paul@62 | 373 | |
paul@62 | 374 | return i; |
paul@62 | 375 | } |
paul@62 | 376 | |
paul@62 | 377 | void Hdmi_jz4780_chip::i2c_set_address(uint8_t address) |
paul@62 | 378 | { |
paul@62 | 379 | _regs[I2c_device_address] = address; |
paul@62 | 380 | _segment_read = false; |
paul@62 | 381 | i2c_set_register(0); |
paul@62 | 382 | } |
paul@62 | 383 | |
paul@62 | 384 | void Hdmi_jz4780_chip::i2c_set_segment(uint8_t segment) |
paul@62 | 385 | { |
paul@62 | 386 | _regs[I2c_segment_address] = 0x30; |
paul@62 | 387 | _regs[I2c_segment_pointer] = segment; |
paul@62 | 388 | _segment_read = true; |
paul@62 | 389 | i2c_set_register(0); |
paul@62 | 390 | } |
paul@62 | 391 | |
paul@62 | 392 | void Hdmi_jz4780_chip::i2c_set_register(uint8_t device_register) |
paul@62 | 393 | { |
paul@62 | 394 | _device_register = device_register; |
paul@62 | 395 | } |
paul@62 | 396 | |
paul@65 | 397 | void Hdmi_jz4780_chip::hotplug_init() |
paul@65 | 398 | { |
paul@65 | 399 | // Set PHY interrupt polarities. |
paul@65 | 400 | |
paul@65 | 401 | _regs[Phy_polarity] = Phy_status_hotplug_detect | Phy_status_rx_sense_all; |
paul@65 | 402 | |
paul@65 | 403 | // Enable/unmask second-level interrupts. |
paul@65 | 404 | |
paul@65 | 405 | _regs[Phy_mask] = _regs[Phy_mask] & ~(Phy_status_hotplug_detect | Phy_status_rx_sense_all); |
paul@65 | 406 | |
paul@65 | 407 | // Clear pending interrupts. |
paul@65 | 408 | |
paul@65 | 409 | _regs[Phy_int_status] = Phy_int_status_hotplug_detect | Phy_int_status_rx_sense_all; |
paul@65 | 410 | |
paul@65 | 411 | // Enable/unmask interrupts. |
paul@65 | 412 | |
paul@65 | 413 | _regs[Phy_int_mask] = _regs[Phy_int_mask] & ~(Phy_int_status_hotplug_detect | Phy_int_status_rx_sense_all); |
paul@65 | 414 | } |
paul@65 | 415 | |
paul@65 | 416 | bool Hdmi_jz4780_chip::connected() |
paul@65 | 417 | { |
paul@65 | 418 | return (_regs[Phy_status] & Phy_status_hotplug_detect) != 0; |
paul@65 | 419 | } |
paul@65 | 420 | |
paul@65 | 421 | long Hdmi_jz4780_chip::wait_for_connection() |
paul@65 | 422 | { |
paul@65 | 423 | long err; |
paul@65 | 424 | uint8_t int_status, polarity; |
paul@65 | 425 | l4_msgtag_t tag; |
paul@65 | 426 | |
paul@65 | 427 | do |
paul@65 | 428 | { |
paul@65 | 429 | tag = l4_irq_receive(_irq, L4_IPC_NEVER); |
paul@65 | 430 | |
paul@65 | 431 | err = l4_ipc_error(tag, l4_utcb()); |
paul@65 | 432 | if (err) |
paul@65 | 433 | return err; |
paul@65 | 434 | |
paul@65 | 435 | // Obtain the details. |
paul@65 | 436 | |
paul@65 | 437 | int_status = _regs[Phy_int_status]; |
paul@65 | 438 | polarity = _regs[Phy_polarity]; |
paul@65 | 439 | |
paul@65 | 440 | // Acknowledge the interrupt. |
paul@65 | 441 | |
paul@65 | 442 | _regs[Phy_int_status] = int_status; |
paul@65 | 443 | |
paul@65 | 444 | // Continue without a hotplug event indicating connection. |
paul@65 | 445 | |
paul@65 | 446 | } while (!((int_status & Phy_int_status_hotplug_detect) && |
paul@65 | 447 | (polarity & Phy_status_hotplug_detect))); |
paul@65 | 448 | |
paul@65 | 449 | return L4_EOK; |
paul@65 | 450 | } |
paul@65 | 451 | |
paul@62 | 452 | |
paul@62 | 453 | |
paul@62 | 454 | // C language interface functions. |
paul@62 | 455 | |
paul@62 | 456 | void *jz4780_hdmi_init(l4_addr_t start, l4_addr_t end, l4_cap_idx_t irq) |
paul@62 | 457 | { |
paul@62 | 458 | return (void *) new Hdmi_jz4780_chip(start, end, irq); |
paul@62 | 459 | } |
paul@62 | 460 | |
paul@62 | 461 | void jz4780_hdmi_get_version(void *hdmi, uint8_t *major, uint16_t *minor) |
paul@62 | 462 | { |
paul@62 | 463 | static_cast<Hdmi_jz4780_chip *>(hdmi)->get_version(major, minor); |
paul@62 | 464 | } |
paul@62 | 465 | |
paul@62 | 466 | int jz4780_hdmi_i2c_read(void *hdmi, uint8_t *buf, unsigned int length) |
paul@62 | 467 | { |
paul@62 | 468 | return static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_read(buf, length); |
paul@62 | 469 | } |
paul@62 | 470 | |
paul@62 | 471 | void jz4780_hdmi_i2c_set_address(void *hdmi, uint8_t address) |
paul@62 | 472 | { |
paul@62 | 473 | static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_address(address); |
paul@62 | 474 | } |
paul@62 | 475 | |
paul@62 | 476 | void jz4780_hdmi_i2c_set_segment(void *hdmi, uint8_t segment) |
paul@62 | 477 | { |
paul@62 | 478 | static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_segment(segment); |
paul@62 | 479 | } |
paul@62 | 480 | |
paul@62 | 481 | void jz4780_hdmi_i2c_set_register(void *hdmi, uint8_t device_register) |
paul@62 | 482 | { |
paul@62 | 483 | static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_register(device_register); |
paul@62 | 484 | } |
paul@65 | 485 | |
paul@65 | 486 | int jz4780_hdmi_connected(void *hdmi) |
paul@65 | 487 | { |
paul@65 | 488 | return (int) static_cast<Hdmi_jz4780_chip *>(hdmi)->connected(); |
paul@65 | 489 | } |
paul@65 | 490 | |
paul@65 | 491 | long jz4780_hdmi_wait_for_connection(void *hdmi) |
paul@65 | 492 | { |
paul@65 | 493 | return static_cast<Hdmi_jz4780_chip *>(hdmi)->wait_for_connection(); |
paul@65 | 494 | } |