# HG changeset patch # User Paul Boddie # Date 1591018771 -7200 # Node ID 0406572fe6b4f9d26761fab4167e1f139c7825fb # Parent 06245dc45ea3727e44e5f945f5fcb21f5bde804b Fixed LCD controller clock initialisation for the JZ4780. diff -r 06245dc45ea3 -r 0406572fe6b4 pkg/devices/lib/cpm/include/cpm-jz4780.h --- a/pkg/devices/lib/cpm/include/cpm-jz4780.h Mon Jun 01 15:37:38 2020 +0200 +++ b/pkg/devices/lib/cpm/include/cpm-jz4780.h Mon Jun 01 15:39:31 2020 +0200 @@ -51,6 +51,8 @@ int have_pll(uint32_t pll_reg); int pll_enabled(uint32_t pll_reg); int pll_bypassed(uint32_t pll_reg); + void pll_disable(uint32_t pll_reg); + void pll_enable(uint32_t pll_reg); // General frequency modifiers. @@ -64,7 +66,7 @@ // Clock dividers. void set_hdmi_divider(uint16_t division); - void set_lcd_pixel_divider(uint16_t division); + void set_lcd_pixel_divider(uint8_t controller, uint16_t division); // Clock control. @@ -78,7 +80,7 @@ void set_hclock2_source(uint8_t source); void set_hdmi_source(uint8_t source); - void set_lcd_source(uint8_t source); + void set_lcd_source(uint8_t controller, uint8_t source); public: void set_pclock_source(uint8_t source); @@ -94,7 +96,7 @@ uint8_t get_hclock2_divider(); uint8_t get_pclock_divider(); uint8_t get_hdmi_divider(); - uint8_t get_lcd_pixel_divider(); + uint8_t get_lcd_pixel_divider(uint8_t controller = 0); uint8_t get_memory_divider(); // Clock control. @@ -119,7 +121,7 @@ uint8_t get_hclock0_source(); uint8_t get_hclock2_source(); uint8_t get_hdmi_source(); - uint8_t get_lcd_source(); + uint8_t get_lcd_source(uint8_t controller = 0); uint8_t get_memory_source(); uint8_t get_pclock_source(); @@ -127,7 +129,7 @@ uint32_t get_hclock0_source_frequency(); uint32_t get_hclock2_source_frequency(); uint32_t get_hdmi_source_frequency(); - uint32_t get_lcd_source_frequency(); + uint32_t get_lcd_source_frequency(uint8_t controller = 0); uint32_t get_memory_source_frequency(); uint32_t get_pclock_source_frequency(); @@ -137,7 +139,7 @@ uint32_t get_hclock0_frequency(); uint32_t get_hclock2_frequency(); uint32_t get_hdmi_frequency(); - uint32_t get_lcd_pixel_frequency(); + uint32_t get_lcd_pixel_frequency(uint8_t controller = 0); uint32_t get_memory_frequency(); uint32_t get_pclock_frequency(); diff -r 06245dc45ea3 -r 0406572fe6b4 pkg/devices/lib/cpm/src/jz4780.cc --- a/pkg/devices/lib/cpm/src/jz4780.cc Mon Jun 01 15:37:38 2020 +0200 +++ b/pkg/devices/lib/cpm/src/jz4780.cc Mon Jun 01 15:39:31 2020 +0200 @@ -114,8 +114,8 @@ enum Clock_gate_bits : unsigned { - Clock_gate_lcd = 28, // LCD (in CLKGR0) - Clock_gate_tve = 27, // TVE (in CLKGR0) + Clock_gate_lcd1 = 28, // LCD (in CLKGR0) + Clock_gate_lcd0 = 27, // TVE (in CLKGR0) Clock_gate_hdmi = 9, // HDMI (in CLKGR1) Clock_gate_smb4 = 12, // SMB4 (in CLKGR1) Clock_gate_smb3 = 0, // SMB3 (in CLKGR1) @@ -210,7 +210,7 @@ { uint8_t d = get_field(reg, mask, shift); - // NOTE: Value 14 stops the clock, 15 presumably resumes the clock. + // NOTE: Value 15 stops the clock, 14 presumably resumes the clock. return (d < 14) ? d + 1 : 1; } @@ -239,6 +239,20 @@ return _regs[pll_reg] & (1 << Pll_bypassed); } +void +Cpm_jz4780_chip::pll_enable(uint32_t pll_reg) +{ + _regs[pll_reg] = _regs[pll_reg] | (1 << Pll_enabled); + while (!(_regs[pll_reg] & (1 << Pll_stable))); +} + +void +Cpm_jz4780_chip::pll_disable(uint32_t pll_reg) +{ + _regs[pll_reg] = _regs[pll_reg] & ~(1 << Pll_enabled); + while (_regs[pll_reg] & (1 << Pll_stable)); +} + // Feedback (13-bit) multiplier. uint16_t @@ -352,12 +366,12 @@ return get_field(Hdmi_divider, 0xff, Hdmi_divider_value) + 1; } -// LCD clock (LPCLK) divider for LCD0 pixel clock. +// LCD clock (LPCLK) divider for LCD0 or LCD1 pixel clock. uint8_t -Cpm_jz4780_chip::get_lcd_pixel_divider() +Cpm_jz4780_chip::get_lcd_pixel_divider(uint8_t controller) { - return get_field(Lcd_divider0, 0xff, Lcd_divider_value) + 1; + return get_field(controller ? Lcd_divider1 : Lcd_divider0, 0xff, Lcd_divider_value) + 1; } // Memory clock (DDR_CLK) divider. @@ -394,23 +408,25 @@ // NOTE: This only supports the first LCD peripheral. void -Cpm_jz4780_chip::set_lcd_pixel_divider(uint16_t division) +Cpm_jz4780_chip::set_lcd_pixel_divider(uint8_t controller, uint16_t division) { + uint32_t divider = controller ? Lcd_divider1 : Lcd_divider0; + if ((division < 1) || (division > 256)) return; // Enable change. - _regs[Lcd_divider0] = _regs[Lcd_divider0] | Lcd_change_enable; + _regs[divider] = _regs[divider] | Lcd_change_enable; // Set the divider. - set_field(Lcd_divider0, 0xff, Lcd_divider_value, division - 1); + set_field(divider, 0xff, Lcd_divider_value, division - 1); // Restart clock and disable change. - while (_regs[Lcd_divider0] & Lcd_change_busy); - _regs[Lcd_divider0] = _regs[Lcd_divider0] & ~Lcd_change_enable; + while (_regs[divider] & Lcd_change_busy); + _regs[divider] = _regs[divider] & ~Lcd_change_enable; } @@ -432,13 +448,22 @@ void Cpm_jz4780_chip::start_lcd() { - _regs[Clock_gate0] = _regs[Clock_gate0] & ~(1 << Clock_gate_lcd); + // JZ4780 apparently needs LCD0/TVE to be ungated for the LCD peripheral to + // work. The Linux 3.0.8 vendor kernel reveals that the TVE clock is actually + // LCD0 and that the LCD clock is actually LCD1. + + // According to the 3.0.8 kernel, LCD1 is the parent of LCD0. However, LCD0 + // does seem to operate without LCD1 enabled. + + _regs[Clock_gate0] = _regs[Clock_gate0] & ~(1 << Clock_gate_lcd1); + _regs[Clock_gate0] = _regs[Clock_gate0] & ~(1 << Clock_gate_lcd0); } void Cpm_jz4780_chip::stop_lcd() { - _regs[Clock_gate0] = _regs[Clock_gate0] | (1 << Clock_gate_lcd); + _regs[Clock_gate0] = _regs[Clock_gate0] | (1 << Clock_gate_lcd1); + _regs[Clock_gate0] = _regs[Clock_gate0] | (1 << Clock_gate_lcd0); } void @@ -609,15 +634,15 @@ } uint8_t -Cpm_jz4780_chip::get_lcd_source() +Cpm_jz4780_chip::get_lcd_source(uint8_t controller) { - return get_field(Lcd_divider0, 0x3, Clock_source_lcd); + return get_field(controller ? Lcd_divider1 : Lcd_divider0, 0x3, Clock_source_lcd); } uint32_t -Cpm_jz4780_chip::get_lcd_source_frequency() +Cpm_jz4780_chip::get_lcd_source_frequency(uint8_t controller) { - switch (get_lcd_source()) + switch (get_lcd_source(controller)) { case Source_main: return get_main_frequency(); @@ -631,20 +656,22 @@ } void -Cpm_jz4780_chip::set_lcd_source(uint8_t source) +Cpm_jz4780_chip::set_lcd_source(uint8_t controller, uint8_t source) { + uint32_t divider = controller ? Lcd_divider1 : Lcd_divider0; + // Stop clock and enable change. - _regs[Lcd_divider0] = _regs[Lcd_divider0] | Lcd_change_enable | Lcd_clock_stop; + _regs[divider] = _regs[divider] | Lcd_change_enable | Lcd_clock_stop; // Set the source. - set_field(Lcd_divider0, 0x03, Clock_source_lcd, source); + set_field(divider, 0x03, Clock_source_lcd, source); // Restart clock and disable change. - while (_regs[Lcd_divider0] & Lcd_change_busy); - _regs[Lcd_divider0] = _regs[Lcd_divider0] & ~(Lcd_change_enable | Lcd_clock_stop); + while (_regs[divider] & Lcd_change_busy); + _regs[divider] = _regs[divider] & ~(Lcd_change_enable | Lcd_clock_stop); } uint8_t @@ -731,12 +758,12 @@ return get_hdmi_source_frequency() / get_hdmi_divider(); } -// Clock frequency for the LCD0 controller. +// Clock frequency for the LCD0 or LCD1 controller. uint32_t -Cpm_jz4780_chip::get_lcd_pixel_frequency() +Cpm_jz4780_chip::get_lcd_pixel_frequency(uint8_t controller) { - return get_lcd_source_frequency() / get_lcd_pixel_divider(); + return get_lcd_source_frequency(controller) / get_lcd_pixel_divider(controller); } // Clock frequency for the memory. @@ -779,6 +806,7 @@ // Switch to the video PLL and attempt to set the divider. set_hdmi_source(Source_pll_V); + pll_enable(Pll_control_V); set_hdmi_divider(get_hdmi_source_frequency() / pclk); } @@ -791,8 +819,11 @@ { // Switch to the video PLL and attempt to set the divider. - set_lcd_source(Source_pll_V); - set_lcd_pixel_divider(get_lcd_source_frequency() / pclk); + set_lcd_source(0, Source_pll_V); + set_lcd_source(1, Source_pll_V); + pll_enable(Pll_control_V); + set_lcd_pixel_divider(0, get_lcd_source_frequency() / pclk); + set_lcd_pixel_divider(1, get_lcd_source_frequency() / pclk); } // NOTE: Compatibility method. Probably needs reviewing.