diff options
Diffstat (limited to 'drivers/video/wmt/bootanimation/animation.c')
-rw-r--r-- | drivers/video/wmt/bootanimation/animation.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/drivers/video/wmt/bootanimation/animation.c b/drivers/video/wmt/bootanimation/animation.c new file mode 100644 index 00000000..ce37c44d --- /dev/null +++ b/drivers/video/wmt/bootanimation/animation.c @@ -0,0 +1,459 @@ +/*++ + 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 <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#include <linux/mm.h> +#include <linux/timer.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/version.h> +#include <linux/module.h> +#include <asm/io.h> +#include <linux/proc_fs.h> +#include <linux/ioport.h> +#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); + + |