rockbox/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c
Tomasz Moń 6c084ac3ea
Sansa Connect: Ensure LCD starts before backlight
Extra delay prevents white flash after hold switch release.

Change-Id: Ib0d33937dcbb738b913388e7df92ffdf9346c9f9
2021-07-10 13:20:29 +02:00

343 lines
9 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id: $
*
* Copyright (C) 2011 by Tomasz Moń
*
* 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.
*
****************************************************************************/
#include <sys/types.h>
#include "config.h"
#include "cpu.h"
#include "string.h"
#include "kernel.h"
#include "system.h"
#include "system-target.h"
#include "lcd.h"
#include "lcd-target.h"
#include "avr-sansaconnect.h"
#include "backlight-target.h"
/* See lcd-memframe.c */
extern void lcd_set_active(bool active);
static bool lcd_powered;
void lcd_sleep(void)
{
if (lcd_powered)
{
lcd_set_active(false);
avr_hid_lcm_power_off();
mdelay(100);
/* Disable OSD window */
bitclr16(&IO_OSD_OSDWINMD0, 0x01);
/* disable video encoder */
bitclr16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC);
mdelay(66);
/* disable video encoder and OSD clocks */
bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
lcd_powered = false;
}
}
void lcd_awake(void)
{
if (!lcd_powered)
{
/* Enable Video Encoder and OSD clocks */
bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
/* Enable OSD window */
bitset16(&IO_OSD_OSDWINMD0, 0x01);
/* Enable video encoder */
bitset16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC);
avr_hid_lcm_power_on();
mdelay(66);
lcd_set_active(true);
avr_hid_lcm_wake();
lcd_powered = true;
send_event(LCD_EVENT_ACTIVATION, NULL);
}
}
void lcd_shutdown(void)
{
backlight_hw_off();
lcd_sleep();
}
void lcd_enable(bool enable)
{
if (lcd_active() == enable)
return;
lcd_set_active(enable);
if (enable)
{
/* Enable Video Encoder and OSD clocks */
bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
/* Enable OSD window */
bitset16(&IO_OSD_OSDWINMD0, 0x01);
/* Enable video encoder */
bitset16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC);
avr_hid_lcm_wake();
mdelay(30);
send_event(LCD_EVENT_ACTIVATION, NULL);
}
else
{
mdelay(30);
avr_hid_lcm_sleep();
mdelay(10);
/* Disable OSD window */
bitclr16(&IO_OSD_OSDWINMD0, 0x01);
/* disable video encoder */
bitclr16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC);
mdelay(66);
/* disable video encoder and OSD clocks */
bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
}
}
void lcd_init_device(void)
{
unsigned int addr;
/* Disable Video Encoder clock */
bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC);
/* configure GIO39, GIO34 as outputs */
IO_GIO_DIR2 &= ~((1 << 7) /* GIO39 */ | (1 << 2) /* GIO34 */);
IO_GIO_FSEL3 = (IO_GIO_FSEL3 & ~(0x300F)) |
(0x1000) /* GIO39 - FIELD_VENC */ |
(0x4); /* GIO34 - PWM1 (brightness control) */
/* OSD Clock = VENC Clock /2,
CCD clock PCLK,
VENC Clock from PLLA */
IO_CLK_SEL1 = 0x3;
/* Set VENC Clock Division to 11 (PLLA / 11 = 297 MHz / 11 = 27 MHz)
OF bootloader sets division to 8, vmlinux sets it to 11 */
IO_CLK_DIV3 = (IO_CLK_DIV3 & ~(0x1F00)) | 0xB00;
/* Enable OSD clock */
bitset16(&IO_CLK_MOD1, CLK_MOD1_OSD);
/* magic values based on OF bootloader initialization */
/* Set DAC to powerdown mode (bit 2 to 1 in VMOD) */
IO_VID_ENC_VMOD = 0x2014; /* OF sets 0x2010 */
IO_VID_ENC_VDPRO = 0x80;
IO_VID_ENC_HSPLS = 0x4;
IO_VID_ENC_HINT = 0x4B0;
IO_VID_ENC_HSTART = 0x88;
IO_VID_ENC_HVALID = 0x3C0;
IO_VID_ENC_HSDLY = 0;
IO_VID_ENC_VSPLS = 0x2;
IO_VID_ENC_VINT = 0x152;
IO_VID_ENC_VSTART = 0x6;
IO_VID_ENC_VVALID = 0x140;
IO_VID_ENC_VSDLY = 0;
IO_VID_ENC_DCLKCTL = 0x3;
IO_VID_ENC_DCLKPTN0 = 0xC;
IO_VID_ENC_VDCTL = 0x6000;
IO_VID_ENC_SYNCTL = 0x2;
IO_VID_ENC_LCDOUT = 0x101;
IO_VID_ENC_VMOD = 0x2015; /* OF sets 0x2011 */
/* Copy Rockbox frame buffer to the second framebuffer */
lcd_set_active(true);
lcd_update();
/* set framebuffer address - OF sets RAM start address to 0x1000000 */
addr = ((int)FRAME-CONFIG_SDRAM_START)/32;
IO_OSD_OSDWINADH = addr >> 16;
IO_OSD_OSDWIN0ADL = addr & 0xFFFF;
IO_OSD_BASEPX = 0x44;
IO_OSD_BASEPY = 0x6;
IO_OSD_OSDWIN0XP = 0;
IO_OSD_OSDWIN0YP = 0;
IO_OSD_OSDWIN0XL = LCD_WIDTH*2; /* OF bootloader sets 480 */
IO_OSD_OSDWIN0YL = LCD_HEIGHT; /* OF bootloader sets 320 */
IO_OSD_OSDWIN0OFST = 0xF;
IO_OSD_OSDWINMD0 = 0x25FB;/* OF bootloader sets 25C3,
vmlinux changes this to 0x25FB */
IO_OSD_VIDWINMD = 0; /* disable video windows (OF sets 0x03) */
IO_OSD_OSDWINMD1 = 0; /* disable OSD window 1 */
/* Enable Video Encoder and OSD clocks */
bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
/* Enable Video Encoder - RGB666, custom timing */
IO_VID_ENC_VMOD = 0x2015;
lcd_powered = true;
avr_hid_lcm_wake();
}
#ifdef LCD_USE_DMA
static void dma_lcd_copy_buffer_rect(int x, int y, int width, int height)
{
char *dst, *src;
/* Image buffer A is 4KW, every pixel is one Word */
/* lines is maximum number of lines we can transfer in single run */
int lines = 4096/width;
if (lines > height)
lines = height;
/* Set source and destination addresses */
dst = (char*)(FRAME + LCD_WIDTH*y + x);
src = (char*)(FBADDR(x,y));
/* Flush cache to memory */
commit_dcache();
/* Addresses are relative to start of SDRAM */
src -= CONFIG_SDRAM_START;
dst -= CONFIG_SDRAM_START;
/* Enable Image Buffer clock */
bitset16(&IO_CLK_MOD1, CLK_MOD1_IMGBUF);
/* Use Image Buffer A for DMA */
COP_BUF_MUX0 = (COP_BUF_MUX0 & 0xFFF0) | 0x0003;
/* Setup buffer offsets and transfer width/height */
COP_BUF_LOFST = width;
COP_DMA_XNUM = width;
COP_DMA_YNUM = lines;
/* DMA: No byte SWAP, no transformation, data bus shift down 0 bit */
COP_IMG_MODE &= 0xC0;
/* Set the start address of buffer */
COP_BUF_ADDR = 0x0000;
/* Setup SDRAM stride */
COP_SDEM_LOFST = LCD_WIDTH;
do {
int addr;
addr = (int)src;
addr >>= 1; /* Addresses are in 16-bit words */
/* Setup the registers to initiate the read from SDRAM */
COP_SDEM_ADDRH = addr >> 16;
COP_SDEM_ADDRL = addr & 0xFFFF;
/* Set direction and start */
COP_DMA_CTRL = 0x0001;
COP_DMA_CTRL |= 0x0002;
/* Wait for read to finish */
while (COP_DMA_CTRL & 0x02) {};
addr = (int)dst;
addr >>= 1;
COP_SDEM_ADDRH = addr >> 16;
COP_SDEM_ADDRL = addr & 0xFFFF;
/* Set direction and start transfer */
COP_DMA_CTRL = 0x0000;
COP_DMA_CTRL |= 0x0002;
/* Wait for the transfer to complete */
while (COP_DMA_CTRL & 0x02) {};
/* Decrease height, update pointers */
src += (LCD_WIDTH << 1)*lines;
dst += (LCD_WIDTH << 1)*lines;
height -= lines;
if (height < lines)
{
lines = height;
COP_DMA_YNUM = height;
}
} while (height > 0);
/* Disable Image Buffer clock */
bitclr16(&IO_CLK_MOD1, CLK_MOD1_IMGBUF);
}
/* Update a fraction of the display. */
void lcd_update_rect(int x, int y, int width, int height)
__attribute__ ((section(".icode")));
void lcd_update_rect(int x, int y, int width, int height)
{
if (!lcd_active())
return;
if ((width | height) < 0)
return; /* Nothing left to do */
if (x + width > LCD_WIDTH)
width = LCD_WIDTH - x; /* Clip right */
if (x < 0)
width += x, x = 0; /* Clip left */
if (y + height > LCD_HEIGHT)
height = LCD_HEIGHT - y; /* Clip bottom */
if (y < 0)
height += y, y = 0; /* Clip top */
dma_lcd_copy_buffer_rect(x, y, width, height);
}
/* Update the display.
This must be called after all other LCD functions that change the display. */
void lcd_update(void) __attribute__ ((section(".icode")));
void lcd_update(void)
{
if (!lcd_active())
return;
lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
}
#endif
void lcd_set_contrast(int val) {
(void) val;
// TODO:
}
void lcd_set_invert_display(bool yesno) {
(void) yesno;
// TODO:
}
void lcd_set_flip(bool yesno) {
(void) yesno;
// TODO:
}