From 1ce870b88b9c9b745a716bca027736ad54bba31c Mon Sep 17 00:00:00 2001 From: Wenting Zhang Date: Tue, 30 Apr 2024 17:29:38 -0400 Subject: [PATCH] Initial driver support for ADV7611 --- fw/CMakeLists.txt | 1 + fw/adv7611.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++ fw/adv7611.h | 28 ++++++++ fw/fw.c | 26 +++++-- fw/tcpm_driver.c | 2 - fw/usb_mux.c | 2 +- 6 files changed, 230 insertions(+), 8 deletions(-) create mode 100644 fw/adv7611.c create mode 100644 fw/adv7611.h diff --git a/fw/CMakeLists.txt b/fw/CMakeLists.txt index 549407e..ba62fe0 100644 --- a/fw/CMakeLists.txt +++ b/fw/CMakeLists.txt @@ -24,6 +24,7 @@ pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable(fw + adv7611.c bitstream.c button.c caster.c diff --git a/fw/adv7611.c b/fw/adv7611.c new file mode 100644 index 0000000..b16af04 --- /dev/null +++ b/fw/adv7611.c @@ -0,0 +1,179 @@ +// +// Copyright 2024 Wenting Zhang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "config.h" +#include "adv7611.h" +#include "edid.h" + +#ifdef INPUT_ADV7611 + +#define ADV7611_INT_PIN (1) +#define ADV7611_RST_PIN (25) + +#define ADV7611_I2C (i2c1) + +#define ADV7611_I2C_ADDR (0x4C) + +// Sub addresses, make sure they don't conflict with anything else! +#define CEC_I2C_ADDR (0x40) // Default 0x80(0x40) +#define INFOFRAME_I2C_ADDR (0x3E) // Default 0x7C(0x3E) +#define DPLL_I2C_ADDR (0x26) // Default 0x26(0x4C) +#define KSV_I2C_ADDR (0x32) // Default 0x32(0x64) +#define EDID_I2C_ADDR (0x36) // Default 0x36(0x6C) +#define HDMI_I2C_ADDR (0x34) // Default 0x34(0x68) +#define CP_I2C_ADDR (0x23) // Default 0x22(0x44) + +static const uint8_t adv7611_init_0[] = { + ADV7611_I2C_ADDR, 0xf4, (CEC_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xf5, (INFOFRAME_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xf8, (DPLL_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xf9, (KSV_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xfa, (EDID_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xfb, (HDMI_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xfd, (CP_I2C_ADDR << 1), +}; + +static const uint8_t adv7611_init_1[] = { + ADV7611_I2C_ADDR, 0xf4, (CEC_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xf5, (INFOFRAME_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xf8, (DPLL_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xf9, (KSV_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xfa, (EDID_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xfb, (HDMI_I2C_ADDR << 1), + ADV7611_I2C_ADDR, 0xfd, (CP_I2C_ADDR << 1), + + ADV7611_I2C_ADDR, 0x01, 0x05, // Prim_mode = 110b HDMI-GR + ADV7611_I2C_ADDR, 0x00, 0x13, + ADV7611_I2C_ADDR, 0x02, 0xf2, // F8 = YUV, F2 = RGB + ADV7611_I2C_ADDR, 0x03, 0x40, // 40 = 444 + ADV7611_I2C_ADDR, 0x04, 0x42, // P[23:16] V/R, P[15:8] Y/G, P[7:0] U/CrCb/B CLK=28.63636MHz + ADV7611_I2C_ADDR, 0x05, 0x28, + ADV7611_I2C_ADDR, 0x06, 0xa7, + ADV7611_I2C_ADDR, 0x0b, 0x44, + ADV7611_I2C_ADDR, 0x0C, 0x42, + ADV7611_I2C_ADDR, 0x15, 0x80, + ADV7611_I2C_ADDR, 0x19, 0x8a, + ADV7611_I2C_ADDR, 0x33, 0x40, + ADV7611_I2C_ADDR, 0x14, 0x3f, + CP_I2C_ADDR, 0xba, 0x01, + CP_I2C_ADDR, 0x7c, 0x01, + KSV_I2C_ADDR, 0x40, 0x81, // DSP_Ctrl4 :00/01 : YUV or RGB; 10 : RAW8; 11 : RAW10 + HDMI_I2C_ADDR, 0x9b, 0x03, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc1, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc2, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc3, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc4, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc5, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc6, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc7, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc8, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xc9, 0x01, // ADI recommanded settin g + HDMI_I2C_ADDR, 0xca, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xcb, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0xcc, 0x01, // ADI recommanded setting + HDMI_I2C_ADDR, 0x00, 0x00, // Set HDMI input Port A + HDMI_I2C_ADDR, 0x83, 0xfe, // terminator for Port A + HDMI_I2C_ADDR, 0x6f, 0x08, // ADI recommended setting + HDMI_I2C_ADDR, 0x85, 0x1f, // ADI recommended setting + HDMI_I2C_ADDR, 0x87, 0x70, // ADI recommended setting + HDMI_I2C_ADDR, 0x8d, 0x04, // LFG + HDMI_I2C_ADDR, 0x8e, 0x1e, // HFG + HDMI_I2C_ADDR, 0x1a, 0x8a, // unmute audio + HDMI_I2C_ADDR, 0x57, 0xda, // ADI recommended setting + HDMI_I2C_ADDR, 0x58, 0x01, + HDMI_I2C_ADDR, 0x75, 0x10, + HDMI_I2C_ADDR, 0x6c, 0xa3, // enable manual HPA + ADV7611_I2C_ADDR, 0x20, 0x70, // HPD low + KSV_I2C_ADDR, 0x74, 0x00, // disable internal EDID +}; + +static const uint8_t adv7611_init_2[] = { + KSV_I2C_ADDR, 0x74, 0x01, // Enable the Internal EDID for Ports + ADV7611_I2C_ADDR, 0x20, 0xf0, // HPD high + HDMI_I2C_ADDR, 0x6c, 0xa2, // disable manual HPA + ADV7611_I2C_ADDR, 0xf4, 0x00 +}; + +static void adv7611_send_init_seq(const uint8_t *seq, int entries) { + uint8_t addr; + uint8_t buf[2]; + int result; + for (int i = 0; i < entries; i++) { + addr = seq[i*3]; + buf[0] = seq[i*3 + 1]; + buf[1] = seq[i*3 + 2]; + result = i2c_write_blocking(ADV7611_I2C, addr, buf, 2, false); + if (result != 2) { + fatal("Failed writing data to ADV7611\n"); + } + } +} + +static void adv7611_load_edid(uint8_t *edid) { + uint8_t buf[2]; + int result; + for (int i = 0; i < 128; i++) { + buf[0] = i; + buf[1] = edid[i]; + result = i2c_write_blocking(ADV7611_I2C, EDID_I2C_ADDR, buf, 2, false); + if (result != 2) { + fatal("Failed writing data to ADV7611\n"); + } + } +} + +void adv7611_early_init() { + // Initialize IO, reset ADV7611 and allocate I2C addresses + // So it won't conflict with other ICs + + gpio_init(ADV7611_RST_PIN); + gpio_set_dir(ADV7611_RST_PIN, GPIO_OUT); + gpio_init(ADV7611_INT_PIN); + gpio_set_dir(ADV7611_INT_PIN, GPIO_IN); + + gpio_put(ADV7611_RST_PIN, 1); + sleep_ms(100); + gpio_put(ADV7611_RST_PIN, 0); + sleep_ms(100); + gpio_put(ADV7611_RST_PIN, 1); + sleep_ms(100); +} + +void adv7611_init() { + adv7611_send_init_seq(adv7611_init_0, sizeof(adv7611_init_0) / 3); + adv7611_send_init_seq(adv7611_init_1, sizeof(adv7611_init_1) / 3); + + uint8_t *edid = edid_get_raw(); + adv7611_load_edid(edid + 1); + + adv7611_send_init_seq(adv7611_init_2, sizeof(adv7611_init_2) / 3); + + printf("ADV7611 initialization done\n"); +} + +bool adv7611_is_valid(void) { + return false; // TODO +} + +#endif diff --git a/fw/adv7611.h b/fw/adv7611.h new file mode 100644 index 0000000..225f58a --- /dev/null +++ b/fw/adv7611.h @@ -0,0 +1,28 @@ +// +// Copyright 2024 Wenting Zhang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#pragma once + +#ifdef INPUT_ADV7611 +void adv7611_early_init(void); +void adv7611_init(void); +bool adv7611_is_valid(void); +#endif diff --git a/fw/fw.c b/fw/fw.c index 4badf3e..4af2340 100644 --- a/fw/fw.c +++ b/fw/fw.c @@ -29,6 +29,7 @@ #include "tcpm_driver.h" #include "usb_pd.h" #include "ptn3460.h" +#include "adv7611.h" #include "power.h" #include "fpga.h" #include "edid.h" @@ -44,6 +45,11 @@ int main() printf("\n"); printf("Glider\n"); +#ifdef INPUT_ADV7611 + // Need to release ADV from reset, otherwise it would hold the I2C bus + adv7611_early_init(); +#endif + // Initialize I2C for TCPC/PTN3460/ADV7611 use i2c_init(i2c1, 100*1000); gpio_set_function(2, GPIO_FUNC_I2C); @@ -51,6 +57,7 @@ int main() gpio_pull_up(2); gpio_pull_up(3); +#ifdef HAS_TYPEC int result = tcpm_init(0); if (result) fatal("Failed to initialize TCPC\n"); @@ -58,25 +65,32 @@ int main() int cc1, cc2; tcpc_config[0].drv->get_cc(0, &cc1, &cc2); printf("CC status %d %d\n", cc1, cc2); +#endif power_init(); - edid_init(); - ptn3460_init(); pd_init(0); sleep_ms(50); + edid_init(); +#ifdef INPUT_PTN3460 + ptn3460_init(); +#endif +#ifdef INPUT_ADV7611 + adv7611_init(); +#endif + power_enable(true); //sleep_run_from_xosc(); //sleep_goto_dormant_until_edge_high(8); // https://ghubcoder.github.io/posts/awaking-the-pico/ - fpga_init(); - button_init(); - //sleep_ms(5000); + //fpga_init(); //caster_init(); + //button_init(); + int mode_max = 6; int mode = 1; UPDATE_MODE modes[6] = { @@ -93,6 +107,7 @@ int main() bool dp_valid = false; while (1) { +#ifdef HAS_TYPEC // TODO: Implement interrupt fusb302_tcpc_alert(0); pd_run_state_machine(0); @@ -105,6 +120,7 @@ int main() dp_valid = ptn3460_is_valid(); printf(dp_valid ? "Input is valid\n" : "Input is invalid\n"); } +#endif // Key press logic uint32_t keys = button_scan(); diff --git a/fw/tcpm_driver.c b/fw/tcpm_driver.c index 9f5f686..cd06397 100644 --- a/fw/tcpm_driver.c +++ b/fw/tcpm_driver.c @@ -30,8 +30,6 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = { }; #define TCPC_I2C i2c1 -#define TCPC_I2C_SDA 0 -#define TCPC_I2C_SCL 1 void tcpc_i2c_init(void) { // Should be initialized at board level init to avoid dependencies between diff --git a/fw/usb_mux.c b/fw/usb_mux.c index 68a5976..bbe449d 100644 --- a/fw/usb_mux.c +++ b/fw/usb_mux.c @@ -28,7 +28,7 @@ #include "utils.h" #include "usb_mux.h" -#ifdef INPUT_TYPEC +#ifdef HAS_TYPEC #define USBC_ORI_PIN 10