星期六, 6月 30, 2007
星期五, 5月 18, 2007
串列埠控制
V1.0
email: mesmerli@hotmail.com
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>
const char *com2 = "/dev/ttyS1";
static struct termios newtios, oldtios; // terminal settings
static int saved_portfd = -1; // serial port fd
// cleanup atexit handler
static void reset_tty_atexit(void)
{
if(saved_portfd != -1) {
tcsetattr(saved_portfd, TCSANOW, &oldtios);
}
}
// cleanup signal handler
static void reset_tty_handler(int signal)
{
if(saved_portfd != -1) {
tcsetattr(saved_portfd, TCSANOW, &oldtios);
}
_exit(EXIT_FAILURE);
}
static int open_port(const char *portname)
{
struct sigaction sa;
int portfd;
printf("opening serial port: %sn", portname);
// open serial port
if((portfd = open(portname, O_RDWR|O_NOCTTY)) < 0) {
printf("open serial port %s failn", portname);
return portfd;
}
// get serial port parameters, save away
tcgetattr(portfd, &newtios);
memcpy(&oldtios, &newtios, sizeof newtios);
// configure new values
cfmakeraw(&newtios); // see man page
newtios.c_iflag |= IGNPAR; // ignore parity on input
newtios.c_oflag &= ~(OPOST|ONLCR|OLCUC|OCRNL|ONOCR|ONLRET|OFILL);
newtios.c_cflag = CS8|CLOCAL|CREAD;
newtios.c_cc[VMIN] = 1; // block until 1 char received
newtios.c_cc[VTIME] = 0; // no inter-character timer
// 115200 bps
cfsetospeed(&newtios, B115200);
cfsetispeed(&newtios, B115200);
// register cleanup stuff
atexit(reset_tty_atexit);
memset(&sa, 0 , sizeof sa);
sa.sa_handler = reset_tty_handler;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
// apply modified termios
saved_portfd = portfd;
tcflush(portfd, TCIFLUSH);
tcsetattr(portfd, TCSADRAIN, &newtios);
return portfd;
}
static void close_port(int portfd)
{
tcsetattr(portfd, TCSANOW, &oldtios);
close(portfd);
saved_portfd = -1;
}
int main(int argc, char *argv[])
{
fd_set fds;
int portfd;
int retval;
unsigned char c;
if((portfd = open_port(com2)) < 0)
return -1;
while(1) {
FD_ZERO(&fds);
FD_SET(portfd, &fds);
retval = select(portfd+1, &fds, NULL, NULL, NULL);
if(FD_ISSET(portfd, &fds)) {
if(read(portfd, &c, 1) == 1)
putchar(c);
}
fflush(NULL);
}
return 0;
}
UART driver (serial_s3c2410.c)
/*
* linux/drivers/char/serial_mx1ads.c
*
* Driver for Samsung S3C2410's internal serial ports (UART0 & UART1)
*
* Copyright (C) 2002 Steve Hein, SGI Inc. (ssh@sgi.com)
* Cho, Hyun-ho <firebird@legend.co.kr> corrected baudrate config.
*
* Adapted from:
*
* Driver for MX1s' dual serial ports.
*
* Based on drivers/serial/serial_amba.c
*
*
* Copyright 1999 ARM Limited
* Copyright (C) 2000 Deep Blue Solutions Ltd.
* Copyright (C) 2002 Shane Nay (shane@minirl.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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* Based on the AMBA driver, this is a driver for the S3C2410 boards
* UARTs.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#if defined(CONFIG_SERIAL_S3C2410_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
#include <asm/hardware/serial_s3c2410.h>
#define UART_NR 2
#define MINOR_START 64
#define SERIAL_S3C2410_NAME "ttyS"
#define SERIAL_S3C2410_MAJOR TTY_MAJOR
#define SERIAL_S3C2410_MINOR MINOR_START
#define SERIAL_S3C2410_NR UART_NR
#define CALLOUT_S3C2410_NAME "cuas"
#define CALLOUT_S3C2410_MAJOR 205
#define CALLOUT_S3C2410_MINOR MINOR_START
#define CALLOUT_S3C2410_NR UART_NR
static struct tty_driver normal, callout;
static struct tty_struct *s3c2410_table[UART_NR];
static struct termios *s3c2410_termios[UART_NR], *s3c2410_termios_locked[UART_NR];
#ifdef SUPPORT_SYSRQ
static struct console s3c2410_console;
#endif
#define S3C2410_ISR_PASS_LIMIT 256
/*
* Access macros for the S3C2410 UARTs
*/
#define UART_GET_CHAR(p) __raw_readb((p)->membase + S3C2410_UARTRXH0_OFF)
#define UART_PUT_CHAR(p,c) __raw_writeb((c), (p)->membase + S3C2410_UARTTXH0_OFF)
#define UART_GET_ULCON(p) __raw_readl((p)->membase + S3C2410_UARTLCON_OFF)
#define UART_GET_UCON(p) __raw_readl((p)->membase + S3C2410_UARTCON_OFF)
#define UART_GET_UFCON(p) __raw_readl((p)->membase + S3C2410_UARTFCON_OFF)
#define UART_GET_UMCON(p) __raw_readl((p)->membase + S3C2410_UARTMCON_OFF)
#define UART_GET_UBRDIV(p) __raw_readl((p)->membase + S3C2410_UARTBRDIV_OFF)
#define UART_GET_UTRSTAT(p) __raw_readl((p)->membase + S3C2410_UARTTRSTAT_OFF)
#define UART_GET_UERSTAT(p) __raw_readl((p)->membase + S3C2410_UARTERSTAT_OFF)
#define UART_GET_UFSTAT(p) __raw_readl((p)->membase + S3C2410_UARTFSTAT_OFF)
#define UART_GET_UMSTAT(p) __raw_readl((p)->membase + S3C2410_UARTMSTAT_OFF)
#define UART_PUT_ULCON(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTLCON_OFF)
#define UART_PUT_UCON(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTCON_OFF)
#define UART_PUT_UFCON(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTFCON_OFF)
#define UART_PUT_UMCON(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTMCON_OFF)
#define UART_PUT_UBRDIV(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTBRDIV_OFF)
/* When using the integer divisor for the UART,
* we must set the IR to 0xf prior to programming
* the divisor
*/
#define UART_RX_DATA(s) (((s) & S3C2410_UTRSTAT_RXDR) == S3C2410_UTRSTAT_RXDR)
#define UART_TX_READY(s) (((s) & S3C2410_UTRSTAT_TXFE) == S3C2410_UTRSTAT_TXFE)
#define TX_FIFOCOUNT(port) (((UART_GET_UFSTAT(port))>>4)&0xf)
#define TX_IRQ(port) (port->irq + 1)
#define RX_IRQ(port) (port->irq)
#define UART_DUMMY_RSR_RX 256
#define UART_PORT_SIZE 64
/*
* Our private driver data mappings.
*/
#define drv_old_status driver_priv
static void
s3c2410uart_stop_tx(struct uart_port *port, u_int from_tty)
{
disable_irq(TX_IRQ(port));
}
static void
s3c2410uart_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty)
{
if (nonempty) {
enable_irq(TX_IRQ(port));
}
}
static void
s3c2410uart_stop_rx(struct uart_port *port)
{
disable_irq(RX_IRQ(port));
}
static void
s3c2410uart_enable_ms(struct uart_port *port)
{
/* What is MS? */
}
static void
#ifdef SUPPORT_SYSRQ
s3c2410uart_rx_chars(struct uart_info *info, struct pt_regs *regs)
#else
s3c2410uart_rx_chars(struct uart_info *info)
#endif
{
struct tty_struct *tty = info->tty;
unsigned int status, ch, max_count = 256;
struct uart_port *port = info->port;
status = UART_GET_UTRSTAT(port);
while (UART_RX_DATA(status) && max_count--) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty->flip.tqueue.routine((void *) tty);
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
printk(KERN_WARNING "TTY_DONT_FLIP setn");
return;
}
}
ch = UART_GET_CHAR(port);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
/* No error handling just yet.
* On the MX1 these are seperate
* IRQs, so we need to deal with
* the sanity of 5 IRQs for one
* serial port before we deal
* with the error path properly.
*/
status = UART_GET_UTRSTAT(port);
}
tty_flip_buffer_push(tty);
return;
}
static void
s3c2410uart_tx_chars(struct uart_info *info)
{
struct uart_port *port = info->port;
int count;
if (port->x_char) {
UART_PUT_CHAR(port, port->x_char);
port->icount.tx++;
port->x_char = 0;
return;
}
if (info->xmit.head == info->xmit.tail
|| info->tty->stopped || info->tty->hw_stopped) {
s3c2410uart_stop_tx(port, 0);
return;
}
count = port->fifosize - TX_FIFOCOUNT(port);
do {
UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]);
info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (info->xmit.head == info->xmit.tail)
break;
} while (--count > 0);
if (CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) <
WAKEUP_CHARS)
uart_event(info, EVT_WRITE_WAKEUP);
if (info->xmit.head == info->xmit.tail)
s3c2410uart_stop_tx(info->port, 0);
}
static void
s3c2410uart_modem_status(struct uart_info *info)
{
/*
* Not currently supported.
*
*/
}
static void
s3c2410uart_int(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_info *info = dev_id;
unsigned int status;
status = UART_GET_UTRSTAT(info->port);
if(irq==(TX_IRQ(info->port))) {
if (UART_TX_READY(status))
s3c2410uart_tx_chars(info);
}
if (UART_RX_DATA(status)) {
#ifdef SUPPORT_SYSRQ
s3c2410uart_rx_chars(info, regs);
#else
s3c2410uart_rx_chars(info);
#endif
}
}
static u_int
s3c2410uart_tx_empty(struct uart_port *port)
{
return UART_GET_UTRSTAT(port) & S3C2410_UTRSTAT_TXFE ? TIOCSER_TEMT : 0;
}
static u_int
s3c2410uart_get_mctrl(struct uart_port *port)
{
/* Not currently supported */
return 0;
}
static void
s3c2410uart_set_mctrl(struct uart_port *port, u_int mctrl)
{
/* Not currently supported */
}
static void
s3c2410uart_break_ctl(struct uart_port *port, int break_state)
{
unsigned long ucon=UART_GET_UCON(port);
if (break_state==-1)
ucon |= S3C2410_UCON_SBREAK;
else
ucon &= ~S3C2410_UCON_SBREAK;
UART_PUT_UCON(port,ucon);
}
static int
s3c2410uart_startup(struct uart_port *port, struct uart_info *info)
{
int retval;
/*
* Allocate the IRQ
*/
retval = request_irq(RX_IRQ(port), s3c2410uart_int, 0, "s3c2410uarx", info);
if (retval)
return retval;
retval = request_irq(TX_IRQ(port), s3c2410uart_int, 0, "s3c2410uatx", info);
if (retval)
return retval;
UART_PUT_UCON(port, S3C2410_UCON_DEFAULT);
UART_PUT_UFCON(port, S3C2410_UFCON_DEFAULT);
UART_PUT_UMCON(port, 0);
/* printk("PORT 0x%08X ULCON 0x%08X UCON 0x%08X UFCON 0x%08X UMCON 0x%08X n",
port->membase, UART_GET_ULCON(port), UART_GET_UCON(port),
UART_GET_UFCON(port), UART_GET_UMCON(port));
*/
// UART_PUT_UBRDIV(port,(int)(PCLK/CURRENT_BAUD_RATE*16))-1);
return 0;
}
static void
s3c2410uart_shutdown(struct uart_port *port, struct uart_info *info)
{
/*
* Free the interrupt
*/
free_irq(RX_IRQ(port), info);
free_irq(TX_IRQ(port), info);
}
static void
s3c2410uart_change_speed(struct uart_port *port, u_int cflag, u_int iflag,
u_int quot)
{
unsigned long flags, lcon;
#if DEBUG
printk("s3c2410uart_set_cflag(0x%x) calledn", cflag);
#endif
/* first, disable everything */
save_flags(flags);
cli();
lcon=UART_GET_ULCON(port);
/* Clear the bits we're going to configure */
lcon &= ~(S3C2410_LCON_CFGMASK);
switch (cflag & CSIZE) {
case CS5:
lcon |= S3C2410_LCON_CS5;
break;
case CS6:
lcon |= S3C2410_LCON_CS6;
break;
case CS7:
lcon |= S3C2410_LCON_CS7;
break;
default:
lcon |= S3C2410_LCON_CS8;
break;
}
if(cflag & CSTOPB) {
lcon |= S3C2410_LCON_STOP;
}
if(cflag & PARENB) {
lcon |= (cflag & PARODD)? S3C2410_LCON_PODD:S3C2410_LCON_PEVEN;
} else {
lcon |= S3C2410_LCON_PNONE;
}
UART_PUT_ULCON(port, lcon);
if(iflag & BRKINT) {
unsigned long ucon=UART_GET_UCON(port);
ucon |= S3C2410_UCON_SBREAK;
UART_PUT_UCON(port, ucon);
}
UART_PUT_UBRDIV(port, quot);
restore_flags(flags);
}
static const char *
s3c2410uart_type(struct uart_port *port)
{
return port->type == PORT_S3C2410 ? "S3C2410" : NULL;
}
/*
* Release the memory region(s) being used by 'port'.
*/
static void
s3c2410uart_release_port(struct uart_port *port)
{
return;
}
/*
* Request the memory region(s) being used by 'port'.
*/
static int
s3c2410uart_request_port(struct uart_port *port)
{
return 0;
}
/*
* Configure/autoconfigure the port.
*/
static void
s3c2410uart_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE && s3c2410uart_request_port(port) == 0)
port->type = PORT_S3C2410;
}
static int
s3c2410uart_verify_port(struct uart_port *port, struct serial_struct *ser)
{
return 0;
}
static struct uart_ops s3c2410_pops = {
tx_empty: s3c2410uart_tx_empty,
set_mctrl: s3c2410uart_set_mctrl,
get_mctrl: s3c2410uart_get_mctrl,
stop_tx: s3c2410uart_stop_tx,
start_tx: s3c2410uart_start_tx,
stop_rx: s3c2410uart_stop_rx,
enable_ms: s3c2410uart_enable_ms,
break_ctl: s3c2410uart_break_ctl,
startup: s3c2410uart_startup,
shutdown: s3c2410uart_shutdown,
change_speed: s3c2410uart_change_speed,
type: s3c2410uart_type,
release_port: s3c2410uart_release_port,
request_port: s3c2410uart_request_port,
config_port: s3c2410uart_config_port,
verify_port: s3c2410uart_verify_port,
};
static struct uart_port s3c2410_ports[UART_NR] = {
{
membase: (void *) VA_UART_BASE,
mapbase: VA_UART_BASE,
iotype: SERIAL_IO_MEM,
irq: IRQ_RXD0,
uartclk: PCLK,
fifosize: 16,
unused: {4, 5},
ops: &s3c2410_pops,
type: PORT_S3C2410,
flags: ASYNC_BOOT_AUTOCONF,
},
{
membase: (void *) (VA_UART_BASE + S3C2410_UART1_OFF),
mapbase: (VA_UART_BASE + S3C2410_UART1_OFF),
iotype: SERIAL_IO_MEM,
irq: IRQ_RXD1,
uartclk: PCLK,
fifosize: 16,
unused: {6, 7},
ops: &s3c2410_pops,
type: PORT_S3C2410,
flags: ASYNC_BOOT_AUTOCONF,
}
};
#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
#ifdef used_and_not_const_char_pointer
static int
s3c2410uart_console_read(struct uart_port *port, char *s, u_int count)
{
unsigned int status;
int c;
#if DEBUG
printk("s3c2410uart_console_read() calledn");
#endif
c = 0;
while (c < count) {
status = UART_GET_USR2(port);
if (UART_RX_DATA(status)) {
*s++ = UART_GET_CHAR(port);
c++;
} else {
// nothing more to get, return
return c;
}
}
// return the count
return c;
}
#endif
static void
s3c2410uart_console_write(struct console *co, const char *s, u_int count)
{
struct uart_port *port = s3c2410_ports + co->index;
unsigned int status;
int i;
/*
* Now, do each character
*/
for (i = 0; i < count; i++) {
do {
status = UART_GET_UTRSTAT(port);
} while (!UART_TX_READY(status));
UART_PUT_CHAR(port, (s[i]) & 0xff);
if (s[i] == 'n') {
do {
status = UART_GET_UTRSTAT(port);
} while (!UART_TX_READY(status));
UART_PUT_CHAR(port, 'r');
}
}
}
static kdev_t
s3c2410uart_console_device(struct console *co)
{
return MKDEV(SERIAL_S3C2410_MAJOR, SERIAL_S3C2410_MINOR + co->index);
}
static int
s3c2410uart_console_wait_key(struct console *co)
{
struct uart_port *port = s3c2410_ports + co->index;
unsigned int status;
do {
status = UART_GET_UTRSTAT(port);
} while (!UART_RX_DATA(status));
return UART_GET_CHAR(port);
}
static void __init
s3c2410uart_console_get_options(struct uart_port *port, int *baud, int *parity,
int *bits)
{
#if 0
*baud = (7833600)/(UART_GET_LCR(port) +1);
#else
*baud = CURRENT_BAUD_RATE;
#endif
*bits = 8;
*parity = 'n';
}
static int __init
s3c2410uart_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = CURRENT_BAUD_RATE;
int bits = 8;
int parity = 'n';
int flow = 'n';
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
port = uart_get_console(s3c2410_ports, UART_NR, co);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
s3c2410uart_console_get_options(port, &baud, &parity, &bits);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct console s3c2410_console = {
name: SERIAL_S3C2410_NAME,
write: s3c2410uart_console_write,
#ifdef used_and_not_const_char_pointer
read: s3c2410uart_console_read,
#endif
device: s3c2410uart_console_device,
wait_key: s3c2410uart_console_wait_key,
setup: s3c2410uart_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
void __init
s3c2410uart_console_init(void)
{
register_console(&s3c2410_console);
}
#define S3C2410_CONSOLE &s3c2410_console
#else
#define S3C2410_CONSOLE NULL
#endif
static struct uart_driver s3c2410_reg = {
owner: THIS_MODULE,
normal_major: SERIAL_S3C2410_MAJOR,
#ifdef CONFIG_DEVFS_FS
normal_name: SERIAL_S3C2410_NAME "%d",
callout_name: CALLOUT_S3C2410_NAME "%d",
#else
normal_name: SERIAL_S3C2410_NAME,
callout_name: CALLOUT_S3C2410_NAME,
#endif
normal_driver :&normal,
callout_major: CALLOUT_S3C2410_MAJOR,
callout_driver: &callout,
table: s3c2410_table,
termios: s3c2410_termios,
termios_locked: s3c2410_termios_locked,
minor: SERIAL_S3C2410_MINOR,
nr: UART_NR,
port: s3c2410_ports,
cons: S3C2410_CONSOLE,
};
static int __init
s3c2410uart_init(void)
{
return uart_register_driver(&s3c2410_reg);
}
static void __exit
s3c2410uart_exit(void)
{
uart_unregister_driver(&s3c2410_reg);
}
module_init(s3c2410uart_init);
module_exit(s3c2410uart_exit);
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Steve Hein <ssh@sgi.com>");
MODULE_DESCRIPTION("Samsung S3C2410X01 serial port driver");
MODULE_LICENSE("GPL");
張貼者:
邦邦
於
星期五, 5月 18, 2007
0
意見
標籤: Linux 綜合開發篇
星期六, 5月 12, 2007
Shared Library
V1.0
email: mesmerli@hotmail.com
dltest.c (呼叫 Shared Library)
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <dlfcn.h>
#include <ctype.h>
typedef void (*func_t) (const char *);
void dltest(const char *s)
{
printf("From dltest:");
for(; *s; s++)
{
putchar(toupper(*s));
}
putchar('n');
}
int main(int argc, char **argv)
{
void *handle;
func_t fptr;
char *libname = "./libfoo.so";
char **name = NULL;
char *funcname = "foo";
char *param = "Dynamic Loading Test";
int ch;
int mode = RTLD_LAZY;
while((ch = getopt(argc, argv, "a:b:f:l:")) != EOF)
{
switch(ch)
{
case 'a': // argument
param = optarg;
break;
case 'b': // how to bind
switch(*optarg)
{
case '1': //lazy
mode = RTLD_LAZY;
break;
case 'n': // now
mode = RTLD_NOW;
break;
}
break;
case 'l': // which shared library
libname = optarg;
break;
case 'f': // which function
funcname = optarg;
}
}
// open libfoo.so
handle = dlopen(libname, mode);
if(handle == NULL)
{ // open failed
fprintf(stderr, "%s:dlopen:'%s'n", libname, dlerror());
exit(1);
}
// call dlsym lib functions to get pointers of foo or bar function in libraries
fptr = (func_t)dlsym(handle, funcname);
if(fptr == NULL)
{ // failed to get function pointers
fprintf(stderr, "%s:dlsym:'%s'n", funcname, dlerror());
exit(1);
}
// get lib name
name = (char **) dlsym(handle, "libname");
if(name == NULL)
{ // failed to get lib name
fprintf(stderr, "%s:dlsym:'libname'n", dlerror());
exit(1);
}
printf("Call '%s' in '%s':n", funcname, *name);
// call foo or bar function with 'param'parameters
(*fptr)(param);
dlclose(handle);
return 0;
}
libfoo.c (Shared Library NO.1)
#include <stdio.h>
extern void dltest(const char *s);
const char *const libname = "libfoo.so";
void foo(const char *s)
{
const char *saved = s;
dltest("Called from libfoo");
printf("libfoo:");
for(; *s; s++);
for(s--; s>=saved; s--)
{
putchar(*s);
}
putchar('n');
}
libbar.c (Shared Library NO.2)
#include <stdio.h>
extern void dltest(const char *);
const char * const libname = "libbar.so";
void bar(const char *s)
{
dltest("Called from libbar.");
printf("libbar:%sn",s);
}
Makefile_Cross
CC = arm-linux-gcc
LDFLAGS=-rdynamic
SHLDFLAGS=
RM=rm
all:dltest_Cross.exe
libfoo_Cross.o:libfoo.c
$(CC) -o $@ -c -fPIC $<
libfoo_Cross.so:libfoo_Cross.o
$(CC) $(SHLDFLAGS) -shared -o $@ $^
libbar_Cross.o:libbar.c
$(CC) -o $@ -c -fPIC $<
libbar_Cross.so:libbar_Cross.o
$(CC) $(SHLDFLAGS) -shared -o $@ $^
dltest_Cross.o:dltest.c
$(CC) -o $@ -c $^
dltest_Cross.exe: dltest_Cross.o libbar_Cross.so libfoo_Cross.so
$(CC) $(LDFLAGS) -o $@ dltest_Cross.o -ldl
clean:
$(RM) *.o *.so *.exe
張貼者:
邦邦
於
星期六, 5月 12, 2007
0
意見
標籤: Linux 綜合開發篇
MP3 Player
V1.0
email: mesmerli@hotmail.com
Makefile (playmp3)
CROSS_COMPILE = arm-linux-
LINUXDIR = /usr/src/creator/s3c2410/linux
export LINUXDIR
#
# Include the make variables (CC, etc...)
#
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
export AS LD CC CPP AR
MP3LIBNAME = mad
CFLAGS= -O0 -gdwarf-2 -DHAVE_CONFIG_H -DFPM_DEFAULT -Dlinux -Dunix -DNDEBUG -Ilibmad -I.
OBJS= demo.o bstdfile.o madlld.o
EXEC = playmp3_Cross
all: $(EXEC).exe $(EXEC)_Static.exe
$(EXEC).exe: mp3lib $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) -L./libmad -l$(MP3LIBNAME) $(LDLIBS)
$(EXEC)_Static.exe: mp3lib $(OBJS)
$(CC) $(LDFLAGS) -static -o $@ $(OBJS) -L./libmad -l$(MP3LIBNAME) $(LDLIBS)
mp3lib:
$(MAKE) -C libmad
.PHONY : clean
clean:
$(MAKE) -C libmad clean
rm -f $(EXEC) *.bin *.elf *.o *.s *.gdb *.bak *.*~ *.exe
Makefile (libmad)
CROSS_COMPILE = arm-linux-
LINUXDIR = /usr/src/creator/s3c2410/linux
INCLUDE = /usr/src/creator/s3c2410/linux/include
export LINUXDIR
#
# Include the make variables (CC, etc...)
#
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
export AS LD CC CPP AR
LIBNAME = mad
SONAME = libmad.so.1.0.0
SOVERSION = libmad.so.1.0
CFLAGS = -Wall
CC += -I$(LINUXDIR)/include
#CFLAGS= -nostartfiles -g -I. -trigraphs -DHAVE_CONFIG_H -DFPM_DEFAULT -DNDEBUG -O2 -Wa,-a=$@.l
CFLAGS= -gdwarf-2 -I. -DHAVE_CONFIG_H -DFPM_DEFAULT -DNDEBUG -Wa,-a=$@.l
AFLAGS=-mapcs-32 -msoft-float -mno-fpu $(CFLAGS)
LFLAGS= -Wl,-M,-Map=$@.map
OBJS= bit.o decoder.o fixed.o frame.o huffman.o layer12.o layer3.o stream.o synth.o timer.o
.c.o:
$(CC) -fPIC -DPIC $(CFLAGS) -c -o $@ $<
all : lib$(LIBNAME).a lib$(LIBNAME).so
lib$(LIBNAME).so: $(OBJS)
$(CC) -shared $(CFLAGS) $(OBJS) -lc -Wl,-soname -Wl,$(SOVERSION)
-o $(SONAME) &&
ln -sf $(SONAME) $(SOVERSION) &&
ln -sf $(SONAME) lib$(LIBNAME).so
lib$(LIBNAME).a: $(OBJS)
$(AR) rs libmad.a bit.o decoder.o fixed.o frame.o huffman.o layer12.o layer3.o stream.o synth.o timer.o
.PHONY : clean
clean:
rm -f *.a *.o *.bak *.*~ *.o.l *.so
Audio Driver
s3c2410-uda1341.c
/*
* Glue audio driver for the S3C2410 & Philips UDA1341 codec.
*
* Copyright (c) 2000 John Dorsey
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License.
*
* History:
*
* 2000-09-04 John Dorsey SA-1111 Serial Audio Controller support
* was initially added to the sa1100-uda1341.c
* driver.
*
* 2001-06-03 Nicolas Pitre Made this file a separate module, based on
* the former sa1100-uda1341.c driver.
*
* 2001-09-23 Russell King Remove old L3 bus driver.
* 2002-02-26 Sangwook Lee Modifed for S3C2400 <hitchcar@samsung.co.kr>
*
* 2002-06-10 Sangwook Lee Modified for S3C2410 <hitchcar@sec.samsung.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/pm.h>
#include <linux/l3/l3.h>
#include <linux/l3/uda1341.h>
#include <asm/semaphore.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include "s3c2410-audio.h"
#undef DEBUG
#ifdef DEBUG
#define DPRINTK( x... ) printk( ##x )
#define swldebug( x... ) printk( ##x)
#else
#define DPRINTK( x... )
#define swldebug( x...)
#endif
static struct l3_client uda1341;
#ifdef YOU_WANT_CHANGE_MPLL
static unsigned long SAVE_MPLLCON,SAVE_CLKDIVN;
/************************** MPLL *******************************/
static void ChangeMPllValue(int mdiv,int pdiv,int sdiv)
{
rMPLLCON=(mdiv<<12)|(pdiv<<4)|sdiv;
}
/************************ HCLK, PCLK ***************************/
static void ChangeClockDivider(int hdivn,int pdivn)
// hdivn:pdivn FCLK:HCLK:PCLK
// 0:0 1:1:1
// 0:1 1:1:2
// 1:0 1:2:2
// 1:1 1:2:4
{
rCLKDIVN=(hdivn<<1)|pdivn;
}
#endif
static int
mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
{
/*
* We only accept mixer (type 'M') ioctls.
*/
if (_IOC_TYPE(cmd) != 'M')
return -EINVAL;
// printk("%s[%d] %s : before l3_command n", __FILE__,__LINE__,__FUNCTION__);
return l3_command(&uda1341, cmd, (void *)arg);
}
static struct file_operations uda1341_mixer_fops = {
ioctl: mixer_ioctl,
owner: THIS_MODULE
};
/*
* Audio interface
*/
static void s3c2410_audio_init(void *dummy)
{
/* Initialize the UDA1341 internal state */
l3_open(&uda1341);
}
static void s3c2410_audio_shutdown(void *dummy)
{
#ifdef SWL_CHANGE_CLOCK
rCLKDIVN = SAVE_CLKDIVN;
rMPLLCON = SAVE_MPLLCON;
#endif
l3_close(&uda1341);
}
static int s3c2410_audio_ioctl( struct inode *inode, struct file *file,
uint cmd, ulong arg)
{
long val = 0;
int ret = 0;
switch (cmd) {
case SNDCTL_DSP_STEREO:
ret = get_user(val, (int *) arg);
if (ret)
return ret;
/* the UDA1341 is stereo only */
ret = (val == 0) ? -EINVAL : 1;
return put_user(ret, (int *) arg);
case SNDCTL_DSP_CHANNELS:
case SOUND_PCM_READ_CHANNELS:
/* the UDA1341 is stereo only */
return put_user(2, (long *) arg);
case SNDCTL_DSP_SPEED:
ret = get_user(val, (long *) arg);
if (ret) break;
if (val < 8000) val = 8000;
if (val > 44100) val = 44100;
/* For example
* my wav file 8.0 khz sampling rate.
* codeclk is 384fs = 8.0*384 = 3.0720Mhz
* IISPSR :
* Prescaler control A : PCLK / ( N +1) = 3.072 Mhz
* N = 50.75/3.072 -1 = 15.67 = 0x10
* Prescaler control B : A=B could be
*
*/
switch(val) {
case 8000:
#if (AUDIO_CODEC_CLOCK == 256)
rIISPSR=(24<<5)+24;
#else
rIISPSR=(15<<5)+15;
#endif
break;
case 11025:
#if (AUDIO_CODEC_CLOCK == 256)
rIISPSR=(17<<5)+17;
#else
rIISPSR=(11<<5)+11;
#endif
break;
case 22050:
#if (AUDIO_CODEC_CLOCK == 256)
rIISPSR=(16<<5)+16;
#else
#if (LCD_TYPE == TFT640_480 )
rIISPSR=(10<<5)+10;
#else
rIISPSR=(5<<5)+5;
//Prescaler_A/B= for 8.4672MHz, 1:2:4, Fclk = 203MHz
#endif
#endif
break;
case 44100:
#if (AUDIO_CODEC_CLOCK == 256)
rIISPSR=(7<<5)+7;
//Prescaler_A/B= for 11.2896MHz, 1:2:4, Fclk = 203MHz, 640*480
#else
rIISPSR=(2<<5)+2;
#endif
break;
default:
#if (AUDIO_CODEC_CLOCK == 256)
rIISPSR=(16<<5)+16;
#else
rIISPSR=(2<<5)+2;
#endif
break;
}
case SOUND_PCM_READ_RATE: /* I am n't sure what it is */
return put_user(val , (long *) arg);
case SNDCTL_DSP_SETFMT:
case SNDCTL_DSP_GETFMTS:
/*printk("%s[%d] %s : DSP_SAMPLESIZE n", __FILE__,__LINE__,__FUNCTION__); */
/* we can do 16-bit only */
return put_user(AFMT_S16_LE, (long *) arg);
default:
/* Maybe this is meant for the mixer (as per OSS Docs) */
return mixer_ioctl(inode, file, cmd, arg);
}
return ret;
}
static audio_stream_t output_stream;
static audio_state_t audio_state = {
output_stream: &output_stream,
skip_dma_init: 0, /* done locally */
hw_init: s3c2410_audio_init,
hw_shutdown: s3c2410_audio_shutdown,
client_ioctl: s3c2410_audio_ioctl,
sem: __MUTEX_INITIALIZER(audio_state.sem),
};
static int s3c2410_audio_open(struct inode *inode, struct file *file)
{
/* Acquire and initialize DMA for transfer */
output_stream.dma_ch = 2;/* used by s3c2410_audio_attach function */
audio_state.output_id = "S3C2410 Audio Out";
audio_state.output_dma = DMA2_SOURCE0;
/******* !!! VERY IMPORTANT !!!!! *************************/
/*
* THIS FUNCTION ENTRY POINT TO drivers/sound/s3c2410-audio.c
*/
return s3c2410_audio_attach(inode, file, &audio_state);
}
/*
* Missing fields of this structure will be patched with the call
* to sa1100_audio_attach().
*/
static struct file_operations s3c2410_audio_fops = {
open: s3c2410_audio_open,
owner: THIS_MODULE
};
static int audio_dev_id, mixer_dev_id;
static int __init s3c2410_uda1341_init(void)
{
struct uda1341_cfg cfg;
int ret;
DPRINTK(" s3c2410_uda1341_init n");
ret = l3_attach_client(&uda1341, "l3-s3c2410", "uda1341");
if (ret) goto out;
#if (AUDIO_CODEC_CLOCK == 256)
cfg.fs = 256;
#else
cfg.fs = 384;
#endif
cfg.format = FMT_I2S;
/*---------------!!! VERY IMPORTANT !!!!---------------
*
* THIS FUNCTION ENTRY POINT TO drivers/l3/l3-s3c2410.c
* l3_command defined in include/linux/l3/l3.h
*/
/*
*
l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg);
------> drivers/l3/l3-s3c2410.c init1341();
*
*/
l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg);
/* register devices */
audio_dev_id = register_sound_dsp(&s3c2410_audio_fops, -1);
mixer_dev_id = register_sound_mixer(&uda1341_mixer_fops, -1);
printk(KERN_INFO "S3C2410 UDA1341 / IIS initializedn");
return 0;
out:
return ret;
}
static void __exit s3c2410_uda1341_exit(void)
{
unregister_sound_dsp(audio_dev_id);
unregister_sound_mixer(mixer_dev_id);
s3c2410_free_dma(output_stream.dma_ch);
/* s3c2410_free_dma(input_stream.dma_ch); */
l3_detach_client(&uda1341);
}
module_init(s3c2410_uda1341_init);
module_exit(s3c2410_uda1341_exit);
MODULE_AUTHOR("Sangwook Lee ");
MODULE_DESCRIPTION("Audio driver for the S3C2410 & Philips UDA1341 codec.");
EXPORT_NO_SYMBOLS;
s3c2410-audio.c
/*
* Common audio handling for the S3C2410 processor
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License.
*
*
* This module handles the generic buffering/DMA/mmap audio interface for
* codecs connected to the S3C2410 chip. All features depending on specific
* hardware implementations like supported audio formats or samplerates are
* relegated to separate specific modules.
*
*
* 2001-10-19 Nicolas Pitre - brought DMA registration processing
* into this module for better ressource
* management. This also fixes a bug
* with the suspend/resume logic.
* 2002-03-4 Sangwook Lee <hitchcar@samsung.co.kr>
* modified for s3c2410
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/pm.h>
#include <linux/errno.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/sysrq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/semaphore.h>
#include <asm/dma.h>
#include "s3c2410-audio.h"
#ifdef DEBUG
#define DPRINTK( x... ) printk( ##x )
#define swldebug(x...) printk( ##x )
#else
#define DPRINTK( x... )
#define swldebug( x... )
#endif
#define AUDIO_NAME "s3c2410-audio"
#define AUDIO_NBFRAGS_DEFAULT 16
/* 8192 works on 22.05Khz but not 44.1Khz */
#define AUDIO_FRAGSIZE_DEFAULT 16384
#define NEXT_BUF(_s_,_b_) {
(_s_)->_b_##_idx++;
(_s_)->_b_##_idx %= (_s_)->nbfrags;
(_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; }
#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)
/*
* This function frees all buffers
*/
static void audio_clear_buf(audio_stream_t * s)
{
DPRINTK("audio_clear_bufn");
/* ensure DMA won't run anymore */
s->active = 0;
s->stopped = 0;
s3c2410_dma_flush_all(s->dma_ch);
if (s->buffers) {
int frag;
for (frag = 0; frag < s->nbfrags; frag++) {
if (!s->buffers[frag].master)
continue;
consistent_free(s->buffers[frag].start,
s->buffers[frag].master,
s->buffers[frag].dma_addr);
}
kfree(s->buffers);
s->buffers = NULL;
}
s->buf_idx = 0;
s->buf = NULL;
}
/*
* This function allocates the buffer structure array and buffer data space
* according to the current number of fragments and fragment size.
*/
static int audio_setup_buf(audio_stream_t * s)
{
int frag;
int dmasize = 0;
char *dmabuf = NULL;
dma_addr_t dmaphys = 0;
if (s->buffers) return -EBUSY;
s->buffers = (audio_buf_t *)
kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
if (!s->buffers) goto err;
memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags);
for (frag = 0; frag < s->nbfrags; frag++) {
audio_buf_t *b = &s->buffers[frag];
/*
* Let's allocate non-cached memory for DMA buffers.
* We try to allocate all memory at once.
* If this fails (a common reason is memory fragmentation),
* then we allocate more smaller buffers.
*/
if (!dmasize) {
dmasize = (s->nbfrags - frag) * s->fragsize;
do {
dmabuf =
consistent_alloc(GFP_KERNEL|GFP_DMA, dmasize, &dmaphys);
if (!dmabuf) dmasize -= s->fragsize;
} while (!dmabuf && dmasize);
if (!dmabuf) goto err;
b->master = dmasize;
memzero(dmabuf, dmasize);
}
b->start = dmabuf;
b->dma_addr = dmaphys;
b->stream = s;
sema_init(&b->sem, 1);
DPRINTK("buf %d: virt_start %p dma_addr %pn", frag, b->start,
b->dma_addr);
dmabuf += s->fragsize;
dmaphys += s->fragsize;
dmasize -= s->fragsize;
}
s->buf_idx = 0;
s->buf = &s->buffers[0];
s->bytecount = 0;
s->fragcount = 0;
return 0;
err:
printk(AUDIO_NAME ": unable to allocate audio memoryn ");
audio_clear_buf(s);
return -ENOMEM;
}
/*
* This function yanks all buffers from the DMA code's control and
* resets them ready to be used again.
*/
static void audio_reset_buf(audio_stream_t * s)
{
int frag;
s->active = 0;
s->stopped = 0;
s3c2410_dma_flush_all(s->dma_ch);
if (s->buffers) {
for (frag = 0; frag < s->nbfrags; frag++) {
audio_buf_t *b = &s->buffers[frag];
b->size = 0;
sema_init(&b->sem, 1);
}
}
s->bytecount = 0;
s->fragcount = 0;
}
/*
* DMA callback functions
*/
static void audio_dmaout_done_callback(void *buf_id, int size)
{
audio_buf_t *b = (audio_buf_t *) buf_id;
audio_stream_t *s = b->stream;
/* Accounting */
s->bytecount += size;
s->fragcount++;
/* Recycle buffer */
if (s->mapped) {
s3c2410_dma_queue_buffer(s->dma_ch, buf_id,
b->dma_addr, s->fragsize);
}
else{
up(&b->sem);
}
/* And any process polling on write. */
wake_up(&s->wq);
}
static void audio_dmain_done_callback(void *buf_id, int size)
{
audio_buf_t *b = (audio_buf_t *) buf_id;
audio_stream_t *s = b->stream;
/* Accounting */
s->bytecount += size;
s->fragcount++;
/* Recycle buffer */
if (s->mapped) {
s3c2410_dma_queue_buffer(s->dma_ch, buf_id,
b->dma_addr, s->fragsize);
} else {
b->size = size;
up(&b->sem);
}
/* And any process polling on write. */
wake_up(&s->wq);
}
static int audio_sync(struct file *file)
{
audio_state_t *state = (audio_state_t *)file->private_data;
audio_stream_t *s = state->output_stream;
audio_buf_t *b;
DPRINTK("audio_syncn");
if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped)
return 0;
/*
* Send current buffer if it contains data. Be sure to send
* a full sample count.
*/
b = s->buf;
if (b->size &= ~3) {
down(&b->sem);
s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,
b->dma_addr, b->size);
b->size = 0;
NEXT_BUF(s, buf);
}
/*
* Let's wait for the last buffer we sent i.e. the one before the
* current buf_idx. When we acquire the semaphore, this means either:
* - DMA on the buffer completed or
* - the buffer was already free thus nothing else to sync.
*/
b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags);
if (down_interruptible(&b->sem))
return -EINTR;
up(&b->sem);
return 0;
}
static int audio_write(struct file *file, const char *buffer,
size_t count, loff_t * ppos)
{
const char *buffer0 = buffer;
audio_state_t *state = (audio_state_t *)file->private_data;
audio_stream_t *s = state->output_stream;
int chunksize, ret = 0;
// printk("n 1~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ n");
// printk(__FUNCTION__ " continue write fucntion here n");
if (ppos != &file->f_pos)return -ESPIPE;
if (s->mapped) return -ENXIO;
if (!s->buffers && audio_setup_buf(s)) return -ENOMEM;
while (count > 0) {
audio_buf_t *b = s->buf;
/* Wait for a buffer to become free */
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
if (down_trylock(&b->sem)) break;
} else {
ret = -ERESTARTSYS;
if (down_interruptible(&b->sem)) break;
}
/* Feed the current buffer */
chunksize = s->fragsize - b->size;
// printk(__FUNCTION__ " s->fragsize 0x%08X b->size 0x%08X count 0x%08X n", s->fragsize,b->size,count );
if (chunksize > count) chunksize = count;
DPRINTK("write %d to buf_idx %dn", chunksize, s->buf_idx);
if (copy_from_user(b->start + b->size, buffer, chunksize)) {
up(&b->sem);
return -EFAULT;
}
b->size += chunksize;
buffer += chunksize;
count -= chunksize;
if (b->size < s->fragsize) {
// printk(" b->size 0x%08X s->fragsize 0x%08X n",b->size,s->fragsize);
up(&b->sem);
break;
}
/*
* Send current buffer to dma
* b->dma_addr is physical address
*/
s->active = 1;
s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,
b->dma_addr, b->size);
b->size = 0; /* indicate that the buffer has been sent */
NEXT_BUF(s, buf);
}
if ((buffer - buffer0)) ret = buffer - buffer0;
DPRINTK("audio_write: return=%dn", ret);
return ret;
}
static inline void audio_check_tx_spin(audio_state_t *state)
{
/*
* With some codecs like the Philips UDA1341 we must ensure
* there is an output stream at any time while recording since
* this is how the UDA1341 gets its clock from the S3C2410.
* So while there is no playback data to send, the output DMA
* will spin with all zeroes. We use the cache flush special
* area for that.
*/
#ifdef S3C2410_AUDIO_REC
if (state->need_tx_for_rx && !state->tx_spinning) {
s3c2410_dma_set_spin(state->output_stream->dma_ch,
(dma_addr_t)FLUSH_BASE_PHYS, 2048);
state->tx_spinning = 1;
}
#endif
}
static void audio_prime_dma(audio_stream_t *s)
{
int i;
s->active = 1;
for (i = 0; i < s->nbfrags; i++) {
audio_buf_t *b = s->buf;
down(&b->sem);
s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,
b->dma_addr, s->fragsize);
NEXT_BUF(s, buf);
}
}
static int audio_read(struct file *file, char *buffer,
size_t count, loff_t * ppos)
{
char *buffer0 = buffer;
audio_state_t *state = (audio_state_t *)file->private_data;
audio_stream_t *s = state->input_stream;
int chunksize, ret = 0;
DPRINTK("audio_read: count=%dn", count);
if (ppos != &file->f_pos)
return -ESPIPE;
if (s->mapped)
return -ENXIO;
if (!s->active) {
if (!s->buffers && audio_setup_buf(s))
return -ENOMEM;
audio_check_tx_spin(state);
audio_prime_dma(s);
}
while (count > 0) {
audio_buf_t *b = s->buf;
/* Wait for a buffer to become full */
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
if (down_trylock(&b->sem))
break;
} else {
ret = -ERESTARTSYS;
if (down_interruptible(&b->sem))
break;
}
/* Grab data from the current buffer */
chunksize = b->size;
if (chunksize > count)
chunksize = count;
DPRINTK("read %d from %dn", chunksize, s->buf_idx);
if (copy_to_user(buffer,
b->start + s->fragsize - b->size,
chunksize)) {
up(&b->sem);
return -EFAULT;
}
b->size -= chunksize;
buffer += chunksize;
count -= chunksize;
if (b->size > 0) {
up(&b->sem);
break;
}
/* Make current buffer available for DMA again */
s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,
b->dma_addr, s->fragsize);
NEXT_BUF(s, buf);
}
if ((buffer - buffer0))
ret = buffer - buffer0;
DPRINTK("audio_read: return=%dn", ret);
return ret;
}
static int audio_mmap(struct file *file, struct vm_area_struct *vma)
{
audio_state_t *state = (audio_state_t *)file->private_data;
audio_stream_t *s;
unsigned long size, vma_addr;
int i, ret;
if (vma->vm_pgoff != 0)
return -EINVAL;
if (vma->vm_flags & VM_WRITE) {
if (!state->wr_ref)
return -EINVAL;;
s = state->output_stream;
} else if (vma->vm_flags & VM_READ) {
if (!state->rd_ref)
return -EINVAL;
s = state->input_stream;
} else return -EINVAL;
if (s->mapped)
return -EINVAL;
size = vma->vm_end - vma->vm_start;
if (size != s->fragsize * s->nbfrags)
return -EINVAL;
if (!s->buffers && audio_setup_buf(s))
return -ENOMEM;
vma_addr = vma->vm_start;
for (i = 0; i < s->nbfrags; i++) {
audio_buf_t *buf = &s->buffers[i];
if (!buf->master)
continue;
ret = remap_page_range(vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot);
if (ret)
return ret;
vma_addr += buf->master;
}
s->mapped = 1;
return 0;
}
static unsigned int audio_poll(struct file *file,
struct poll_table_struct *wait)
{
audio_state_t *state = (audio_state_t *)file->private_data;
audio_stream_t *is = state->input_stream;
audio_stream_t *os = state->output_stream;
unsigned int mask = 0;
int i;
DPRINTK("audio_poll(): mode=%s%sn",
(file->f_mode & FMODE_READ) ? "r" : "",
(file->f_mode & FMODE_WRITE) ? "w" : "");
if (file->f_mode & FMODE_READ) {
/* Start audio input if not already active */
if (!is->active) {
if (!is->buffers && audio_setup_buf(is))
return -ENOMEM;
audio_check_tx_spin(state);
audio_prime_dma(is);
}
poll_wait(file, &is->wq, wait);
}
if (file->f_mode & FMODE_WRITE) {
if (!os->buffers && audio_setup_buf(os))
return -ENOMEM;
poll_wait(file, &os->wq, wait);
}
if (file->f_mode & FMODE_READ) {
if (is->mapped) {
if (is->bytecount > 0)
mask |= POLLIN | POLLRDNORM;
} else {
for (i = 0; i < is->nbfrags; i++) {
if (atomic_read(&is->buffers[i].sem.count) > 0) {
mask |= POLLIN | POLLRDNORM;
break;
}
}
}
}
if (file->f_mode & FMODE_WRITE) {
if (os->mapped) {
if (os->bytecount > 0)
mask |= POLLOUT | POLLWRNORM;
} else {
for (i = 0; i < os->nbfrags; i++) {
if (atomic_read(&os->buffers[i].sem.count) > 0) {
mask |= POLLOUT | POLLWRNORM;
break;
}
}
}
}
DPRINTK("audio_poll() returned mask of %s%sn",
(mask & POLLIN) ? "r" : "",
(mask & POLLOUT) ? "w" : "");
return mask;
}
static loff_t audio_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
static int audio_set_fragments(audio_stream_t *s, int val)
{
if (s->active)
return -EBUSY;
if (s->buffers)
audio_clear_buf(s);
s->nbfrags = (val >> 16) & 0x7FFF;
val &= 0xffff;
if (val < 4)
val = 4;
if (val > 15)
val = 15;
s->fragsize = 1 << val;
if (s->nbfrags < 2)
s->nbfrags = 2;
if (s->nbfrags * s->fragsize > 128 * 1024)
s->nbfrags = 128 * 1024 / s->fragsize;
if (audio_setup_buf(s))
return -ENOMEM;
return val|(s->nbfrags << 16);
}
static int audio_ioctl(struct inode *inode, struct file *file,
uint cmd, ulong arg)
{
audio_state_t *state = (audio_state_t *)file->private_data;
audio_stream_t *os = state->output_stream;
audio_stream_t *is = state->input_stream;
long val;
/* dispatch based on command */
switch (cmd) {
case OSS_GETVERSION:
return put_user(SOUND_VERSION, (int *)arg);
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE)
return put_user(os->fragsize, (int *)arg);
else
return put_user(is->fragsize, (int *)arg);
case SNDCTL_DSP_GETCAPS:
val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP;
if (is && os)
val |= DSP_CAP_DUPLEX;
return put_user(val, (int *)arg);
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, (long *) arg))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
int ret = audio_set_fragments(is, val);
if (ret < 0)
return ret;
ret = put_user(ret, (int *)arg);
if (ret)
return ret;
}
if (file->f_mode & FMODE_WRITE) {
int ret = audio_set_fragments(os, val);
if (ret < 0)
return ret;
ret = put_user(ret, (int *)arg);
if (ret)
return ret;
}
return 0;
case SNDCTL_DSP_SYNC:
return audio_sync(file);
case SNDCTL_DSP_SETDUPLEX:
return 0;
case SNDCTL_DSP_POST:
return 0;
case SNDCTL_DSP_GETTRIGGER:
val = 0;
if (file->f_mode & FMODE_READ && is->active && !is->stopped)
val |= PCM_ENABLE_INPUT;
if (file->f_mode & FMODE_WRITE && os->active && !os->stopped)
val |= PCM_ENABLE_OUTPUT;
return put_user(val, (int *)arg);
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, (int *)arg))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT) {
if (!is->active) {
if (!is->buffers && audio_setup_buf(is))
return -ENOMEM;
audio_prime_dma(is);
}
audio_check_tx_spin(state);
if (is->stopped) {
is->stopped = 0;
s3c2410_dma_resume(is->dma_ch);
}
} else {
s3c2410_dma_stop(is->dma_ch);
is->stopped = 1;
}
}
if (file->f_mode & FMODE_WRITE) {
if (val & PCM_ENABLE_OUTPUT) {
if (!os->active) {
if (!os->buffers && audio_setup_buf(os))
return -ENOMEM;
if (os->mapped)
audio_prime_dma(os);
}
if (os->stopped) {
os->stopped = 0;
s3c2410_dma_resume(os->dma_ch);
}
} else {
s3c2410_dma_stop(os->dma_ch);
os->stopped = 1;
}
}
return 0;
case SNDCTL_DSP_GETOPTR:
case SNDCTL_DSP_GETIPTR:
{
count_info inf = { 0, };
audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is;
audio_buf_t *b;
dma_addr_t ptr;
int bytecount, offset, flags;
if ((s == is && !(file->f_mode & FMODE_READ)) ||
(s == os && !(file->f_mode & FMODE_WRITE)))
return -EINVAL;
if (s->active) {
save_flags_cli(flags);
offset = 0;
bytecount = s->bytecount + offset;
s->bytecount = -offset;
inf.blocks = s->fragcount;
s->fragcount = 0;
restore_flags(flags);
if (bytecount < 0)
bytecount = 0;
inf.bytes = bytecount;
}
return copy_to_user((void *)arg, &inf, sizeof(inf));
}
case SNDCTL_DSP_GETOSPACE:
{
audio_buf_info inf = { 0, };
int i;
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (!os->buffers && audio_setup_buf(os))
return -ENOMEM;
for (i = 0; i < os->nbfrags; i++) {
if (atomic_read(&os->buffers[i].sem.count) > 0) {
if (os->buffers[i].size == 0)
inf.fragments++;
inf.bytes += os->fragsize - os->buffers[i].size;
}
}
inf.fragstotal = os->nbfrags;
inf.fragsize = os->fragsize;
return copy_to_user((void *)arg, &inf, sizeof(inf));
}
case SNDCTL_DSP_GETISPACE:
{
audio_buf_info inf = { 0, };
int i;
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
if (!is->buffers && audio_setup_buf(is))
return -ENOMEM;
for (i = 0; i < is->nbfrags; i++) {
if (atomic_read(&is->buffers[i].sem.count) > 0) {
if (is->buffers[i].size == is->fragsize)
inf.fragments++;
inf.bytes += is->buffers[i].size;
}
}
inf.fragstotal = is->nbfrags;
inf.fragsize = is->fragsize;
return copy_to_user((void *)arg, &inf, sizeof(inf));
}
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_RESET:
if (file->f_mode & FMODE_READ) {
audio_reset_buf(is);
}
if (file->f_mode & FMODE_WRITE) {
audio_reset_buf(os);
}
return 0;
default:
/*
* Let the client of this module handle the
* non generic ioctls
* your can find other functions in s3c2410-uda1341.c
*/
return state->client_ioctl(inode, file, cmd, arg);
}
return 0;
}
static int audio_release(struct inode *inode, struct file *file)
{
audio_state_t *state = (audio_state_t *)file->private_data;
DPRINTK("audio_releasen");
down(&state->sem);
if (file->f_mode & FMODE_READ) {
audio_clear_buf(state->input_stream);
if (!state->skip_dma_init) {
s3c2410_free_dma(state->input_stream->dma_ch);
if (state->need_tx_for_rx && !state->wr_ref)
s3c2410_free_dma(state->output_stream->dma_ch);
}
state->rd_ref = 0;
}
if (file->f_mode & FMODE_WRITE) {
audio_sync(file);
audio_clear_buf(state->output_stream);
if (!state->skip_dma_init)
if (!state->need_tx_for_rx || !state->rd_ref) {
s3c2410_free_dma(state->output_stream->dma_ch);
}
state->wr_ref = 0;
}
if (!AUDIO_ACTIVE(state)) {
if (state->hw_shutdown)
state->hw_shutdown(state->data);
}
up(&state->sem);
return 0;
}
extern int s3c2410_audio_attach(struct inode *inode, struct file *file,
audio_state_t *state)
{
int err, need_tx_dma;
down(&state->sem);
/* access control */
err = -ENODEV;
if ((file->f_mode & FMODE_WRITE) && !state->output_stream)
goto out;
err = -EBUSY;
if ((file->f_mode & FMODE_WRITE) && state->wr_ref)
goto out;
/* request DMA channels */
need_tx_dma = (file->f_mode & FMODE_WRITE);
if (state->wr_ref) need_tx_dma = 0;
if (need_tx_dma) {
err = s3c2410_request_dma(state->output_stream->dma_ch,
state->output_id,
state->output_dma);
if (err) goto out;
}
/* now complete initialisation */
if (!AUDIO_ACTIVE(state)) {
if (state->hw_init)state->hw_init(state->data);
}
if ((file->f_mode & FMODE_WRITE)) {
state->wr_ref = 1;
audio_clear_buf(state->output_stream);
state->output_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT;
state->output_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT;
state->output_stream->mapped = 0;
s3c2410_dma_set_callback(state->output_stream->dma_ch,
audio_dmaout_done_callback);
init_waitqueue_head(&state->output_stream->wq);
}
file->private_data = state;
file->f_op->release = audio_release;
file->f_op->write = audio_write;
file->f_op->read = audio_read;
file->f_op->mmap = audio_mmap;
file->f_op->poll = audio_poll;
file->f_op->ioctl = audio_ioctl;
file->f_op->llseek = audio_llseek;
err = 0;
out:
up(&state->sem);
return err;
}
//EXPORT_SYMBOL(s3c2410_audio_attach);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Common audio handling for the SA11x0 processor");
MODULE_LICENSE("GPL");
s3c2410-audio.h
/*
* Common audio handling for the S3C2400
*
* Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License.
*/
/*
* Buffer Management
*/
typedef struct {
int size; /* buffer size */
char *start; /* points to actual buffer */
dma_addr_t dma_addr; /* physical buffer address */
struct semaphore sem; /* down before touching the buffer */
int master; /* owner for buffer allocation, contain size when true */
struct audio_stream_s *stream; /* owning stream */
struct semaphore *wsem; /* down before touching the buffer */
} audio_buf_t;
typedef struct audio_stream_s {
audio_buf_t *buffers; /* pointer to audio buffer structures */
audio_buf_t *buf; /* current buffer used by read/write */
u_int buf_idx; /* index for the pointer above... */
u_int fragsize; /* fragment i.e. buffer size */
u_int nbfrags; /* nbr of fragments i.e. buffers */
int bytecount; /* nbr of processed bytes */
int fragcount; /* nbr of fragment transitions */
dmach_t dma_ch; /* DMA channel ID */
wait_queue_head_t wq; /* for poll */
int mapped:1; /* mmap()'ed buffers */
int active:1; /* actually in progress */
int stopped:1; /* might be active but stopped */
} audio_stream_t;
/*
* State structure for one instance
*/
typedef struct {
audio_stream_t *output_stream;
audio_stream_t *input_stream;
dma_device_t output_dma;
dma_device_t input_dma;
char *output_id;
char *input_id;
int rd_ref:1; /* open reference for recording */
int wr_ref:1; /* open reference for playback */
int need_tx_for_rx:1; /* if data must be sent while receiving */
int tx_spinning:1; /* tx spinning active */
int skip_dma_init:1; /* hack for the S3C2400 */
void *data;
void (*hw_init)(void *);
void (*hw_shutdown)(void *);
int (*client_ioctl)(struct inode *, struct file *, uint, ulong);
struct pm_dev *pm_dev;
struct semaphore sem; /* to protect against races in attach() */
} audio_state_t;
/*
* Functions exported by this module
*/
extern int s3c2410_audio_attach( struct inode *inode, struct file *file,
audio_state_t *state);
張貼者:
邦邦
於
星期六, 5月 12, 2007
0
意見
標籤: Linux 綜合開發篇
LCD Bitmap Demo
V1.0
email: mesmerli@hotmail.com
實驗步驟
bmphead.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#pragma pack(1)
struct BITMAP_HEAD {
char bfType[2];
unsigned int bfSize;
char bfReserved1[2];
char bfReserved2[2];
unsigned int bfOffBits;
unsigned int biHeadSize;
unsigned int biWidth;
unsigned int biHeight;
char biPlanes[2];
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
unsigned int biXPelsPerMeter;
unsigned int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
};
#pragma pack(0)
struct BITMAP_HEAD bmpHead;
char bmpdata[128];
int main()
{
int fd;
int i;
fd = open("panda.bmp", O_RDONLY);
if(fd == -1)
{
printf("open-file failed!rn");
}
i = read(fd, &bmpHead, sizeof(struct BITMAP_HEAD));
printf("// BITMAP HEAD SIZE: %drn", i);
printf("// bfType: %c%crn", bmpHead.bfType[0], bmpHead.bfType[1]);
printf("// bfOffBits: %drn", bmpHead.bfOffBits);
printf("// biHeadSize: %drn", bmpHead.biHeadSize);
printf("// biWidth: %drn", bmpHead.biWidth);
printf("// biHeight: %drn", bmpHead.biHeight);
printf("// biBitCount: %drn", bmpHead.biBitCount);
printf("// biCompression: %drn", bmpHead.biCompression);
lseek(fd, bmpHead.bfOffBits, SEEK_SET);
while(1) {
i = read(fd, bmpdata, 32);
if(i == 0)
{
break;
}
for(i=0; i<16; i++)
{
printf("0x%02x", 0xff & (
(bmpdata[2*i] & 0xc0) |
((bmpdata[2*i] & 0x0c) << 2) |
((bmpdata[2*i+1] & 0xc0) >> 4) |
((bmpdata[2*i+1] & 0x0c) >> 2)
));
i++;
printf("%02x,", 0xff & (
(bmpdata[2*i] & 0xc0) |
((bmpdata[2*i] & 0x0c) << 2) |
((bmpdata[2*i+1] & 0xc0) >> 4) |
((bmpdata[2*i+1] & 0x0c) >> 2)
));
}
printf("rn");
}
close(fd);
return 0;
}
lcd-demo.c
#include <signal.h>
#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>
#include "lcd-creator.h"
#include "panda.c"
int main()
{
int fd;
lcd_write_info_t display;
lcd_full_image_info_t img;
int i, j, shift;
int ret;
unsigned short pattern, ImgValue, pixelvalue;
fd = open("/dev/lcd0", O_RDWR);
if (fd < 0)
{
printf("open /dev/lcd0 errorn");
return (-1);
}
for(i=0; i<0x800; i++)
{
pattern = ~panda_img[i];
ImgValue = 0;
shift = 14;
for (j=0; j < 8; j++){
pixelvalue = pattern & 3;
pixelvalue <<= shift;
ImgValue += pixelvalue;
shift -=2;
pattern >>=2;
}
img.data[i] = ImgValue;
}
ret = ioctl(fd, LCD_IOCTL_DRAW_FULL_IMAGE, &img);
// display.DB_Row=0;
// ret = ioctl(fd,LCD_IOCTL_DB_HEIGHT,&display);
printf("Hello World!!!n");
return 0;
}
張貼者:
邦邦
於
星期六, 5月 12, 2007
0
意見
標籤: Linux 綜合開發篇
Frame Buffer
V1.0
email: mesmerli@hotmail.com
fbdemo.c
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/fb.h>
#define FBDEV "/dev/fb0"
static char* default_framebuffer=FBDEV;
struct fb_dev
{
int fb;
void *fb_mem;
int fb_width, fb_height, fb_line_len, fb_size;
int fb_bpp;
};
static struct fb_dev fbdev;
static void draw(int color)
{
int i, j;
unsigned short int *p=(unsigned short int*) fbdev.fb_mem;
for(i=0; i<fbdev.fb_height; i++, p+=fbdev.fb_line_len/2)
{
for(j=0; j<fbdev.fb_width; j++)
p[j]=color;
}
}
int framebuffer_open(void)
{
int fb;
struct fb_var_screeninfo fb_vinfo;
struct fb_fix_screeninfo fb_finfo;
char* fb_dev_name = NULL;
if(!(fb_dev_name = getenv("FRAMEBUFFER")))
{
fb_dev_name = default_framebuffer;
fb = open(fb_dev_name, O_RDWR);
if(fb<0)
{
printf("device %s open failedn", fb_dev_name);
return -1;
}
}
if(ioctl(fb, FBIOGET_VSCREENINFO, &fb_vinfo))
{
printf("Can't get VSCREENINFO: %sn", strerror(errno));
close(fb);
return -1;
}
if(ioctl(fb, FBIOGET_FSCREENINFO, &fb_finfo))
{
printf("Can't get FSCREENINFO: %sn", strerror(errno));
return 1;
}
fbdev.fb_bpp = fb_vinfo.red.length + fb_vinfo.green.length + fb_vinfo.blue.length + fb_vinfo.transp.length;
fbdev.fb_width = fb_vinfo.xres;
fbdev.fb_height = fb_vinfo.yres;
fbdev.fb_line_len = fb_finfo.line_length;
fbdev.fb_size = fb_finfo.smem_len;
printf("frame buffer: %d(%d)x%d, %dbpp, 0x%xbyten",
fbdev.fb_width, fbdev.fb_line_len, fbdev.fb_height,
fbdev.fb_bpp, fbdev.fb_size);
if(fbdev.fb_bpp!=16)
{
printf("frame buffer must be 16bpp moden");
exit(0);
}
fbdev.fb_mem = mmap(NULL, fbdev.fb_size,
PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
if(fbdev.fb_mem == NULL || (int) fbdev.fb_mem == -1)
{
fbdev.fb_mem = NULL;
printf("mmap failedn");
close(fb);
return -1;
}
fbdev.fb = fb;
memset(fbdev.fb_mem, 0x0, fbdev.fb_size);
return 0;
}
void framebuffer_close()
{
if(fbdev.fb_mem)
{
munmap(fbdev.fb_mem, fbdev.fb_size);
fbdev.fb_mem = NULL;
}
if(fbdev.fb)
{
close(fbdev.fb);
fbdev.fb = 0;
}
}
int main(void)
{
int i;
framebuffer_open();
for(i=0; i<16; i++)
{
printf("%d:color=0x%x",i,1<<i);
draw(1<<i);
getchar();
}
framebuffer_close();
return 0;
}
Frame Buffer Driver
s3c2410fb_3_5.c
/*
* linux/drivers/video/s3c2410fb.c
*
* CopyRight (C) 2002 SAMSUNG ELECTRONICS
* SW.LEE <hitchcar@sec.samsung.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <video/fbcon.h>
#include <video/fbcon-mfb.h>
#include <video/fbcon-cfb4.h>
#include <video/fbcon-cfb8.h>
#include <video/fbcon-cfb16.h>
#define LCD_XRES 240
#define LCD_YRES 320
#define LCD_VBPD ((2-1)&0xff)
#define LCD_VFPD ((3-1)&0xff)
#define LCD_VSPW ((2-1)&0x3f)
#define LCD_HBPD ((40-1)&0x7f)
#define LCD_HFPD ((20-1)&0xff)
#define LCD_HSPW ((20-1)&0xff)
#define LCD_CLKVAL_TFT (7)
/*
* enable this if your panel appears to have broken
*/
#undef CHECK_COMPAT
/*
* debugging?
*/
#define DEBUG 0
#define YOU_WANT_TO_DRAW_TETRAGON
#define DELAY(x) mdelay(x)
/*
* Complain if VAR is out of range.
*/
#define DEBUG_VAR 0
#include "s3c2410fb.h"
void (*s3c2410fb_blank_helper)(int blank);
/* EXPORT_SYMBOL(s3c2410fb_blank_helper); */
/*
* IMHO this looks wrong. In 8BPP, length should be 8.
*/
static struct s3c2410fb_rgb rgb_8 = {
red: { offset: 0, length: 4, },
green: { offset: 0, length: 4, },
blue: { offset: 0, length: 4, },
transp: { offset: 0, length: 0, },
};
/* S3C2410
* 256 Palette Usage (TFT)
* 256 color palette consist of 256(depth) * 16 bit SPSRAM
*/
static struct s3c2410fb_rgb def_rgb_16 = {
red: { offset: 11, length: 5, },
green: { offset: 5, length: 6, },
blue: { offset: 0, length: 5, },
transp: { offset: 0, length: 0, },
};
/*******
* role : Fill Machine dependant data ,according to STN or TFT
*
*/
static void __init s3c2410fb_get_machine_info(struct s3c2410fb_info *fbi)
{
static struct s3c2410fb_mach_info inf __initdata = {
pixclock: 174757, bpp: 16,
xres: LCD_XRES, yres: LCD_YRES,
//hsync_len: 0, vsync_len: 0,
//left_margin: 0, upper_margin: 0,
//right_margin: 0, lower_margin: 0,
hsync_len : 5, vsync_len : 1,
left_margin : 7, upper_margin : 1,
right_margin: 3, lower_margin : 3,
sync: 0, cmap_static: 1,
};
fbi->max_xres = inf.xres;
fbi->fb.var.xres = inf.xres;
fbi->fb.var.xres_virtual = inf.xres;
fbi->max_yres = inf.yres;
fbi->fb.var.yres = inf.yres;
fbi->fb.var.yres_virtual = inf.yres;
fbi->max_bpp = inf.bpp;
fbi->fb.var.bits_per_pixel = inf.bpp;
fbi->fb.var.pixclock = inf.pixclock;
fbi->fb.var.hsync_len = inf.hsync_len;
fbi->fb.var.left_margin = inf.left_margin;
fbi->fb.var.right_margin = inf.right_margin;
fbi->fb.var.vsync_len = inf.vsync_len;
fbi->fb.var.upper_margin = inf.upper_margin;
fbi->fb.var.lower_margin = inf.lower_margin;
fbi->fb.var.sync = inf.sync;
fbi->fb.var.grayscale = inf.cmap_greyscale;
fbi->cmap_inverse = inf.cmap_inverse;
fbi->cmap_static = inf.cmap_static;
}
static void s3c2410fb_lcd_port_init(void );
static int s3c2410fb_activate_var(struct fb_var_screeninfo *var, struct s3c2410fb_info *);
static void set_ctrlr_state(struct s3c2410fb_info *fbi, u_int state);
#define FR_WIDTH LCD_XRES
#define FR_HEIGHT LCD_YRES
struct FrameBuffer {
unsigned short pixel[FR_HEIGHT][FR_WIDTH];
};
struct FrameBuffer *FBuf;
#ifdef YOU_WANT_TO_DRAW_TETRAGON
static void lcd_demo(void)
{
// Test LCD Initialization. by displaying R.G.B and White.
int i,j;
for(i=0 ;i<FR_HEIGHT/2;i++)
{
for(j=0;j<FR_WIDTH;j++)
{
if(j<FR_WIDTH/2)
FBuf->pixel[i][j]= 0xffff;
else
FBuf->pixel[i][j]= 0xf800;
}
}
for(i=FR_HEIGHT/2 ;i<FR_HEIGHT;i++)
{
for(j=0;j<FR_WIDTH;j++)
{
if(j<FR_WIDTH/2)
FBuf->pixel[i][j]= 0x07e0;
else
FBuf->pixel[i][j]= 0x001f;
}
}
}
#endif
static void s3c2410fb_lcd_port_init(void)
{
rGPCUP =0xffffffff; // Disable Pull-up register
rGPCCON =0xaaaaaaaa; //Initialize VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
rGPDUP =0xffffffff; // Disable Pull-up register
rGPDCON =0xaaaaaaaa; //Initialize VD[23:8]
DPRINTK("Initializing GPIO Portsn");
}
static inline void s3c2410fb_schedule_task(struct s3c2410fb_info *fbi, u_int state)
{
unsigned long flags;
local_irq_save(flags);
/*
* We need to handle two requests being made at the same time.
* There are two important cases:
* 1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE)
* We must perform the unblanking, which will do our REENABLE for us.
* 2. When we are blanking, but immediately unblank before we have
* blanked. We do the "REENABLE" thing here as well, just to be sure.
*/
if (fbi->task_state == C_ENABLE && state == C_REENABLE)
state = (u_int) -1;
if (fbi->task_state == C_DISABLE && state == C_ENABLE)
state = C_REENABLE;
if (state != (u_int)-1) {
fbi->task_state = state;
schedule_task(&fbi->task);
}
local_irq_restore(flags);
}
/*
* Get the VAR structure pointer for the specified console
*/
static inline struct fb_var_screeninfo *get_con_var(struct fb_info *info, int con)
{
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
return (con == fbi->currcon || con == -1) ? &fbi->fb.var : &fb_display[con].var;
}
/*
* Get the DISPLAY structure pointer for the specified console
*
* struct display fb_display[MAX_NR_CONSOLES]; from fbcon.c
*/
static inline struct display *get_con_display(struct fb_info *info, int con)
{
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
return (con < 0) ? fbi->fb.disp : &fb_display[con];
}
static inline struct fb_cmap *get_con_cmap(struct fb_info *info, int con)
{
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
return (con == fbi->currcon || con == -1) ? &fbi->fb.cmap : &fb_display[con].cmap;
}
static inline u_int
chan_to_field(u_int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
/*
* Convert bits-per-pixel to a hardware palette PBS value.
*/
static inline u_int
palette_pbs(struct fb_var_screeninfo *var)
{
int ret = 0;
switch (var->bits_per_pixel) {
#ifdef FBCON_HAS_CFB4
case 4: ret = 0 << 12; break;
#endif
#ifdef FBCON_HAS_CFB8
case 8: ret = 1 << 12; break;
#endif
#ifdef FBCON_HAS_CFB16
case 16: ret = 2 << 12; break;
#endif
}
return ret;
}
/******
* bit mask RGB=565 -> RRRR RGGG GGGB BBBB
* 1111 1000 0000 0000 0xf800
* 0000 0111 1110 0000 0x07e0
* 0000 0000 0001 1111 0x001f
*******************************************************/
static int
s3c2410fb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
u_int trans, struct fb_info *info)
{
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
u_int val, ret = 1;
if (regno < fbi->palette_size) {
val = ((red >> 5) & 0xf800);
val |= ((green >> 11) & 0x07e0);
val |= ((blue >> 16) & 0x001f);
if (regno == 0)
val |= palette_pbs(&fbi->fb.var);
fbi->palette_cpu[regno] = val;
ret = 0;
}
return ret;
}
static int
s3c2410fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int trans, struct fb_info *info)
{
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
struct display *disp = get_con_display(info, fbi->currcon);
u_int val;
int ret = 1;
/*
* If inverse mode was selected, invert all the colours
* rather than the register number. The register number
* is what you poke into the framebuffer to produce the
* colour you requested.
*/
if (disp->inverse) {
red = 0xffff - red;
green = 0xffff - green;
blue = 0xffff - blue;
}
/*
* If greyscale is true, then we convert the RGB value
* to greyscale no mater what visual we are using.
*/
if (fbi->fb.var.grayscale)
red = green = blue = (19595 * red + 38470 * green +
7471 * blue) >> 16;
switch (fbi->fb.disp->visual) {
case FB_VISUAL_TRUECOLOR:
/*
* 12 or 16-bit True Colour. We encode the RGB value
* according to the RGB bitfield information.
*/
if (regno < 16) {
u16 *pal = fbi->fb.pseudo_palette;
val = chan_to_field(red, &fbi->fb.var.red);
val |= chan_to_field(green, &fbi->fb.var.green);
val |= chan_to_field(blue, &fbi->fb.var.blue);
pal[regno] = val;
ret = 0;
}
break;
case FB_VISUAL_STATIC_PSEUDOCOLOR:
case FB_VISUAL_PSEUDOCOLOR:
ret = s3c2410fb_setpalettereg(regno, red, green, blue, trans, info);
break;
}
return ret;
}
/*
* s3c2410fb_decode_var():
* Get the video params out of 'var'. If a value doesn't fit, round it up,
* if it's too big, return -EINVAL.
*
* Suggestion: Round up in the following order: bits_per_pixel, xres,
* yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
* bitfields, horizontal timing, vertical timing.
*/
static int
s3c2410fb_validate_var(struct fb_var_screeninfo *var,
struct s3c2410fb_info *fbi)
{
int ret = -EINVAL;
if (var->xres < MIN_XRES)
var->xres = MIN_XRES;
if (var->yres < MIN_YRES)
var->yres = MIN_YRES;
if (var->xres > fbi->max_xres)
var->xres = fbi->max_xres;
if (var->yres > fbi->max_yres)
var->yres = fbi->max_yres;
var->xres_virtual =
var->xres_virtual < var->xres ? var->xres : var->xres_virtual;
var->yres_virtual =
var->yres_virtual < var->yres ? var->yres : var->yres_virtual;
switch (var->bits_per_pixel) {
#ifdef FBCON_HAS_CFB4
case 4: ret = 0; break;
#endif
#ifdef FBCON_HAS_CFB8
case 8: ret = 0; break;
#endif
#ifdef FBCON_HAS_CFB16
case 16: ret = 0; break;
#endif
default:
break;
}
return ret;
}
static inline void s3c2410fb_set_truecolor(u_int is_true_color)
{
DPRINTK("The TFT Panel true_color = %dn", is_true_color);
DELAY(10);
}
static void
s3c2410fb_hw_set_var(struct fb_var_screeninfo *var, struct s3c2410fb_info *fbi)
{
u_long palette_mem_size;
fbi->palette_size = 256;
palette_mem_size = fbi->palette_size * sizeof(u16);
fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
fb_set_cmap(&fbi->fb.cmap, 1, s3c2410fb_setcolreg, &fbi->fb);
/* Set board control register to handle new color depth */
s3c2410fb_set_truecolor(var->bits_per_pixel >= 16);
s3c2410fb_activate_var(var, fbi);
fbi->palette_cpu[0] = (fbi->palette_cpu[0] &
0xcfff) | palette_pbs(var);
}
/*
* s3c2410fb_set_var():
* Set the user defined part of the display for the specified console
*/
static int
s3c2410fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
{
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
struct fb_var_screeninfo *dvar = get_con_var(&fbi->fb, con);
struct display *display = get_con_display(&fbi->fb, con);
int err, chgvar = 0, rgbidx;
/*
* Decode var contents into a par structure, adjusting any
* out of range values.
*/
err = s3c2410fb_validate_var(var, fbi);
if (err) return err;
if (var->activate & FB_ACTIVATE_TEST)
{
return 0;
}
if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
{
return -EINVAL;
}
if (dvar->xres != var->xres)
chgvar = 1;
if (dvar->yres != var->yres)
chgvar = 1;
if (dvar->xres_virtual != var->xres_virtual)
chgvar = 1;
if (dvar->yres_virtual != var->yres_virtual)
chgvar = 1;
if (dvar->bits_per_pixel != var->bits_per_pixel)
chgvar = 1;
if (con < 0)
chgvar = 0;
switch (var->bits_per_pixel) {
case 16:
display->visual = FB_VISUAL_TRUECOLOR;
display->line_length = var->xres * 2;
display->dispsw = &fbcon_cfb16;
display->dispsw_data = fbi->fb.pseudo_palette;
rgbidx = RGB_16;
break;
default:
rgbidx = 0;
display->dispsw = &fbcon_dummy;
break;
}
display->screen_base = fbi->screen_cpu;
display->next_line = display->line_length;
display->type = fbi->fb.fix.type;
display->type_aux = fbi->fb.fix.type_aux;
display->ypanstep = fbi->fb.fix.ypanstep;
display->ywrapstep = fbi->fb.fix.ywrapstep;
display->can_soft_blank = 1;
display->inverse = fbi->cmap_inverse;
*dvar = *var;
dvar->activate &= ~FB_ACTIVATE_ALL;
/*
* Copy the RGB parameters for this display
* from the machine specific parameters.
*/
dvar->red = fbi->rgb[rgbidx]->red;
dvar->green = fbi->rgb[rgbidx]->green;
dvar->blue = fbi->rgb[rgbidx]->blue;
dvar->transp = fbi->rgb[rgbidx]->transp;
/*
* Update the old var. The fbcon drivers still use this.
* Once they are using fbi->fb.var, this can be dropped.
*/
display->var = *dvar;
/*
* If we are setting all the virtual consoles, also set the
* defaults used to create new consoles.
*/
if (var->activate & FB_ACTIVATE_ALL)
fbi->fb.disp->var = *dvar;
/*
* If the console has changed and the console has defined
* a changevar function, call that function.
*/
if (chgvar && info && fbi->fb.changevar)
fbi->fb.changevar(con);
/* If the current console is selected, activate the new var. */
if (con != fbi->currcon)
return 0;
s3c2410fb_hw_set_var(dvar, fbi);
return 0;
}
static int
__do_set_cmap(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info)
{
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
struct fb_cmap *dcmap = get_con_cmap(info, con);
int err = 0;
if (con == -1)
con = fbi->currcon;
/* no colormap allocated? (we always have "this" colour map allocated) */
if (con >= 0)
err = fb_alloc_cmap(&fb_display[con].cmap, fbi->palette_size, 0);
if (!err && con == fbi->currcon)
err = fb_set_cmap(cmap, kspc, s3c2410fb_setcolreg, info);
if (!err)
fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
return err;
}
static int
s3c2410fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info)
{
struct display *disp = get_con_display(info, con);
if (disp->visual == FB_VISUAL_TRUECOLOR ||
disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
return -EINVAL;
return __do_set_cmap(cmap, kspc, con, info);
}
static int
s3c2410fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
{
struct display *display = get_con_display(info, con);
*fix = info->fix;
fix->line_length = display->line_length;
fix->visual = display->visual;
return 0;
}
static int
s3c2410fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
{
*var = *get_con_var(info, con);
return 0;
}
static int
s3c2410fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
{
struct fb_cmap *dcmap = get_con_cmap(info, con);
fb_copy_cmap(dcmap, cmap, kspc ? 0 : 2);
return 0;
}
static struct fb_ops s3c2410fb_ops = {
owner: THIS_MODULE,
fb_get_fix: s3c2410fb_get_fix,
fb_get_var: s3c2410fb_get_var,
fb_set_var: s3c2410fb_set_var,
fb_get_cmap: s3c2410fb_get_cmap,
fb_set_cmap: s3c2410fb_set_cmap,
};
/*
* s3c2410fb_switch():
* Change to the specified console. Palette and video mode
* are changed to the console's stored parameters.
*
* Uh oh, this can be called from a tasklet (IRQ)
*/
static int s3c2410fb_switch(int con, struct fb_info *info)
{
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
struct display *disp;
struct fb_cmap *cmap;
if (con == fbi->currcon)
return 0;
if (fbi->currcon >= 0) {
disp = fb_display + fbi->currcon;
/*
* Save the old colormap and video mode.
*/
disp->var = fbi->fb.var;
if (disp->cmap.len)
fb_copy_cmap(&fbi->fb.cmap, &disp->cmap, 0);
}
fbi->currcon = con;
disp = fb_display + con;
/*
* Make sure that our colourmap contains 256 entries.
*/
fb_alloc_cmap(&fbi->fb.cmap, 256, 0);
if (disp->cmap.len)
cmap = &disp->cmap;
else
cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
fb_copy_cmap(cmap, &fbi->fb.cmap, 0);
fbi->fb.var = disp->var;
fbi->fb.var.activate = FB_ACTIVATE_NOW;
s3c2410fb_set_var(&fbi->fb.var, con, info);
return 0;
}
/*
* Formal definition of the VESA spec:
* On
* This refers to the state of the display when it is in full operation
* Stand-By
* This defines an optional operating state of minimal power reduction with
* the shortest recovery time
* Suspend
* This refers to a level of power management in which substantial power
* reduction is achieved by the display. The display can have a longer
* recovery time from this state than from the Stand-by state
* Off
* This indicates that the display is consuming the lowest level of power
* and is non-operational. Recovery from this state may optionally require
* the user to manually power on the monitor
*
* Now, the fbdev driver adds an additional state, (blank), where they
* turn off the video (maybe by colormap tricks), but don't mess with the
* video itself: think of it semantically between on and Stand-By.
*
* So here's what we should do in our fbdev blank routine:
*
* VESA_NO_BLANKING (mode 0) Video on, front/back light on
* VESA_VSYNC_SUSPEND (mode 1) Video on, front/back light off
* VESA_HSYNC_SUSPEND (mode 2) Video on, front/back light off
* VESA_POWERDOWN (mode 3) Video off, front/back light off
*
* This will match the matrox implementation.
*/
/*
* s3c2410fb_blank():
* Blank the display by setting all palette values to zero. Note, the
* 12 and 16 bpp modes don't really use the palette, so this will not
* blank the display in all modes.
* blank = 0 unblank ;
* blank > 0 , VESA level
*/
static void s3c2410fb_blank(int blank, struct fb_info *info)
{
#ifdef S3C2410_REAL_BLANK
struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
int i,j;
struct FrameBuffer * pBlankFB;
#endif
#ifdef S3C2410_SUPPORT_PALETTE
switch (blank) {
case VESA_POWERDOWN:
case VESA_VSYNC_SUSPEND:
case VESA_HSYNC_SUSPEND:
case VESA_NO_BLANKING:
if (s3c2410fb_blank_helper)
s3c2410fb_blank_helper(blank);
if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
fb_set_cmap(&fbi->fb.cmap, 1, s3c2410fb_setcolreg, info);
s3c2410fb_schedule_task(fbi, C_ENABLE);
}
#endif
#ifdef S3C2410_REAL_BLANK
pBlankFB = (struct FrameBuffer *) (fbi->screen_cpu);
for(i=0 ;i<FR_HEIGHT;i++)
for(j=0;j<FR_WIDTH;j++)
pBlankFB->pixel[i][j]= 0xffff;
#endif
}
static int s3c2410fb_updatevar(int con, struct fb_info *info)
{
DPRINTK("enteredn");
DELAY(10);
return 0;
}
/*
* s3c2410fb_activate_var():
* Configures LCD Controller based on entries in var parameter. Settings are
* only written to the controller if changes were made.
*/
static int s3c2410fb_activate_var(struct fb_var_screeninfo *var, struct s3c2410fb_info *fbi)
{
struct s3c2410fb_lcd_reg new_regs;
u_long flags;
#if DEBUG_VAR
if (var->xres < 16 || var->xres > 1024)
printk(KERN_ERR "%s: invalid xres %dn",
fbi->fb.fix.id, var->xres);
if (var->hsync_len < 1 || var->hsync_len > 64)
printk(KERN_ERR "%s: invalid hsync_len %dn",
fbi->fb.fix.id, var->hsync_len);
if (var->left_margin < 1 || var->left_margin > 255)
printk(KERN_ERR "%s: invalid left_margin %dn",
fbi->fb.fix.id, var->left_margin);
if (var->right_margin < 1 || var->right_margin > 255)
printk(KERN_ERR "%s: invalid right_margin %dn",
fbi->fb.fix.id, var->right_margin);
if (var->yres < 1 || var->yres > 1024)
printk(KERN_ERR "%s: invalid yres %dn",
fbi->fb.fix.id, var->yres);
if (var->vsync_len < 1 || var->vsync_len > 64)
printk(KERN_ERR "%s: invalid vsync_len %dn",
fbi->fb.fix.id, var->vsync_len);
if (var->upper_margin < 0 || var->upper_margin > 255)
printk(KERN_ERR "%s: invalid upper_margin %dn",
fbi->fb.fix.id, var->upper_margin);
if (var->lower_margin < 0 || var->lower_margin > 255)
printk(KERN_ERR "%s: invalid lower_margin %dn",
fbi->fb.fix.id, var->lower_margin);
#endif
new_regs.lcdcon1=(LCD_CLKVAL_TFT<<8)|(MVAL_USED<<7)|(3<<5)|(12<<1)|0;
// TFT LCD panel,16bpp TFT,ENVID=off
new_regs.lcdcon2=(LCD_VBPD<<24)|((var->yres-1)<<14)|(LCD_VFPD<<6)|(LCD_VSPW);
new_regs.lcdcon3=(LCD_HBPD<<19)|((var->xres-1)<<8)|(LCD_HFPD);
new_regs.lcdcon4=(MVAL<<8)|(LCD_HSPW);
new_regs.lcdcon5=(1<<11)|(1<<9)|(1<<8)|(1<<3)|1;
new_regs.lcdsaddr1=
(((unsigned long)fbi->screen_dma>>22)<<21)|M5D((unsigned long)fbi->screen_dma>>1);
new_regs.lcdsaddr2=
M5D(((unsigned long)fbi->screen_dma+(var->xres*var->yres*2))>>1);
new_regs.lcdsaddr3=(((var->xres - var->xres)/1)<<11)|(var->xres/1);
/* Update shadow copy atomically */
local_irq_save(flags);
fbi->reg_lcdcon1 = new_regs.lcdcon1;
fbi->reg_lcdcon2 = new_regs.lcdcon2;
fbi->reg_lcdcon3 = new_regs.lcdcon3;
fbi->reg_lcdcon4 = new_regs.lcdcon4;
fbi->reg_lcdcon5 = new_regs.lcdcon5;
fbi->reg_lcdsaddr1 = new_regs.lcdsaddr1;
fbi->reg_lcdsaddr2 = new_regs.lcdsaddr2;
fbi->reg_lcdsaddr3 = new_regs.lcdsaddr3;
fbi->reg_lpcsel = new_regs.lpcsel;
local_irq_restore(flags);
/*
* Only update the registers if the controller is enabled
* and something has changed.
*/
if(
(rLCDCON1 != fbi->reg_lcdcon1) || (rLCDCON2 != fbi->reg_lcdcon2) ||
(rLCDCON3 != fbi->reg_lcdcon3) || (rLCDCON4 != fbi->reg_lcdcon4) ||
(rLCDCON5 != fbi->reg_lcdcon5) || (rLCDSADDR1 != fbi->reg_lcdsaddr1)||
(rLCDSADDR2 != fbi->reg_lcdsaddr2) || (rLCDSADDR3 != fbi->reg_lcdsaddr3)
)
{
s3c2410fb_schedule_task(fbi, C_REENABLE);
}
return 0;
}
static void s3c2410fb_enable_controller(struct s3c2410fb_info *fbi)
{
/*
* Make sure the mode bits are present in the first palette entry
*/
fbi->palette_cpu[0] &= 0xcfff;
fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var);
rLCDCON1 =fbi->reg_lcdcon1;
rLCDCON2 =fbi->reg_lcdcon2;
rLCDCON3 =fbi->reg_lcdcon3;
rLCDCON4 =fbi->reg_lcdcon4;
rLCDCON5 =fbi->reg_lcdcon5;
rLCDSADDR1=fbi->reg_lcdsaddr1;
rLCDSADDR2=fbi->reg_lcdsaddr2;
rLCDSADDR3=fbi->reg_lcdsaddr3;
rLCDINTMSK |= 3;
rTPAL=0; // temporary palette register, Disabled
rLPCSEL &= (~7);
rLCDCON1|=1; // ENVID=ON
FBuf = (struct FrameBuffer *)fbi->screen_cpu;
#ifdef YOU_WANT_TO_DRAW_TETRAGON
lcd_demo();
#endif
}
static void s3c2410fb_disable_controller(struct s3c2410fb_info *fbi)
{
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&fbi->ctrlr_wait, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
rLCDCON1=fbi->reg_lcdcon1;
rLCDCON2=fbi->reg_lcdcon2;
rLCDCON3=fbi->reg_lcdcon3;
rLCDCON4=fbi->reg_lcdcon4;
rLCDCON5=fbi->reg_lcdcon5;
rLCDSADDR1=fbi->reg_lcdsaddr1;
rLCDSADDR2=fbi->reg_lcdsaddr2;
rLCDSADDR3=fbi->reg_lcdsaddr3;
rLPCSEL = fbi->reg_lpcsel;
rLCDCON1 = rLCDCON1 & 0x3fffe; /* ENVID Clear */
schedule_timeout(20 * HZ / 1000);
current->state = TASK_RUNNING;
remove_wait_queue(&fbi->ctrlr_wait, &wait);
}
/*
* This function must be called from task context only, since it will
* sleep when disabling the LCD controller, or if we get two contending
* processes trying to alter state.
*/
static void set_ctrlr_state(struct s3c2410fb_info *fbi, u_int state)
{
u_int old_state;
down(&fbi->ctrlr_sem);
old_state = fbi->state;
switch (state) {
case C_DISABLE_CLKCHANGE:
/*
* Disable controller for clock change. If the
* controller is already disabled, then do nothing.
*/
if (old_state != C_DISABLE) {
fbi->state = state;
s3c2410fb_disable_controller(fbi);
}
break;
case C_DISABLE:
/*
* Disable controller
*/
if (old_state != C_DISABLE) {
fbi->state = state;
if (old_state != C_DISABLE_CLKCHANGE)
s3c2410fb_disable_controller(fbi);
}
break;
case C_ENABLE_CLKCHANGE:
/*
* Enable the controller after clock change. Only
* do this if we were disabled for the clock change.
*/
if (old_state == C_DISABLE_CLKCHANGE) {
fbi->state = C_ENABLE;
s3c2410fb_enable_controller(fbi);
}
break;
case C_REENABLE:
/*
* Re-enable the controller only if it was already
* enabled. This is so we reprogram the control
* registers.
*/
if (old_state == C_ENABLE) {
s3c2410fb_disable_controller(fbi);
s3c2410fb_enable_controller(fbi);
}
break;
case C_ENABLE:
/*
* Power up the LCD screen, enable controller, and
* turn on the backlight.
*/
if (old_state != C_ENABLE) {
fbi->state = C_ENABLE;
s3c2410fb_enable_controller(fbi);
}
break;
}
up(&fbi->ctrlr_sem);
}
/*
* Our LCD controller task (which is called when we blank or unblank)
* via keventd.
*/
static void s3c2410fb_task(void *dummy)
{
struct s3c2410fb_info *fbi = dummy;
u_int state = xchg(&fbi->task_state, -1);
set_ctrlr_state(fbi, state);
}
/*
* s3c2410fb_map_video_memory():
* Allocates the DRAM memory for the frame buffer. This buffer is
* remapped into a non-cached, non-buffered, memory region to
* allow palette and pixel writes to occur without flushing the
* cache. Once this area is remapped, all virtual memory
* access to the video memory should occur at the new region.
*/
static int __init s3c2410fb_map_video_memory(struct s3c2410fb_info *fbi)
{
/*
* We reserve one page for the palette, plus the size
* of the framebuffer.
*
* fbi->map_dma is bus address (physical)
* fbi->screen_dma = fbi->map_dma + PAGE_SIZE
* fbi->map_cpu is virtual address
* fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE
*
* S3C2400 Result
* map_size is 0x00027000
* map_cpu is 0xC2800000
* smem_start is 0x0CFC1000
* palette_mem_size 0x20
*/
fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE );
fbi->map_cpu = consistent_alloc(GFP_KERNEL , fbi->map_size,
&fbi->map_dma);
if (fbi->map_cpu) {
fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE ;
fbi->screen_dma = fbi->map_dma + PAGE_SIZE ;
fbi->fb.fix.smem_start = fbi->screen_dma;
}
return fbi->map_cpu ? 0 : -ENOMEM;
}
/* Fake monspecs to fill in fbinfo structure */
static struct fb_monspecs monspecs __initdata = {
30000, 70000, 50, 65, 0 /* Generic */
};
static struct s3c2410fb_info * __init s3c2410fb_init_fbinfo(void)
{
struct s3c2410fb_info *fbi;
fbi = kmalloc(sizeof(struct s3c2410fb_info) + sizeof(struct display) +
sizeof(u16) * 16, GFP_KERNEL);
if (!fbi)
return NULL;
memset(fbi, 0, sizeof(struct s3c2410fb_info) + sizeof(struct display));
fbi->currcon = -1;
strcpy(fbi->fb.fix.id, S3C2410_NAME);
fbi->fb.node = -1; /* What is this ? */
fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS;
fbi->fb.fix.type_aux = 0;
fbi->fb.fix.xpanstep = 0;
fbi->fb.fix.ypanstep = 0;
fbi->fb.fix.ywrapstep = 0;
fbi->fb.fix.accel = FB_ACCEL_NONE; /* No hardware Accelerator */
fbi->fb.var.nonstd = 0;
fbi->fb.var.activate = FB_ACTIVATE_NOW;
fbi->fb.var.height = -1;
fbi->fb.var.width = -1;
fbi->fb.var.accel_flags = 0;
fbi->fb.var.vmode = FB_VMODE_NONINTERLACED;
strcpy(fbi->fb.modename, S3C2410_NAME);
strcpy(fbi->fb.fontname, "Acorn8x8");
fbi->fb.fbops = &s3c2410fb_ops;
fbi->fb.changevar = NULL;
fbi->fb.switch_con = s3c2410fb_switch;
fbi->fb.updatevar = s3c2410fb_updatevar;
fbi->fb.blank = s3c2410fb_blank;
fbi->fb.flags = FBINFO_FLAG_DEFAULT;
fbi->fb.node = -1;
fbi->fb.monspecs = monspecs;
fbi->fb.disp = (struct display *)(fbi + 1); /* golbal display */
fbi->fb.pseudo_palette = (void *)(fbi->fb.disp + 1);
fbi->rgb[RGB_8] = &rgb_8;
fbi->rgb[RGB_16] = &def_rgb_16;
s3c2410fb_get_machine_info(fbi);
fbi->state = C_DISABLE;
fbi->task_state = (u_char)-1;
fbi->fb.fix.smem_len = (fbi->max_xres * fbi->max_yres * fbi->max_bpp) / 8;
init_waitqueue_head(&fbi->ctrlr_wait);
INIT_TQUEUE(&fbi->task, s3c2410fb_task, fbi);
init_MUTEX(&fbi->ctrlr_sem);
return fbi;
}
int __init s3c2410fb_init(void)
{
struct s3c2410fb_info *fbi;
int ret;
printk("S3C2410 TFT LCD FrameBuffer driver for MTLCD-0353224n");
s3c2410fb_lcd_port_init();
fbi = s3c2410fb_init_fbinfo();
ret = -ENOMEM;
if (!fbi)
goto failed;
/* Initialize video memory */
ret = s3c2410fb_map_video_memory(fbi);
if (ret)
goto failed;
s3c2410fb_set_var(&fbi->fb.var, -1, &fbi->fb);
ret = register_framebuffer(&fbi->fb);
if (ret < 0)
goto failed;
/*
* Ok, now enable the LCD controller
*/
set_ctrlr_state(fbi, C_ENABLE);
/* This driver cannot be unloaded at the moment */
MOD_INC_USE_COUNT;
return 0;
failed:
if (fbi) kfree(fbi);
return ret;
}
int __init s3c2410fb_setup(char *options)
{
return 0;
}
module_init(s3c2410fb_init);
MODULE_DESCRIPTION("S3C2410/SAMSUNG framebuffer driver");
MODULE_LICENSE("GPL");
s3c2419fb.h
/*
* linux/drivers/video/s3c2410fb.h
* -- StrongARM 1100 LCD Controller Frame Buffer Device
*
* Copyright (C) 1999 Eric A. Thomas
* Based on acornfb.c Copyright (C) Russell King.
* 2001 Sangwook Lee (hitchcar@sec.samsung.com) S3C2400
* 2002 Sangwook Lee (hitchcar@sec.samsung.com) S3C2410
*
*/
struct s3c2410fb_rgb {
struct fb_bitfield red;
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp;
};
/*
* This structure describes the machine which we are running on.
*/
struct s3c2410fb_mach_info {
u_long pixclock;
u_short xres;
u_short yres;
u_char bpp;
u_char hsync_len;
u_char left_margin;
u_char right_margin;
u_char vsync_len;
u_char upper_margin;
u_char lower_margin;
u_char sync;
u_int cmap_greyscale:1,
cmap_inverse:1,
cmap_static:1,
unused:29;
};
/* Shadows for LCD controller registers */
struct s3c2410fb_lcd_reg {
unsigned long lcdcon1;
unsigned long lcdcon2;
unsigned long lcdcon3;
unsigned long lcdcon4;
unsigned long lcdcon5;
unsigned long lcdsaddr1;
unsigned long lcdsaddr2;
unsigned long lcdsaddr3;
unsigned long lpcsel;
unsigned long lcdintmsk;
unsigned long tpal;
};
#define RGB_8 (0)
#define RGB_16 (1)
#define NR_RGB 2
struct s3c2410fb_info {
struct fb_info fb;
signed int currcon;
struct s3c2410fb_rgb *rgb[NR_RGB];
u_int max_bpp;
u_int max_xres;
u_int max_yres;
/*
* These are the addresses we mapped
* the framebuffer memory region to.
*/
dma_addr_t map_dma;
u_char * map_cpu;
u_int map_size;
u_char * screen_cpu;
dma_addr_t screen_dma;
u16 * palette_cpu;
dma_addr_t palette_dma;
u_int palette_size;
u_int cmap_inverse:1,
cmap_static:1,
unused:30;
u_int reg_lcdcon1;
u_int reg_lcdcon2;
u_int reg_lcdcon3;
u_int reg_lcdcon4;
u_int reg_lcdcon5;
u_int reg_lcdsaddr1;
u_int reg_lcdsaddr2;
u_int reg_lcdsaddr3;
u_int reg_lpcsel;
u_int reg_lcdintmsk;
volatile u_char state;
volatile u_char task_state;
struct semaphore ctrlr_sem;
wait_queue_head_t ctrlr_wait;
struct tq_struct task;
#ifdef CONFIG_PM
struct pm_dev *pm;
#endif
#ifdef CONFIG_CPU_FREQ
struct notifier_block clockchg;
#endif
};
#define __type_entry(ptr,type,member) ((type *)((char *)(ptr)-offsetof(type,member)))
#define TO_INF(ptr,member) __type_entry(ptr,struct s3c2410fb_info,member)
#define S3C2410_PALETTE_MODE_VAL(bpp) (((bpp) & 0x018) << 9)
/*
* These are the actions for set_ctrlr_state
*/
#define C_DISABLE (0)
#define C_ENABLE (1)
#define C_DISABLE_CLKCHANGE (2)
#define C_ENABLE_CLKCHANGE (3)
#define C_REENABLE (4)
#define S3C2410_NAME "S3C2410"
/*
* Debug macros
*/
#if DEBUG
# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
#else
# define DPRINTK(fmt, args...)
#endif
/*
* Minimum X and Y resolutions
*/
#define MIN_XRES 64
#define MIN_YRES 64
張貼者:
邦邦
於
星期六, 5月 12, 2007
0
意見
標籤: Linux 綜合開發篇