/*++
linux/drivers/video/wmt/animation.c
Copyright (c) 2013 WonderMedia Technologies, Inc.
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, see .
WonderMedia Technologies, Inc.
10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
--*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "animation.h"
#include "buffer.h"
#include "LzmaDec.h"
#include "../memblock.h"
#include "anim_data.h"
#define ANIM_STOP_PROC_FILE "kernel_animation"
#define MAX_CLIP_COUNT 6
extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
//extern void clear_animation_fb(void * p);
//extern void flip_animation_fb(int pingpong);
#define THE_MB_USER "Boot-Animation"
#define DEFAULT_BUF_IMAGES 4 //default buffered image count
#undef THIS_DEBUG
//#define THIS_DEBUG
#ifdef THIS_DEBUG
#define LOG_DBG(fmt,args...) printk(KERN_INFO "[Boot Animation] " fmt , ## args)
#define LOG_INFO(fmt,args...) printk(KERN_INFO "[Boot Animation] " fmt, ## args)
#define LOG_ERROR(fmt,args...) printk(KERN_ERR "[Boot Animation] " fmt , ## args)
#else
#define LOG_DBG(fmt,args...)
#define LOG_INFO(fmt,args...) printk(KERN_INFO "[Boot Animation] " fmt, ## args)
#define LOG_ERROR(fmt,args...) printk(KERN_ERR "[Boot Animation] " fmt , ## args)
#endif
// MUST match Windows PC tool. Don't change it.
struct animation_clip_header{
int xres;
int yres;
int linesize;
unsigned char x_mode;
unsigned char y_mode;
short x_offset;
short y_offset;
unsigned char repeat;
unsigned char reserved;
int interval;
int image_count;
int data_len;
};
// MUST match Windows PC tool. Don't change it.
struct file_header {
int maigc;
unsigned short version;
unsigned char clip_count;
unsigned char color_format;
unsigned int file_len;
};
struct play_context {
struct animation_clip_header *clip;
int xpos; // top postion
int ypos; // left postion
volatile int play_thread;
animation_buffer buf;
};
// globe value to stop the animation loop
static volatile int g_logo_stop = 0;
static struct animation_fb_info fb;
static void *SzAlloc(void *p, size_t size)
{
void * add = (void *)mb_alloc(size);
LOG_DBG("alloc: size %d, add = %p \n", size, add);
return add;
}
static void SzFree(void *p, void *address) {
if (address != 0) {
LOG_DBG("free: address = %p \n", address);
mb_free((int)address);
}
}
static ISzAlloc g_Alloc = { SzAlloc, SzFree };
static int show_frame(struct play_context *ctx, unsigned char *data)
{
unsigned char * dest;
int linesize = fb.width * (fb.color_fmt + 1) * 2;
int i = 0;
struct animation_clip_header *clip = ctx->clip;
if (g_logo_stop)
return 0;
dest = fb.addr;
// printk(KERN_INFO "dest = 0x%p src = 0x%p\n", dest, data);
if(data) {
LOG_DBG("dest %p, data %p (%d,%d) (%dx%d) linesize(%d,%d)", dest, data,
ctx->xpos, ctx->ypos, clip->xres, clip->yres, clip->linesize, linesize);
dest += ctx->ypos * linesize;
dest += ctx->xpos * (fb.color_fmt + 1) * 2;
for (i = 0; i < clip->yres; i++) {
memcpy(dest, data, clip->xres * (fb.color_fmt + 1) * 2);
dest += linesize;
data += clip->linesize;
}
}
LOG_DBG("show_frame data %p, fb.addr %p\n", data, fb.addr);
return 0;
}
static int decompress(struct play_context * ctx, unsigned char *src, unsigned int src_len)
{
SRes res = 0;
CLzmaDec state;
size_t inPos = 0;
unsigned char * inBuf;
SizeT inProcessed;
// 1) read LZMA properties (5 bytes) and uncompressed size (8 bytes, little-endian) to header
UInt64 unpackSize = 0;
int i;
unsigned char * header = src;
for (i = 0; i < 8; i++)
unpackSize += (UInt64)header[LZMA_PROPS_SIZE + i] << (i * 8);
// 2) Allocate CLzmaDec structures (state + dictionary) using LZMA properties
LzmaDec_Construct(&state);
RINOK(LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc));
if (res != SZ_OK)
return res;
// 3) Init LzmaDec structure before any new LZMA stream. And call LzmaDec_DecodeToBuf in loop
LzmaDec_Init(&state);
inBuf = header + LZMA_PROPS_SIZE + 8;
for (;;)
{
unsigned int outSize;
unsigned char * outBuf = animation_buffer_get_writable(&ctx->buf, &outSize);
unsigned int frame_size = outSize;
unsigned int decoded = 0;
ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
ELzmaStatus status;
while(1) {
inProcessed = src_len - LZMA_PROPS_SIZE - 8 - inPos;
res = LzmaDec_DecodeToBuf(&state, outBuf + frame_size - outSize, &outSize,
inBuf + inPos, &inProcessed, finishMode, &status);
inPos += inProcessed;
decoded += outSize;
unpackSize -= outSize;
outSize = frame_size - decoded;
LOG_DBG("Decoded %d bytes, inPos = %d\n", decoded, inPos);
if(res != SZ_OK)
break;
if (outSize == 0)
break;
}
animation_buffer_write_finish(&ctx->buf, outBuf);
if (res != SZ_OK || unpackSize == 0 || g_logo_stop)
break;
}
// 4) decompress finished, do clean job
LzmaDec_Free(&state, &g_Alloc);
return res;
}
static int animation_play(void * arg)
{
unsigned char * data;
static int not_first_play;
struct play_context *ctx = (struct play_context *)arg;
LOG_DBG( "animation_play thread start...\n");
if(!not_first_play) {
msleep(500); // sleep a while to wait deocde few frames
// clear_animation_fb(fb.addr);
not_first_play = 1;
}
// try to get a valid frame and show it
while(!g_logo_stop) {
data = animation_buffer_get_readable(&ctx->buf);
if(data) {
show_frame(ctx, data);
animation_buffer_read_finish(&ctx->buf, data);
}
else {
if( ctx->buf.eof ) //no data and reach eof
break;
LOG_DBG("animation_buffer_get_readable return NULL\n");
}
if(g_logo_stop)
break;
//else
msleep(ctx->clip->interval);
}
LOG_DBG( "animation_play thread End\n");
animation_buffer_stop(&ctx->buf);
ctx->play_thread = 0;
return 0;
}
static void decode_clip(struct animation_clip_header *clip, unsigned char * data, int index)
{
// start timer for animation playback
struct play_context ctx;
int buf_images;
LOG_DBG("Start playing clip %d, %dx%d, linesize %d, image %d, data_len %d\n",
index, clip->xres, clip->yres, clip->linesize, clip->image_count, clip->data_len);
ctx.clip = clip;
// init the decompress buffer
if (clip->repeat == 0) {
buf_images = DEFAULT_BUF_IMAGES;
if(buf_images > clip->image_count)
buf_images = clip->image_count;
}
else {
// for the repeat clip, alloc a big memory to store all the frames
buf_images = clip->image_count;
}
if( 0 != animation_buffer_init(&ctx.buf, clip->linesize * clip->yres, buf_images, &g_Alloc)){
LOG_ERROR("Can't init animation buffer %dx%d\n", clip->linesize * clip->yres, buf_images);
return;
}
ctx.xpos = clip->x_mode * (fb.width / 2 - clip->xres / 2) + clip->x_offset;
ctx.ypos = clip->y_mode * (fb.height / 2 - clip->yres / 2) + clip->y_offset;
kthread_run(animation_play, &ctx, "wmt-boot-play");
ctx.play_thread = 1;
LOG_DBG("Start Decompressing ... \n");
decompress(&ctx, data, clip->data_len);
if (clip->repeat) {
while (!g_logo_stop) {
// Fake decompress for REPEAT mode. (Only decompress the clip once so we can save more CPU)
unsigned int outSize;
unsigned char * outBuf;
outBuf = animation_buffer_get_writable(&ctx.buf, &outSize);
animation_buffer_write_finish(&ctx.buf, outBuf);
}
}
LOG_DBG("Decompress finished!\n");
ctx.buf.eof = 1;
//wait the play thread exit
while(ctx.play_thread) {
msleep(10);
}
LOG_DBG("Play clip %d finished\n", index);
animation_buffer_release(&ctx.buf, &g_Alloc);
}
static int animation_main(void * arg)
{
unsigned char * clip;
int i;
struct file_header *header = (struct file_header *)arg;
int clip_count = header->clip_count;
struct animation_clip_header clip_headers[MAX_CLIP_COUNT];
unsigned char * clip_datas[MAX_CLIP_COUNT];
if( clip_count > MAX_CLIP_COUNT)
clip_count = MAX_CLIP_COUNT;
LOG_DBG( "animation_main thread start, clip_cout = %d\n", clip_count);
clip = (unsigned char *)(header + 1);
for (i = 0; i< clip_count; i++){
memcpy(&clip_headers[i], clip, sizeof(struct animation_clip_header));
clip += sizeof(struct animation_clip_header);
clip_datas[i] = clip;
clip += clip_headers[i].data_len;
}
LOG_DBG( "Found %d clip(s)\n", clip_count);
for (i = 0; i < clip_count; i++) {
if (!g_logo_stop)
decode_clip(&clip_headers[i], clip_datas[i], i);
}
g_Alloc.Free(&g_Alloc, arg);
LOG_DBG( "animation_main thread finished \n");
return 0;
}
static int stop_proc_write( struct file *file,
const char *buffer,
unsigned long count,
void *data )
{
/*
char value[20];
int len = count;
if( len >= sizeof(value))
len = sizeof(value) - 1;
if(copy_from_user(value, buffer, len))
return -EFAULT;
value[len] = '\0';
LOG_DBG("procfile_write get %s\n", value);
*/
//anything will stop the boot animation
animation_stop();
return count; // discard other chars
}
struct proc_dir_entry *stop_proc_file;
static struct proc_dir_entry * create_stop_proc_file(void)
{
stop_proc_file = create_proc_entry(ANIM_STOP_PROC_FILE, 0644, NULL);
if( stop_proc_file != NULL )
stop_proc_file->write_proc = stop_proc_write;
else
LOG_ERROR("Can not create /proc/%s file", ANIM_STOP_PROC_FILE);
return stop_proc_file;
}
int animation_start(struct animation_fb_info *info)
{
struct file_header *header;
unsigned char * buffer;
const void * animation_data = anim_data;
if (animation_data == NULL)
return -1;
if( !create_stop_proc_file() )
return -1;
header = (struct file_header *)animation_data;
if (header->maigc != 0x12344321) {
LOG_ERROR ("It's not a valid Animation file at 0x%p, first 4 bytes: 0x%x\n", animation_data, header->maigc);
return -1;
}
buffer = g_Alloc.Alloc(&g_Alloc, header->file_len);
if(!buffer) {
LOG_ERROR ("Can't alloc enough memory, length %d\n", header->file_len);
return -1;
}
memcpy(&fb, info, sizeof(fb));
//copy it to the new buffer and start the play thread
memcpy(buffer, header, header->file_len);
g_logo_stop = 0;
LOG_DBG("Start animation_main thread ...\n");
kthread_run(animation_main, buffer, "wmt-boot-anim");
return 0;
}
int animation_stop(void)
{
LOG_INFO("animation_stop\n");
g_logo_stop = 1;
if( stop_proc_file ) {
remove_proc_entry(ANIM_STOP_PROC_FILE,stop_proc_file);
stop_proc_file = NULL;
}
return 0;
}
EXPORT_SYMBOL(animation_start);
EXPORT_SYMBOL(animation_stop);