/*++ 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);