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