forked from len0rd/rockbox
Sandisk Sansa Connect port (FS #12363)
Included are drivers for buttons, backlight, lcd, audio and storage. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31000 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
992d4eb775
commit
e8a8a1be43
99 changed files with 4302 additions and 46 deletions
461
firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c
Normal file
461
firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* 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 <stdio.h>
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "kernel.h"
|
||||
#include "logf.h"
|
||||
#include "avr-sansaconnect.h"
|
||||
#include "uart-target.h"
|
||||
#include "button.h"
|
||||
#include "backlight.h"
|
||||
#include "powermgmt.h"
|
||||
|
||||
//#define BUTTON_DEBUG
|
||||
|
||||
#ifdef BUTTON_DEBUG
|
||||
#include "lcd-target.h"
|
||||
#include "lcd.h"
|
||||
#include "font.h"
|
||||
#include "common.h"
|
||||
#endif
|
||||
|
||||
#ifdef BUTTON_DEBUG
|
||||
#define dbgprintf DEBUGF
|
||||
#else
|
||||
#define dbgprintf(...)
|
||||
#endif
|
||||
|
||||
#define CMD_SYNC 0xAA
|
||||
#define CMD_CLOSE 0xCC
|
||||
#define CMD_LCM_POWER 0xC9
|
||||
#define LCM_POWER_OFF 0x00
|
||||
#define LCM_POWER_ON 0x01
|
||||
#define LCM_POWER_SLEEP 0x02
|
||||
#define LCM_POWER_WAKE 0x03
|
||||
#define LCM_REPOWER_ON 0x04
|
||||
|
||||
#define CMD_STATE 0xBB
|
||||
#define CMD_VER 0xBC
|
||||
#define CMD_WHEEL_EN 0xD0
|
||||
#define CMD_SET_INTCHRG 0xD1
|
||||
#define CMD_CODEC_RESET 0xD7
|
||||
#define CMD_FILL 0xFF
|
||||
|
||||
#define CMD_SYS_CTRL 0xDA
|
||||
#define SYS_CTRL_POWEROFF 0x00
|
||||
|
||||
/* protects spi avr commands from concurrent access */
|
||||
static struct mutex avr_mtx;
|
||||
|
||||
/* buttons thread */
|
||||
#define BTN_INTERRUPT 1
|
||||
static int btn = 0;
|
||||
static bool hold_switch;
|
||||
#ifndef BOOTLOADER
|
||||
static long btn_stack[DEFAULT_STACK_SIZE/sizeof(long)];
|
||||
static const char btn_thread_name[] = "buttons";
|
||||
static struct event_queue btn_queue;
|
||||
#endif
|
||||
|
||||
static inline unsigned short be2short(unsigned char* buf)
|
||||
{
|
||||
return (unsigned short)((buf[0] << 8) | buf[1]);
|
||||
}
|
||||
|
||||
#define BUTTON_DIRECT_MASK (BUTTON_LEFT | BUTTON_UP | BUTTON_RIGHT | BUTTON_DOWN | BUTTON_SELECT | BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_NEXT | BUTTON_PREV)
|
||||
|
||||
#ifndef BOOTLOADER
|
||||
static void handle_wheel(unsigned char wheel)
|
||||
{
|
||||
static int key = 0;
|
||||
static unsigned char velocity = 0;
|
||||
static unsigned long wheel_delta = 1ul << 24;
|
||||
static unsigned char wheel_prev = 0;
|
||||
static long next_backlight_on = 0;
|
||||
static int prev_key = -1;
|
||||
static int prev_key_post = 0;
|
||||
|
||||
if (TIME_AFTER(current_tick, next_backlight_on))
|
||||
{
|
||||
backlight_on();
|
||||
reset_poweroff_timer();
|
||||
next_backlight_on = current_tick + HZ/4;
|
||||
}
|
||||
|
||||
if (wheel_prev < wheel)
|
||||
{
|
||||
key = BUTTON_SCROLL_FWD;
|
||||
velocity = wheel - wheel_prev;
|
||||
}
|
||||
else if (wheel_prev > wheel)
|
||||
{
|
||||
key = BUTTON_SCROLL_BACK;
|
||||
velocity = wheel_prev - wheel;
|
||||
}
|
||||
|
||||
if (prev_key != key && velocity < 2 /* filter "rewinds" */)
|
||||
{
|
||||
/* direction reversal */
|
||||
prev_key = key;
|
||||
wheel_delta = 1ul << 24;
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: take velocity into account */
|
||||
if (queue_empty(&button_queue))
|
||||
{
|
||||
if (prev_key_post == key)
|
||||
{
|
||||
key |= BUTTON_REPEAT;
|
||||
}
|
||||
|
||||
/* Post directly, don't update btn as avr doesn't give
|
||||
interrupt on scroll stop */
|
||||
queue_post(&button_queue, key, wheel_delta);
|
||||
|
||||
wheel_delta = 1ul << 24;
|
||||
|
||||
prev_key_post = key;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* skipped post - increment delta and limit to 7 bits */
|
||||
wheel_delta += 1ul << 24;
|
||||
|
||||
if (wheel_delta > (0x7ful << 24))
|
||||
wheel_delta = 0x7ful << 24;
|
||||
}
|
||||
|
||||
wheel_prev = wheel;
|
||||
|
||||
prev_key = key;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* buf must be 11-byte array of byte (reply from avr_hid_get_state() */
|
||||
static void parse_button_state(unsigned char *buf)
|
||||
{
|
||||
unsigned short main_btns_state = be2short(&buf[4]);
|
||||
#ifdef BUTTON_DEBUG
|
||||
unsigned short main_btns_changed = be2short(&buf[6]);
|
||||
#endif
|
||||
|
||||
/* make sure other bits doesn't conflict with our "free bits" buttons */
|
||||
main_btns_state &= BUTTON_DIRECT_MASK;
|
||||
|
||||
if (buf[3] & 0x01) /* is power button pressed? */
|
||||
{
|
||||
main_btns_state |= BUTTON_POWER;
|
||||
}
|
||||
|
||||
btn = main_btns_state;
|
||||
|
||||
#ifndef BOOTLOADER
|
||||
/* check if stored hold_switch state changed (prevents lost changes) */
|
||||
if ((buf[3] & 0x20) /* hold change notification */ ||
|
||||
(hold_switch != ((buf[3] & 0x02) >> 1)))
|
||||
{
|
||||
#endif
|
||||
hold_switch = (buf[3] & 0x02) >> 1;
|
||||
#ifdef BUTTON_DEBUG
|
||||
dbgprintf("HOLD changed (%d)", hold_switch);
|
||||
#endif
|
||||
#ifndef BOOTLOADER
|
||||
backlight_hold_changed(hold_switch);
|
||||
}
|
||||
#endif
|
||||
#ifndef BOOTLOADER
|
||||
if ((hold_switch == false) && (buf[3] & 0x80)) /* scrollwheel change */
|
||||
{
|
||||
handle_wheel(buf[2]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BUTTON_DEBUG
|
||||
if (buf[3] & 0x10) /* power button change */
|
||||
{
|
||||
/* power button state has changed */
|
||||
main_btns_changed |= BUTTON_POWER;
|
||||
}
|
||||
|
||||
if (btn & BUTTON_LEFT) dbgprintf("LEFT");
|
||||
if (btn & BUTTON_UP) dbgprintf("UP");
|
||||
if (btn & BUTTON_RIGHT) dbgprintf("RIGHT");
|
||||
if (btn & BUTTON_DOWN) dbgprintf("DOWN");
|
||||
if (btn & BUTTON_SELECT) dbgprintf("SELECT");
|
||||
if (btn & BUTTON_VOL_UP) dbgprintf("VOL UP");
|
||||
if (btn & BUTTON_VOL_DOWN) dbgprintf("VOL DOWN");
|
||||
if (btn & BUTTON_NEXT) dbgprintf("NEXT");
|
||||
if (btn & BUTTON_PREV) dbgprintf("PREV");
|
||||
if (btn & BUTTON_POWER) dbgprintf("POWER");
|
||||
if (btn & BUTTON_HOLD) dbgprintf("HOLD");
|
||||
if (btn & BUTTON_SCROLL_FWD) dbgprintf("SCROLL FWD");
|
||||
if (btn & BUTTON_SCROLL_BACK) dbgprintf("SCROLL BACK");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* HID Slave Select - GIO14 */
|
||||
#define HID_SS (1<<14)
|
||||
|
||||
static inline void select_hid(bool on)
|
||||
{
|
||||
if (on == true)
|
||||
{
|
||||
/* SS is active low */
|
||||
IO_GIO_BITCLR0 = HID_SS;
|
||||
}
|
||||
else
|
||||
{
|
||||
IO_GIO_BITSET0 = HID_SS;
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_txrx(unsigned char *buf_tx, unsigned char *buf_rx, int n)
|
||||
{
|
||||
int i;
|
||||
unsigned short rxdata;
|
||||
|
||||
mutex_lock(&avr_mtx);
|
||||
|
||||
bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF1);
|
||||
IO_SERIAL1_TX_ENABLE = 0x0001;
|
||||
select_hid(true);
|
||||
|
||||
for (i = 0; i<n; i++)
|
||||
{
|
||||
IO_SERIAL1_TX_DATA = buf_tx[i];
|
||||
udelay(100);
|
||||
|
||||
do
|
||||
{
|
||||
rxdata = IO_SERIAL1_RX_DATA;
|
||||
} while (rxdata & (1<<8));
|
||||
|
||||
if (buf_rx != NULL)
|
||||
buf_rx[i] = rxdata & 0xFF;
|
||||
|
||||
//udelay(100);
|
||||
}
|
||||
|
||||
select_hid(false);
|
||||
IO_SERIAL1_TX_ENABLE = 0;
|
||||
bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF1);
|
||||
|
||||
mutex_unlock(&avr_mtx);
|
||||
}
|
||||
|
||||
static void avr_hid_sync(void)
|
||||
{
|
||||
int i;
|
||||
unsigned char prg[4] = {CMD_SYNC, CMD_VER, CMD_FILL, CMD_CLOSE};
|
||||
|
||||
/* Send SYNC three times */
|
||||
for (i = 0; i<3; i++)
|
||||
{
|
||||
spi_txrx(prg, NULL, sizeof(prg));
|
||||
}
|
||||
}
|
||||
|
||||
void avr_hid_init(void)
|
||||
{
|
||||
/*
|
||||
setup alternate GIO functions:
|
||||
GIO29 - SIF1 Enable
|
||||
GIO30 - SIF1 Clock
|
||||
GIO31 - SIF1 Data In
|
||||
GIO32 - SIF1 Data Out
|
||||
*/
|
||||
IO_GIO_FSEL2 = (IO_GIO_FSEL2 & 0x00FF) | 0xAA00;
|
||||
|
||||
bitclr16(&IO_GIO_DIR0, HID_SS); /* set GIO14 as output */
|
||||
|
||||
/* RATE = 219 (0xDB) -> 200 kHz */
|
||||
IO_SERIAL1_MODE = 0x6DB;
|
||||
|
||||
mutex_init(&avr_mtx);
|
||||
|
||||
avr_hid_sync();
|
||||
}
|
||||
|
||||
|
||||
static void avr_hid_get_state(void)
|
||||
{
|
||||
static unsigned char cmd[11] = {CMD_SYNC, CMD_STATE,
|
||||
CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL,
|
||||
CMD_CLOSE};
|
||||
|
||||
static unsigned char buf[11];
|
||||
static unsigned char cmd_empty[1] = {0xCC};
|
||||
|
||||
spi_txrx(cmd, buf, sizeof(cmd));
|
||||
|
||||
spi_txrx(cmd_empty, NULL, 1); /* request interrupt on button press */
|
||||
|
||||
parse_button_state(buf);
|
||||
}
|
||||
|
||||
static void avr_hid_enable_wheel(void)
|
||||
{
|
||||
unsigned char wheel_en[4] = {CMD_SYNC, CMD_WHEEL_EN, 0x01, CMD_CLOSE};
|
||||
|
||||
spi_txrx(wheel_en, NULL, sizeof(wheel_en));
|
||||
}
|
||||
|
||||
/* command that is sent by "hidtool -J 1" issued on every OF boot */
|
||||
void avr_hid_enable_charger(void)
|
||||
{
|
||||
unsigned char charger_en[4] = {CMD_SYNC, CMD_SET_INTCHRG, 0x01, CMD_CLOSE};
|
||||
|
||||
spi_txrx(charger_en, NULL, sizeof(charger_en));
|
||||
}
|
||||
|
||||
void avr_hid_lcm_sleep(void)
|
||||
{
|
||||
unsigned char lcm_sleep[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_SLEEP, CMD_CLOSE};
|
||||
|
||||
spi_txrx(lcm_sleep, NULL, sizeof(lcm_sleep));
|
||||
}
|
||||
|
||||
|
||||
void avr_hid_lcm_wake(void)
|
||||
{
|
||||
unsigned char lcm_wake[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_WAKE, CMD_CLOSE};
|
||||
|
||||
spi_txrx(lcm_wake, NULL, sizeof(lcm_wake));
|
||||
}
|
||||
|
||||
void avr_hid_lcm_power_on(void)
|
||||
{
|
||||
unsigned char lcm_power_on[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_ON, CMD_CLOSE};
|
||||
|
||||
spi_txrx(lcm_power_on, NULL, sizeof(lcm_power_on));
|
||||
}
|
||||
|
||||
void avr_hid_lcm_power_off(void)
|
||||
{
|
||||
unsigned char lcm_power_off[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_OFF, CMD_CLOSE};
|
||||
|
||||
spi_txrx(lcm_power_off, NULL, sizeof(lcm_power_off));
|
||||
}
|
||||
|
||||
void avr_hid_reset_codec(void)
|
||||
{
|
||||
unsigned char codec_reset[4] = {CMD_SYNC, CMD_CODEC_RESET, CMD_CLOSE, CMD_FILL};
|
||||
|
||||
spi_txrx(codec_reset, NULL, sizeof(codec_reset));
|
||||
}
|
||||
|
||||
void avr_hid_power_off(void)
|
||||
{
|
||||
unsigned char prg[4] = {CMD_SYNC, CMD_SYS_CTRL, SYS_CTRL_POWEROFF, CMD_CLOSE};
|
||||
|
||||
spi_txrx(prg, NULL, sizeof(prg));
|
||||
}
|
||||
|
||||
#ifndef BOOTLOADER
|
||||
void btn_thread(void)
|
||||
{
|
||||
struct queue_event ev;
|
||||
|
||||
while (1)
|
||||
{
|
||||
queue_wait(&btn_queue, &ev);
|
||||
|
||||
/* Ignore all messages except BTN_INTERRUPT */
|
||||
if (ev.id != BTN_INTERRUPT)
|
||||
continue;
|
||||
|
||||
/* Enable back button interrupt */
|
||||
IO_INTC_EINT1 |= INTR_EINT1_EXT0;
|
||||
|
||||
/* Read buttons state */
|
||||
avr_hid_get_state();
|
||||
|
||||
yield();
|
||||
|
||||
if (queue_empty(&btn_queue) && ((IO_GIO_BITSET0 & 0x1) == 0))
|
||||
{
|
||||
/* for some reason we have lost next interrupt */
|
||||
queue_post(&btn_queue, BTN_INTERRUPT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GIO0(void) __attribute__ ((section(".icode")));
|
||||
void GIO0(void)
|
||||
{
|
||||
/* Clear interrupt */
|
||||
IO_INTC_IRQ1 = (1 << 5);
|
||||
/* Disable interrupt */
|
||||
IO_INTC_EINT1 &= ~INTR_EINT1_EXT0;
|
||||
|
||||
/* interrupt will be enabled back after button read */
|
||||
queue_post(&btn_queue, BTN_INTERRUPT, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void button_init_device(void)
|
||||
{
|
||||
btn = 0;
|
||||
hold_switch = false;
|
||||
#ifndef BOOTLOADER
|
||||
queue_init(&btn_queue, true);
|
||||
create_thread(btn_thread, btn_stack, sizeof(btn_stack), 0,
|
||||
btn_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE)
|
||||
IF_COP(, CPU));
|
||||
#endif
|
||||
IO_GIO_DIR0 |= 0x01; /* Set GIO0 as input */
|
||||
|
||||
/* Enable wheel */
|
||||
avr_hid_enable_wheel();
|
||||
/* Read button status and tell avr we want interrupt on next change */
|
||||
avr_hid_get_state();
|
||||
|
||||
#ifndef BOOTLOADER
|
||||
IO_GIO_IRQPORT |= 0x01; /* Enable GIO0 external interrupt */
|
||||
IO_GIO_INV0 &= ~0x01; /* Clear INV for GIO0 (falling edge detection) */
|
||||
IO_GIO_IRQEDGE &= ~0x01; /* Set edge detection (falling) */
|
||||
|
||||
/* Enable GIO0 interrupt */
|
||||
IO_INTC_EINT1 |= INTR_EINT1_EXT0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int button_read_device(void)
|
||||
{
|
||||
if(hold_switch)
|
||||
return 0;
|
||||
else
|
||||
return btn;
|
||||
}
|
||||
|
||||
bool button_hold(void)
|
||||
{
|
||||
return hold_switch;
|
||||
}
|
||||
|
||||
void lcd_enable(bool on)
|
||||
{
|
||||
(void)on;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue