| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2008 by Jens Arnold |
| * |
| * 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 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| |
| #define CLOCK_MASK 0x00004000 |
| #define DATA_MASK 0x00002000 |
| #define RS_MASK 0x00008000 |
| #define GPIO_OUT_ADDR 0x80000004 |
| |
| #define CS_MASK 0x00000020 /* used in moveq.l */ |
| #define GPIO1_OUT_ADDR 0x800000b4 |
| |
| .extern cpu_frequency /* Global variable from system.c */ |
| |
| .section .icode,"ax",@progbits |
| |
| /* Output 8 bits to the LCD. Instruction order is devised to maximize the |
| * delay between changing the data line and the CLK L->H transition, which |
| * makes the LCD controller sample DATA. |
| * Requires CLK = 1 on entry. |
| * |
| * Custom calling convention: |
| * %a0 - GPIO_OUT_ADDR |
| * %d3 - data byte |
| * %d6 - DATA_MASK |
| * %d7 - CLOCK_MASK |
| * Clobbers: |
| * %d0..%d3 |
| */ |
| .write_byte: |
| move.w %sr, %d2 |
| move.w #0x2700, %sr |
| |
| move.l (%a0), %d0 /* Get current state of data port */ |
| move.l %d0, %d1 |
| and.l %d6, %d1 /* Check current state of data line */ |
| beq.s 1f /* and set it as previous-state bit */ |
| bset #8, %d3 |
| 1: |
| move.l %d3, %d1 /* Compute the 'bit derivative', i.e. a value */ |
| lsr.l #1, %d1 /* with 1's where the data changes from the */ |
| eor.l %d1, %d3 /* previous state, and 0's where it doesn't */ |
| swap %d3 /* Shift data to upper byte */ |
| lsl.l #8, %d3 |
| |
| move.l %d0, %d1 /* precalculate opposite state of clock line */ |
| eor.l %d7, %d1 |
| |
| lsl.l #1, %d3 /* Shift out MSB */ |
| bcc.s 1f |
| eor.l %d6, %d0 /* 1: Flip data bit */ |
| eor.l %d6, %d1 |
| 1: |
| move.l %d1, (%a0) /* Output new state and set CLK = 0*/ |
| bra.w .wr_bit7 |
| |
| |
| /* Output 16 bits to the LCD. Instruction order is devised to maximize the |
| * delay between changing the data line and the CLK L->H transition, which |
| * makes the LCD controller sample DATA. |
| * Requires CLK = 1 on entry. |
| * |
| * Custom calling convention: |
| * %a0 - GPIO_OUT_ADDR |
| * %d3 - data word |
| * %d6 - DATA_MASK |
| * %d7 - CLOCK_MASK |
| * Clobbers: |
| * %d0..%d3 |
| */ |
| .write_word: |
| move.w %sr, %d2 |
| move.w #0x2700, %sr |
| |
| move.l (%a0), %d0 /* Get current state of data port */ |
| move.l %d0, %d1 |
| and.l %d6, %d1 /* Check current state of data line */ |
| beq.s 1f /* and set it as previous-state bit */ |
| bset #16, %d3 |
| 1: |
| move.l %d3, %d1 /* Compute the 'bit derivative', i.e. a value */ |
| lsr.l #1, %d1 /* with 1's where the data changes from the */ |
| eor.l %d1, %d3 /* previous state, and 0's where it doesn't */ |
| swap %d3 /* Shift data to upper word */ |
| |
| move.l %d0, %d1 /* precalculate opposite state of clock line */ |
| eor.l %d7, %d1 |
| |
| lsl.l #1, %d3 /* Shift out MSB */ |
| bcc.s 1f |
| eor.l %d6, %d0 /* 1: Flip data bit */ |
| eor.l %d6, %d1 |
| 1: |
| move.l %d1, (%a0) /* Output new state and set CLK = 0*/ |
| |
| .macro bit_out |
| move.l %d0, (%a0) /* Set CLK = 1 */ |
| lsl.l #1, %d3 |
| bcc.s 1f |
| eor.l %d6, %d0 |
| eor.l %d6, %d1 |
| 1: |
| move.l %d1, (%a0) |
| .endm |
| |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| |
| nop |
| .wr_bit7: |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| bit_out |
| nop |
| nop |
| |
| move.l %d0, (%a0) /* Set CLK = 1 */ |
| move.w %d2, %sr |
| rts |
| |
| |
| /* Output 16 bits to the LCD as fast as possible. Use only at < 60MHz. |
| * |
| * Custom calling convention: |
| * %a0 - GPIO_OUT_ADDR |
| * %d3 - data word |
| * %d6 - DATA_MASK |
| * %d7 - CLOCK_MASK |
| * Clobbers: |
| * %d0..%d3 |
| */ |
| .write_word_fast: |
| move.w %sr, %d2 /* Get current interrupt level */ |
| move.w #0x2700, %sr /* Disable interrupts */ |
| |
| move.l (%a0), %d0 /* Get current state of data port */ |
| move.l %d0, %d1 |
| and.l %d6, %d1 /* Check current state of data line */ |
| beq.s 1f /* and set it as previous-state bit */ |
| bset #16, %d3 |
| 1: |
| move.l %d3, %d1 /* Compute the 'bit derivative', i.e. a value */ |
| lsr.l #1, %d1 /* with 1's where the data changes from the */ |
| eor.l %d1, %d3 /* previous state, and 0's where it doesn't */ |
| swap %d3 /* Shift data to upper byte */ |
| |
| move.l %d0, %d1 /* precalculate opposite state of clock line */ |
| eor.l %d7, %d1 |
| |
| .macro bit_out_fast |
| lsl.l #1,%d3 /* Shift out MSB */ |
| bcc.s 1f |
| eor.l %d6, %d0 /* 1: Flip data bit */ |
| eor.l %d6, %d1 /* for both clock states */ |
| 1: |
| move.l %d1, (%a0) /* Output new state and set CLK = 0*/ |
| move.l %d0, (%a0) /* set CLK = 1 */ |
| .endm |
| |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| bit_out_fast |
| |
| move.w %d2, %sr /* Restore interrupt level */ |
| rts |
| |
| |
| .global lcd_remote_write_command |
| .type lcd_remote_write_command, @function |
| |
| lcd_remote_write_command: |
| lea.l (-4*4, %sp), %sp |
| movem.l %d2-%d3/%d6-%d7, (%sp) |
| |
| move.l (4*4+4, %sp), %d3 /* cmd */ |
| |
| lea.l GPIO_OUT_ADDR, %a0 |
| lea.l GPIO1_OUT_ADDR, %a1 |
| move.l #DATA_MASK, %d6 |
| move.l #CLOCK_MASK, %d7 |
| |
| move.l #~RS_MASK, %d0 |
| and.l %d0, (%a0) |
| moveq.l #~CS_MASK, %d0 |
| and.l %d0, (%a1) |
| |
| bsr.w .write_byte |
| |
| moveq.l #CS_MASK, %d0 |
| or.l %d0, (%a1) |
| |
| movem.l (%sp), %d2-%d3/%d6-%d7 |
| lea.l (4*4, %sp), %sp |
| rts |
| |
| |
| .global lcd_remote_write_command_ex |
| .type lcd_remote_write_command_ex, @function |
| |
| lcd_remote_write_command_ex: |
| lea.l (-4*4, %sp), %sp |
| movem.l %d2-%d3/%d6-%d7, (%sp) |
| |
| movem.l (4*4+4, %sp), %d2-%d3 /* cmd, data */ |
| |
| lea.l GPIO_OUT_ADDR, %a0 |
| lea.l GPIO1_OUT_ADDR, %a1 |
| move.l #DATA_MASK, %d6 |
| move.l #CLOCK_MASK, %d7 |
| |
| move.l #~RS_MASK, %d0 |
| and.l %d0, (%a0) |
| moveq.l #~CS_MASK, %d0 |
| and.l %d0, (%a1) |
| |
| lsl.l #8, %d2 |
| or.l %d2, %d3 |
| bsr.w .write_word |
| |
| moveq.l #CS_MASK, %d0 |
| or.l %d0, (%a1) |
| |
| movem.l (%sp), %d2-%d3/%d6-%d7 |
| lea.l (4*4, %sp), %sp |
| rts |
| |
| |
| .global lcd_remote_write_data |
| .type lcd_remote_write_data, @function |
| |
| lcd_remote_write_data: |
| lea.l (-7*4, %sp), %sp |
| movem.l %d2-%d4/%d6-%d7/%a2-%a3, (%sp) |
| |
| move.l (7*4+4, %sp), %a2 /* p_words */ |
| move.l (7*4+8, %sp), %d4 /* count */ |
| |
| lea.l GPIO_OUT_ADDR, %a0 |
| lea.l GPIO1_OUT_ADDR, %a1 |
| move.l #DATA_MASK, %d6 |
| move.l #CLOCK_MASK, %d7 |
| |
| lea.l .write_word, %a3 |
| move.l cpu_frequency, %d0 |
| cmp.l #60000000, %d0 |
| bhi.b 1f |
| lea.l .write_word_fast, %a3 |
| 1: |
| |
| move.l #RS_MASK, %d0 |
| or.l %d0, (%a0) |
| moveq.l #~CS_MASK, %d0 |
| and.l %d0, (%a1) |
| |
| .wd_loop: |
| clr.l %d3 |
| move.w (%a2)+, %d3 |
| jsr (%a3) |
| subq.l #1, %d4 |
| bne.s .wd_loop |
| |
| moveq.l #CS_MASK, %d0 |
| or.l %d0, (%a1) |
| |
| movem.l (%sp), %d2-%d4/%d6-%d7/%a2-%a3 |
| lea.l (7*4, %sp), %sp |
| rts |