/** */ /* * Copyright (C) 2013 Magic Lantern Team * * 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., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CONFIG_RAW_LIVEVIEW #define dbg_printf(fmt,...) {} typedef struct { uint32_t frameSize; void *frameBuffer; void *lvBuffer; uint16_t xRes; uint16_t yRes; uint16_t y1; uint16_t y2; uint16_t quality; uint16_t bpp; } frame_buf_t; struct s_DefcData { void *pSrcAddress; void *pDestAddr; uint32_t xRes; uint32_t yRes; uint32_t rawDepth14; uint32_t dstModeRaw; }; void *get_lcd_422_buf(); static struct msg_queue *mlv_play_queue_raw_to_bpp16; static struct msg_queue *mlv_play_queue_bpp16_to_yuv; static int preview_rect_x; static int preview_rect_y; static int preview_rect_w; static int preview_rect_h; static int raw_twk_task_abort = 0; static struct semaphore * edmac_write_done_sem = 0; static struct LockEntry * resLock = 0; #define CBITS_709 (8) #define CRANGE_709 (1 << CBITS_709) #define CYR_709 ((int32_t) ( 0.2126f * CRANGE_709)) #define CYG_709 ((int32_t) ( 0.7152f * CRANGE_709)) #define CYB_709 ((int32_t) ( 0.0722f * CRANGE_709)) #define CUR_709 ((int32_t) (-0.1146f * CRANGE_709)) #define CUG_709 ((int32_t) (-0.3854f * CRANGE_709)) #define CUB_709 ((int32_t) ( 0.5000f * CRANGE_709)) #define CVR_709 ((int32_t) ( 0.5000f * CRANGE_709)) #define CVG_709 ((int32_t) (-0.4541f * CRANGE_709)) #define CVB_709 ((int32_t) (-0.0458f * CRANGE_709)) #define PACK_HALVES(u,l) (((uint32_t)(u & 0xFFFF) << 16) | (uint32_t)(l & 0xFFFF)) const uint32_t yuv_709_table[] = { PACK_HALVES(CYR_709,CUR_709), PACK_HALVES(CYG_709,CUG_709), PACK_HALVES(CYB_709,CUB_709), CVR_709, CVG_709, CVB_709 }; static inline uint32_t rgb2yuv422_rec709_opt(int R, int G, int B) { /* basically it is this code, just optimzied as much as i can: int Y = (CYR_709 * R + CYG_709 * G + CYB_709 * B) / CRANGE_709; int U = (CUR_709 * R + CUG_709 * G + CUB_709 * B) / CRANGE_709; int V = (CVR_709 * R + CVG_709 * G + CVB_709 * B) / CRANGE_709; return UYVY_PACK(U,Y,V,Y); */ int ret = 0; /* warning: reordered instructions to (hopefully) prevent pipelining effects */ asm volatile ( /* load parameters */ "LDR R3, =yuv_709_table\r\n" "LDMIA R3!, {R4-R9}\r\n" /* multiply RGB into 16 bit fields in 32 bit registers */ "MOV R12, #0xFF\r\n" /* YU */ "MUL R10, R4, %[R]\r\n" /* YU */ "MLA R10, R5, %[G], R10\r\n" /* YU */ "MLA R10, R6, %[B], R10\r\n" "AND R11, R10, R12, LSL#24\r\n" /* channel V is multiplied separately */ /* V */ "MUL R4, R7, %[R]\r\n" "ORR R11, R11, LSR#16\r\n" /* R11: YY00YY00 */ /* V */ "MLA R4, R8, %[G], R4\r\n" "AND R10, R10, R12, LSL#8\r\n" /* YYYYUUUU -> 0000UU00 */ /* V */ "MLA R4, R9, %[B], R4\r\n" "ORR R11, R10, LSR#8\r\n" /* R11: YY00YYUU */ "AND R4, R4, R12, LSL#8\r\n" /* 0000VVVV -> 0000VV00 */ "ORR %[ret], R11, R4, LSL#8\r\n" /* R11: YYVVYYUU */ : [ret]"=r"(ret) : [R]"r"(R), [G]"r"(G), [B]"r"(B) : "r3", /* table */ "r4", "r5", "r6", "r7", "r8", "r9", /* multipliers from table */ "r10", "r11", "r12" /* temporary results */ ); return ret; } static void bpp16_to_yuv_task() { TASK_LOOP { frame_buf_t *in_buf; /* signal to stop rendering */ if(raw_twk_task_abort) { break; } /* is there something to render? */ if(msg_queue_receive(mlv_play_queue_bpp16_to_yuv, &in_buf, 50)) { continue; } if(!in_buf->frameBuffer) { bmp_printf(FONT_MED, 30, 400, "buffer empty"); beep(); msleep(1000); break; } int y1 = in_buf->y1; int y2 = in_buf->y2; uint16_t* lv16 = CACHEABLE(in_buf->lvBuffer); uint32_t* lv32 = (uint32_t*) lv16; uint64_t* lv64 = (uint64_t*) lv16; if (!lv16) return; int x1 = BM2LV_X(os.x0); int x2 = BM2LV_X(os.x_max); x1 = MAX(x1, RAW2LV_X(MAX(raw_info.active_area.x1, preview_rect_x))); x2 = MIN(x2, RAW2LV_X(MIN(raw_info.active_area.x2, preview_rect_x + preview_rect_w))); if (x2 < x1) return; /* cache the LV to RAW transformation for the inner loop to make it faster */ /* white balance 2,1,2 => use two gamma curves to simplify code */ static uint8_t gamma_rb[1024]; static uint8_t gamma_g[1024]; static uint8_t gamma[1024]; static int* lv2rx = NULL; static int last_x1 = 0; static int last_x2 = 0; static int last_black_level = 0; if(last_black_level != raw_info.black_level || last_x1 != x1 || last_x2 != x2) { if(lv2rx) { free(lv2rx); } lv2rx = NULL; } if(lv2rx == NULL) { last_x1 = x1; last_x2 = x2; last_black_level = raw_info.black_level; int black = (raw_info.black_level << (14 - in_buf->bpp) >> 4); for (int i = 0; i < 1024; i++) { int g = (i > black) ? log2f(i - black) * 255 / 10 : 0; gamma[i] = g * g / 255; /* idk, looks better this way */ } for (int i = 0; i < 1024; i++) { /* only show 10 bits */ int g_rb = (i > black) ? (log2f(i - black) + 1) * 255 / 10 : 0; int g_g = (i > black) ? (log2f(i - black)) * 255 / 10 : 0; gamma_rb[i] = COERCE(g_rb * g_rb / 255, 0, 255); /* idk, looks better this way */ gamma_g[i] = COERCE(g_g * g_g / 255, 0, 255); /* (it's like a nonlinear curve applied on top of log) */ } lv2rx = malloc(x2 * 4); if (!lv2rx) return; for (int x = x1; x < x2; x++) { lv2rx[x] = LV2RAW_X(x) & ~1; } } /* full-res vertically */ for (int y = y1; y < y2; y+=1) { int yr = LV2RAW_Y(y) & ~1; /* on HDMI screens, BM2LV_DX() may get negative */ if((yr <= preview_rect_y || yr >= preview_rect_y + preview_rect_h) && BM2LV_DX(x2-x1) > 0) { /* out of range, just fill with black */ memset(&lv32[LV(0,y)/4], 0, BM2LV_DX(x2-x1)*2); continue; } uint16_t * row = ((uint16_t *)in_buf->frameBuffer) + (yr * in_buf->xRes); if(in_buf->quality == RAW_PREVIEW_GRAY_ULTRA_FAST) { /* half-res horizontally, to simplify YUV422 math */ for (int x = x1; x < x2; x += 4) { int xr = lv2rx[x]; uint32_t c = row[xr]; uint64_t Y = gamma[c>>6]; Y = (Y << 8) | (Y << 24) | (Y << 40) | (Y << 56); int idx = LV(x,y)/8; lv64[idx] = Y; lv64[idx + vram_lv.pitch/8] = Y; } } else { /* half-res horizontally, to simplify YUV422 math */ for (int x = x1; x < x2; x += 2) { int xr = lv2rx[x]; uint32_t rg = *((uint32_t*) &row[xr]) >> 6; uint16_t r = rg & 0x3FF; uint16_t g = rg >> 16; uint16_t b = row[in_buf->xRes + xr + 1] >> 6; r = gamma_rb[r]; g = gamma_g [g]; b = gamma_rb[b]; lv32[LV(x,y)/4] = rgb2yuv422_rec709_opt(r, g, b); } } } free(in_buf->frameBuffer); free(in_buf); } } static void edmac_read_complete_cbr(void *ctx) { } static void edmac_write_complete_cbr(void * ctx) { give_semaphore(edmac_write_done_sem); } static void upconvert_bpp(frame_buf_t *frame, void* buf_out) { uint32_t edmac_read_chan = 10; uint32_t edmac_write_chan = 1; uint32_t edmac_read_connection = 1; uint32_t edmac_write_connection = 16; /* it locks up without reslock */ if (!resLock) { int edmac_read_ch_index = edmac_channel_to_index(edmac_read_chan); int edmac_write_ch_index = edmac_channel_to_index(edmac_write_chan); uint32_t resIds[] = { 0x00000000 | edmac_write_ch_index, /* write edmac channel */ 0x00010000 | edmac_read_ch_index, /* read edmac channel */ 0x00020000 | edmac_write_connection, /* write connection */ 0x00030000 | edmac_read_connection, /* read connection */ 0x00050002, /* DSUNPACK? */ 0x00050005, /* DEFC? */ 0x0005001d, /* PACK16/WDMAC16 */ 0x0005001f, /* PACK16/WDMAC16 */ }; resLock = CreateResLockEntry(resIds, COUNT(resIds)); } LockEngineResources(resLock); /* configure image processing modules */ /* register addresses from strings */ /* register values from a QEMU trace of ProcessPathForFurikake */ const uint32_t DS_SEL = 0xC0F08104; const uint32_t PACK16_ISEL = 0xC0F082D0; const uint32_t PACK16_ISEL2 = 0xC0F0839C; const uint32_t WDMAC16_ISEL = 0xC0F082D8; const uint32_t DSUNPACK_ENB = 0xC0F08060; const uint32_t DSUNPACK_MODE = 0xC0F08064; const uint32_t DSUNPACK_DM_EN = 0xC0F08274; const uint32_t DEF_ENB = 0xC0F080A0; const uint32_t DEF_80A4 = 0xC0F080A4; const uint32_t DEF_MODE = 0xC0F080A8; const uint32_t DEF_CTRL = 0xC0F080AC; const uint32_t DEF_YB_XB = 0xC0F080B0; const uint32_t DEF_YN_XN = 0xC0F080B4; const uint32_t DEF_YA_XA = 0xC0F080BC; const uint32_t DEF_INTR_EN = 0xC0F080D0; const uint32_t DEF_HOSEI = 0xC0F080D4; const uint32_t DEFC_X2MODE = 0xC0F08270; const uint32_t DEFC_DET_MODE = 0xC0F082B4; const uint32_t PACK16_ENB = 0xC0F08120; const uint32_t PACK16_MODE = 0xC0F08124; const uint32_t PACK16_DEFM_ON = 0xC0F082B8; const uint32_t PACK16_ILIM = 0xC0F085B4; const uint32_t PACK16_CCD2_DM_EN= 0xC0F0827C; /* for PACK16_MODE, DSUNPACK_MODE, ADUNPACK_MODE (mask 0x131) */ const uint32_t MODE_10BIT = 0x000; const uint32_t MODE_12BIT = 0x010; const uint32_t MODE_14BIT = 0x020; const uint32_t MODE_16BIT = 0x120; const uint32_t DARK_MODE_SUBTRACT = (1<<16); /* else it seems MAX(a,b) or smth like that */ const uint32_t DARK_MODE_WHOLE = (1<<12); /* if set, uses whole data stream as input, else use only a line? */ const uint32_t DARK_MODE_UNK8 = (1<<8); /* use image B as first operand? */ const uint32_t DARK_MODE_APPLY = (1<<4); uint32_t pixel_mode = 0; switch(frame->bpp) { case 10: pixel_mode = MODE_10BIT; break; case 12: pixel_mode = MODE_12BIT; break; case 14: pixel_mode = MODE_14BIT; break; case 16: pixel_mode = MODE_16BIT; break; } engio_write((uint32_t[]) { /* input selection for the processing modules? */ DS_SEL, 0, PACK16_ISEL, 4, PACK16_ISEL2, 0, WDMAC16_ISEL, 0, /* DSUNPACK module (input image data) */ DSUNPACK_ENB, 0x80000000, DSUNPACK_MODE, pixel_mode, DSUNPACK_DM_EN, 0, /* DEF(C) module (is it needed?) */ DEF_ENB, 0x80000000, DEF_80A4, 0, DEF_CTRL, 0, DEF_MODE, 0x104, DEF_YN_XN, 0, DEF_YB_XB, ((frame->yRes-1) << 16) | (frame->xRes-1), DEF_YA_XA, 0, DEF_INTR_EN, 0, DEF_HOSEI, 0x11, DEFC_X2MODE, 0, DEFC_DET_MODE, 1, /* PACK16 module (output image data) */ PACK16_ENB, 0x80000000, PACK16_MODE, MODE_16BIT, PACK16_DEFM_ON, 1, PACK16_ILIM, 0x3FFF, /* white level? */ PACK16_CCD2_DM_EN, 0, /* whew! */ 0xFFFFFFFF, 0xFFFFFFFF }); /* EDMAC setup */ RegisterEDmacCompleteCBR(edmac_read_chan, edmac_read_complete_cbr, 0); RegisterEDmacCompleteCBR(edmac_write_chan, edmac_write_complete_cbr, 0); ConnectWriteEDmac(edmac_write_chan, edmac_write_connection); ConnectReadEDmac(edmac_read_chan, edmac_read_connection); struct edmac_info src_edmac_info = { .xb = frame->xRes * frame->bpp / 8, .yb = frame->yRes - 1, }; struct edmac_info dst_edmac_info = { .xb = frame->xRes * 2, .yb = frame->yRes - 1, }; SetEDmac(edmac_read_chan, frame->frameBuffer, &src_edmac_info, 0x20000); SetEDmac(edmac_write_chan, buf_out, &dst_edmac_info, 1); /* start processing */ StartEDmac(edmac_write_chan, 0); /* start operation */ engio_write((uint32_t[]) { PACK16_ENB, 1, DEF_ENB, 1, DSUNPACK_ENB, 1, 0xFFFFFFFF, 0xFFFFFFFF }); StartEDmac(edmac_read_chan, 2); /* wait for everything to finish */ take_semaphore(edmac_write_done_sem, 0); /* reset processing modules */ engio_write((uint32_t[]) { PACK16_ENB, 0x80000000, DEF_ENB, 0x80000000, DSUNPACK_ENB, 0x80000000, 0xFFFFFFFF, 0xFFFFFFFF }); UnregisterEDmacCompleteCBR(edmac_read_chan); UnregisterEDmacCompleteCBR(edmac_write_chan); UnLockEngineResources(resLock); } static void raw_to_bpp16_task() { TASK_LOOP { frame_buf_t *in_buf; /* signal to stop rendering */ if(raw_twk_task_abort) { break; } /* is there something to render? */ if(msg_queue_receive(mlv_play_queue_raw_to_bpp16, &in_buf, 50)) { continue; } if(!in_buf->frameBuffer) { bmp_printf(FONT_MED, 30, 400, "in_buf empty"); beep(); msleep(1000); break; } void *out_frame = malloc(in_buf->xRes * in_buf->yRes * 16 / 8); upconvert_bpp(in_buf, out_frame); frame_buf_t *out_buf = malloc(sizeof(frame_buf_t)); *out_buf = *in_buf; out_buf->frameBuffer = out_frame; msg_queue_post(mlv_play_queue_bpp16_to_yuv, (uint32_t)out_buf); free(in_buf); } } static void raw_preview_color_work(void* raw_buffer, void* lv_buf, int y1, int y2) { frame_buf_t *msg = malloc(sizeof(frame_buf_t)); msg->xRes = raw_info.width; msg->yRes = raw_info.height; msg->frameBuffer = raw_buffer; msg->lvBuffer = lv_buf; msg->y1 = y1; msg->y2 = y2; msg->bpp = raw_info.bits_per_pixel; msg->quality = RAW_PREVIEW_COLOR_HALFRES; msg_queue_post(mlv_play_queue_raw_to_bpp16, (uint32_t)msg); } static void raw_preview_fast_work(void* raw_buffer, void* lv_buf, int y1, int y2) { frame_buf_t *msg = malloc(sizeof(frame_buf_t)); msg->xRes = raw_info.width; msg->yRes = raw_info.height; msg->frameBuffer = raw_buffer; msg->lvBuffer = lv_buf; msg->y1 = y1; msg->y2 = y2; msg->bpp = raw_info.bits_per_pixel; msg->quality = RAW_PREVIEW_GRAY_ULTRA_FAST; msg_queue_post(mlv_play_queue_raw_to_bpp16, (uint32_t)msg); } void raw_preview_fast_ex(void* raw_buffer, void* lv_buffer, int y1, int y2, int quality) { if (raw_buffer == (void*)-1) raw_buffer = (void*)raw_info.buffer; if (lv_buffer == (void*)-1) lv_buffer = (void*)get_lcd_422_buf(); if (y1 == -1) y1 = BM2LV_Y(os.y0); if (y2 == -1) y2 = BM2LV_Y(os.y_max); if (quality == -1) quality = 0; switch (quality) { case RAW_PREVIEW_GRAY_ULTRA_FAST: raw_preview_fast_work(raw_buffer, lv_buffer, y1, y2); break; case RAW_PREVIEW_COLOR_HALFRES: default: raw_preview_color_work(raw_buffer, lv_buffer, y1, y2); break; } } void raw_preview_fast() { raw_preview_fast_ex((void*)-1, (void*)-1, -1, -1, -1); } /* may not be correct on 4:3 screens */ void raw_force_aspect_ratio_1to1() { if (lv2raw.sy < lv2raw.sx) /* image too tall */ { lv2raw.sy = lv2raw.sx; int height = RAW2LV_DY(preview_rect_h); int offset = (BM2LV_DY(os.y_ex) - height) / 2; int skip_top = preview_rect_y; lv2raw.ty = skip_top - LV2RAW_DY(os.y0) - LV2RAW_DY(offset); } else if (lv2raw.sx < lv2raw.sy) /* image too wide */ { lv2raw.sx = lv2raw.sy; int width = RAW2LV_DX(preview_rect_w); int offset = (vram_lv.width - width) / 2; int skip_left = preview_rect_x; lv2raw.tx = skip_left - LV2RAW_DX(os.x0) - LV2RAW_DX(offset); } } void raw_set_preview_rect(int x, int y, int w, int h) { preview_rect_x = x; preview_rect_y = y; preview_rect_w = w; preview_rect_h = h; /* preview area (usually active area) should match the area from os.x0/y0 to os.x_max/y_max */ /* note: this will call BMP_LOCK */ /* not exactly a good idea when we have already acquired raw_sem */ //~ get_yuv422_vram(); // update vram parameters /* scaling factor: raw width should match os.x_ex, same for raw height and os.y_ex */ lv2raw.sx = 1024 * w / BM2LV_DX(os.x_ex); lv2raw.sy = 1024 * h / BM2LV_DY(os.y_ex); /* translation: raw top-left corner (x,y) should match (os.x0,os.y0) */ int x0_lv = BM2LV_X(os.x0); int y0_lv = BM2LV_Y(os.y0); lv2raw.tx = x - LV2RAW_DX(x0_lv); lv2raw.ty = y - LV2RAW_DY(y0_lv); } void raw_set_geometry(int width, int height, int skip_left, int skip_right, int skip_top, int skip_bottom) { raw_info.width = width; raw_info.height = height; raw_info.pitch = raw_info.width * raw_info.bits_per_pixel / 8; raw_info.frame_size = raw_info.height * raw_info.pitch; raw_info.active_area.x1 = skip_left; raw_info.active_area.y1 = skip_top; raw_info.active_area.x2 = raw_info.width - skip_right; raw_info.active_area.y2 = raw_info.height - skip_bottom; raw_info.jpeg.x = 0; raw_info.jpeg.y = 0; raw_info.jpeg.width = raw_info.width - skip_left - skip_right; raw_info.jpeg.height = raw_info.height - skip_top - skip_bottom; dbg_printf("active area: x=%d..%d, y=%d..%d\n", raw_info.active_area.x1, raw_info.active_area.x2, raw_info.active_area.y1, raw_info.active_area.y2); /* Canon does not render the entire active area - these numbers give pixel-perfect alignment on 5D3 */ int preview_skip_left = skip_left + 14; int preview_skip_top = skip_top + 8; int preview_width = raw_info.jpeg.width - 28; int preview_height = raw_info.jpeg.height - 16; #ifdef CONFIG_RAW_LIVEVIEW if (lv_dispsize > 1) { int delta_x, delta_y; if (focus_box_get_raw_crop_offset(&delta_x, &delta_y)) { /* in 10x, the yuv area is twice as small than in 5x */ int zoom_corr = lv_dispsize == 10 ? 2 : 1; /** * |<-----------------raw_info.width--------------------------->| * | | * | raw_info.jpeg.width | * | |<---------------------------------------------------------| * | | *->|-|<--- skip_left | * | | | skip_top * +-------preview_height | * .-----:------------------------------------------------------. -----------------v----------------------------- * | |```:``````````````````````````````````````````````````````| `````````````````^`` ^ ^ * | | : |<-preview_width->| | delta_y | | preview_skip_top * | | _V_ _________________ | | | _____________v___ * | | | | | | v | * | | | | | C_raw | ------+-- raw_info.height * | | | | C_yuv | | ------+-- | * | | | | | | ^ | * | | _|_ |_________________| | | | * | | ^ | v * '------------------------------------------------------------' ---------------------------- * | | * | | |---delta_x--->| * | | *->|----------|<-- preview_skip_left * | | * */ /* if the yuv window is on the left side, delta_x is > 0 */ preview_skip_left += (raw_info.jpeg.width - vram_hd.width / zoom_corr) / 2 - delta_x; preview_skip_top += (raw_info.jpeg.height - vram_hd.height / zoom_corr) / 2 - delta_y; preview_width = vram_hd.width / zoom_corr; preview_height = vram_hd.height / zoom_corr; } } #endif raw_set_preview_rect(preview_skip_left, preview_skip_top, preview_width, preview_height); dbg_printf("lv2raw sx:%d sy:%d tx:%d ty:%d\n", lv2raw.sx, lv2raw.sy, lv2raw.tx, lv2raw.ty); dbg_printf("raw2lv test: (%d,%d) - (%d,%d)\n", RAW2LV_X(raw_info.active_area.x1), RAW2LV_Y(raw_info.active_area.y1), RAW2LV_X(raw_info.active_area.x2), RAW2LV_Y(raw_info.active_area.y2)); dbg_printf(" should be: (%d,%d) - (%d,%d)\n", BM2LV_X(os.x0), BM2LV_Y(os.y0), BM2LV_X(os.x_max), BM2LV_Y(os.y_max)); dbg_printf("raw2bm test: (%d,%d) - (%d,%d)\n", RAW2BM_X(raw_info.active_area.x1), RAW2BM_Y(raw_info.active_area.y1), RAW2BM_X(raw_info.active_area.x2), RAW2BM_Y(raw_info.active_area.y2)); dbg_printf(" should be: (%d,%d) - (%d,%d)\n", os.x0, os.y0, os.x_max, os.y_max); dbg_printf("bm2raw test: (%d,%d) - (%d,%d)\n", BM2RAW_X(os.x0), BM2RAW_Y(os.y0), BM2RAW_X(os.x_max), BM2RAW_Y(os.y_max)); dbg_printf(" should be: (%d,%d) - (%d,%d)\n", raw_info.active_area.x1, raw_info.active_area.y1, raw_info.active_area.x2, raw_info.active_area.y2); } static unsigned int raw_twk_init() { edmac_write_done_sem = create_named_semaphore("edmac_write_done_sem", 0); mlv_play_queue_raw_to_bpp16 = (struct msg_queue *) msg_queue_create("mlv_play_queue_raw_to_bpp16", 10); mlv_play_queue_bpp16_to_yuv = (struct msg_queue *) msg_queue_create("mlv_play_queue_bpp16_to_yuv", 10); task_create("bpp16_to_yuv_task", 0x15, 0x4000, bpp16_to_yuv_task, 0); task_create("raw_to_bpp16_task", 0x15, 0x4000, raw_to_bpp16_task, 0); return 0; } static unsigned int raw_twk_deinit() { raw_twk_task_abort = 1; return 0; } MODULE_INFO_START() MODULE_INIT(raw_twk_init) MODULE_DEINIT(raw_twk_deinit) MODULE_INFO_END() MODULE_CBRS_START() MODULE_CBRS_END() MODULE_PROPHANDLERS_START() MODULE_PROPHANDLERS_END() MODULE_CONFIGS_START() MODULE_CONFIGS_END()