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 101 // Identification values. 102 103 enum Product_id_values : unsigned 104 { 105 Product_id0_transmitter = 0xa0, // PRODUCT_ID0_HDMI_TX 106 107 Product_id1_hdcp = 0xc0, // PRODUCT_ID1_HDCP 108 Product_id1_receiver = 0x02, // PRODUCT_ID1_HDMI_RX 109 Product_id1_transmitter = 0x01, // PRODUCT_ID1_HDMI_TX 110 }; 111 112 // Configuration values. 113 114 enum Config_id_values : unsigned 115 { 116 Config_id0_i2s = 0x10, // CONFIG0_I2S 117 Config_id0_cec = 0x02, // CONFIG0_CEC 118 119 Config_id1_ahb = 0x01, // CONFIG1_AHB 120 121 Config2_dwc_hdmi_tx_phy = 0x00, // DWC_HDMI_TX_PHY 122 Config2_dwc_mhl_phy_heac = 0xb2, // DWC_MHL_PHY_HEAC 123 Config2_dwc_mhl_phy = 0xc2, // DWC_MHL_PHY 124 Config2_dwc_hdmi_3d_tx_phy_heac = 0xe2, // DWC_HDMI_3D_TX_PHY_HEAC 125 Config2_dwc_hdmi_3d_tx_phy = 0xf2, // DWC_HDMI_3D_TX_PHY 126 Config2_dwc_hdmi20_tx_phy = 0xf3, // DWC_HDMI20_TX_PHY 127 Config2_vendor_phy = 0xfe, // VENDOR_PHY 128 129 Config_id3_ahb_audio_dma = 0x02, // CONFIG3_AHBAUDDMA 130 Config_id3_gp_audio = 0x01, // CONFIG3_GPAUD 131 }; 132 133 // Status and mask bits. 134 135 enum Int_mask_bits : unsigned 136 { 137 Int_mask_wakeup = 0x2, 138 Int_mask_all = 0x1, 139 }; 140 141 enum I2c_int_status_bits : unsigned 142 { 143 I2c_int_status_done = 0x2, 144 I2c_int_status_error = 0x1, 145 }; 146 147 // I2C operation bits. 148 149 enum I2c_operation_bits : unsigned 150 { 151 I2c_operation_write = 0x10, 152 I2c_operation_segment_read = 0x2, 153 I2c_operation_read = 0x1, 154 }; 155 156 // Interrupt configuration bits. 157 158 enum I2c_int_config0_bits : unsigned 159 { 160 I2c_int_config_done_polarity = 0x8, 161 I2c_int_config_done_mask = 0x4, 162 }; 163 164 enum I2c_int_config1_bits : unsigned 165 { 166 I2c_int_config_nack_polarity = 0x80, 167 I2c_int_config_nack_mask = 0x40, 168 I2c_int_config_arb_polarity = 0x8, 169 I2c_int_config_arb_mask = 0x4, 170 }; 171 172 173 174 // Initialise the HDMI peripheral. 175 176 Hdmi_jz4780_chip::Hdmi_jz4780_chip(l4_addr_t start, l4_addr_t end, 177 l4_cap_idx_t irq) 178 : _start(start), _end(end), _irq(irq) 179 { 180 // 8-bit registers with 2-bit address shifting. 181 182 _regs = new Hw::Mmio_register_block<8>(start, 2); 183 184 _segment_read = false; 185 _device_register = 0; 186 187 get_identification(); 188 int_init(); 189 i2c_init(); 190 } 191 192 void Hdmi_jz4780_chip::get_identification() 193 { 194 _version = (_regs[Design_id] << 8) | _regs[Revision_id]; 195 } 196 197 void Hdmi_jz4780_chip::get_version(uint8_t *major, uint16_t *minor) 198 { 199 *major = _version >> 12; 200 *minor = _version & 0xfff; 201 } 202 203 void Hdmi_jz4780_chip::int_init() 204 { 205 // Disable interrupts. 206 207 _regs[Int_mask] = _regs[Int_mask] | (Int_mask_wakeup | Int_mask_all); 208 209 // Mask all interrupts. 210 211 _regs[Fc_int_mask0] = 0xff; 212 _regs[Fc_int_mask1] = 0xff; 213 _regs[Fc_int_mask2] = 0xff; 214 _regs[As_int_mask] = 0xff; 215 _regs[Phy_int_mask] = 0xff; 216 _regs[I2c_int_mask] = 0xff; 217 _regs[Cec_int_mask] = 0xff; 218 _regs[Vp_int_mask] = 0xff; 219 _regs[I2c_phy_int_mask] = 0xff; 220 _regs[Ahb_dma_audio_int_mask] = 0xff; 221 222 // Enable interrupts. 223 224 _regs[Int_mask] = _regs[Int_mask] & ~(Int_mask_wakeup | Int_mask_all); 225 } 226 227 void Hdmi_jz4780_chip::i2c_init() 228 { 229 // Set PHY interrupt priorities. 230 231 _regs[I2c_phy_int_config0] = I2c_int_config_done_polarity; 232 _regs[I2c_phy_int_config1] = I2c_int_config_nack_polarity | 233 I2c_int_config_arb_polarity; 234 235 // Software reset. 236 237 _regs[I2c_software_reset] = 0; 238 239 // Standard mode (100kHz). 240 241 _regs[I2c_divider] = 0; 242 243 // Set interrupt polarities. 244 245 _regs[I2c_int_config0] = I2c_int_config_done_polarity; 246 _regs[I2c_int_config1] = I2c_int_config_nack_polarity | 247 I2c_int_config_arb_polarity; 248 249 // Clear and mask/mute interrupts. 250 251 _regs[I2c_int_status] = I2c_int_status_done | I2c_int_status_error; 252 _regs[I2c_int_mask] = I2c_int_status_done | I2c_int_status_error; 253 } 254 255 long Hdmi_jz4780_chip::i2c_wait() 256 { 257 long err; 258 259 // Wait for around 1s. 260 261 l4_msgtag_t tag = l4_irq_receive(_irq, L4_IPC_NEVER); 262 263 err = l4_ipc_error(tag, l4_utcb()); 264 if (err) 265 return err; 266 267 // Test for an error condition. 268 269 if (_regs[I2c_int_status] & I2c_int_status_error) 270 return -L4_EIO; 271 272 _regs[I2c_int_status] = _regs[I2c_int_status] | I2c_int_status_done; 273 274 return L4_EOK; 275 } 276 277 int Hdmi_jz4780_chip::i2c_read(uint8_t *buf, unsigned int length) 278 { 279 unsigned int i; 280 long err; 281 282 // Clear interrupts. 283 284 _regs[I2c_int_mask] = 0; 285 286 for (i = 0; i < length; i++) 287 { 288 // Increment the device register. 289 290 _regs[I2c_register] = _device_register++; 291 _regs[I2c_operation] = _segment_read ? I2c_operation_segment_read 292 : I2c_operation_read; 293 294 // Wait and then read. 295 296 err = i2c_wait(); 297 if (err) 298 break; 299 300 buf[i] = _regs[I2c_data_in]; 301 } 302 303 // Mask interrupts again. 304 305 _regs[I2c_int_mask] = I2c_int_status_done | I2c_int_status_error; 306 307 return i; 308 } 309 310 void Hdmi_jz4780_chip::i2c_set_address(uint8_t address) 311 { 312 _regs[I2c_device_address] = address; 313 _segment_read = false; 314 i2c_set_register(0); 315 } 316 317 void Hdmi_jz4780_chip::i2c_set_segment(uint8_t segment) 318 { 319 _regs[I2c_segment_address] = 0x30; 320 _regs[I2c_segment_pointer] = segment; 321 _segment_read = true; 322 i2c_set_register(0); 323 } 324 325 void Hdmi_jz4780_chip::i2c_set_register(uint8_t device_register) 326 { 327 _device_register = device_register; 328 } 329 330 331 332 // C language interface functions. 333 334 void *jz4780_hdmi_init(l4_addr_t start, l4_addr_t end, l4_cap_idx_t irq) 335 { 336 return (void *) new Hdmi_jz4780_chip(start, end, irq); 337 } 338 339 void jz4780_hdmi_get_version(void *hdmi, uint8_t *major, uint16_t *minor) 340 { 341 static_cast<Hdmi_jz4780_chip *>(hdmi)->get_version(major, minor); 342 } 343 344 int jz4780_hdmi_i2c_read(void *hdmi, uint8_t *buf, unsigned int length) 345 { 346 return static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_read(buf, length); 347 } 348 349 void jz4780_hdmi_i2c_set_address(void *hdmi, uint8_t address) 350 { 351 static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_address(address); 352 } 353 354 void jz4780_hdmi_i2c_set_segment(void *hdmi, uint8_t segment) 355 { 356 static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_segment(segment); 357 } 358 359 void jz4780_hdmi_i2c_set_register(void *hdmi, uint8_t device_register) 360 { 361 static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_register(device_register); 362 }