--- /dev/null
+// ARMware - an ARM emulator
+// Copyright (C) <2007> Wei Hu <wei.hu.tw@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "lcd_controller.h"
+
+#include "logger.h"
+
+namespace SA1100 {
+
+LCDController::LCDController() : mEnabled(false) { initRegisters(); }
+
+void LCDController::initRegisters() {
+ mLCCR0 = 0;
+ mLCSR = 0;
+ mDBAR1 = 0;
+ mDCAR1 = 0;
+ mDBAR2 = 0;
+ mDCAR2 = 0;
+ mLCCR1 = 0;
+ mLCCR2 = 0;
+ mLCCR3 = 0;
+}
+
+void LCDController::reset() {
+ mLCCR0 = 0;
+ mLCCR1 = 0;
+ mLCCR2 = 0;
+ mLCCR3 = 0;
+ mLCSR = 0;
+ mEnabled = false;
+}
+
+uint32_t LCDController::getFramebufferBase(Framebuffer fb) const {
+ switch (fb) {
+ case Framebuffer::Top:
+ return mDBAR1;
+ case Framebuffer::Bottom:
+ return mDBAR2;
+ }
+}
+
+uint32_t LCDController::read32(const uint32_t address) const {
+ switch (address) {
+ case LCCR0:
+ LOG_REG_R("LCD LCCR0", address, mLCCR0);
+ return mLCCR0;
+ case LCCR1:
+ LOG_REG_R("LCD LCCR1", address, mLCCR1);
+ return mLCCR1;
+ case LCCR2:
+ LOG_REG_R("LCD LCCR2", address, mLCCR2);
+ return mLCCR2;
+ case LCCR3:
+ LOG_REG_R("LCD LCCR3", address, mLCCR3);
+ return mLCCR3;
+ case DBAR1:
+ LOG_REG_R("LCD DBAR1", address, mDBAR1);
+ return mDBAR1;
+ case DCAR1:
+ LOG_REG_R("LCD DCAR1", address, mDCAR1);
+ return mDCAR1;
+ case DBAR2:
+ LOG_REG_R("LCD DBAR2", address, mDBAR2);
+ return mDBAR2;
+ case DCAR2:
+ LOG_REG_R("LCD DCAR2", address, mDCAR2);
+ return mDCAR2;
+ case LCSR:
+ LOG_REG_R("LCD LCSR", address, mLCSR);
+ return mLCSR;
+
+ default:
+ LOG_REG_R("LCD Unknown", address, 0);
+ return 0;
+ }
+}
+
+void LCDController::write32(const uint32_t address, const uint32_t value) {
+ switch (address) {
+ case LCCR0: {
+ LOG_REG_W("LCD LCCR0", address, value);
+ const uint32_t diff = (mLCCR0 ^ value);
+
+ if ((diff & LCCR0_LEN) != 0) {
+ if (0 == (value & LCCR0_LEN)) {
+ mEnabled = false;
+ mLCSR |= LCSR_LDD;
+ } else {
+ mEnabled = true;
+ // :SA-1110 Developer's Manual: Wei 2003-Dec-08:
+ //
+ // Value in DBAR1(or 2) is transferred to DCAR1(or 2) when LCD is first
+ // enabled (LEN = 0->1).
+ mDCAR1 = mDBAR1;
+ mDCAR2 = mDBAR2;
+
+ // :SA-1110 Developer's Manual: p.249: Wei 2004-Jan-15:
+ //
+ // The base address update status (BAU) is a read/write status bit
+ // that is set after the contents of the DMA base address register 1
+ // are transferred to the DMA current Address register 1.
+ mLCSR |= LCSR_BAU;
+ }
+ }
+
+ mLCCR0 = (value & (0xFFFFF ^ (1 << 6)));
+ } break;
+
+ case LCCR1:
+ LOG_REG_W("LCD LCCR1", address, value);
+ // :SA-1110 Developer's Manual: Wei 2004-Jan-13:
+ //
+ // Note that the bottom four bits of PPL are not implemented and
+ // therefore are not writable. Reads of these bits return zeros
+ // because the LCD controller only supports displays that are a
+ // multiple of 16 pixels wide.
+ mLCCR1 = (value & 0xFFFFFFF0);
+ break;
+
+ case LCCR2:
+ LOG_REG_W("LCD LCCR2", address, value);
+ mLCCR2 = value;
+ break;
+
+ case LCCR3:
+ LOG_REG_W("LCD LCCR3", address, value);
+ mLCCR3 = (value & 0xFFFFFF);
+ break;
+
+ case DBAR1:
+ LOG_REG_W("LCD DBAR1", address, value);
+ // :SA-1110 Developer's Manual: p.245: Wei 2003-Dec-08:
+ //
+ // Addresses programmed in the base address register must be aligned
+ // on quadword boundaries;
+ // the least significant four bits (DBAR1[3:0]) must always be written with
+ // zeros.
+
+ // :NOTE: Wei 2004-Jan-15:
+ //
+ // If we want to change the frame buffer address,
+ // we are likely need to update the whole screen.
+ mDBAR1 = value;
+ break;
+
+ case DBAR2:
+ LOG_REG_W("LCD DBAR2", address, value);
+ // :SA-1110 Developer's Manual: p.245: Wei 2003-Dec-08:
+ //
+ // Addresses programmed in the base address register must be aligned
+ // on quadword boundaries;
+ // the least significant four bits (DBAR1[3:0]) must always be written with
+ // zeros.
+
+ mDBAR2 = value;
+ break;
+
+ case DCAR1:
+ LOG_REG_W("LCD DCAR1", address, value);
+ break;
+
+ case DCAR2:
+ LOG_REG_W("LCD DCAR2", address, value);
+ // :SA-1110 Developer's Manual: p.247: Wei 2004-Jun-06:
+ // :SA-1110 Developer's Manual: p.248: Wei 2004-Jun-06:
+ //
+ // These are read-only registers.
+ break;
+
+ case LCSR:
+ LOG_REG_W("LCD LCSR", address, value);
+ // :SA-1110 Developer's Manual: p.248: Wei 2004-Jun-06:
+ //
+ // Status bits are referred to as 'sticky' (once set by hardware,
+ // they must be cleared by software). Writing a 1 to a sticky status bit
+ // clears it; writing a zero has no effect. Read-only flags are set and
+ // cleared by hardware; writes have no effect.
+
+ mLCSR &= ~(mLCSR & (value & LCSR_READ_WRITE_BITMASK));
+ break;
+
+ default:
+ LOG_REG_W("LCD Unknown", address, value);
+ break;
+ }
+}
+
+} // namespace SA1100
--- /dev/null
+// ARMware - an ARM emulator
+// Copyright (C) <2007> Wei Hu <wei.hu.tw@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef LCD_CONTROLLER_H
+#define LCD_CONTROLLER_H
+
+#include <cstdint>
+
+namespace SA1100 {
+
+class IntController;
+
+class LCDController {
+public:
+ LCDController();
+ void reset();
+
+ bool isLCDEnabled() { return mEnabled; }
+
+ unsigned int getWidth() { return (mLCCR1 & 0x3FF) + 16; }
+ unsigned int getHeight() { return (mLCCR2 & 0x3FF) + 1; }
+
+ enum Framebuffer { Top, Bottom };
+
+ uint32_t getFramebufferBase(Framebuffer fb) const;
+
+ inline void endFrame() {
+ mDCAR1 = mDBAR1;
+ mDCAR2 = mDBAR2;
+
+ // :SA-1110 Developer's Manual: Wei 2003-Dec-08:
+ //
+ // Value in DBAR1(or 2) is transferred to DCAR1(or 2) when LCD is first
+ // enabled (LEN = 0->1) and when the current address pointer value equals
+ // the end-of-frame buffer.
+
+ // :NOTE: Wei 2004-Jan-15:
+ //
+ // Althought I don't update the mDCAR1(or 2) when (mDBAR1 == mDCAR1)
+ // here, however, they are actually transfered in the real SA-1110, thus the
+ // following LCSR_BAU setting operation are always performed.
+
+ // :SA-1110 Developer's Manual: p.249: Wei 2004-Jan-15:
+ //
+ // The base address update status (BAU) is a read/write status bit
+ // that is set after the contents of the DMA base address register 1
+ // are transferred to the DMA current Address register 1
+ mLCSR |= LCSR_BAU;
+ }
+
+ inline uint32_t getInterruptStatus() const { return mLCSR; }
+ inline uint32_t getLcdCtrlRegister() const { return mLCCR0; }
+
+ uint32_t read32(const uint32_t address) const;
+ void write32(const uint32_t address, const uint32_t value);
+
+private:
+ enum {
+ LCCR0_LEN = (1 << 0), // LCD controller enable
+ LCCR0_CMS = (1 << 1), // Color/monochrome select.
+ LCCR0_SDS = (1 << 2), // Single-/dual-panel display select.
+ LCCR0_LDM = (1 << 3), // LCD disable done mask
+ LCCR0_BAM = (1 << 4), // Base address update interrupt mask
+ LCCR0_ERM = (1 << 5), // Error mask.
+ LCCR0_PAS = (1 << 7), // Passive/active display select
+ LCCR0_BLE = (1 << 8), // Big/little endian select.
+ LCCR0_DPD = (1 << 9), // Double-pixel data pin mode.
+ LCCR0_VCS = ((1 << 10) | (1 << 11)), // Vertical slant line correction.
+ LCCR0_PDD =
+ ((1 << 12) | (1 << 13) | (1 << 14) | (1 << 15) | (1 << 16) | (1 << 17) |
+ (1 << 18) | (1 << 19)) // Palette DMA request delay.
+ };
+
+ enum {
+ LCSR_LDD = (1 << 0), // LCD disable done status.
+ LCSR_BAU =
+ (1 << 1), // Base address update flag (read-only, maskable interrupt)
+ LCSR_BER = (1 << 2), // Bus error status.
+ LCSR_ABC = (1 << 3), // AC bias count status.
+
+ // :NOTE: Wei 2004-Jun-06:
+ //
+ // I think in ARMware, we shouldn't raise the following bits.
+ // As a result, no interrupts should be raised because of the following
+ // bits.
+ LCSR_IOL = (1 << 4), // Input FIFO overrun lower panel status.
+ LCSR_IUL = (1 << 5), // Input FIFO underrun lower panel status.
+ LCSR_IOU = (1 << 6), // Input FIFO overrun upper panel status.
+ LCSR_IUU = (1 << 7), // Input FIFO underrun upper panel status.
+ LCSR_OOL = (1 << 8), // Output FIFO overrun lower panel status.
+ LCSR_OUL = (1 << 9), // Output FIFO underrun lower panel status.
+ LCSR_OOU = (1 << 10), // Output FIFO overrun upper panel status.
+ LCSR_OUU = (1 << 11), // Output FIFO underrun upper panel status.
+
+ // :NOTE: Wei 2004-Jun-30:
+ //
+ // Althought page 249 in the Intel SA-1110 Developer's Manual says that BAU
+ // flag is read-only, however, in the same page (p.249), it says that BAU
+ // flag can be cleared when it is written to a 1. After searching google, I
+ // find a post related this:
+ //
+ // �H��̡Gbratfi_t@my-deja.com (bratfi_t@my-deja.com)
+ // �D���GRe: BAU flag on SA1100
+ // ���W�¡Gcomp.sys.arm
+ // ����G1999/08/31
+ //
+ // ...
+ // According to the manual, the BAU flag is read-only, but it can be cleared
+ // like a status bit!
+ // ...
+ //
+ // Thus I include BAU in the following constant.
+ LCSR_READ_WRITE_BITMASK =
+ (LCSR_LDD | LCSR_BAU | LCSR_BER | LCSR_ABC | LCSR_IOL | LCSR_IUL |
+ LCSR_IOU | LCSR_IUU | LCSR_OOL |
+ // LCSR_OUL |
+ LCSR_OOU | LCSR_OUU)
+ };
+
+ enum {
+ LCCR0 = 0xB0100000,
+ LCSR = 0xB0100004,
+ DBAR1 = 0xB0100010,
+ DCAR1 = 0xB0100014,
+ DBAR2 = 0xB0100018,
+ DCAR2 = 0xB010001C,
+ LCCR1 = 0xB0100020,
+ LCCR2 = 0xB0100024,
+ LCCR3 = 0xB0100028
+ };
+
+ bool mEnabled;
+
+ uint32_t mLCCR0; // LCD controller control register 0
+ uint32_t mLCSR; // LCD controller status register 1
+ uint32_t mDBAR1; // DMA channel 1 base address register
+ uint32_t mDCAR1; // DMA channel 1 current address register
+ uint32_t mDBAR2; // DMA channel 2 base address register
+ uint32_t mDCAR2; // DMA channel 2 current address register
+ uint32_t mLCCR1; // LCD controller control register 1
+ uint32_t mLCCR2; // LCD controller control register 2
+ uint32_t mLCCR3; // LCD controller control register 3
+
+ void initRegisters();
+
+ friend class IntController;
+};
+
+} // namespace SA1100
+
+#endif // LCD_CONTROLLER_H