1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/hdmi/src/jz4780.cc Fri May 22 00:17:25 2020 +0200
1.3 @@ -0,0 +1,362 @@
1.4 +/*
1.5 + * JZ4780 HDMI peripheral support.
1.6 + *
1.7 + * Copyright (C) 2020 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <l4/devices/hdmi-jz4780.h>
1.26 +#include <l4/devices/hw_mmio_register_block.h>
1.27 +
1.28 +#include <l4/sys/irq.h>
1.29 +#include <l4/util/util.h>
1.30 +
1.31 +#include <cstdio>
1.32 +
1.33 +/*
1.34 +I2C pins:
1.35 +
1.36 +HDMI: PF25/SMB4_SDA/DDCSDA, PF24/SMB4_SCK/DDCSCK
1.37 +
1.38 +See: http://mipscreator.imgtec.com/CI20/hardware/board/ci20_jz4780_v2.0.pdf
1.39 +*/
1.40 +
1.41 +enum Regs
1.42 +{
1.43 + // Identification.
1.44 +
1.45 + Design_id = 0x000, // DESIGN_ID
1.46 + Revision_id = 0x001, // REVISION_ID
1.47 + Product_id0 = 0x002, // PRODUCT_ID0
1.48 + Product_id1 = 0x003, // PRODUCT_ID1
1.49 + Config_id0 = 0x004, // CONFIG_ID0
1.50 + Config_id1 = 0x005, // CONFIG_ID1
1.51 + Config_id2 = 0x006, // CONFIG_ID2
1.52 + Config_id3 = 0x007, // CONFIG_ID3
1.53 +
1.54 + // Top-level interrupt control.
1.55 +
1.56 + Int_mask = 0x1ff, // MUTE
1.57 +
1.58 + // Interrupt status and mask for various functions.
1.59 +
1.60 + Fc_int_status0 = 0x100, // FC_STAT0
1.61 + Fc_int_status1 = 0x101, // FC_STAT1
1.62 + Fc_int_status2 = 0x102, // FC_STAT2
1.63 + As_int_status = 0x103, // AS_STAT0
1.64 + Phy_int_status = 0x104, // PHY_STAT0
1.65 + Cec_int_status = 0x106, // CEC_STAT0
1.66 + Vp_int_status = 0x107, // VP_STAT0
1.67 + Ahb_dma_audio_int_status = 0x109, // AHBDMAAUD_STAT0
1.68 +
1.69 + Fc_int_mask0 = 0x180, // MUTE_FC_STAT0
1.70 + Fc_int_mask1 = 0x181, // MUTE_FC_STAT1
1.71 + Fc_int_mask2 = 0x182, // MUTE_FC_STAT2
1.72 + As_int_mask = 0x183, // MUTE_AS_STAT0
1.73 + Phy_int_mask = 0x184, // MUTE_PHY_STAT0
1.74 + Cec_int_mask = 0x186, // MUTE_CEC_STAT0
1.75 + Vp_int_mask = 0x187, // MUTE_VP_STAT0
1.76 + Ahb_dma_audio_int_mask = 0x189, // MUTE_AHBDMAAUD_STAT0
1.77 +
1.78 + // I2C for E-DDC.
1.79 +
1.80 + I2c_int_status = 0x105, // I2CM_STAT0
1.81 + I2c_int_mask = 0x185, // MUTE_I2CM_STAT0
1.82 +
1.83 + I2c_device_address = 0x7e00, // I2CM_SLAVE
1.84 + I2c_register = 0x7e01, // I2CM_ADDRESS
1.85 + I2c_data_out = 0x7e02, // I2CM_DATAO
1.86 + I2c_data_in = 0x7e03, // I2CM_DATAI
1.87 + I2c_operation = 0x7e04, // I2CM_OPERATION
1.88 + I2c_int_config0 = 0x7e05, // I2CM_INT
1.89 + I2c_int_config1 = 0x7e06, // I2CM_CTLINT
1.90 + I2c_divider = 0x7e07, // I2CM_DIV
1.91 + I2c_segment_address = 0x7e08, // I2CM_SEGADDR
1.92 + I2c_software_reset = 0x7e09, // I2CM_SOFTRSTZ
1.93 + I2c_segment_pointer = 0x7e0a, // I2CM_SEGPTR
1.94 +
1.95 + // I2C for PHY.
1.96 +
1.97 + I2c_phy_int_status = 0x108, // I2CMPHY_STAT0
1.98 + I2c_phy_int_mask = 0x188, // MUTE_I2CMPHY_STAT0
1.99 +
1.100 + I2c_phy_int_config0 = 0x3027, // PHY_I2CM_INT_ADDR
1.101 + I2c_phy_int_config1 = 0x3028, // PHY_I2CM_CTLINT_ADDR
1.102 +};
1.103 +
1.104 +// Identification values.
1.105 +
1.106 +enum Product_id_values : unsigned
1.107 +{
1.108 + Product_id0_transmitter = 0xa0, // PRODUCT_ID0_HDMI_TX
1.109 +
1.110 + Product_id1_hdcp = 0xc0, // PRODUCT_ID1_HDCP
1.111 + Product_id1_receiver = 0x02, // PRODUCT_ID1_HDMI_RX
1.112 + Product_id1_transmitter = 0x01, // PRODUCT_ID1_HDMI_TX
1.113 +};
1.114 +
1.115 +// Configuration values.
1.116 +
1.117 +enum Config_id_values : unsigned
1.118 +{
1.119 + Config_id0_i2s = 0x10, // CONFIG0_I2S
1.120 + Config_id0_cec = 0x02, // CONFIG0_CEC
1.121 +
1.122 + Config_id1_ahb = 0x01, // CONFIG1_AHB
1.123 +
1.124 + Config2_dwc_hdmi_tx_phy = 0x00, // DWC_HDMI_TX_PHY
1.125 + Config2_dwc_mhl_phy_heac = 0xb2, // DWC_MHL_PHY_HEAC
1.126 + Config2_dwc_mhl_phy = 0xc2, // DWC_MHL_PHY
1.127 + Config2_dwc_hdmi_3d_tx_phy_heac = 0xe2, // DWC_HDMI_3D_TX_PHY_HEAC
1.128 + Config2_dwc_hdmi_3d_tx_phy = 0xf2, // DWC_HDMI_3D_TX_PHY
1.129 + Config2_dwc_hdmi20_tx_phy = 0xf3, // DWC_HDMI20_TX_PHY
1.130 + Config2_vendor_phy = 0xfe, // VENDOR_PHY
1.131 +
1.132 + Config_id3_ahb_audio_dma = 0x02, // CONFIG3_AHBAUDDMA
1.133 + Config_id3_gp_audio = 0x01, // CONFIG3_GPAUD
1.134 +};
1.135 +
1.136 +// Status and mask bits.
1.137 +
1.138 +enum Int_mask_bits : unsigned
1.139 +{
1.140 + Int_mask_wakeup = 0x2,
1.141 + Int_mask_all = 0x1,
1.142 +};
1.143 +
1.144 +enum I2c_int_status_bits : unsigned
1.145 +{
1.146 + I2c_int_status_done = 0x2,
1.147 + I2c_int_status_error = 0x1,
1.148 +};
1.149 +
1.150 +// I2C operation bits.
1.151 +
1.152 +enum I2c_operation_bits : unsigned
1.153 +{
1.154 + I2c_operation_write = 0x10,
1.155 + I2c_operation_segment_read = 0x2,
1.156 + I2c_operation_read = 0x1,
1.157 +};
1.158 +
1.159 +// Interrupt configuration bits.
1.160 +
1.161 +enum I2c_int_config0_bits : unsigned
1.162 +{
1.163 + I2c_int_config_done_polarity = 0x8,
1.164 + I2c_int_config_done_mask = 0x4,
1.165 +};
1.166 +
1.167 +enum I2c_int_config1_bits : unsigned
1.168 +{
1.169 + I2c_int_config_nack_polarity = 0x80,
1.170 + I2c_int_config_nack_mask = 0x40,
1.171 + I2c_int_config_arb_polarity = 0x8,
1.172 + I2c_int_config_arb_mask = 0x4,
1.173 +};
1.174 +
1.175 +
1.176 +
1.177 +// Initialise the HDMI peripheral.
1.178 +
1.179 +Hdmi_jz4780_chip::Hdmi_jz4780_chip(l4_addr_t start, l4_addr_t end,
1.180 + l4_cap_idx_t irq)
1.181 +: _start(start), _end(end), _irq(irq)
1.182 +{
1.183 + // 8-bit registers with 2-bit address shifting.
1.184 +
1.185 + _regs = new Hw::Mmio_register_block<8>(start, 2);
1.186 +
1.187 + _segment_read = false;
1.188 + _device_register = 0;
1.189 +
1.190 + get_identification();
1.191 + int_init();
1.192 + i2c_init();
1.193 +}
1.194 +
1.195 +void Hdmi_jz4780_chip::get_identification()
1.196 +{
1.197 + _version = (_regs[Design_id] << 8) | _regs[Revision_id];
1.198 +}
1.199 +
1.200 +void Hdmi_jz4780_chip::get_version(uint8_t *major, uint16_t *minor)
1.201 +{
1.202 + *major = _version >> 12;
1.203 + *minor = _version & 0xfff;
1.204 +}
1.205 +
1.206 +void Hdmi_jz4780_chip::int_init()
1.207 +{
1.208 + // Disable interrupts.
1.209 +
1.210 + _regs[Int_mask] = _regs[Int_mask] | (Int_mask_wakeup | Int_mask_all);
1.211 +
1.212 + // Mask all interrupts.
1.213 +
1.214 + _regs[Fc_int_mask0] = 0xff;
1.215 + _regs[Fc_int_mask1] = 0xff;
1.216 + _regs[Fc_int_mask2] = 0xff;
1.217 + _regs[As_int_mask] = 0xff;
1.218 + _regs[Phy_int_mask] = 0xff;
1.219 + _regs[I2c_int_mask] = 0xff;
1.220 + _regs[Cec_int_mask] = 0xff;
1.221 + _regs[Vp_int_mask] = 0xff;
1.222 + _regs[I2c_phy_int_mask] = 0xff;
1.223 + _regs[Ahb_dma_audio_int_mask] = 0xff;
1.224 +
1.225 + // Enable interrupts.
1.226 +
1.227 + _regs[Int_mask] = _regs[Int_mask] & ~(Int_mask_wakeup | Int_mask_all);
1.228 +}
1.229 +
1.230 +void Hdmi_jz4780_chip::i2c_init()
1.231 +{
1.232 + // Set PHY interrupt priorities.
1.233 +
1.234 + _regs[I2c_phy_int_config0] = I2c_int_config_done_polarity;
1.235 + _regs[I2c_phy_int_config1] = I2c_int_config_nack_polarity |
1.236 + I2c_int_config_arb_polarity;
1.237 +
1.238 + // Software reset.
1.239 +
1.240 + _regs[I2c_software_reset] = 0;
1.241 +
1.242 + // Standard mode (100kHz).
1.243 +
1.244 + _regs[I2c_divider] = 0;
1.245 +
1.246 + // Set interrupt polarities.
1.247 +
1.248 + _regs[I2c_int_config0] = I2c_int_config_done_polarity;
1.249 + _regs[I2c_int_config1] = I2c_int_config_nack_polarity |
1.250 + I2c_int_config_arb_polarity;
1.251 +
1.252 + // Clear and mask/mute interrupts.
1.253 +
1.254 + _regs[I2c_int_status] = I2c_int_status_done | I2c_int_status_error;
1.255 + _regs[I2c_int_mask] = I2c_int_status_done | I2c_int_status_error;
1.256 +}
1.257 +
1.258 +long Hdmi_jz4780_chip::i2c_wait()
1.259 +{
1.260 + long err;
1.261 +
1.262 + // Wait for around 1s.
1.263 +
1.264 + l4_msgtag_t tag = l4_irq_receive(_irq, L4_IPC_NEVER);
1.265 +
1.266 + err = l4_ipc_error(tag, l4_utcb());
1.267 + if (err)
1.268 + return err;
1.269 +
1.270 + // Test for an error condition.
1.271 +
1.272 + if (_regs[I2c_int_status] & I2c_int_status_error)
1.273 + return -L4_EIO;
1.274 +
1.275 + _regs[I2c_int_status] = _regs[I2c_int_status] | I2c_int_status_done;
1.276 +
1.277 + return L4_EOK;
1.278 +}
1.279 +
1.280 +int Hdmi_jz4780_chip::i2c_read(uint8_t *buf, unsigned int length)
1.281 +{
1.282 + unsigned int i;
1.283 + long err;
1.284 +
1.285 + // Clear interrupts.
1.286 +
1.287 + _regs[I2c_int_mask] = 0;
1.288 +
1.289 + for (i = 0; i < length; i++)
1.290 + {
1.291 + // Increment the device register.
1.292 +
1.293 + _regs[I2c_register] = _device_register++;
1.294 + _regs[I2c_operation] = _segment_read ? I2c_operation_segment_read
1.295 + : I2c_operation_read;
1.296 +
1.297 + // Wait and then read.
1.298 +
1.299 + err = i2c_wait();
1.300 + if (err)
1.301 + break;
1.302 +
1.303 + buf[i] = _regs[I2c_data_in];
1.304 + }
1.305 +
1.306 + // Mask interrupts again.
1.307 +
1.308 + _regs[I2c_int_mask] = I2c_int_status_done | I2c_int_status_error;
1.309 +
1.310 + return i;
1.311 +}
1.312 +
1.313 +void Hdmi_jz4780_chip::i2c_set_address(uint8_t address)
1.314 +{
1.315 + _regs[I2c_device_address] = address;
1.316 + _segment_read = false;
1.317 + i2c_set_register(0);
1.318 +}
1.319 +
1.320 +void Hdmi_jz4780_chip::i2c_set_segment(uint8_t segment)
1.321 +{
1.322 + _regs[I2c_segment_address] = 0x30;
1.323 + _regs[I2c_segment_pointer] = segment;
1.324 + _segment_read = true;
1.325 + i2c_set_register(0);
1.326 +}
1.327 +
1.328 +void Hdmi_jz4780_chip::i2c_set_register(uint8_t device_register)
1.329 +{
1.330 + _device_register = device_register;
1.331 +}
1.332 +
1.333 +
1.334 +
1.335 +// C language interface functions.
1.336 +
1.337 +void *jz4780_hdmi_init(l4_addr_t start, l4_addr_t end, l4_cap_idx_t irq)
1.338 +{
1.339 + return (void *) new Hdmi_jz4780_chip(start, end, irq);
1.340 +}
1.341 +
1.342 +void jz4780_hdmi_get_version(void *hdmi, uint8_t *major, uint16_t *minor)
1.343 +{
1.344 + static_cast<Hdmi_jz4780_chip *>(hdmi)->get_version(major, minor);
1.345 +}
1.346 +
1.347 +int jz4780_hdmi_i2c_read(void *hdmi, uint8_t *buf, unsigned int length)
1.348 +{
1.349 + return static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_read(buf, length);
1.350 +}
1.351 +
1.352 +void jz4780_hdmi_i2c_set_address(void *hdmi, uint8_t address)
1.353 +{
1.354 + static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_address(address);
1.355 +}
1.356 +
1.357 +void jz4780_hdmi_i2c_set_segment(void *hdmi, uint8_t segment)
1.358 +{
1.359 + static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_segment(segment);
1.360 +}
1.361 +
1.362 +void jz4780_hdmi_i2c_set_register(void *hdmi, uint8_t device_register)
1.363 +{
1.364 + static_cast<Hdmi_jz4780_chip *>(hdmi)->i2c_set_register(device_register);
1.365 +}