static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; struct vb2_queue *vq; struct fimc_frame *frame; struct v4l2_pix_format_mplane *pix; int i, ret = 0; ret = fimc_try_fmt_mplane(ctx, f); if (ret) return ret; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (vb2_is_busy(vq)) { v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type); return -EBUSY; } if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) frame = &ctx->s_frame; else frame = &ctx->d_frame; pix = &f->fmt.pix_mp; frame->fmt = fimc_find_format(&pix->pixelformat, NULL, get_m2m_fmt_flags(f->type), 0); if (!frame->fmt) return -EINVAL; /* Update RGB Alpha control state and value range */ fimc_alpha_ctrl_update(ctx); for (i = 0; i < frame->fmt->colplanes; i++) { frame->payload[i] = (pix->width * pix->height * frame->fmt->depth[i]) / 8; } fimc_fill_frame(frame, f); ctx->scaler.enabled = 1; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); else fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); return 0; }
static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct fimc_fmt *fmt; fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), f->index); if (!fmt) return -EINVAL; strncpy(f->description, fmt->name, sizeof(f->description) - 1); f->pixelformat = fmt->fourcc; return 0; }
static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct fimc_fmt *fmt; fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M, f->index); if (!fmt) return -EINVAL; strncpy(f->description, fmt->name, sizeof(f->description) - 1); f->pixelformat = fmt->fourcc; if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8) f->flags |= V4L2_FMT_FLAG_COMPRESSED; return 0; }
static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) { struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_variant *variant = fimc->variant; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; u32 max_w, mod_x, mod_y; if (!IS_M2M(f->type)) return -EINVAL; dbg("w: %d, h: %d", pix->width, pix->height); fmt = fimc_find_format(&pix->pixelformat, NULL, get_m2m_fmt_flags(f->type), 0); if (WARN(fmt == NULL, "Pixel format lookup failed")) return -EINVAL; if (pix->field == V4L2_FIELD_ANY) pix->field = V4L2_FIELD_NONE; else if (pix->field != V4L2_FIELD_NONE) return -EINVAL; if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { max_w = variant->pix_limit->scaler_dis_w; mod_x = ffs(variant->min_inp_pixsize) - 1; } else { max_w = variant->pix_limit->out_rot_dis_w; mod_x = ffs(variant->min_out_pixsize) - 1; } if (tiled_fmt(fmt)) { mod_x = 6; /* 64 x 32 pixels tile */ mod_y = 5; } else { if (variant->min_vsize_align == 1) mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; else mod_y = ffs(variant->min_vsize_align) - 1; } v4l_bound_align_image(&pix->width, 16, max_w, mod_x, &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); return 0; }
static int fimc_m2m_set_default_format(struct fimc_ctx *ctx) { struct v4l2_pix_format_mplane pixm = { .pixelformat = V4L2_PIX_FMT_RGB32, .width = 800, .height = 600, .plane_fmt[0] = { .bytesperline = 800 * 4, .sizeimage = 800 * 4 * 600, }, }; struct fimc_fmt *fmt; fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0); if (!fmt) return -EINVAL; __set_frame_format(&ctx->s_frame, fmt, &pixm); __set_frame_format(&ctx->d_frame, fmt, &pixm); return 0; }
static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_fmt *fmt; struct vb2_queue *vq; struct fimc_frame *frame; int ret; ret = fimc_try_fmt_mplane(ctx, f); if (ret) return ret; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (vb2_is_busy(vq)) { v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type); return -EBUSY; } if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) frame = &ctx->s_frame; else frame = &ctx->d_frame; fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL, get_m2m_fmt_flags(f->type), 0); if (!fmt) return -EINVAL; __set_frame_format(frame, fmt, &f->fmt.pix_mp); /* Update RGB Alpha control state and value range */ fimc_alpha_ctrl_update(ctx); return 0; }
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *allocators[]) { const struct v4l2_pix_format_mplane *pixm = NULL; struct fimc_ctx *ctx = vq->drv_priv; struct fimc_frame *frame = &ctx->d_frame; struct fimc_fmt *fmt = frame->fmt; unsigned long wh; int i; if (pfmt) { pixm = &pfmt->fmt.pix_mp; fmt = fimc_find_format(&pixm->pixelformat, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M, -1); wh = pixm->width * pixm->height; } else { wh = frame->f_width * frame->f_height; } if (fmt == NULL) return -EINVAL; *num_planes = fmt->memplanes; for (i = 0; i < fmt->memplanes; i++) { unsigned int size = (wh * fmt->depth[i]) / 8; if (pixm) sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); else sizes[i] = max_t(u32, size, frame->payload[i]); allocators[i] = ctx->fimc_dev->alloc_ctx; } return 0; }
/** * fimc_pipeline_try_format - negotiate and/or set formats at pipeline * elements * @ctx: FIMC capture context * @tfmt: media bus format to try/set on subdevs * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output) * @set: true to set format on subdevs, false to try only */ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, struct v4l2_mbus_framefmt *tfmt, struct fimc_fmt **fmt_id, bool set) { struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_subdev *sd = fimc->pipeline.sensor; struct v4l2_subdev *csis = fimc->pipeline.csis; struct v4l2_subdev_format sfmt; struct v4l2_mbus_framefmt *mf = &sfmt.format; struct fimc_fmt *ffmt = NULL; int ret, i = 0; if (WARN_ON(!sd || !tfmt)) return -EINVAL; memset(&sfmt, 0, sizeof(sfmt)); sfmt.format = *tfmt; sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; while (1) { ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL, FMT_FLAGS_CAM, i++); if (ffmt == NULL) { /* * Notify user-space if common pixel code for * host and sensor does not exist. */ return -EINVAL; } mf->code = tfmt->code = ffmt->mbus_code; ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); if (ret) return ret; if (mf->code != tfmt->code) { mf->code = 0; continue; } if (mf->width != tfmt->width || mf->height != tfmt->height) { u32 fcc = ffmt->fourcc; tfmt->width = mf->width; tfmt->height = mf->height; ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, NULL, &fcc, FIMC_SD_PAD_SOURCE); if (ffmt && ffmt->mbus_code) mf->code = ffmt->mbus_code; if (mf->width != tfmt->width || mf->height != tfmt->height) continue; tfmt->code = mf->code; } if (csis) ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt); if (mf->code == tfmt->code && mf->width == tfmt->width && mf->height == tfmt->height) break; } if (fmt_id && ffmt) *fmt_id = ffmt; *tfmt = *mf; dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt); return 0; }
static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, u32 *width, u32 *height, u32 *code, u32 *fourcc, int pad) { bool rotation = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; struct samsung_fimc_variant *var = fimc->variant; struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *dst = &ctx->d_frame; u32 depth, min_w, max_w, min_h, align_h = 3; u32 mask = FMT_FLAGS_CAM; struct fimc_fmt *ffmt; /* Color conversion from/to JPEG is not supported */ if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE && fimc_fmt_is_jpeg(ctx->s_frame.fmt->color)) *code = V4L2_MBUS_FMT_JPEG_1X8; if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK) mask |= FMT_FLAGS_M2M; ffmt = fimc_find_format(fourcc, code, mask, 0); if (WARN_ON(!ffmt)) return NULL; if (code) *code = ffmt->mbus_code; if (fourcc) *fourcc = ffmt->fourcc; if (pad == FIMC_SD_PAD_SINK) { max_w = fimc_fmt_is_jpeg(ffmt->color) ? pl->scaler_dis_w : pl->scaler_en_w; /* Apply the camera input interface pixel constraints */ v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4, height, max_t(u32, *height, 32), FIMC_CAMIF_MAX_HEIGHT, fimc_fmt_is_jpeg(ffmt->color) ? 3 : 1, 0); return ffmt; } /* Can't scale or crop in transparent (JPEG) transfer mode */ if (fimc_fmt_is_jpeg(ffmt->color)) { *width = ctx->s_frame.f_width; *height = ctx->s_frame.f_height; return ffmt; } /* Apply the scaler and the output DMA constraints */ max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w; min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize; min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize; if (var->min_vsize_align == 1 && !rotation) align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1; depth = fimc_get_format_depth(ffmt); v4l_bound_align_image(width, min_w, max_w, ffs(var->min_out_pixsize) - 1, height, min_h, FIMC_CAMIF_MAX_HEIGHT, align_h, 64/(ALIGN(depth, 8))); dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d", pad, code ? *code : 0, *width, *height, dst->f_width, dst->f_height); return ffmt; }