/* linux/drivers/media/video/samsung/fimc/fimc_output.c * * Copyright (c) 2010 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * V4L2 Output device support file for Samsung Camera Interface (FIMC) driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fimc.h" static __u32 fimc_get_pixel_format_type(__u32 pixelformat) { switch (pixelformat) { case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_RGB565: return FIMC_RGB; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV12T: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_YUV420: return FIMC_YUV420; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_YUV422P: return FIMC_YUV422; default: return FIMC_YUV444; } } void fimc_outdev_set_src_addr(struct fimc_control *ctrl, dma_addr_t *base) { fimc_hwset_addr_change_disable(ctrl); fimc_hwset_input_address(ctrl, base); fimc_hwset_addr_change_enable(ctrl); } int fimc_outdev_start_camif(void *param) { struct fimc_control *ctrl = (struct fimc_control *)param; fimc_hwset_start_scaler(ctrl); fimc_hwset_enable_capture(ctrl, 0); /* bypass disable */ fimc_hwset_start_input_dma(ctrl); return 0; } static int fimc_outdev_stop_camif(void *param) { struct fimc_control *ctrl = (struct fimc_control *)param; fimc_hwset_stop_input_dma(ctrl); fimc_hwset_disable_autoload(ctrl); fimc_hwset_stop_scaler(ctrl); fimc_hwset_disable_capture(ctrl); fimc_hwset_sw_reset(ctrl); fimc_clk_en(ctrl, false); return 0; } int fimc_outdev_stop_streaming(struct fimc_control *ctrl, struct fimc_ctx *ctx) { int ret = 0; fimc_dbg("%s: called\n", __func__); switch (ctx->overlay.mode) { case FIMC_OVLY_DMA_AUTO: /* fall through */ case FIMC_OVLY_DMA_MANUAL: if (ctx->status == FIMC_STREAMON_IDLE) ctx->status = FIMC_STREAMOFF; else ctx->status = FIMC_READY_OFF; break; case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */ case FIMC_OVLY_NONE_MULTI_BUF: #ifdef CONFIG_MACH_ARIES if (ctx->status == FIMC_STREAMON_IDLE) #else // CONFIG_MACH_P1 if (ctx->status <= FIMC_READY_ON || ctx->status == FIMC_STREAMON_IDLE) #endif ctx->status = FIMC_STREAMOFF; else ctx->status = FIMC_READY_OFF; ret = wait_event_timeout(ctrl->wq, (ctx->status == FIMC_STREAMOFF), FIMC_ONESHOT_TIMEOUT); if (ret == 0) { fimc_dump_context(ctrl, ctx); fimc_err("fail %s: %d\n", __func__, ctx->ctx_num); } break; default: break; } return 0; } int fimc_outdev_resume_dma(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct v4l2_rect fimd_rect; struct fb_var_screeninfo var; struct fb_info *fbinfo; struct s3cfb_window *win; int ret = -1, idx; #if defined(CONFIG_VIDEO_NM6XX) struct fimc_global *fimc = get_fimc_dev(); #endif fbinfo = registered_fb[ctx->overlay.fb_id]; win = (struct s3cfb_window *)fbinfo->par; memcpy(&var, &fbinfo->var, sizeof(struct fb_var_screeninfo)); memset(&fimd_rect, 0, sizeof(struct v4l2_rect)); ret = fimc_fimd_rect(ctrl, ctx, &fimd_rect); if (ret < 0) { fimc_err("fimc_fimd_rect fail\n"); return -EINVAL; } /* set window path & owner */ win->path = DATA_PATH_DMA; win->owner = DMA_MEM_OTHER; win->other_mem_addr = ctx->dst[1].base[FIMC_ADDR_Y]; win->other_mem_size = ctx->dst[1].length[FIMC_ADDR_Y]; #ifdef CONFIG_MACH_ARIES /* Update WIN size */ var.xres_virtual = fimd_rect.width; var.yres_virtual = fimd_rect.height; #endif #if defined(CONFIG_VIDEO_NM6XX) if (fimc->active_camera == CAMERA_ID_MOBILETV) { var.xres = 1024; var.yres = 600; } else #endif { var.xres = fimd_rect.width; var.yres = fimd_rect.height; } /* Update WIN position */ win->x = fimd_rect.left; win->y = fimd_rect.top; var.activate = FB_ACTIVATE_FORCE; ret = fb_set_var(fbinfo, &var); if (ret < 0) { fimc_err("fb_set_var fail (ret=%d)\n", ret); return -EINVAL; } idx = ctx->outq[0]; if (idx == -1) { fimc_err("out going queue is empty.\n"); return -EINVAL; } win->other_mem_addr = ctx->dst[idx].base[FIMC_ADDR_Y]; ret = fb_pan_display(fbinfo, &fbinfo->var); if (ret < 0) { fimc_err("%s: fb_pan_display fail (ret=%d)\n", __func__, ret); return -EINVAL; } ctrl->fb.is_enable = 1; return 0; } static void fimc_init_out_buf(struct fimc_ctx *ctx) { int i; for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->src[i].state = VIDEOBUF_IDLE; ctx->src[i].flags = 0x0; ctx->inq[i] = -1; ctx->outq[i] = -1; } } static int fimc_outdev_set_src_buf(struct fimc_control *ctrl, struct fimc_ctx *ctx) { u32 width = ctx->pix.width; u32 height = ctx->pix.height; u32 format = ctx->pix.pixelformat; u32 y_size = width * height; u32 cb_size = 0, cr_size = 0; u32 i, size; dma_addr_t *curr = &ctrl->mem.curr; switch (format) { case V4L2_PIX_FMT_RGB32: size = PAGE_ALIGN(y_size << 2); break; case V4L2_PIX_FMT_RGB565: /* fall through */ case V4L2_PIX_FMT_UYVY: /* fall through */ case V4L2_PIX_FMT_YVYU: /* fall through */ case V4L2_PIX_FMT_VYUY: /* fall through */ case V4L2_PIX_FMT_YUYV: size = PAGE_ALIGN(y_size << 1); break; case V4L2_PIX_FMT_YUV420: cb_size = y_size >> 2; cr_size = y_size >> 2; size = PAGE_ALIGN(y_size + cb_size + cr_size); break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: cb_size = y_size >> 1; size = PAGE_ALIGN(y_size + cb_size); break; case V4L2_PIX_FMT_NV12T: fimc_get_nv12t_size(width, height, &y_size, &cb_size, 0); size = PAGE_ALIGN(y_size + cb_size); break; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: cb_size = y_size; size = PAGE_ALIGN(y_size + cb_size); break; default: fimc_err("%s: Invalid pixelformt : %d\n", __func__, format); return -EINVAL; } if ((*curr + size * FIMC_OUTBUFS) > (ctrl->mem.base + ctrl->mem.size)) { fimc_err("%s: Reserved memory is not sufficient\n", __func__); return -EINVAL; } /* Initialize source buffer addr */ switch (format) { case V4L2_PIX_FMT_RGB565: /* fall through */ case V4L2_PIX_FMT_RGB32: /* fall through */ case V4L2_PIX_FMT_UYVY: /* fall through */ case V4L2_PIX_FMT_YVYU: /* fall through */ case V4L2_PIX_FMT_VYUY: /* fall through */ case V4L2_PIX_FMT_YUYV: for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->src[i].base[FIMC_ADDR_Y] = *curr; ctx->src[i].length[FIMC_ADDR_Y] = size; ctx->src[i].base[FIMC_ADDR_CB] = 0; ctx->src[i].length[FIMC_ADDR_CB] = 0; ctx->src[i].base[FIMC_ADDR_CR] = 0; ctx->src[i].length[FIMC_ADDR_CR] = 0; *curr += size; } break; case V4L2_PIX_FMT_NV12: /* fall through */ case V4L2_PIX_FMT_NV21: /* fall through */ case V4L2_PIX_FMT_NV16: /* fall through */ case V4L2_PIX_FMT_NV61: /* fall through */ case V4L2_PIX_FMT_NV12T: for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->src[i].base[FIMC_ADDR_Y] = *curr; ctx->src[i].length[FIMC_ADDR_Y] = y_size; ctx->src[i].base[FIMC_ADDR_CB] = *curr + y_size; ctx->src[i].length[FIMC_ADDR_CB] = cb_size; ctx->src[i].base[FIMC_ADDR_CR] = 0; ctx->src[i].length[FIMC_ADDR_CR] = 0; *curr += size; } break; case V4L2_PIX_FMT_YUV420: for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->src[i].base[FIMC_ADDR_Y] = *curr; ctx->src[i].base[FIMC_ADDR_CB] = *curr + y_size; ctx->src[i].base[FIMC_ADDR_CR] = *curr + y_size + cb_size; ctx->src[i].length[FIMC_ADDR_Y] = y_size; ctx->src[i].length[FIMC_ADDR_CB] = cb_size; ctx->src[i].length[FIMC_ADDR_CR] = cr_size; *curr += size; } break; default: fimc_err("%s: Invalid pixelformt : %d\n", __func__, format); return -EINVAL; } return 0; } #ifdef CONFIG_MACH_P1 static void fimc_outdev_clear_dst_buf(struct fimc_control *ctrl, struct fimc_ctx *ctx, int index) { void *dst_mem = NULL; if ((ctx->overlay.mode != FIMC_OVLY_DMA_AUTO) && (ctx->overlay.mode != FIMC_OVLY_DMA_MANUAL)) return; /* clear buffer */ dst_mem = (void *)ioremap((int)ctx->dst[index].base[FIMC_ADDR_Y], ctx->dst[index].length[FIMC_ADDR_Y]); if (dst_mem) { memset(dst_mem, 0x0, ctx->dst[index].length[FIMC_ADDR_Y]); iounmap(dst_mem); } else fimc_warn("%s: Failed to clear destination buffers\n", __func__); } #endif static int fimc_outdev_set_dst_buf(struct fimc_control *ctrl, struct fimc_ctx *ctx) { dma_addr_t *curr = &ctrl->mem.curr; dma_addr_t end; #ifdef CONFIG_MACH_P1 void *dst_mem = NULL; #endif u32 width = ctrl->fb.lcd_hres; u32 height = ctrl->fb.lcd_vres; u32 i, size; end = ctrl->mem.base + ctrl->mem.size; size = PAGE_ALIGN(width * height * 4); if ((*curr + (size * FIMC_OUTBUFS)) > end) { fimc_err("%s: Reserved memory is not sufficient\n", __func__); return -EINVAL; } #ifdef CONFIG_MACH_P1 if (ctx->dst[0].base[FIMC_ADDR_Y]) { fimc_warn("%s: already allocated destination buffers\n", __func__); return 0; } /* clear buffer */ dst_mem = (void *)ioremap((int)*curr, size*FIMC_OUTBUFS); if (dst_mem) { memset(dst_mem, 0x0, size*FIMC_OUTBUFS); iounmap(dst_mem); } else fimc_warn("%s: Failed to clear destination buffers\n", __func__); #endif /* Initialize destination buffer addr */ for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->dst[i].base[FIMC_ADDR_Y] = *curr; ctx->dst[i].length[FIMC_ADDR_Y] = size; ctx->dst[i].base[FIMC_ADDR_CB] = 0; ctx->dst[i].length[FIMC_ADDR_CB] = 0; ctx->dst[i].base[FIMC_ADDR_CR] = 0; ctx->dst[i].length[FIMC_ADDR_CR] = 0; *curr += size; } return 0; } static int fimc_set_rot_degree(struct fimc_control *ctrl, struct fimc_ctx *ctx, int degree) { switch (degree) { case 0: /* fall through */ case 90: /* fall through */ case 180: /* fall through */ case 270: ctx->rotate = degree; break; default: fimc_err("Invalid rotate value : %d\n", degree); return -EINVAL; } return 0; } int fimc_outdev_check_param(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct v4l2_rect dst, bound; u32 rot = 0; int ret = 0, i, exclusive = 0; rot = fimc_mapping_rot_flip(ctx->rotate, ctx->flip); dst.top = ctx->win.w.top; dst.left = ctx->win.w.left; dst.width = ctx->win.w.width; dst.height = ctx->win.w.height; switch (ctx->overlay.mode) { case FIMC_OVLY_DMA_AUTO: /* fall through */ case FIMC_OVLY_DMA_MANUAL: if (rot & FIMC_ROT) { bound.width = ctrl->fb.lcd_vres; bound.height = ctrl->fb.lcd_hres; } else { bound.width = ctrl->fb.lcd_hres; bound.height = ctrl->fb.lcd_vres; } break; case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */ case FIMC_OVLY_NONE_MULTI_BUF: bound.width = ctx->fbuf.fmt.width; bound.height = ctx->fbuf.fmt.height; break; default: fimc_err("%s: invalid ovelay mode.\n", __func__); return -EINVAL; } if ((dst.left + dst.width) > bound.width) { fimc_err("Horizontal position setting is failed\n"); fimc_err("\tleft = %d, width = %d, bound width = %d,\n", dst.left, dst.width, bound.width); ret = -EINVAL; } else if ((dst.top + dst.height) > bound.height) { fimc_err("Vertical position setting is failed\n"); fimc_err("\ttop = %d, height = %d, bound height = %d,\n", dst.top, dst.height, bound.height); ret = -EINVAL; } if ((ctx->status != FIMC_STREAMOFF) && (ctx->status != FIMC_READY_ON) && (ctx->status != FIMC_ON_IDLE_SLEEP)) { fimc_err("FIMC is running\n"); return -EBUSY; } /* check other open instance */ for (i = 0; i < FIMC_MAX_CTXS; i++) { switch (ctrl->out->ctx[i].overlay.mode) { case FIMC_OVLY_DMA_AUTO: /* fall through */ case FIMC_OVLY_DMA_MANUAL: exclusive++; break; case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */ case FIMC_OVLY_NONE_MULTI_BUF: /* fall through */ case FIMC_OVLY_NOT_FIXED: break; } } if (exclusive > 1) { for (i = 0; i < FIMC_MAX_CTXS; i++) fimc_err("%s: ctx %d mode = %d", __func__, i, ctrl->out->ctx[i].overlay.mode); return -EBUSY; } return ret; } static void fimc_outdev_set_src_format(struct fimc_control *ctrl, u32 pixfmt, enum v4l2_field field) { fimc_hwset_input_burst_cnt(ctrl, 4); fimc_hwset_input_colorspace(ctrl, pixfmt); fimc_hwset_input_yuv(ctrl, pixfmt); fimc_hwset_input_rgb(ctrl, pixfmt); fimc_hwset_intput_field(ctrl, field); fimc_hwset_ext_rgb(ctrl, 1); fimc_hwset_input_addr_style(ctrl, pixfmt); } static void fimc_outdev_set_dst_format(struct fimc_control *ctrl, struct v4l2_pix_format *pixfmt) { fimc_hwset_output_colorspace(ctrl, pixfmt->pixelformat); fimc_hwset_output_yuv(ctrl, pixfmt->pixelformat); fimc_hwset_output_rgb(ctrl, pixfmt->pixelformat); fimc_hwset_output_scan(ctrl, pixfmt); fimc_hwset_output_addr_style(ctrl, pixfmt->pixelformat); } static void fimc_outdev_set_format(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct v4l2_pix_format pixfmt; memset(&pixfmt, 0, sizeof(pixfmt)); fimc_outdev_set_src_format(ctrl, ctx->pix.pixelformat, ctx->pix.field); switch (ctx->overlay.mode) { case FIMC_OVLY_DMA_AUTO: /* fall through */ case FIMC_OVLY_DMA_MANUAL: /* Non-destructive overlay mode */ if (ctx->pix.field == V4L2_FIELD_NONE) { pixfmt.pixelformat = V4L2_PIX_FMT_RGB32; pixfmt.field = V4L2_FIELD_NONE; } else if (ctx->pix.field == V4L2_FIELD_INTERLACED_TB) { pixfmt.pixelformat = V4L2_PIX_FMT_YUV444; pixfmt.field = V4L2_FIELD_INTERLACED_TB; } else if (ctx->pix.field == V4L2_FIELD_ANY) { pixfmt.pixelformat = V4L2_PIX_FMT_RGB32; pixfmt.field = V4L2_FIELD_NONE; } break; case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */ case FIMC_OVLY_NONE_MULTI_BUF: /* Destructive overlay mode */ pixfmt.pixelformat = ctx->fbuf.fmt.pixelformat; pixfmt.field = V4L2_FIELD_NONE; break; default: fimc_err("Invalid overlay mode %d\n", ctx->overlay.mode); break; } fimc_outdev_set_dst_format(ctrl, &pixfmt); } static void fimc_outdev_set_path(struct fimc_control *ctrl, struct fimc_ctx *ctx) { /* source path */ fimc_hwset_input_source(ctrl, FIMC_SRC_MSDMA); fimc_hwset_disable_lcdfifo(ctrl); fimc_hwset_disable_autoload(ctrl); } static void fimc_outdev_set_rot(struct fimc_control *ctrl, struct fimc_ctx *ctx) { u32 rot = ctx->rotate; u32 flip = ctx->flip; fimc_hwset_input_rot(ctrl, 0, 0); fimc_hwset_input_flip(ctrl, 0, 0); fimc_hwset_output_rot_flip(ctrl, rot, flip); } static void fimc_outdev_set_src_dma_offset(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct v4l2_rect bound, crop; u32 pixfmt = ctx->pix.pixelformat; bound.width = ctx->pix.width; bound.height = ctx->pix.height; crop.left = ctx->crop.left; crop.top = ctx->crop.top; crop.width = ctx->crop.width; crop.height = ctx->crop.height; fimc_hwset_input_offset(ctrl, pixfmt, &bound, &crop); } static int fimc4x_outdev_check_src_size(struct fimc_control *ctrl, struct fimc_ctx *ctx, struct v4l2_rect *real, struct v4l2_rect *org) { /* No Input Rotator */ if (real->height < 8) { fimc_err("SRC Real_H: Min 8\n"); return -EINVAL; } if (real->width < 16) { fimc_err("SRC Real_W: Min 16\n"); return -EINVAL; } if (real->width > ctrl->limit->real_w_no_rot) { fimc_err("SRC REAL_W: Real_W <= %d\n", ctrl->limit->real_w_no_rot); return -EINVAL; } if (org->height < real->height) { fimc_err("SRC Org_H: larger than Real_H\n"); return -EINVAL; } if (org->width < real->width) { fimc_err("SRC Org_W: Org_W >= Real_W\n"); return -EINVAL; } if (ctx->sc.pre_vratio) { if (real->height % ctx->sc.pre_vratio) { fimc_err("SRC Real_H: multiple of pre_vratio!\n"); return -EINVAL; } } if (real->width % 16) { fimc_err("SRC Real_W: multiple of 16 !\n"); return -EINVAL; } if (ctx->sc.pre_hratio) { if (real->width % (ctx->sc.pre_hratio * 4)) { fimc_err("SRC Real_W: multiple of 4 * pre_hratio!\n"); return -EINVAL; } } if (org->width % 16) { fimc_err("SRC Org_W: multiple of 16\n"); return -EINVAL; } if (org->height < 8) { fimc_err("SRC Org_H: Min 8\n"); return -EINVAL; } return 0; } static int fimc50_outdev_check_src_size(struct fimc_control *ctrl, struct fimc_ctx *ctx, struct v4l2_rect *real, struct v4l2_rect *org) { u32 pixelformat = ctx->pix.pixelformat; /* No Input Rotator */ if (real->height < 8) { fimc_err("SRC Real_H: Min 8\n"); return -EINVAL; } if (real->width < 16) { fimc_err("SRC Real_W: Min 16\n"); return -EINVAL; } if (real->width > ctrl->limit->real_w_no_rot) { fimc_err("SRC REAL_W: Real_W <= %d\n", ctrl->limit->real_w_no_rot); return -EINVAL; } if (org->height < real->height) { fimc_err("SRC Org_H: larger than Real_H\n"); return -EINVAL; } if (org->width < real->width) { fimc_err("SRC Org_W: Org_W >= Real_W\n"); return -EINVAL; } if (ctx->pix.field == V4L2_FIELD_INTERLACED_TB) { switch (pixelformat) { case V4L2_PIX_FMT_YUV444: /* fall through */ case V4L2_PIX_FMT_RGB32: if (real->height % 2) { fimc_err("SRC Real_H: multiple of 2\n"); return -EINVAL; } case V4L2_PIX_FMT_YUV422P: if (real->height % 2) { fimc_err("SRC Real_H: multiple of 2\n"); return -EINVAL; } else if (real->width % 2) { fimc_err("SRC Real_H: multiple of 2\n"); return -EINVAL; } case V4L2_PIX_FMT_YVU420: if (real->height % 4) { fimc_err("SRC Real_H: multiple of 4\n"); return -EINVAL; } else if (real->width % 2) { fimc_err("SRC Real_H: multiple of 2\n"); return -EINVAL; } } } else if (ctx->pix.field == V4L2_FIELD_NONE) { if (pixelformat == V4L2_PIX_FMT_YUV422P) { if (real->height % 2) { fimc_err("SRC Real_H: multiple of 2\n"); return -EINVAL; } else if (real->width % 2) { fimc_err("SRC Real_H: multiple of 2\n"); return -EINVAL; } } } return 0; } static int fimc_outdev_set_src_dma_size(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); struct v4l2_rect real, org; int ret = 0; real.width = ctx->crop.width; real.height = ctx->crop.height; org.width = ctx->pix.width; org.height = ctx->pix.height; if (pdata->hw_ver == 0x50) ret = fimc50_outdev_check_src_size(ctrl, ctx, &real, &org); else ret = fimc4x_outdev_check_src_size(ctrl, ctx, &real, &org); if (ret < 0) return ret; fimc_hwset_org_input_size(ctrl, org.width, org.height); fimc_hwset_real_input_size(ctrl, real.width, real.height); return 0; } static void fimc_outdev_set_dst_dma_offset(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct v4l2_rect bound, win; struct v4l2_rect *w = &ctx->win.w; u32 pixfmt = ctx->fbuf.fmt.pixelformat; #if defined(CONFIG_VIDEO_NM6XX) struct fimc_global *fimc = get_fimc_dev(); #endif memset(&bound, 0, sizeof(bound)); memset(&win, 0, sizeof(win)); switch (ctx->rotate) { case 0: bound.width = ctx->fbuf.fmt.width; bound.height = ctx->fbuf.fmt.height; win.left = w->left; win.top = w->top; win.width = w->width; win.height = w->height; break; case 90: bound.width = ctx->fbuf.fmt.height; bound.height = ctx->fbuf.fmt.width; win.left = ctx->fbuf.fmt.height - (w->height + w->top); win.top = w->left; win.width = w->height; win.height = w->width; break; case 180: bound.width = ctx->fbuf.fmt.width; bound.height = ctx->fbuf.fmt.height; win.left = ctx->fbuf.fmt.width - (w->left + w->width); win.top = ctx->fbuf.fmt.height - (w->top + w->height); win.width = w->width; win.height = w->height; break; case 270: bound.width = ctx->fbuf.fmt.height; bound.height = ctx->fbuf.fmt.width; win.left = ctx->win.w.top; win.top = ctx->fbuf.fmt.width - (w->left + w->width); win.width = w->height; win.height = w->width; break; default: fimc_err("Rotation degree is invalid\n"); break; } switch (ctx->overlay.mode) { case FIMC_OVLY_DMA_AUTO: // fall through for aries #if defined(CONFIG_VIDEO_NM6XX) if(fimc->active_camera != CAMERA_ID_MOBILETV) { win.left = 0; win.top = 0; } fimc_hwset_output_offset(ctrl, pixfmt, &bound, &win); break; #endif case FIMC_OVLY_DMA_MANUAL: memset(&bound, 0, sizeof(bound)); memset(&win, 0, sizeof(win)); fimc_hwset_output_offset(ctrl, pixfmt, &bound, &win); break; default: fimc_hwset_output_offset(ctrl, pixfmt, &bound, &win); break; } fimc_dbg("bound:width(%d), height(%d)\n", bound.width, bound.height); fimc_dbg("win:width(%d), height(%d)\n", win.width, win.height); fimc_dbg("win:top(%d), left(%d)\n", win.top, win.left); } static int fimc_outdev_check_dst_size(struct fimc_control *ctrl, struct fimc_ctx *ctx, struct v4l2_rect *real, struct v4l2_rect *org) { u32 rot = ctx->rotate; __u32 pixel_type; pixel_type = fimc_get_pixel_format_type(ctx->fbuf.fmt.pixelformat); if (FIMC_YUV420 == pixel_type && real->height % 2) { fimc_err("DST Real_H: even number for YUV420 formats\n"); return -EINVAL; } if ((rot == 90) || (rot == 270)) { /* Use Output Rotator */ if (org->height < real->width) { fimc_err("DST Org_H: Org_H(%d) >= Real_W(%d)\n", org->height, real->width); return -EINVAL; } if (org->width < real->height) { fimc_err("DST Org_W: Org_W(%d) >= Real_H(%d)\n", org->width, real->height); return -EINVAL; } if (real->height > ctrl->limit->trg_h_rot) { fimc_err("DST REAL_H: Real_H <= %d\n", ctrl->limit->trg_h_rot); return -EINVAL; } } else { /* No Output Rotator */ if (org->height < 8) { fimc_err("DST Org_H: Min 8\n"); return -EINVAL; } if (org->height < real->height) { fimc_err("DST Org_H: Org_H >= Real_H\n"); return -EINVAL; } if (org->width % 8) { fimc_err("DST Org_W: multiple of 8\n"); return -EINVAL; } if (org->width < real->width) { fimc_err("DST Org_W: Org_W >= Real_W\n"); return -EINVAL; } if (real->height > ctrl->limit->trg_h_no_rot) { fimc_err("DST REAL_H: Real_H <= %d\n", ctrl->limit->trg_h_no_rot); return -EINVAL; } } return 0; } static int fimc_outdev_set_dst_dma_size(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct v4l2_rect org, real; int ret = -1; memset(&org, 0, sizeof(org)); memset(&real, 0, sizeof(real)); switch (ctx->overlay.mode) { case FIMC_OVLY_NONE_MULTI_BUF: /* fall through */ case FIMC_OVLY_NONE_SINGLE_BUF: #ifdef CONFIG_MACH_P1 case FIMC_OVLY_DMA_AUTO: #endif real.width = ctx->win.w.width; real.height = ctx->win.w.height; switch (ctx->rotate) { case 0: /* fall through */ case 180: org.width = ctx->fbuf.fmt.width; org.height = ctx->fbuf.fmt.height; break; case 90: /* fall through */ case 270: org.width = ctx->fbuf.fmt.height; org.height = ctx->fbuf.fmt.width; break; default: fimc_err("Rotation degree is invalid\n"); break; } break; case FIMC_OVLY_DMA_MANUAL: /* fall through */ #ifdef CONFIG_MACH_ARIES case FIMC_OVLY_DMA_AUTO: #endif real.width = ctx->win.w.width; real.height = ctx->win.w.height; switch (ctx->rotate) { case 0: /* fall through */ case 180: org.width = ctx->win.w.width; org.height = ctx->win.w.height; break; case 90: /* fall through */ case 270: org.width = ctx->win.w.height; org.height = ctx->win.w.width; break; default: fimc_err("Rotation degree is invalid\n"); break; } break; default: break; } fimc_dbg("DST org: width(%d), height(%d)\n", org.width, org.height); fimc_dbg("DST real: width(%d), height(%d)\n", real.width, real.height); ret = fimc_outdev_check_dst_size(ctrl, ctx, &real, &org); if (ret < 0) return ret; fimc_hwset_output_size(ctrl, real.width, real.height); fimc_hwset_output_area(ctrl, real.width, real.height); fimc_hwset_org_output_size(ctrl, org.width, org.height); fimc_hwset_ext_output_size(ctrl, real.width, real.height); return 0; } static void fimc_outdev_calibrate_scale_info(struct fimc_control *ctrl, struct fimc_ctx *ctx, struct v4l2_rect *src, struct v4l2_rect *dst) { /* OUTPUT ROTATOR */ src->width = ctx->crop.width; src->height = ctx->crop.height; dst->width = ctx->win.w.width; dst->height = ctx->win.w.height; fimc_dbg("src->width(%d), src->height(%d)\n", src->width, src->height); fimc_dbg("dst->width(%d), dst->height(%d)\n", dst->width, dst->height); } static int fimc_outdev_check_scaler(struct fimc_control *ctrl, struct fimc_ctx *ctx, struct v4l2_rect *src, struct v4l2_rect *dst) { u32 pixels = 0, dstfmt = 0; /* Check scaler limitation */ if (ctx->sc.pre_dst_width > ctrl->limit->pre_dst_w) { fimc_err("FIMC%d : MAX PreDstWidth is %d\n", ctrl->id, ctrl->limit->pre_dst_w); return -EDOM; } /* SRC width double boundary check */ switch (ctx->pix.pixelformat) { case V4L2_PIX_FMT_RGB32: pixels = 1; break; case V4L2_PIX_FMT_YUYV: /* fall through */ case V4L2_PIX_FMT_UYVY: /* fall through */ case V4L2_PIX_FMT_YVYU: /* fall through */ case V4L2_PIX_FMT_VYUY: /* fall through */ case V4L2_PIX_FMT_NV16: /* fall through */ case V4L2_PIX_FMT_NV61: /* fall through */ case V4L2_PIX_FMT_RGB565: pixels = 2; break; case V4L2_PIX_FMT_YUV420: /* fall through */ case V4L2_PIX_FMT_NV12: /* fall through */ case V4L2_PIX_FMT_NV21: /* fall through */ case V4L2_PIX_FMT_NV12T: pixels = 8; break; default: fimc_err("Invalid color format\n"); return -EINVAL; } if (src->width % pixels) { fimc_err("source width multiple of %d pixels\n", pixels); return -EDOM; } /* DST width double boundary check */ switch (ctx->overlay.mode) { case FIMC_OVLY_DMA_AUTO: /* fall through */ case FIMC_OVLY_DMA_MANUAL: dstfmt = V4L2_PIX_FMT_RGB32; break; case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */ case FIMC_OVLY_NONE_MULTI_BUF: dstfmt = ctx->fbuf.fmt.pixelformat; break; default: break; } switch (dstfmt) { case V4L2_PIX_FMT_RGB32: pixels = 1; break; case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_YUYV: /* fall through */ case V4L2_PIX_FMT_UYVY: /* fall through */ case V4L2_PIX_FMT_YVYU: /* fall through */ case V4L2_PIX_FMT_VYUY: /* fall through */ case V4L2_PIX_FMT_NV16: /* fall through */ case V4L2_PIX_FMT_NV61: pixels = 2; break; case V4L2_PIX_FMT_YUV420: /* fall through */ case V4L2_PIX_FMT_NV12: /* fall through */ case V4L2_PIX_FMT_NV21: /* fall through */ case V4L2_PIX_FMT_NV12T: pixels = 8; break; default: fimc_err("Invalid color format\n"); return -EINVAL; } if (dst->width % pixels) { fimc_err("source width multiple of %d pixels\n", pixels); return -EDOM; } return 0; } static int fimc_outdev_set_scaler(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct v4l2_rect src, dst; int ret = 0; struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); memset(&src, 0, sizeof(src)); memset(&dst, 0, sizeof(dst)); fimc_outdev_calibrate_scale_info(ctrl, ctx, &src, &dst); ret = fimc_get_scaler_factor(src.width, dst.width, &ctx->sc.pre_hratio, &ctx->sc.hfactor); if (ret < 0) { fimc_err("Fail : Out of Width scale range\n"); return ret; } ret = fimc_get_scaler_factor(src.height, dst.height, &ctx->sc.pre_vratio, &ctx->sc.vfactor); if (ret < 0) { fimc_err("Fail : Out of Height scale range\n"); return ret; } ctx->sc.pre_dst_width = src.width / ctx->sc.pre_hratio; ctx->sc.pre_dst_height = src.height / ctx->sc.pre_vratio; if (pdata->hw_ver == 0x50) { ctx->sc.main_hratio = (src.width << 14) / (dst.width << ctx->sc.hfactor); ctx->sc.main_vratio = (src.height << 14) / (dst.height << ctx->sc.vfactor); } else { ctx->sc.main_hratio = (src.width << 8) / (dst.width << ctx->sc.hfactor); ctx->sc.main_vratio = (src.height << 8) / (dst.height << ctx->sc.vfactor); } fimc_dbg("pre_hratio(%d), hfactor(%d), pre_vratio(%d), vfactor(%d)\n", ctx->sc.pre_hratio, ctx->sc.hfactor, ctx->sc.pre_vratio, ctx->sc.vfactor); fimc_dbg("pre_dst_width(%d), main_hratio(%d), " "pre_dst_height(%d), main_vratio(%d)\n", ctx->sc.pre_dst_width, ctx->sc.main_hratio, ctx->sc.pre_dst_height, ctx->sc.main_vratio); ctx->sc.bypass = 0; /* Input DMA cannot support scaler bypass. */ ctx->sc.scaleup_h = (dst.width >= src.width) ? 1 : 0; ctx->sc.scaleup_v = (dst.height >= src.height) ? 1 : 0; ctx->sc.shfactor = 10 - (ctx->sc.hfactor + ctx->sc.vfactor); if (pdata->hw_ver != 0x50) { ret = fimc_outdev_check_scaler(ctrl, ctx, &src, &dst); if (ret < 0) return ret; } fimc_hwset_prescaler(ctrl, &ctx->sc); fimc_hwset_scaler(ctrl, &ctx->sc); return 0; } int fimc_outdev_set_ctx_param(struct fimc_control *ctrl, struct fimc_ctx *ctx) { int ret; if (ctrl->status == FIMC_READY_ON || ctrl->status == FIMC_STREAMON_IDLE) fimc_hwset_enable_irq(ctrl, 0, 1); fimc_outdev_set_format(ctrl, ctx); fimc_outdev_set_path(ctrl, ctx); fimc_outdev_set_rot(ctrl, ctx); fimc_outdev_set_src_dma_offset(ctrl, ctx); ret = fimc_outdev_set_src_dma_size(ctrl, ctx); if (ret < 0) return ret; fimc_outdev_set_dst_dma_offset(ctrl, ctx); ret = fimc_outdev_set_dst_dma_size(ctrl, ctx); if (ret < 0) return ret; ret = fimc_outdev_set_scaler(ctrl, ctx); if (ret < 0) return ret; return 0; } int fimc_fimd_rect(const struct fimc_control *ctrl, const struct fimc_ctx *ctx, struct v4l2_rect *fimd_rect) { switch (ctx->rotate) { case 0: fimd_rect->left = ctx->win.w.left; fimd_rect->top = ctx->win.w.top; fimd_rect->width = ctx->win.w.width; fimd_rect->height = ctx->win.w.height; break; case 90: fimd_rect->left = ctrl->fb.lcd_hres - (ctx->win.w.top + ctx->win.w.height); fimd_rect->top = ctx->win.w.left; fimd_rect->width = ctx->win.w.height; fimd_rect->height = ctx->win.w.width; break; case 180: fimd_rect->left = ctrl->fb.lcd_hres - (ctx->win.w.left + ctx->win.w.width); fimd_rect->top = ctrl->fb.lcd_vres - (ctx->win.w.top + ctx->win.w.height); fimd_rect->width = ctx->win.w.width; fimd_rect->height = ctx->win.w.height; break; case 270: fimd_rect->left = ctx->win.w.top; fimd_rect->top = ctrl->fb.lcd_vres - (ctx->win.w.left + ctx->win.w.width); fimd_rect->width = ctx->win.w.height; fimd_rect->height = ctx->win.w.width; break; default: fimc_err("Rotation degree is invalid\n"); return -EINVAL; break; } return 0; } int fimc_outdev_overlay_buf(struct file *filp, struct fimc_control *ctrl, struct fimc_ctx *ctx) { int ret = 0, i; struct fimc_overlay_buf *buf; buf = &ctx->overlay.buf; for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->overlay.req_idx = i; buf->size[i] = ctx->dst[i].length[0]; buf->phy_addr[i] = ctx->dst[i].base[0]; buf->vir_addr[i] = do_mmap(filp, 0, buf->size[i], PROT_READ|PROT_WRITE, MAP_SHARED, 0); if (buf->vir_addr[i] == -EINVAL) { fimc_err("%s: fail\n", __func__); return -EINVAL; } fimc_dbg("idx : %d, size(0x%08x), phy_addr(0x%08x), " "vir_addr(0x%08x)\n", i, buf->size[i], buf->phy_addr[i], buf->vir_addr[i]); } ctx->overlay.req_idx = -1; return ret; } int fimc_reqbufs_output(void *fh, struct v4l2_requestbuffers *b) { struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; struct fimc_ctx *ctx; struct fimc_overlay_buf *buf; struct mm_struct *mm = current->mm; enum fimc_overlay_mode mode; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; int ret = -1, i; ctx = &ctrl->out->ctx[ctx_id]; buf = &ctx->overlay.buf; mode = ctx->overlay.mode; fimc_info1("%s: called\n", __func__); if (ctx->status != FIMC_STREAMOFF) { fimc_err("FIMC is running\n"); return -EBUSY; } if (ctx->is_requested == 1 && b->count != 0) { fimc_err("Buffers were already requested\n"); return -EBUSY; } if (b->count > FIMC_OUTBUFS) { fimc_warn("The buffer count is modified by driver " "from %d to %d\n", b->count, FIMC_OUTBUFS); b->count = FIMC_OUTBUFS; } fimc_init_out_buf(ctx); ctx->is_requested = 0; if (b->count == 0) { ctrl->mem.curr = ctrl->mem.base; switch (ctx->overlay.mode) { case FIMC_OVLY_DMA_AUTO: /* fall through */ case FIMC_OVLY_DMA_MANUAL: for (i = 0; i < FIMC_OUTBUFS; i++) { if (buf->vir_addr[i]) { ret = do_munmap(mm, buf->vir_addr[i], buf->size[i]); if (ret < 0) fimc_err("%s: do_munmap fail. " "vir_addr[%d](0x%08x)\n", __func__, i, buf->vir_addr[i]); } } #ifdef CONFIG_MACH_P1 /* clear destination buffer address */ ctrl->mem.curr = ctx->dst[0].base[FIMC_ADDR_Y]; for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->dst[i].base[FIMC_ADDR_Y] = 0; ctx->dst[i].length[FIMC_ADDR_Y] = 0; ctx->dst[i].base[FIMC_ADDR_CB] = 0; ctx->dst[i].length[FIMC_ADDR_CB] = 0; ctx->dst[i].base[FIMC_ADDR_CR] = 0; ctx->dst[i].length[FIMC_ADDR_CR] = 0; } #endif break; default: break; } } else { /* initialize source buffers */ if (b->memory == V4L2_MEMORY_MMAP) { ret = fimc_outdev_set_src_buf(ctrl, ctx); ctx->overlay.req_idx = FIMC_MMAP_IDX; if (ret) return ret; } else if (b->memory == V4L2_MEMORY_USERPTR) { #ifdef CONFIG_MACH_ARIES if (mode == FIMC_OVLY_DMA_AUTO) #else // CONFIG_MACH_P1 if (mode == FIMC_OVLY_DMA_AUTO || mode == FIMC_OVLY_NOT_FIXED) #endif ctx->overlay.req_idx = FIMC_USERPTR_IDX; } #ifdef CONFIG_MACH_P1 /* initialize destination buffers */ if (mode == FIMC_OVLY_DMA_AUTO || mode == FIMC_OVLY_NOT_FIXED) { ret = fimc_outdev_set_dst_buf(ctrl, ctx); if (ret) return ret; } #endif ctx->is_requested = 1; } ctx->buf_num = b->count; return 0; } int fimc_querybuf_output(void *fh, struct v4l2_buffer *b) { struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; struct fimc_ctx *ctx; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; u32 buf_length = 0; fimc_info1("%s: called\n", __func__); ctx = &ctrl->out->ctx[ctx_id]; if (ctx->status != FIMC_STREAMOFF) { fimc_err("FIMC is running\n"); return -EBUSY; } if (b->index >= ctx->buf_num) { fimc_err("The index is out of bounds. You requested %d buffers." "But requested index is %d\n", ctx->buf_num, b->index); return -EINVAL; } b->flags = ctx->src[b->index].flags; b->m.offset = b->index * PAGE_SIZE; buf_length = ctx->src[b->index].length[FIMC_ADDR_Y] + ctx->src[b->index].length[FIMC_ADDR_CB] + ctx->src[b->index].length[FIMC_ADDR_CR]; b->length = PAGE_ALIGN(buf_length); return 0; } int fimc_g_ctrl_output(void *fh, struct v4l2_control *c) { struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev); ctx = &ctrl->out->ctx[ctx_id]; if (ctx->status != FIMC_STREAMOFF) { fimc_err("FIMC is running\n"); return -EBUSY; } switch (c->id) { case V4L2_CID_ROTATION: c->value = ctx->rotate; break; case V4L2_CID_HFLIP: if (ctx->flip & V4L2_CID_HFLIP) c->value = 1; else c->value = 0; break; case V4L2_CID_VFLIP: if (ctx->flip & V4L2_CID_VFLIP) c->value = 1; else c->value = 0; break; case V4L2_CID_OVERLAY_VADDR0: c->value = ctx->overlay.buf.vir_addr[0]; break; case V4L2_CID_OVERLAY_VADDR1: c->value = ctx->overlay.buf.vir_addr[1]; break; case V4L2_CID_OVERLAY_VADDR2: c->value = ctx->overlay.buf.vir_addr[2]; break; case V4L2_CID_OVERLAY_AUTO: if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO) c->value = 1; else c->value = 0; break; case V4L2_CID_RESERVED_MEM_BASE_ADDR: c->value = ctrl->mem.base; if (2 == ctrl->id) { /* Clearing the buffer for FIMC-2 * This is required because the same buffer is used * for both Camcorder recording and HDMI display. */ char *fimc_mem = NULL; fimc_mem = (char *) ioremap(ctrl->mem.base, \ ctrl->mem.size); if (fimc_mem) { memset(fimc_mem, 0x00, ctrl->mem.size); iounmap(fimc_mem); } } break; case V4L2_CID_FIMC_VERSION: c->value = pdata->hw_ver; break; default: fimc_err("Invalid control id: %d\n", c->id); return -EINVAL; } return 0; } static int fimc_set_dst_info(struct fimc_control *ctrl, struct fimc_ctx *ctx, struct fimc_buf *fimc_buf) { struct fimc_buf *buf; int i; for (i = 0; i < ctx->buf_num; i++) { buf = &fimc_buf[i]; ctx->dst[i].base[FIMC_ADDR_Y] = buf->base[FIMC_ADDR_Y]; ctx->dst[i].length[FIMC_ADDR_Y] = buf->length[FIMC_ADDR_Y]; ctx->dst[i].base[FIMC_ADDR_CB] = buf->base[FIMC_ADDR_CB]; ctx->dst[i].length[FIMC_ADDR_CB] = buf->length[FIMC_ADDR_CB]; ctx->dst[i].base[FIMC_ADDR_CR] = buf->base[FIMC_ADDR_CR]; ctx->dst[i].length[FIMC_ADDR_CR] = buf->length[FIMC_ADDR_CR]; } for (i = ctx->buf_num; i < FIMC_OUTBUFS; i++) { ctx->dst[i].base[FIMC_ADDR_Y] = 0; ctx->dst[i].length[FIMC_ADDR_Y] = 0; ctx->dst[i].base[FIMC_ADDR_CB] = 0; ctx->dst[i].length[FIMC_ADDR_CB] = 0; ctx->dst[i].base[FIMC_ADDR_CR] = 0; ctx->dst[i].length[FIMC_ADDR_CR] = 0; } /* for debugging */ for (i = 0; i < FIMC_OUTBUFS; i++) { fimc_dbg("dst[%d]: base[0]=0x%08x, size[0]=0x%08x\n", i, ctx->dst[i].base[0], ctx->dst[i].length[0]); fimc_dbg("dst[%d]: base[1]=0x%08x, size[1]=0x%08x\n", i, ctx->dst[i].base[1], ctx->dst[i].length[2]); fimc_dbg("dst[%d]: base[2]=0x%08x, size[2]=0x%08x\n", i, ctx->dst[i].base[1], ctx->dst[i].length[2]); } return 0; } int fimc_s_ctrl_output(struct file *filp, void *fh, struct v4l2_control *c) { struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; int ret = 0; ctx = &ctrl->out->ctx[ctx_id]; if (ctx->status != FIMC_STREAMOFF) { fimc_err("FIMC is running\n"); return -EBUSY; } switch (c->id) { case V4L2_CID_ROTATION: ret = fimc_set_rot_degree(ctrl, ctx, c->value); break; case V4L2_CID_HFLIP: if (c->value) ctx->flip |= FIMC_XFLIP; else ctx->flip &= ~FIMC_XFLIP; break; case V4L2_CID_VFLIP: if (c->value) ctx->flip |= FIMC_YFLIP; else ctx->flip &= ~FIMC_YFLIP; break; case V4L2_CID_OVERLAY_AUTO: if (c->value == 1) { ctx->overlay.mode = FIMC_OVLY_DMA_AUTO; } else { ctx->overlay.mode = FIMC_OVLY_DMA_MANUAL; ret = fimc_outdev_set_dst_buf(ctrl, ctx); fimc_outdev_overlay_buf(filp, ctrl, ctx); } break; case V4L2_CID_OVLY_MODE: ctx->overlay.mode = c->value; break; case V4L2_CID_DST_INFO: ret = fimc_set_dst_info(ctrl, ctx, (struct fimc_buf *)c->value); break; case V4L2_CID_GET_PHY_SRC_YADDR: c->value = ctx->src[c->value].base[FIMC_ADDR_Y]; break; case V4L2_CID_GET_PHY_SRC_CADDR: c->value = ctx->src[c->value].base[FIMC_ADDR_CB]; break; default: fimc_err("Invalid control id: %d\n", c->id); ret = -EINVAL; } return ret; } int fimc_cropcap_output(void *fh, struct v4l2_cropcap *a) { struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; u32 is_rotate = 0, max_w = 0, max_h = 0, pixelformat; fimc_info1("%s: called\n", __func__); ctx = &ctrl->out->ctx[ctx_id]; pixelformat = ctx->pix.pixelformat; if (ctx->status != FIMC_STREAMOFF) { fimc_err("FIMC is running\n"); return -EBUSY; } is_rotate = fimc_mapping_rot_flip(ctx->rotate, ctx->flip); switch (pixelformat) { case V4L2_PIX_FMT_NV12: /* fall through */ case V4L2_PIX_FMT_NV21: /* fall through */ case V4L2_PIX_FMT_NV12T: /* fall through */ case V4L2_PIX_FMT_YUYV: /* fall through */ case V4L2_PIX_FMT_UYVY: /* fall through */ case V4L2_PIX_FMT_YVYU: /* fall through */ case V4L2_PIX_FMT_VYUY: /* fall through */ case V4L2_PIX_FMT_NV16: /* fall through */ case V4L2_PIX_FMT_NV61: /* fall through */ case V4L2_PIX_FMT_YUV420: max_w = FIMC_SRC_MAX_W; max_h = FIMC_SRC_MAX_H; case V4L2_PIX_FMT_RGB32: /* fall through */ case V4L2_PIX_FMT_RGB565: /* fall through */ if (is_rotate & FIMC_ROT) { /* Landscape mode */ max_w = ctrl->fb.lcd_vres; max_h = ctrl->fb.lcd_hres; } else { /* Portrait */ max_w = ctrl->fb.lcd_hres; max_h = ctrl->fb.lcd_vres; } break; default: fimc_warn("Supported format : \ V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, \ V4L2_PIX_FMT_YVYU, V4L2_PIX_FMT_VYUY, \ V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV12T, \ V4L2_PIX_FMT_NV21, V4L2_PIX_FMT_RGB32, \ V4L2_PIX_FMT_RGB565\n"); fimc_warn("%s: Received pixel format = %x\n", __func__, pixelformat); return -EINVAL; } /* crop bounds */ ctx->cropcap.bounds.left = 0; ctx->cropcap.bounds.top = 0; ctx->cropcap.bounds.width = max_w; ctx->cropcap.bounds.height = max_h; /* crop default values */ ctx->cropcap.defrect.left = 0; ctx->cropcap.defrect.top = 0; ctx->cropcap.defrect.width = max_w; ctx->cropcap.defrect.height = max_h; /* crop pixel aspec values */ /* To Do : Have to modify but I don't know the meaning. */ ctx->cropcap.pixelaspect.numerator = 16; ctx->cropcap.pixelaspect.denominator = 9; a->bounds = ctx->cropcap.bounds; a->defrect = ctx->cropcap.defrect; a->pixelaspect = ctx->cropcap.pixelaspect; return 0; } int fimc_g_crop_output(void *fh, struct v4l2_crop *a) { struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; ctx = &ctrl->out->ctx[ctx_id]; fimc_info1("%s: called\n", __func__); mutex_lock(&ctrl->v4l2_lock); a->c.left = ctx->crop.left; a->c.top = ctx->crop.top; a->c.width = ctx->crop.width; a->c.height = ctx->crop.height; mutex_unlock(&ctrl->v4l2_lock); return 0; } int fimc_s_crop_output(void *fh, struct v4l2_crop *a) { struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; fimc_info1("%s: called: left(%d), top(%d), width(%d), height(%d),\n", __func__, a->c.left, a->c.top, a->c.width, a->c.height); ctx = &ctrl->out->ctx[ctx_id]; if (ctx->status != FIMC_STREAMOFF) { fimc_err("FIMC is running\n"); return -EBUSY; } /* Check arguments : widht and height */ if ((a->c.width < 0) || (a->c.height < 0)) { fimc_err("The crop rect must be bigger than 0\n"); return -EINVAL; } if ((a->c.width > FIMC_SRC_MAX_W) || (a->c.height > FIMC_SRC_MAX_H)) { fimc_err("The crop width/height must be smaller than " "%d and %d\n", FIMC_SRC_MAX_W, FIMC_SRC_MAX_H); return -EINVAL; } /* Check arguments : left and top */ if ((a->c.left < 0) || (a->c.top < 0)) { fimc_err("The crop left, top must be bigger than 0\n"); return -EINVAL; } if ((a->c.left > FIMC_SRC_MAX_W) || (a->c.top > FIMC_SRC_MAX_H)) { fimc_err("The crop left, top must be smaller than %d, %d\n", FIMC_SRC_MAX_W, FIMC_SRC_MAX_H); return -EINVAL; } if ((a->c.left + a->c.width) > FIMC_SRC_MAX_W) { fimc_err("The crop rect must be in bound rect\n"); return -EINVAL; } if ((a->c.top + a->c.height) > FIMC_SRC_MAX_H) { fimc_err("The crop rect must be in bound rect\n"); return -EINVAL; } ctx->crop.left = a->c.left; ctx->crop.top = a->c.top; ctx->crop.width = a->c.width; ctx->crop.height = a->c.height; return 0; } int fimc_streamon_output(void *fh) { struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; int ret = -1; fimc_info1("%s: called\n", __func__); ctx = &ctrl->out->ctx[ctx_id]; if (ctx->overlay.mode == FIMC_OVLY_NOT_FIXED) ctx->overlay.mode = FIMC_OVLY_MODE; #ifdef CONFIG_MACH_ARIES /* initialize destination buffers */ if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO) { ret = fimc_outdev_set_dst_buf(ctrl, ctx); if (ret) return ret; } #endif ret = fimc_outdev_check_param(ctrl, ctx); if (ret < 0) { fimc_err("Fail: fimc_outdev_check_param\n"); return ret; } ctx->status = FIMC_READY_ON; if (ctrl->status == FIMC_STREAMOFF) ctrl->status = FIMC_READY_ON; return ret; } void fimc_outdev_init_idxs(struct fimc_control *ctrl) { ctrl->out->idxs.prev.ctx = -1; ctrl->out->idxs.prev.idx = -1; ctrl->out->idxs.active.ctx = -1; ctrl->out->idxs.active.idx = -1; ctrl->out->idxs.next.ctx = -1; ctrl->out->idxs.next.idx = -1; } int fimc_streamoff_output(void *fh) { struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; int ret = -1, i = 0, off_cnt = 0; fimc_info1("%s: called\n", __func__); ctx = &ctrl->out->ctx[ctx_id]; ret = fimc_outdev_stop_streaming(ctrl, ctx); if (ret < 0) { fimc_err("Fail: fimc_outdev_stop_streaming\n"); return -EINVAL; } ret = fimc_init_in_queue(ctrl, ctx); if (ret < 0) { fimc_err("Fail: fimc_init_in_queue\n"); return -EINVAL; } ret = fimc_init_out_queue(ctrl, ctx); if (ret < 0) { fimc_err("Fail: fimc_init_out_queue\n"); return -EINVAL; } /* Make all buffers DQUEUED state. */ for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->src[i].state = VIDEOBUF_IDLE; ctx->src[i].flags = V4L2_BUF_FLAG_MAPPED; } ctx->status = FIMC_STREAMOFF; if (ctrl->out->last_ctx == ctx->ctx_num) ctrl->out->last_ctx = -1; #ifdef CONFIG_MACH_ARIES if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO) { ctrl->mem.curr = ctx->dst[0].base[FIMC_ADDR_Y]; for (i = 0; i < FIMC_OUTBUFS; i++) { ctx->dst[i].base[FIMC_ADDR_Y] = 0; ctx->dst[i].length[FIMC_ADDR_Y] = 0; ctx->dst[i].base[FIMC_ADDR_CB] = 0; ctx->dst[i].length[FIMC_ADDR_CB] = 0; ctx->dst[i].base[FIMC_ADDR_CR] = 0; ctx->dst[i].length[FIMC_ADDR_CR] = 0; } } #endif /* check all ctx to change ctrl->status from streamon to streamoff */ for (i = 0; i < FIMC_MAX_CTXS; i++) { if (ctrl->out->ctx[i].status == FIMC_STREAMOFF) off_cnt++; } if (off_cnt == FIMC_MAX_CTXS) { ctrl->status = FIMC_STREAMOFF; fimc_outdev_init_idxs(ctrl); fimc_outdev_stop_camif(ctrl); } if (ctx_id == ctrl->out->last_ctx) ctrl->out->last_ctx = -1; mutex_lock(&ctrl->lock); if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO && ctrl->fb.is_enable == 1) { fimc_info2("WIN_OFF for FIMC%d\n", ctrl->id); ret = fb_blank(registered_fb[ctx->overlay.fb_id], FB_BLANK_POWERDOWN); if (ret < 0) { fimc_err("%s: fb_blank: fb[%d] " \ "mode=FB_BLANK_POWERDOWN\n", __func__, ctx->overlay.fb_id); mutex_unlock(&ctrl->lock); return -EINVAL; } ctrl->fb.is_enable = 0; } mutex_unlock(&ctrl->lock); return 0; } int fimc_output_set_dst_addr(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx) { struct fimc_buf_set buf_set; /* destination addr */ u32 format = ctx->fbuf.fmt.pixelformat; u32 width = ctx->fbuf.fmt.width; u32 height = ctx->fbuf.fmt.height; u32 y_size = width * height; u32 c_size = y_size >> 2; int i; memset(&buf_set, 0x00, sizeof(buf_set)); if (V4L2_PIX_FMT_NV12T == format) fimc_get_nv12t_size(width, height, &y_size, &c_size, ctx->rotate); switch (format) { case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_YUYV: /* fall through */ case V4L2_PIX_FMT_UYVY: /* fall through */ case V4L2_PIX_FMT_YVYU: /* fall through */ case V4L2_PIX_FMT_VYUY: /* fall through */ if (ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF) buf_set.base[FIMC_ADDR_Y] = (dma_addr_t)ctx->fbuf.base; else buf_set.base[FIMC_ADDR_Y] = ctx->dst[idx].base[FIMC_ADDR_Y]; break; case V4L2_PIX_FMT_YUV420: if (ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF) { buf_set.base[FIMC_ADDR_Y] = (dma_addr_t)ctx->fbuf.base; buf_set.base[FIMC_ADDR_CB] = buf_set.base[FIMC_ADDR_Y] + y_size; buf_set.base[FIMC_ADDR_CR] = buf_set.base[FIMC_ADDR_CB] + c_size; } else { buf_set.base[FIMC_ADDR_Y] = ctx->dst[idx].base[FIMC_ADDR_Y]; buf_set.base[FIMC_ADDR_CB] = ctx->dst[idx].base[FIMC_ADDR_CB]; buf_set.base[FIMC_ADDR_CR] = ctx->dst[idx].base[FIMC_ADDR_CR]; } break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV12T: if (ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF) { buf_set.base[FIMC_ADDR_Y] = (dma_addr_t)ctx->fbuf.base; buf_set.base[FIMC_ADDR_CB] = buf_set.base[FIMC_ADDR_Y] + y_size; } else { buf_set.base[FIMC_ADDR_Y] = ctx->dst[idx].base[FIMC_ADDR_Y]; buf_set.base[FIMC_ADDR_CB] = ctx->dst[idx].base[FIMC_ADDR_CB]; } break; default: fimc_err("%s: Invalid pixelformt : %d\n", \ __func__, format); return -EINVAL; } for (i = 0; i < FIMC_PHYBUFS; i++) fimc_hwset_output_address(ctrl, &buf_set, i); return 0; } static int fimc_qbuf_output_single_buf(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx) { int ret = -1; fimc_outdev_set_src_addr(ctrl, ctx->src[idx].base); ret = fimc_output_set_dst_addr(ctrl, ctx, idx); if (ret < 0) { fimc_err("%s: Fail: fimc_output_set_dst_addr\n", __func__); return -EINVAL; } ctrl->out->idxs.active.idx = idx; ctrl->out->idxs.active.ctx = ctx->ctx_num; ctrl->status = FIMC_STREAMON; ctx->status = FIMC_STREAMON; ret = fimc_outdev_start_camif(ctrl); if (ret < 0) { fimc_err("Fail: fimc_start_camif\n"); return -EINVAL; } return 0; } static int fimc_qbuf_output_multi_buf(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx) { int ret = -1; fimc_outdev_set_src_addr(ctrl, ctx->src[idx].base); fimc_output_set_dst_addr(ctrl, ctx, idx); if (ret < 0) { fimc_err("%s: Fail: fimc_output_set_dst_addr\n", __func__); return -EINVAL; } ret = fimc_outdev_start_camif(ctrl); if (ret < 0) { fimc_err("Fail: fimc_start_camif\n"); return -EINVAL; } ctrl->out->idxs.active.idx = idx; ctrl->out->idxs.active.ctx = ctx->ctx_num; ctrl->status = FIMC_STREAMON; ctx->status = FIMC_STREAMON; return 0; } static int fimc_qbuf_output_dma_auto(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx) { struct fb_var_screeninfo var; struct fb_info *fbinfo; struct s3cfb_window *win; struct v4l2_rect fimd_rect; struct fimc_buf_set buf_set; /* destination addr */ int ret = -1, i; #if defined(CONFIG_VIDEO_NM6XX) struct fimc_global *fimc = get_fimc_dev(); #endif switch (ctx->status) { case FIMC_READY_ON: fbinfo = registered_fb[ctx->overlay.fb_id]; win = (struct s3cfb_window *)fbinfo->par; memcpy(&var, &fbinfo->var, sizeof(struct fb_var_screeninfo)); memset(&fimd_rect, 0, sizeof(struct v4l2_rect)); ret = fimc_fimd_rect(ctrl, ctx, &fimd_rect); if (ret < 0) { fimc_err("fimc_fimd_rect fail\n"); return -EINVAL; } /* set window path & owner */ win->path = DATA_PATH_DMA; win->owner = DMA_MEM_OTHER; win->other_mem_addr = ctx->dst[1].base[FIMC_ADDR_Y]; win->other_mem_size = ctx->dst[1].length[FIMC_ADDR_Y]; #ifdef CONFIG_MACH_ARIES /* Update WIN size */ var.xres_virtual = fimd_rect.width; var.yres_virtual = fimd_rect.height; #endif #if defined(CONFIG_VIDEO_NM6XX) if(fimc->active_camera == CAMERA_ID_MOBILETV) { var.xres = 1024; var.yres = 600; } else #endif { var.xres = fimd_rect.width; var.yres = fimd_rect.height; } /* Update WIN position */ win->x = fimd_rect.left; win->y = fimd_rect.top; var.activate = FB_ACTIVATE_FORCE; ret = fb_set_var(fbinfo, &var); if (ret < 0) { fimc_err("fb_set_var fail (ret=%d)\n", ret); return -EINVAL; } #ifdef CONFIG_MACH_P1 mutex_lock(&ctrl->lock); if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO && ctrl->fb.is_enable == 0) { ret = fb_blank(registered_fb[ctx->overlay.fb_id], FB_BLANK_UNBLANK); if (ret < 0) { fimc_warn("%s: fb_blank: fb[%d] " \ "mode=FB_BLANK_UNBLANK\n", __func__, ctx->overlay.fb_id); mutex_unlock(&ctrl->lock); } ctrl->fb.is_enable = 1; } mutex_unlock(&ctrl->lock); #endif /* fall through */ case FIMC_STREAMON_IDLE: fimc_outdev_set_src_addr(ctrl, ctx->src[idx].base); memset(&buf_set, 0x00, sizeof(buf_set)); buf_set.base[FIMC_ADDR_Y] = ctx->dst[idx].base[FIMC_ADDR_Y]; for (i = 0; i < FIMC_PHYBUFS; i++) fimc_hwset_output_address(ctrl, &buf_set, i); ret = fimc_outdev_start_camif(ctrl); if (ret < 0) { fimc_err("Fail: fimc_start_camif\n"); return -EINVAL; } ctrl->out->idxs.active.idx = idx; ctrl->out->idxs.active.ctx = ctx->ctx_num; ctrl->status = FIMC_STREAMON; ctx->status = FIMC_STREAMON; break; default: break; } return 0; } static int fimc_qbuf_output_dma_manual(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx) { struct fimc_buf_set buf_set; /* destination addr */ int ret = -1, i; fimc_outdev_set_src_addr(ctrl, ctx->src[idx].base); memset(&buf_set, 0x00, sizeof(buf_set)); buf_set.base[FIMC_ADDR_Y] = ctx->dst[idx].base[FIMC_ADDR_Y]; for (i = 0; i < FIMC_PHYBUFS; i++) fimc_hwset_output_address(ctrl, &buf_set, i); ret = fimc_outdev_start_camif(ctrl); if (ret < 0) { fimc_err("Fail: fimc_start_camif\n"); return -EINVAL; } ctrl->out->idxs.active.idx = idx; ctrl->out->idxs.active.ctx = ctx->ctx_num; ctrl->status = FIMC_STREAMON; ctx->status = FIMC_STREAMON; return 0; } static int fimc_update_in_queue_addr(struct fimc_control *ctrl, struct fimc_ctx *ctx, u32 idx, dma_addr_t *addr) { if (idx >= FIMC_OUTBUFS) { fimc_err("%s: Failed\n", __func__); return -EINVAL; } ctx->src[idx].base[FIMC_ADDR_Y] = addr[FIMC_ADDR_Y]; ctx->src[idx].base[FIMC_ADDR_CB] = addr[FIMC_ADDR_CB]; ctx->src[idx].base[FIMC_ADDR_CR] = addr[FIMC_ADDR_CR]; return 0; } int fimc_qbuf_output(void *fh, struct v4l2_buffer *b) { struct fimc_buf *buf = (struct fimc_buf *)b->m.userptr; struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; int idx, ctx_num; int ret = -1; ctx = &ctrl->out->ctx[ctx_id]; fimc_info2("ctx(%d) queued idx = %d\n", ctx->ctx_num, b->index); if (b->index >= ctx->buf_num) { fimc_err("The index is out of bounds. " "You requested %d buffers. " "But you set the index as %d\n", ctx->buf_num, b->index); return -EINVAL; } /* Check the buffer state if the state is VIDEOBUF_IDLE. */ if (ctx->src[b->index].state != VIDEOBUF_IDLE) { fimc_err("The index(%d) buffer must be dequeued state(%d)\n", b->index, ctx->src[b->index].state); return -EINVAL; } if (b->memory == V4L2_MEMORY_USERPTR) { ret = fimc_update_in_queue_addr(ctrl, ctx, b->index, buf->base); if (ret < 0) return ret; } /* Attach the buffer to the incoming queue. */ ret = fimc_push_inq(ctrl, ctx, b->index); if (ret < 0) { fimc_err("Fail: fimc_push_inq\n"); return -EINVAL; } if ((ctrl->status == FIMC_READY_ON) || (ctrl->status == FIMC_STREAMON_IDLE)) { ret = fimc_pop_inq(ctrl, &ctx_num, &idx); if (ret < 0) { fimc_err("Fail: fimc_pop_inq\n"); return -EINVAL; } fimc_clk_en(ctrl, true); ctx = &ctrl->out->ctx[ctx_num]; #ifdef CONFIG_MACH_ARIES if (ctx_num != ctrl->out->last_ctx) { #else // CONFIG_MACH_P1 if ((ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF) || (ctx->overlay.mode != FIMC_OVLY_NONE_SINGLE_BUF && ctx_num != ctrl->out->last_ctx)) { #endif ret = fimc_outdev_set_ctx_param(ctrl, ctx); if (ret < 0) { fimc_err("Fail: fimc_outdev_set_ctx_param\n"); return -EINVAL; } ctrl->out->last_ctx = ctx->ctx_num; } switch (ctx->overlay.mode) { case FIMC_OVLY_DMA_AUTO: ret = fimc_qbuf_output_dma_auto(ctrl, ctx, idx); break; case FIMC_OVLY_DMA_MANUAL: ret = fimc_qbuf_output_dma_manual(ctrl, ctx, idx); break; case FIMC_OVLY_NONE_SINGLE_BUF: ret = fimc_qbuf_output_single_buf(ctrl, ctx, idx); break; case FIMC_OVLY_NONE_MULTI_BUF: ret = fimc_qbuf_output_multi_buf(ctrl, ctx, idx); break; default: break; } } return ret; } int fimc_dqbuf_output(void *fh, struct v4l2_buffer *b) { struct fimc_ctx *ctx; struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; int idx = -1, ret = -1; ctx = &ctrl->out->ctx[ctx_id]; ret = fimc_pop_outq(ctrl, ctx, &idx); if (ret < 0) { ret = wait_event_timeout(ctrl->wq, (ctx->outq[0] != -1), FIMC_DQUEUE_TIMEOUT); if (ret == 0) { fimc_dump_context(ctrl, ctx); fimc_err("[0] out_queue is empty\n"); ctx->status = FIMC_STREAMON_IDLE; return -EAGAIN; } else if (ret == -ERESTARTSYS) { fimc_print_signal(ctrl); } else { /* Normal case */ ret = fimc_pop_outq(ctrl, ctx, &idx); if (ret < 0) { fimc_err("[1] out_queue is empty\n"); fimc_dump_context(ctrl, ctx); return -EINVAL; } } } mutex_lock(&ctrl->lock); if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO && ctrl->fb.is_enable == 0) { ret = fb_blank(registered_fb[ctx->overlay.fb_id], FB_BLANK_UNBLANK); if (ret < 0) { fimc_err("%s: fb_blank: fb[%d] " \ "mode=FB_BLANK_UNBLANK\n", __func__, ctx->overlay.fb_id); mutex_unlock(&ctrl->lock); return -EINVAL; } ctrl->fb.is_enable = 1; } mutex_unlock(&ctrl->lock); b->index = idx; fimc_info2("ctx(%d) dqueued idx = %d\n", ctx->ctx_num, b->index); return ret; } int fimc_g_fmt_vid_out(struct file *filp, void *fh, struct v4l2_format *f) { struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; struct fimc_outinfo *out = ctrl->out; struct fimc_ctx *ctx; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; int i, j; fimc_info1("%s: called\n", __func__); if (ctrl->cap) { fimc_err("%s: fimc is already used for capture mode\n", __func__); return -EINVAL; } if (!out) { out = kzalloc(sizeof(*out), GFP_KERNEL); if (!out) { fimc_err("%s: no memory for outdev info\n", __func__); return -ENOMEM; } ctrl->out = out; /* init: struct fimc_outinfo */ out->last_ctx = -1; spin_lock_init(&ctrl->out->lock_in); spin_lock_init(&ctrl->out->lock_out); for (i = 0; i < FIMC_INQUEUES; i++) { ctrl->out->inq[i].ctx = -1; ctrl->out->inq[i].idx = -1; } for (i = 0; i < FIMC_MAX_CTXS; i++) { ctx = &ctrl->out->ctx[i]; ctx->ctx_num = i; ctx->overlay.mode = FIMC_OVLY_NOT_FIXED; ctx->status = FIMC_STREAMOFF; for (j = 0; j < FIMC_OUTBUFS; j++) { ctx->inq[j] = -1; ctx->outq[j] = -1; } } ctrl->out->idxs.prev.ctx = -1; ctrl->out->idxs.prev.idx = -1; ctrl->out->idxs.active.ctx = -1; ctrl->out->idxs.active.idx = -1; ctrl->out->idxs.next.ctx = -1; ctrl->out->idxs.next.idx = -1; } f->fmt.pix = ctrl->out->ctx[ctx_id].pix; return 0; } int fimc_try_fmt_vid_out(struct file *filp, void *fh, struct v4l2_format *f) { struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; struct fimc_ctx *ctx; u32 format = f->fmt.pix.pixelformat; fimc_info1("%s: called. width(%d), height(%d)\n", __func__, f->fmt.pix.width, f->fmt.pix.height); ctx = &ctrl->out->ctx[ctx_id]; if (ctx->status != FIMC_STREAMOFF) { fimc_err("FIMC is running\n"); return -EBUSY; } /* Check pixel format */ switch (format) { case V4L2_PIX_FMT_NV16: /* fall through */ case V4L2_PIX_FMT_NV61: /* fall through */ case V4L2_PIX_FMT_NV12: /* fall through */ case V4L2_PIX_FMT_NV12T: /* fall through */ case V4L2_PIX_FMT_NV21: /* fall through */ case V4L2_PIX_FMT_YUYV: /* fall through */ case V4L2_PIX_FMT_UYVY: /* fall through */ case V4L2_PIX_FMT_YVYU: /* fall through */ case V4L2_PIX_FMT_VYUY: /* fall through */ case V4L2_PIX_FMT_RGB32: /* fall through */ case V4L2_PIX_FMT_RGB565: /* fall through */ case V4L2_PIX_FMT_YUV420: break; default: fimc_warn("Supported format : \ V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, \ V4L2_PIX_FMT_YVYU, V4L2_PIX_FMT_VYUY, \ V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV12T, \ V4L2_PIX_FMT_NV21, V4L2_PIX_FMT_RGB32, \ V4L2_PIX_FMT_RGB565\n"); fimc_warn("Changed format : V4L2_PIX_FMT_RGB32\n"); f->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; return -EINVAL; } /* Fill the return value. */ switch (format) { case V4L2_PIX_FMT_RGB32: f->fmt.pix.bytesperline = f->fmt.pix.width << 2; break; case V4L2_PIX_FMT_NV16: /* fall through */ case V4L2_PIX_FMT_NV61: /* fall through */ case V4L2_PIX_FMT_YUYV: /* fall through */ case V4L2_PIX_FMT_UYVY: /* fall through */ case V4L2_PIX_FMT_YVYU: /* fall through */ case V4L2_PIX_FMT_VYUY: /* fall through */ case V4L2_PIX_FMT_RGB565: f->fmt.pix.bytesperline = f->fmt.pix.width << 1; break; case V4L2_PIX_FMT_YUV420: /* fall through */ case V4L2_PIX_FMT_NV12: /* fall through */ case V4L2_PIX_FMT_NV21: /* fall through */ case V4L2_PIX_FMT_NV12T: f->fmt.pix.bytesperline = (f->fmt.pix.width * 3) >> 1; break; default: /* dummy value*/ f->fmt.pix.bytesperline = f->fmt.pix.width; } f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; return 0; } int fimc_s_fmt_vid_out(struct file *filp, void *fh, struct v4l2_format *f) { struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id; struct fimc_ctx *ctx; int ret = -1; fimc_info1("%s: called\n", __func__); /* Check stream status */ ctx = &ctrl->out->ctx[ctx_id]; if (ctx->status != FIMC_STREAMOFF) { fimc_err("FIMC is running\n"); return -EBUSY; } ret = fimc_try_fmt_vid_out(filp, fh, f); if (ret < 0) return ret; ctx->pix = f->fmt.pix; return ret; } int fimc_init_in_queue(struct fimc_control *ctrl, struct fimc_ctx *ctx) { struct fimc_idx swap_queue[FIMC_INQUEUES]; int swap_cnt = 0, i; unsigned long spin_flags; spin_lock_irqsave(&ctrl->out->lock_in, spin_flags); /* init incoming queue */ for (i = 0; i < FIMC_OUTBUFS; i++) ctx->inq[i] = -1; /* init common incoming queue */ for (i = 0; i < FIMC_INQUEUES; i++) { if (ctrl->out->inq[i].ctx != ctx->ctx_num) { swap_queue[swap_cnt].ctx = ctrl->out->inq[i].ctx; swap_queue[swap_cnt].idx = ctrl->out->inq[i].idx; swap_cnt++; } ctrl->out->inq[i].ctx = -1; ctrl->out->inq[i].idx = -1; } /* restore common incoming queue */ for (i = 0; i < swap_cnt; i++) { ctrl->out->inq[i].ctx = swap_queue[i].ctx; ctrl->out->inq[i].idx = swap_queue[i].idx; } spin_unlock_irqrestore(&ctrl->out->lock_in, spin_flags); return 0; } int fimc_init_out_queue(struct fimc_control *ctrl, struct fimc_ctx *ctx) { unsigned long spin_flags; int i; spin_lock_irqsave(&ctrl->out->lock_out, spin_flags); /* Init incoming queue */ for (i = 0; i < FIMC_OUTBUFS; i++) ctx->outq[i] = -1; spin_unlock_irqrestore(&ctrl->out->lock_out, spin_flags); return 0; } int fimc_push_inq(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx) { struct fimc_idx swap_common_inq[FIMC_INQUEUES]; int swap_queue[FIMC_OUTBUFS]; int i; unsigned long spin_flags; fimc_dbg("%s: idx = %d\n", __func__, idx); if (ctrl->out->inq[FIMC_INQUEUES-1].idx != -1) { fimc_err("FULL: common incoming queue\n"); return -EBUSY; } spin_lock_irqsave(&ctrl->out->lock_in, spin_flags); /* ctx own incoming queue */ /* Backup original queue */ for (i = 0; i < FIMC_OUTBUFS; i++) swap_queue[i] = ctx->inq[i]; /* Attach new idx */ ctx->inq[0] = idx; ctx->src[idx].state = VIDEOBUF_QUEUED; ctx->src[idx].flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED; /* Shift the origonal queue */ for (i = 1; i < FIMC_OUTBUFS; i++) ctx->inq[i] = swap_queue[i-1]; /* Common incoming queue */ /* Backup original queue */ for (i = 0; i < FIMC_INQUEUES; i++) { swap_common_inq[i].ctx = ctrl->out->inq[i].ctx; swap_common_inq[i].idx = ctrl->out->inq[i].idx; } /* Attach new idx */ ctrl->out->inq[0].ctx = ctx->ctx_num; ctrl->out->inq[0].idx = idx; /* Shift the origonal queue */ for (i = 1; i < FIMC_INQUEUES; i++) { ctrl->out->inq[i].ctx = swap_common_inq[i-1].ctx; ctrl->out->inq[i].idx = swap_common_inq[i-1].idx; } spin_unlock_irqrestore(&ctrl->out->lock_in, spin_flags); return 0; } int fimc_pop_inq(struct fimc_control *ctrl, int *ctx_num, int *idx) { struct fimc_ctx *ctx; unsigned long spin_flags; int i, ret = 0; int ctx_idx = -1; spin_lock_irqsave(&ctrl->out->lock_in, spin_flags); /* find valid index from common incoming queue */ for (i = (FIMC_INQUEUES-1); i >= 0; i--) { if (ctrl->out->inq[i].ctx != -1) { *ctx_num = ctrl->out->inq[i].ctx; *idx = ctrl->out->inq[i].idx; ctrl->out->inq[i].ctx = -1; ctrl->out->inq[i].idx = -1; break; } } /* common incoming queue is empty. */ if (i < 0) { spin_unlock_irqrestore(&ctrl->out->lock_in, spin_flags); return -EINVAL; } /* find valid index from incoming queue. */ ctx = &ctrl->out->ctx[*ctx_num]; for (i = (FIMC_OUTBUFS-1); i >= 0; i--) { if (ctx->inq[i] != -1) { ctx_idx = ctx->inq[i]; ctx->inq[i] = -1; ctx->src[ctx_idx].state = VIDEOBUF_ACTIVE; ctx->src[ctx_idx].flags = V4L2_BUF_FLAG_MAPPED; break; } } if (*idx != ctx_idx) fimc_err("common inq(%d) vs inq(%d) mismatch\n", *idx, ctx_idx); /* incoming queue is empty. */ if (i < 0) ret = -EINVAL; else fimc_dbg("%s: index = %d\n", __func__, *idx); spin_unlock_irqrestore(&ctrl->out->lock_in, spin_flags); return ret; } int fimc_push_outq(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx) { unsigned long spin_flags; int swap_queue[FIMC_OUTBUFS]; int i; fimc_dbg("%s: index = %d\n", __func__, idx); spin_lock_irqsave(&ctrl->out->lock_out, spin_flags); /* Backup original queue */ for (i = 0; i < FIMC_OUTBUFS; i++) swap_queue[i] = ctx->outq[i]; /* Attach new index */ ctx->outq[0] = idx; ctx->src[idx].state = VIDEOBUF_DONE; ctx->src[idx].flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; /* Shift the origonal queue */ for (i = 1; i < FIMC_OUTBUFS; i++) ctx->outq[i] = swap_queue[i-1]; spin_unlock_irqrestore(&ctrl->out->lock_out, spin_flags); return 0; } int fimc_pop_outq(struct fimc_control *ctrl, struct fimc_ctx *ctx, int *idx) { unsigned long spin_flags; int i, ret = 0; spin_lock_irqsave(&ctrl->out->lock_out, spin_flags); /* Find last valid idx in outgoing queue. */ for (i = (FIMC_OUTBUFS-1); i >= 0; i--) { if (ctx->outq[i] != -1) { *idx = ctx->outq[i]; ctx->outq[i] = -1; ctx->src[*idx].state = VIDEOBUF_IDLE; ctx->src[*idx].flags = V4L2_BUF_FLAG_MAPPED; break; } } /* outgoing queue is empty. */ if (i < 0) { ret = -EINVAL; fimc_dbg("%s: outgoing queue : %d, %d, %d\n", __func__, ctx->outq[0], ctx->outq[1], ctx->outq[2]); } else fimc_dbg("%s: idx = %d\n", __func__, *idx); spin_unlock_irqrestore(&ctrl->out->lock_out, spin_flags); return ret; } void fimc_dump_context(struct fimc_control *ctrl, struct fimc_ctx *ctx) { int i = 0; fimc_err("ctx%d, ctrl->status: %d, ctx->status: %d\n", ctx->ctx_num, ctrl->status, ctx->status); for (i = 0; i < FIMC_INQUEUES; i++) fimc_err("ctrl->inq[%d]: ctx(%d) idx(%d)\n", i, ctrl->out->inq[i].ctx, ctrl->out->inq[i].idx); for (i = 0; i < FIMC_OUTBUFS; i++) fimc_err("inq[%d] = %d\n", i, ctx->inq[i]); for (i = 0; i < FIMC_OUTBUFS; i++) fimc_err("outq[%d] = %d\n", i, ctx->outq[i]); fimc_err("state : prev.ctx(%d), prev.idx(%d) " "active.ctx(%d), active.idx(%d) " "next.ctx(%d), next.idx(%d)\n", ctrl->out->idxs.prev.ctx, ctrl->out->idxs.prev.idx, ctrl->out->idxs.active.ctx, ctrl->out->idxs.active.idx, ctrl->out->idxs.next.ctx, ctrl->out->idxs.next.idx); } void fimc_print_signal(struct fimc_control *ctrl) { if (signal_pending(current)) { fimc_dbg(".pend=%.8lx shpend=%.8lx\n", current->pending.signal.sig[0], current->signal->shared_pending.signal.sig[0]); } else { fimc_dbg(":pend=%.8lx shpend=%.8lx\n", current->pending.signal.sig[0], current->signal->shared_pending.signal.sig[0]); } }