static int fimc_querycap(struct file *filp, void *fh, struct v4l2_capability *cap) { struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; fimc_info1("%s: called\n", __func__); strcpy(cap->driver, "SEC FIMC Driver"); strlcpy(cap->card, ctrl->vd->name, sizeof(cap->card)); sprintf(cap->bus_info, "FIMC AHB-bus"); cap->version = 0; #ifdef CONFIG_SLP #ifdef CONFIG_SLP_DMABUF cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_SHARE_FD); #else cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING | V4L2_CAP_SHARE_PADDR); #endif #else cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING); #endif return 0; }
int fimc_g_fmt_vid_overlay(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; ctx = &ctrl->out->ctx[ctx_id]; fimc_info1("%s: called\n", __func__); f->fmt.win = ctx->win; return 0; }
int fimc_try_fmt_overlay(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 is_rotate = 0; ctx = &ctrl->out->ctx[ctx_id]; fimc_info1("%s: top(%d) left(%d) width(%d) height(%d)\n", __func__, f->fmt.win.w.top, f->fmt.win.w.left, f->fmt.win.w.width, f->fmt.win.w.height); if (ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF || (ctx->overlay.mode == FIMC_OVLY_NONE_MULTI_BUF)) return 0; /* Check Overlay Size : Overlay size must be smaller than LCD size. */ is_rotate = fimc_mapping_rot_flip(ctx->rotate, ctx->flip); if (is_rotate & FIMC_ROT) { /* Landscape mode */ if (f->fmt.win.w.width > ctrl->fb.lcd_vres) { fimc_warn("The width is changed %d -> %d\n", f->fmt.win.w.width, ctrl->fb.lcd_vres); f->fmt.win.w.width = ctrl->fb.lcd_vres; } if (f->fmt.win.w.height > ctrl->fb.lcd_hres) { fimc_warn("The height is changed %d -> %d\n", f->fmt.win.w.height, ctrl->fb.lcd_hres); f->fmt.win.w.height = ctrl->fb.lcd_hres; } } else { /* Portrait mode */ if (f->fmt.win.w.width > ctrl->fb.lcd_hres) { fimc_warn("The width is changed %d -> %d\n", f->fmt.win.w.width, ctrl->fb.lcd_hres); f->fmt.win.w.width = ctrl->fb.lcd_hres; } if (f->fmt.win.w.height > ctrl->fb.lcd_vres) { fimc_warn("The height is changed %d -> %d\n", f->fmt.win.w.height, ctrl->fb.lcd_vres); f->fmt.win.w.height = ctrl->fb.lcd_vres; } } return 0; }
static int fimc_querycap(struct file *filp, void *fh, struct v4l2_capability *cap) { struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl; fimc_info1("%s: called\n", __func__); strcpy(cap->driver, "SEC FIMC Driver"); strlcpy(cap->card, ctrl->vd->name, sizeof(cap->card)); sprintf(cap->bus_info, "FIMC AHB-bus"); cap->version = 0; cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING); return 0; }
int fimc_s_fmt_vid_overlay(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; ctx = &ctrl->out->ctx[ctx_id]; fimc_info1("%s: called\n", __func__); switch (ctx->status) { case FIMC_STREAMON: ret = fimc_check_pos(ctrl, ctx, f); if (ret < 0) { fimc_err("When FIMC is running, " "you can only move the position.\n"); return -EBUSY; } ret = fimc_try_fmt_overlay(filp, fh, f); if (ret < 0) return ret; ctx->win = f->fmt.win; fimc_change_fifo_position(ctrl, ctx); break; case FIMC_STREAMOFF: ret = fimc_try_fmt_overlay(filp, fh, f); if (ret < 0) return ret; ctx->win = f->fmt.win; break; default: fimc_err("FIMC is running\n"); fimc_err("%s::FIMC is running(%d)\n", __func__, ctx->status); return -EBUSY; } return ret; }
int fimc_g_fbuf(struct file *filp, void *fh, struct v4l2_framebuffer *fb) { 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 bpp = 1, format; ctx = &ctrl->out->ctx[ctx_id]; fimc_info1("%s: called\n", __func__); fb->capability = ctx->fbuf.capability; fb->flags = 0; fb->base = ctx->fbuf.base; fb->fmt.width = ctx->fbuf.fmt.width; fb->fmt.height = ctx->fbuf.fmt.height; fb->fmt.pixelformat = ctx->fbuf.fmt.pixelformat; format = ctx->fbuf.fmt.pixelformat; switch (format) { case V4L2_PIX_FMT_YUV420: /* fall through */ case V4L2_PIX_FMT_YVU420: /* fall through */ case V4L2_PIX_FMT_NV12: bpp = 1; break; case V4L2_PIX_FMT_RGB565: bpp = 2; break; case V4L2_PIX_FMT_RGB32: bpp = 4; break; } ctx->fbuf.fmt.bytesperline = fb->fmt.width * bpp; fb->fmt.bytesperline = ctx->fbuf.fmt.bytesperline; fb->fmt.sizeimage = ctx->fbuf.fmt.sizeimage; fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; fb->fmt.priv = 0; return 0; }
int fimc_s_fbuf(struct file *filp, void *fh, struct v4l2_framebuffer *fb) { 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 bpp = 1; u32 format = fb->fmt.pixelformat; ctx = &ctrl->out->ctx[ctx_id]; fimc_info1("%s: called. width(%d), height(%d)\n", __func__, fb->fmt.width, fb->fmt.height); ctx->fbuf.capability = V4L2_FBUF_CAP_EXTERNOVERLAY; ctx->fbuf.flags = 0; ctx->fbuf.base = fb->base; if (ctx->overlay.mode == FIMC_OVLY_NONE_MULTI_BUF) { ctx->fbuf.fmt.width = fb->fmt.width; ctx->fbuf.fmt.height = fb->fmt.height; ctx->fbuf.fmt.pixelformat = fb->fmt.pixelformat; switch (format) { case V4L2_PIX_FMT_YUV420: /* fall through */ case V4L2_PIX_FMT_NV12: bpp = 1; break; case V4L2_PIX_FMT_RGB565: bpp = 2; break; case V4L2_PIX_FMT_RGB32: bpp = 4; break; } ctx->fbuf.fmt.bytesperline = fb->fmt.width * bpp; ctx->fbuf.fmt.sizeimage = fb->fmt.sizeimage; ctx->fbuf.fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; ctx->fbuf.fmt.priv = 0; } else if (fb->base) { ctx->fbuf.fmt.width = fb->fmt.width; ctx->fbuf.fmt.height = fb->fmt.height; ctx->fbuf.fmt.pixelformat = fb->fmt.pixelformat; switch (format) { case V4L2_PIX_FMT_YUV420: /* fall through */ case V4L2_PIX_FMT_NV12: bpp = 1; break; case V4L2_PIX_FMT_RGB565: bpp = 2; break; case V4L2_PIX_FMT_RGB32: bpp = 4; break; } ctx->fbuf.fmt.bytesperline = fb->fmt.width * bpp; ctx->fbuf.fmt.sizeimage = fb->fmt.sizeimage; ctx->fbuf.fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; ctx->fbuf.fmt.priv = 0; ctx->overlay.mode = FIMC_OVLY_NONE_SINGLE_BUF; } else { int i; unsigned int bits_per_pixel = 0; struct s3cfb_window *win = NULL; ctx->overlay.fb_id = -1; for (i = 0; i < num_registered_fb; i++) { win = (struct s3cfb_window *)registered_fb[i]->par; if (win->id == ctrl->id) { ctx->overlay.fb_id = i; bits_per_pixel = registered_fb[i]->var.bits_per_pixel; fimc_info2("%s: overlay.fb_id = %d\n", __func__, ctx->overlay.fb_id); break; } } if (-1 == ctx->overlay.fb_id) { fimc_err("%s: fb[%d] is not registered. " \ "must be registered for overlay\n", __func__, ctrl->id); return -1; } if (1 == win->enabled) { fimc_err("%s: fb[%d] is already being used. " \ "must be not used for overlay\n", __func__, ctrl->id); return -1; } ctx->overlay.mode = FIMC_OVLY_NOT_FIXED; switch (ctx->rotate) { case 0: case 180: ctx->fbuf.fmt.width = ctrl->fb.lcd_hres; ctx->fbuf.fmt.height = ctrl->fb.lcd_vres; break; case 90: case 270: ctx->fbuf.fmt.width = ctrl->fb.lcd_vres; ctx->fbuf.fmt.height = ctrl->fb.lcd_hres; break; } if (bits_per_pixel == 32) ctx->fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB32; else ctx->fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB565; } return 0; }
/* * Assign v4l2 device and subdev to fimc * it is called per every fimc ctrl registering */ static int fimc_configure_subdev(struct platform_device *pdev, int id) { struct s3c_platform_fimc *pdata; struct s3c_platform_camera *cam; struct i2c_adapter *i2c_adap; struct i2c_board_info *i2c_info; struct v4l2_subdev *sd; struct fimc_control *ctrl; unsigned short addr; char *name; ctrl = get_fimc_ctrl(id); pdata = to_fimc_plat(&pdev->dev); cam = pdata->camera[id]; /* Subdev registration */ if (cam) { i2c_adap = i2c_get_adapter(cam->i2c_busnum); if (!i2c_adap) { fimc_info1("subdev i2c_adapter missing-skip " "registration\n"); } i2c_info = cam->info; if (!i2c_info) { fimc_err("%s: subdev i2c board info missing\n", __func__); return -ENODEV; } name = i2c_info->type; if (!name) { fimc_info1("subdev i2c dirver name missing-skip " "registration\n"); return -ENODEV; } addr = i2c_info->addr; if (!addr) { fimc_info1("subdev i2c address missing-skip " "registration\n"); return -ENODEV; } /* * NOTE: first time subdev being registered, * s_config is called and try to initialize subdev device * but in this point, we are not giving MCLK and power to subdev * so nothing happens but pass platform data through */ sd = v4l2_i2c_new_subdev_board(&ctrl->v4l2_dev, i2c_adap, name, i2c_info, &addr); if (!sd) { fimc_err("%s: v4l2 subdev board registering failed\n", __func__); } /* Assign camera device to fimc */ fimc_dev->camera[cam->id] = cam; /* Assign subdev to proper camera device pointer */ fimc_dev->camera[cam->id]->sd = sd; } return 0; }
static int fimc_release(struct file *filp) { struct fimc_control *ctrl = filp->private_data; struct s3c_platform_fimc *pdata; struct fimc_overlay_buf *buf; struct mm_struct *mm = current->mm; int ret = 0, i; ctrl->mem.curr = ctrl->mem.base; atomic_dec(&ctrl->in_use); filp->private_data = NULL; pdata = to_fimc_plat(ctrl->dev); /* FIXME: turning off actual working camera */ if (ctrl->cam) { /* shutdown the MCLK */ clk_disable(ctrl->cam->clk); /* shutdown */ if (ctrl->cam->cam_power) ctrl->cam->cam_power(0); /* should be initialized at the next open */ ctrl->cam->initialized = 0; } if (ctrl->cap) { for (i = 0; i < FIMC_CAPBUFS; i++) { fimc_dma_free(ctrl, &ctrl->cap->bufs[i], 0); fimc_dma_free(ctrl, &ctrl->cap->bufs[i], 1); } kfree(ctrl->cap); ctrl->cap = NULL; } if (ctrl->out) { if (ctrl->status != FIMC_STREAMOFF) { ret = fimc_outdev_stop_streaming(ctrl); if (ret < 0) fimc_err("Fail: fimc_stop_streaming\n"); ctrl->status = FIMC_STREAMOFF; } buf = &ctrl->out->overlay.buf; 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\n", __func__); } } kfree(ctrl->out); ctrl->out = NULL; } if (pdata->clk_off) pdata->clk_off(to_platform_device(ctrl->dev), ctrl->clk); fimc_info1("%s: successfully released\n", __func__); return 0; }
static int __devinit fimc_probe(struct platform_device *pdev) { struct s3c_platform_fimc *pdata; struct fimc_control *ctrl; int ret; if (!fimc_dev) { fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL); if (!fimc_dev) { dev_err(&pdev->dev, "%s: not enough memory\n", __func__); goto err_fimc; } } ctrl = fimc_register_controller(pdev); if (!ctrl) { printk(KERN_ERR "%s: cannot register fimc\n", __func__); goto err_fimc; } pdata = to_fimc_plat(&pdev->dev); if (pdata->cfg_gpio) pdata->cfg_gpio(pdev); /* V4L2 device-subdev registration */ ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev); if (ret) { fimc_err("%s: v4l2 device register failed\n", __func__); goto err_v4l2; } /* things to initialize once */ if (!fimc_dev->initialized) { ret = fimc_init_global(pdev); if (ret) goto err_global; } /* v4l2 subdev configuration */ ret = fimc_configure_subdev(pdev, ctrl->id); if (ret) { fimc_err("%s: subdev[%d] registering failed\n", __func__, ctrl->id); } /* video device register */ ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id); if (ret) { fimc_err("%s: cannot register video driver\n", __func__); goto err_global; } video_set_drvdata(ctrl->vd, ctrl); ret = device_create_file(&(pdev->dev), &dev_attr_log_level); if (ret < 0) fimc_err("failed to add sysfs entries\n"); fimc_info1("controller %d registered successfully\n", ctrl->id); return 0; err_global: clk_disable(ctrl->clk); clk_put(ctrl->clk); err_v4l2: fimc_unregister_controller(pdev); err_fimc: return -EINVAL; }
int fimc_s_fbuf(struct file *filp, void *fh, struct v4l2_framebuffer *fb) { 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 bpp = 1; u32 format = fb->fmt.pixelformat; ctx = &ctrl->out->ctx[ctx_id]; fimc_info1("%s: called. width(%d), height(%d)\n", __func__, fb->fmt.width, fb->fmt.height); ctx->fbuf.capability = V4L2_FBUF_CAP_EXTERNOVERLAY; ctx->fbuf.flags = 0; ctx->fbuf.base = fb->base; if (ctx->overlay.mode == FIMC_OVLY_NONE_MULTI_BUF) { ctx->fbuf.fmt.width = fb->fmt.width; ctx->fbuf.fmt.height = fb->fmt.height; ctx->fbuf.fmt.pixelformat = fb->fmt.pixelformat; switch (format) { case V4L2_PIX_FMT_NV21: /* fall through */ case V4L2_PIX_FMT_YUV420: /* fall through */ case V4L2_PIX_FMT_NV12: bpp = 1; break; case V4L2_PIX_FMT_RGB565: bpp = 2; break; case V4L2_PIX_FMT_RGB32: bpp = 4; break; } ctx->fbuf.fmt.bytesperline = fb->fmt.width * bpp; ctx->fbuf.fmt.sizeimage = fb->fmt.sizeimage; ctx->fbuf.fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; ctx->fbuf.fmt.priv = 0; } else if (fb->base) { ctx->fbuf.fmt.width = fb->fmt.width; ctx->fbuf.fmt.height = fb->fmt.height; ctx->fbuf.fmt.pixelformat = fb->fmt.pixelformat; switch (format) { case V4L2_PIX_FMT_NV21: /* fall through */ case V4L2_PIX_FMT_YUV420: /* fall through */ case V4L2_PIX_FMT_YVU420: /* fall through */ case V4L2_PIX_FMT_NV12: bpp = 1; break; case V4L2_PIX_FMT_RGB565: bpp = 2; break; case V4L2_PIX_FMT_RGB32: bpp = 4; break; } ctx->fbuf.fmt.bytesperline = fb->fmt.width * bpp; ctx->fbuf.fmt.sizeimage = fb->fmt.sizeimage; ctx->fbuf.fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; ctx->fbuf.fmt.priv = 0; ctx->overlay.mode = FIMC_OVLY_NONE_SINGLE_BUF; } else { ctx->overlay.mode = FIMC_OVLY_NOT_FIXED; } return 0; }